Control a servo from a web page

Drag a slider on your phone and a servo turns to the same angle: a web page sends values to the ESP32 over Wi-Fi.
  • Level:Intermediate
  • Duration: 45 min
  • Ages 12+
  • Platform: ESP32

Tools for this lesson

Helpful for this build. Open a tool to see tips and more projects that use it.

Control a servo from a web page – EEMENTOR STEM™ lesson

In the relay lesson, your phone switched something on and off. Now it controls a position. The ESP32 serves a page with a slider; as you drag it, the page sends the angle to the ESP32, and the servo follows. The same idea is used to steer robot arms and camera mounts from a browser.

What you'll learn

  • How to control a servo on the ESP32 with the ESP32Servo library
  • How a web page can send a value to the ESP32 without reloading
  • How to read a value from a web address
  • Why the servo should run on 5 V

What you need

  • ESP32 board, expansion board and USB cable (ESP32 IoT Starter Kit)
  • Servo motor (ESP32 IoT Starter Kit)
  • A 2.4 GHz Wi-Fi network
  • Breadboard and jumper wires (ESP32 IoT Starter Kit)

Wiring

Breadboard wiring diagram: Control a servo from a web page
Breadboard view. Check each connection against the wiring table below.
From To
Servo brown / black wire GND
Servo red wire 5V
Servo orange / yellow wire ESP32 GPIO 13

The sketch

Install the ESP32Servo library (by Kevin Harrington) in the Arduino IDE. The standard Servo library doesn't work on the ESP32.

Replace YOUR_WIFI_NAME and YOUR_WIFI_PASSWORD with your own network details (2.4 GHz only). Open the Serial Monitor at 115200 baud to see the address, then open it in a browser on a phone or computer on the same Wi-Fi.

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

const char* WIFI_NAME = "YOUR_WIFI_NAME";
const char* WIFI_PASSWORD = "YOUR_WIFI_PASSWORD";
const int SERVO_PIN = 13;

WebServer server(80);
Servo servo;
int angle = 90;

const char PAGE[] = R"rawliteral(
<!DOCTYPE html><html><head><meta charset='utf-8'>
<meta name='viewport' content='width=device-width, initial-scale=1'><title>Servo</title></head>
<body style='font-family:sans-serif;padding:16px;text-align:center'>
<h1>Servo control</h1>
<p style='font-size:48px;margin:10px' id='value'>90°</p>
<input type='range' id='slider' min='0' max='180' value='90' style='width:90%'>
<script>
const slider = document.getElementById('slider');
let waiting = false;
slider.oninput = function () {
  document.getElementById('value').textContent = slider.value + '°';
  if (waiting) return;                 // don't flood the ESP32
  waiting = true;
  fetch('/set?angle=' + slider.value).finally(function () { waiting = false; });
};
</script></body></html>
)rawliteral";

void setup() {
  Serial.begin(115200);
  servo.setPeriodHertz(50);
  servo.attach(SERVO_PIN, 500, 2400);
  servo.write(angle);

  WiFi.begin(WIFI_NAME, WIFI_PASSWORD);
  while (WiFi.status() != WL_CONNECTED) delay(500);
  Serial.print("Open http://");
  Serial.println(WiFi.localIP());

  server.on("/", []() { server.send(200, "text/html", PAGE); });
  server.on("/set", []() {
    angle = constrain(server.arg("angle").toInt(), 0, 180);
    servo.write(angle);
    server.send(200, "text/plain", String(angle));
  });
  server.begin();
}

void loop() {
  server.handleClient();
}

How it works

The page uses an HTML range slider. Every time it moves, a few lines of JavaScript call fetch('/set?angle=120'), which asks the ESP32 for that address in the background. On the ESP32, server.arg("angle") reads the number after angle=, constrain() makes sure it stays between 0 and 180 whatever is sent, and servo.write() moves the servo there.

A slider produces many values per second while you drag it. The waiting flag sends the next value only when the ESP32 has answered the last one, so requests don't pile up and the servo follows smoothly. A servo pulls bursts of current when it moves, which is why it runs from the 5V pin rather than from 3V3.

Try this next

  • Add buttons on the page for fixed positions: 0°, 90° and 180°.
  • Add a second servo on GPIO 14 and a second slider for a pan-and-tilt mount.
  • Show the current angle when the page opens by sending it from the ESP32.

Troubleshooting

  • It never connects to Wi-Fi: check the network name and password (both are case-sensitive) and make sure the network is 2.4 GHz.
  • The page doesn't open: the phone or computer must be on the same Wi-Fi as the ESP32, not on mobile data.
  • The ESP32 restarts when the servo moves: use a good USB port or a powered hub, and check that the servo's red wire is on 5V.
  • The servo buzzes at the ends: it is pushing against its stop. Use a range of 10 to 170 instead.
  • Compile error about Servo.h or ESP32Servo.h: install the ESP32Servo library.

Build it yourself

Everything used in this lesson comes in one kit, with parts tested by our engineers.

ESP32 IoT Starter Kit | Arduino IDE Compatible | Free Tutorials $57.00

Shop the kit