Week 15, Interface Programming¶
Assignment¶
Group assignment¶
- Compare as many tool options as possible.
- Document your work on the group work page and reflect on your individual page what you learned.
See group page
Individual assignment¶
- Write an application for the embedded board that you made. that interfaces a user with an input and/or output device(s)
Individual Work¶
Control Board¶
My final work project will rely upon a stepper motor to control card distribution. I have fabricated boards which control an Nema 17 stepper motor driven by an ESP32 microprocessor. For Interface Programming, I will set up a Wifi interface to control the stepper motor.
The Nema 17 motor needs higher voltage than can be provided from the microprocessor, so I have made a breakout board which outputs 12V. This is based on the QC Hack Power Board. I have my own re-design for this. Notably, I added pin headers so I could place that in a socket.
Schematic and PCB for the QC power hack board
The KiCad files for this QC hack board.
To include in other boards, I made a footprint in KiCad for placement of a socket.
I also made a board which has a socket for a Xiao microprocessor, a socket for the QC hack, and a socket for an A4988 motor controller. The A4988 is used to control the motor. The board also has a 4 pin connector for a Nema 17 step motor. In addition, the board has some additional features for the final project, but are not used for this step now.
My motor control board is documented in the final project pages.
PCB for the motor control
The KiCad files for the control board.
These were fabricated on the Roland MX-20 mill, and the components were soldered on.
Control board with A4988 and power board connected to stepper motor
Programming¶
Since I wanted to control via WiFi, I followed an online tutorial at Last Minute Engineers. This has some basic code which I modified to fix my pin numbers for the LED.
#include <WiFi.h>
#include <WebServer.h>
/* Put your SSID & Password */
const char* ssid = "ESP32"; // Enter SSID here
const char* password = "12345678"; //Enter Password here
/* Put IP Address details */
IPAddress local_ip(192, 168, 1, 1);
IPAddress gateway(192, 168, 1, 1);
IPAddress subnet(255, 255, 255, 0);
WebServer server(80);
uint8_t LEDpin = D10;
bool LEDstatus = LOW;
void setup() {
Serial.begin(115200);
pinMode(LEDpin, OUTPUT);
WiFi.softAP(ssid, password);
WiFi.softAPConfig(local_ip, gateway, subnet);
delay(100);
server.on("/", handle_OnConnect);
server.on("/ledon", handle_ledon);
server.on("/ledoff", handle_ledoff);
server.onNotFound(handle_NotFound);
server.begin();
Serial.println("HTTP server started");
Serial.println(WiFi.localIP());
}
void loop() {
server.handleClient();
if (LEDstatus) {
digitalWrite(LEDpin, HIGH);
} else {
digitalWrite(LEDpin, LOW);
}
}
void handle_OnConnect() {
LEDstatus = LOW;
Serial.println("GPIO4 Status: OFF | GPIO5 Status: OFF");
server.send(200, "text/html", SendHTML(LEDstatus));
}
void handle_ledon() {
LEDstatus = HIGH;
Serial.println("GPIO4 Status: ON");
server.send(200, "text/html", SendHTML(true));
}
void handle_ledoff() {
LEDstatus = LOW;
Serial.println("GPIO4 Status: OFF");
server.send(200, "text/html", SendHTML(false));
}
void handle_NotFound() {
server.send(404, "text/plain", "Not found");
}
String SendHTML(uint8_t ledstat) {
String ptr = "<!DOCTYPE html> <html>\n";
ptr += "<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\">\n";
ptr += "<title>LED Control</title>\n";
ptr += "<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}\n";
ptr += "body{margin-top: 50px;} h1 {color: #444444;margin: 50px auto 30px;} h3 {color: #444444;margin-bottom: 50px;}\n";
ptr += ".button {display: block;width: 80px;background-color: #3498db;border: none;color: white;padding: 13px 30px;text-decoration: none;font-size: 25px;margin: 0px auto 35px;cursor: pointer;border-radius: 4px;}\n";
ptr += ".button-on {background-color: #3498db;}\n";
ptr += ".button-on:active {background-color: #2980b9;}\n";
ptr += ".button-off {background-color: #34495e;}\n";
ptr += ".button-off:active {background-color: #2c3e50;}\n";
ptr += "p {font-size: 14px;color: #888;margin-bottom: 10px;}\n";
ptr += "</style>\n";
ptr += "</head>\n";
ptr += "<body>\n";
ptr += "<h1>ESP32 Web Server</h1>\n";
ptr += "<h3>Using Access Point(AP) Mode</h3>\n";
if (ledstat) {
ptr += "<p>LED Status: ON</p><a class=\"button button-off\" href=\"/ledoff\">OFF</a>\n";
} else {
ptr += "<p>LED Status: OFF</p><a class=\"button button-on\" href=\"/ledon\">ON</a>\n";
}
ptr += "</body>\n";
ptr += "</html>\n";
return ptr;
}
I compiled this with the Arduino IDE and uploaded it to the ESP. I then connnected to the ESP32 network, and browsed to 192.168.1.1. This gave a web page where I could turn the LED off and on.
The other key step is controlling the motor. The following code turns the motor repeatedly backwards and forwards one cycle.
// Define pin connections & motor's steps per revolution
// Pins for original board
// Dir: D8 (GPIO7), Step: D7 (GPIO44)
// Steps per revolution: 3200 (due to microstepping)
const int dirPin = 7;
const int stepPin = 44;
const int stepsPerRevolution = 3200;
void setup()
{
// Declare pins as Outputs
pinMode(stepPin, OUTPUT);
pinMode(dirPin, OUTPUT);
}
void loop()
{
// Set motor direction clockwise
digitalWrite(dirPin, HIGH);
// Spin motor slowly
for(int x = 0; x < stepsPerRevolution; x++)
{
digitalWrite(stepPin, HIGH);
delayMicroseconds(200);
digitalWrite(stepPin, LOW);
delayMicroseconds(200);
}
delay(1000); // Wait a second
// Set motor direction counterclockwise
digitalWrite(dirPin, LOW);
// Spin motor quickly
for(int x = 0; x < stepsPerRevolution; x++)
{
digitalWrite(stepPin, HIGH);
delayMicroseconds(100);
digitalWrite(stepPin, LOW);
delayMicroseconds(100);
}
delay(1000); // Wait a second
}
These are the two key functions I want to control. Basically, I will take the logic to control the motors (without repeating cycles), and use that in the logic for the web server control.
All together¶
My real world example is a meeting monitor. I work from home, and my daughter often bursts into my office when I am in a meeting. I’d like to control the motor which holds a sign indicating whether I am in a meeting or free. For this logic, I will turn the motor one-half turn, and the motor has a two-sided sign which shows the current status. The following code creates an interface to change the meeting status.
#include <WiFi.h>
#include <WebServer.h>
/* Put your SSID & Password */
const char* ssid = "ESP32"; // Enter SSID here
const char* password = "12345678"; //Enter Password here
// Pins for motorcontrol
const int dirPin = 7;
const int stepPin = 44;
const int halfTurn = 1600;
/* Put IP Address details */
IPAddress local_ip(192, 168, 1, 1);
IPAddress gateway(192, 168, 1, 1);
IPAddress subnet(255, 255, 255, 0);
WebServer server(80);
bool meetingSTATUS = LOW;
void setup() {
Serial.begin(115200);
// Declare motor pins as Outputs
pinMode(stepPin, OUTPUT);
pinMode(dirPin, OUTPUT);
WiFi.softAP(ssid, password);
WiFi.softAPConfig(local_ip, gateway, subnet);
delay(100);
server.on("/", handle_OnConnect);
server.on("/meeting",handle_meeting);
server.on("/free", handle_free);
server.onNotFound(handle_NotFound);
server.begin();
Serial.println("HTTP server started");
Serial.println(WiFi.localIP());
}
void loop() {
server.handleClient();
}
void handle_OnConnect() {
Serial.println("Connecting");
server.send(200, "text/html", SendHTML(meetingSTATUS));
}
void handle_meeting() {
meetingSTATUS = HIGH;
Serial.println("Meeting: ON");
digitalWrite(dirPin, LOW);
for(int x = 0; x < halfTurn; x++)
{
digitalWrite(stepPin, HIGH);
delayMicroseconds(100);
digitalWrite(stepPin, LOW);
delayMicroseconds(100);
}
server.send(200, "text/html", SendHTML(true));
}
void handle_free() {
meetingSTATUS = LOW;
Serial.println("Meeting: OFF");
digitalWrite(dirPin, HIGH);
for(int x = 0; x < halfTurn; x++)
{
digitalWrite(stepPin, HIGH);
delayMicroseconds(100);
digitalWrite(stepPin, LOW);
delayMicroseconds(100);
}
server.send(200, "text/html", SendHTML(false));
}
void handle_NotFound() {
server.send(404, "text/plain", "Not found");
}
String SendHTML( uint8_t meeting) {
String ptr = "<!DOCTYPE html> <html>\n";
ptr += "<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\">\n";
ptr += "<title>Meeting Monitor</title>\n";
ptr += "<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}\n";
ptr += "body{margin-top: 50px;} h1 {color: #444444;margin: 50px auto 30px;} h3 {color: #444444;margin-bottom: 50px;}\n";
ptr += ".button {display: block;width: 100px;background-color: #3498db;border: none;color: white;padding: 13px 30px;text-decoration: none;font-size: 25px;margin: 0px auto 35px;cursor: pointer;border-radius: 4px;}\n";
ptr += ".button-on {background-color: #3498db;}\n";
ptr += ".button-on:active {background-color: #2980b9;}\n";
ptr += ".button-off {background-color: #34495e;}\n";
ptr += ".button-off:active {background-color: #2c3e50;}\n";
ptr += "p {font-size: 14px;color: #888;margin-bottom: 10px;}\n";
ptr += "</style>\n";
ptr += "</head>\n";
ptr += "<body>\n";
ptr += "<h1>Meeting Monitor</h1>\n";
ptr += "<h3>Change the meeting sign</h3>\n";
if (meeting) {
ptr += "<h1 style=\"color:red;\">CURRENTLY IN MEETING</h1><a class=\"button button-off\" href=\"/free\">SET TO FREE</a>\n";
} else {
ptr += "<h1 style=\"color:green;\">CURRENTLY FREE</h1><a class=\"button button-on\" href=\"/meeting\">SET TO MEETING</a>\n";
}
ptr += "</body>\n";
ptr += "</html>\n";
return ptr;
}
In creating this code, I made the HTML use color and larger text than the example.
Here is a video of the set up in action. I push the button when a meeting starts, and then afterwards can mark myself as free.
Hero video
With this tool, my daughter might not burst into my office when I am in a meeting.