14. Interface and Application Programming¶
So for this week I focused on making of the interface for my servo. I used the pcb I designed with my ESP32-C3. Here you can read about my PCB DESIGN and here you can read about its production PCB PRODUCTION. So for the application I used html.
Group Assignment¶
In the group work, we looked at a way to connect embedded systems to interfaces. This helpes with understanding how to connect the interface to the hardware.
Individual assignment¶
So for this assignment I used html to make my interface. I thought it would be easy to make a controller with the browser so this week is kinda mixed with my Networking and Communications week where I made my board connect to the other board wirelessly through internet.
I used this code that I made with the help of chatgpt to make my already existing slider from week 11 to look better and work properly for 3 servos connected on one board. first is the Link for the week where the servo connects wirelessly with a minimal interface Then the chatgpt prompt. Link to the exchange
#include <WiFi.h>
#include <WebServer.h>
#include <ESP32Servo.h>
const char* ssid = "ESP32_Servo";
const char* password = "12345678";
WebServer server(80);
// Servos
Servo servo1;
Servo servo2;
Servo servo3;
Servo servo4;
Servo servo5;
Servo servo6;
// Servo pins
const int servoPin1 = D0;
const int servoPin2 = D1;
const int servoPin3 = D2;
const int servoPin4 = D3;
const int servoPin5 = D6;
const int servoPin6 = D7;
// Stepper pins
const int STEP_PIN = D9;
const int DIR_PIN = D10;
// Joint positions
int joint1Pos = 90;
int joint2Pos = 90;
int joint3Pos = 90;
// Stepper control
bool stepperRunning = false;
bool stepPinState = LOW;
unsigned long lastStepMicros = 0;
const unsigned long stepIntervalMicros = 2500; // bigger = slower
const unsigned long stepPulseWidthMicros = 5;
void setMirroredJoint(Servo &a, Servo &b, int angle) {
angle = constrain(angle, 0, 180);
a.write(angle);
b.write(180 - angle);
}
void handleRoot() {
String page = R"rawliteral(
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
* { box-sizing: border-box; }
body {
margin: 0;
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
font-family: Arial, sans-serif;
background: linear-gradient(135deg, #0f172a, #1e293b);
color: white;
}
.container {
width: 92%;
max-width: 700px;
background: rgba(255,255,255,0.08);
backdrop-filter: blur(12px);
padding: 24px;
border-radius: 20px;
box-shadow: 0 10px 30px rgba(0,0,0,0.35);
}
h1 {
margin: 0 0 20px 0;
font-size: 28px;
}
.card {
background: rgba(255,255,255,0.05);
border-radius: 16px;
padding: 18px;
margin-bottom: 16px;
}
.label {
font-size: 16px;
margin-bottom: 10px;
color: #cbd5e1;
}
.value {
margin-top: 10px;
font-size: 28px;
font-weight: bold;
color: #38bdf8;
}
.slider {
width: 100%;
height: 12px;
border-radius: 10px;
background: #334155;
outline: none;
-webkit-appearance: none;
}
.slider::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 28px;
height: 28px;
border-radius: 50%;
background: #38bdf8;
cursor: pointer;
}
.slider::-moz-range-thumb {
width: 28px;
height: 28px;
border-radius: 50%;
background: #38bdf8;
border: none;
cursor: pointer;
}
.buttons {
display: flex;
gap: 12px;
flex-wrap: wrap;
}
.btn {
flex: 1;
min-width: 130px;
padding: 16px;
border: none;
border-radius: 14px;
font-size: 18px;
font-weight: bold;
color: white;
background: #2563eb;
box-shadow: 0 8px 20px rgba(37,99,235,0.35);
}
.btn.down {
background: #7c3aed;
box-shadow: 0 8px 20px rgba(124,58,237,0.35);
}
.hint {
margin-top: 10px;
color: #cbd5e1;
font-size: 14px;
}
</style>
</head>
<body>
<div class="container">
<h1>ESP32 Control Panel</h1>
<div class="card">
<div class="label">Joint 1</div>
<input type="range" min="0" max="180" value="90" class="slider" id="joint1" oninput="setJoint(1, this.value)">
<div class="value"><span id="joint1Val">90</span>°</div>
</div>
<div class="card">
<div class="label">Joint 2</div>
<input type="range" min="0" max="180" value="90" class="slider" id="joint2" oninput="setJoint(2, this.value)">
<div class="value"><span id="joint2Val">90</span>°</div>
</div>
<div class="card">
<div class="label">Joint 3</div>
<input type="range" min="0" max="180" value="90" class="slider" id="joint3" oninput="setJoint(3, this.value)">
<div class="value"><span id="joint3Val">90</span>°</div>
</div>
<div class="card">
<div class="label">Stepper</div>
<div class="buttons">
<button class="btn" onpointerdown="stepUp()" onpointerup="stopStepper()" onpointerleave="stopStepper()">Up</button>
<button class="btn down" onpointerdown="stepDown()" onpointerup="stopStepper()" onpointerleave="stopStepper()">Down</button>
</div>
<div class="hint">Hold a button to move the stepper. Release to stop.</div>
</div>
</div>
<script>
function setJoint(num, val) {
document.getElementById("joint" + num + "Val").textContent = val;
fetch("/joint" + num + "?value=" + val).catch(err => console.log(err));
}
function stepUp() {
fetch("/step?dir=up").catch(err => console.log(err));
}
function stepDown() {
fetch("/step?dir=down").catch(err => console.log(err));
}
function stopStepper() {
fetch("/step?dir=stop").catch(err => console.log(err));
}
</script>
</body>
</html>
)rawliteral";
server.send(200, "text/html", page);
}
void handleJoint1() {
if (server.hasArg("value")) {
joint1Pos = constrain(server.arg("value").toInt(), 0, 180);
setMirroredJoint(servo1, servo2, joint1Pos);
Serial.print("Joint 1: ");
Serial.println(joint1Pos);
Serial.print("Servo 2 mirrored: ");
Serial.println(180 - joint1Pos);
}
server.send(200, "text/plain", "OK");
}
void handleJoint2() {
if (server.hasArg("value")) {
joint2Pos = constrain(server.arg("value").toInt(), 0, 180);
setMirroredJoint(servo3, servo4, joint2Pos);
Serial.print("Joint 2: ");
Serial.println(joint2Pos);
Serial.print("Servo 4 mirrored: ");
Serial.println(180 - joint2Pos);
}
server.send(200, "text/plain", "OK");
}
void handleJoint3() {
if (server.hasArg("value")) {
joint3Pos = constrain(server.arg("value").toInt(), 0, 180);
setMirroredJoint(servo5, servo6, joint3Pos);
Serial.print("Joint 3: ");
Serial.println(joint3Pos);
Serial.print("Servo 6 mirrored: ");
Serial.println(180 - joint3Pos);
}
server.send(200, "text/plain", "OK");
}
void handleStepper() {
if (server.hasArg("dir")) {
String dir = server.arg("dir");
if (dir == "up") {
stepperRunning = true;
digitalWrite(DIR_PIN, HIGH);
Serial.println("Stepper UP");
} else if (dir == "down") {
stepperRunning = true;
digitalWrite(DIR_PIN, LOW);
Serial.println("Stepper DOWN");
} else if (dir == "stop") {
stepperRunning = false;
digitalWrite(STEP_PIN, LOW);
stepPinState = LOW;
Serial.println("Stepper STOP");
}
}
server.send(200, "text/plain", "OK");
}
void runStepper() {
if (!stepperRunning) return;
unsigned long now = micros();
if (!stepPinState) {
if (now - lastStepMicros >= stepIntervalMicros) {
digitalWrite(STEP_PIN, HIGH);
stepPinState = HIGH;
lastStepMicros = now;
}
} else {
if (now - lastStepMicros >= stepPulseWidthMicros) {
digitalWrite(STEP_PIN, LOW);
stepPinState = LOW;
lastStepMicros = now;
}
}
}
void setup() {
Serial.begin(115200);
ESP32PWM::allocateTimer(0);
ESP32PWM::allocateTimer(1);
ESP32PWM::allocateTimer(2);
ESP32PWM::allocateTimer(3);
servo1.setPeriodHertz(50);
servo2.setPeriodHertz(50);
servo3.setPeriodHertz(50);
servo4.setPeriodHertz(50);
servo5.setPeriodHertz(50);
servo6.setPeriodHertz(50);
servo1.attach(servoPin1, 1000, 2000);
servo2.attach(servoPin2, 1000, 2000);
servo3.attach(servoPin3, 1000, 2000);
servo4.attach(servoPin4, 1000, 2000);
servo5.attach(servoPin5, 1000, 2000);
servo6.attach(servoPin6, 1000, 2000);
setMirroredJoint(servo1, servo2, 90);
setMirroredJoint(servo3, servo4, 90);
setMirroredJoint(servo5, servo6, 90);
pinMode(STEP_PIN, OUTPUT);
pinMode(DIR_PIN, OUTPUT);
digitalWrite(STEP_PIN, LOW);
digitalWrite(DIR_PIN, HIGH);
WiFi.mode(WIFI_AP);
WiFi.softAP(ssid, password);
IPAddress IP = WiFi.softAPIP();
Serial.print("Open browser at: http://");
Serial.println(IP);
server.on("/", handleRoot);
server.on("/joint1", handleJoint1);
server.on("/joint2", handleJoint2);
server.on("/joint3", handleJoint3);
server.on("/step", handleStepper);
server.begin();
Serial.println("Web server started");
}
void loop() {
server.handleClient();
runStepper();
}
This is the final code that i used in my final project. This is what the inteface will look like.