14. Interface and Application

Overview

This week, I made a interface for controling my servos for my final project using an httml web interface on the esp32c3.

Group Work

This week me and my partner Andrew compared to interfacing softwares. I chose to look at MIT app. It was a simple program for making interfaces but I could see myself implementing my web server into it for a more semless controler.

Learing about interfaces

I have already had previous experience using the esp32 web servers since I had used one for a porject in school a few months prior. Heres a link to that experiance. Because of that, I was able to make an interface quickly for controlling a servo. I used my output board to test out my interface. I wanted to try out processing origanlly to try and learn something new but when I tried to download it this popped up:

Because of that I decided to make another web server on httml using the esp32c3 on my output board. Heres what the board looked like for refrence:

Heres what the interface looks like:

Code

#include <WiFi.h>
#include <ESP32Servo.h>

const char* ssid = "YOUR_SSID";
const char* password = "YOUR_PASSWORD";

Servo myServo;
const int servoPin = 3;  // Servo on GPIO 3
WiFiServer server(80);

void setup() {
Serial.begin(115200);
WiFi.begin(ssid, password);

    while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.println("Connecting to WiFi...");
}

Serial.print("Connected. IP: ");
Serial.println(WiFi.localIP());

myServo.setPeriodHertz(50);  // Standard 50Hz servo
myServo.attach(servoPin, 500, 2400);  // Calibrate for your servo
myServo.write(90);  // Start centered

server.begin();
}

void wagTail() {
for (int i = 0; i < 2; i++) {
myServo.write(60);  // Move to one side
delay(200);
myServo.write(120);  // Move to the other side
delay(200);
}
myServo.write(90);  // Return to center
}

void loop() {
WiFiClient client = server.available();
if (client) {
String req = client.readStringUntil('\r');
client.flush();

if (req.indexOf("angle=") != -1) {
  int angleIndex = req.indexOf("angle=") + 6;
  int angle = req.substring(angleIndex).toInt();
  angle = constrain(angle, 0, 180);
  myServo.write(angle);  // Move servo to mapped angle
}

if (req.indexOf("/wag") != -1) {
  wagTail();
}

String html = R"rawliteral(
  <html><head>
  <style>
    body { text-align: center; font-family: sans-serif; }
    #joystick {
      width: 200px; height: 200px;
      border: 2px solid #555;
      border-radius: 50%;
      margin: 30px auto;
      position: relative;
      touch-action: none;
    }
    #stick {
      width: 50px; height: 50px;
      background: #2196F3;
      border-radius: 50%;
      position: absolute;
      top: 75px; left: 75px;
      touch-action: none;
      cursor: pointer;
    }
    button {
      padding: 10px 20px;
      font-size: 16px;
      margin-top: 20px;
    }
  </style>
  </head><body>
    <h2>ESP32-C3 Servo Joystick</h2>
    <div id="joystick"><div id="stick"></div></div>
    <button onclick="fetch('/wag')">🐶 Wag Tail</button>

    <script>
      const joystick = document.getElementById("joystick");
      const stick = document.getElementById("stick");
      const radius = 100;
      const center = { x: radius, y: radius };
      let dragging = false;

      function updateStick(clientX, clientY) {
        const rect = joystick.getBoundingClientRect();
        const dx = clientX - rect.left - center.x;
        const dy = clientY - rect.top - center.y;
        const dist = Math.min(Math.sqrt(dx * dx + dy * dy), radius - 25);
        const angleRad = Math.atan2(dy, dx);
        const angle360 = (angleRad * 180 / Math.PI + 360) % 360;
        const mapped = Math.round(angle360 / 2);  // 360° → 180°
        fetch("/?angle=" + mapped);

        const x = center.x + dist * Math.cos(angleRad) - 25;
        const y = center.y + dist * Math.sin(angleRad) - 25;
        stick.style.left = `${x}px`;
        stick.style.top = `${y}px`;
      }

      function resetStick() {
        stick.style.left = "75px";
        stick.style.top = "75px";
        fetch("/?angle=90");
      }

      // Mouse
      stick.addEventListener("mousedown", e => { dragging = true; e.preventDefault(); });
      document.addEventListener("mousemove", e => { if (dragging) updateStick(e.clientX, e.clientY); });
      document.addEventListener("mouseup", () => { if (dragging) { dragging = false; resetStick(); } });

      // Touch
      stick.addEventListener("touchstart", e => { dragging = true; e.preventDefault(); });
      document.addEventListener("touchmove", e => { if (dragging) updateStick(e.touches[0].clientX, e.touches[0].clientY); });
      document.addEventListener("touchend", () => { if (dragging) { dragging = false; resetStick(); } });
    </script>
  </body></html>
)rawliteral";

client.print("HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n");
client.print(html);
client.stop();
}
}

Video

Test with Simple Code

Final Code Test

Reflection

This week was very easy for me. I didnt really have any trouble with setting up the interface. If time permits, I hope to make a better interface using the ESP RainMaker interface instead since it can be made much cleaner and polished but first I need to get the core concept of my final done.