ESP32 Tutorial: Servo Motor Control via WebSocket
Abstract
This article outlines how to create a responsive, real-time control system for an SG90 Servo Motor using the ESP32 and WebSockets. The ESP32 hosts a basic web interface that establishes a persistent WebSocket connection with the client browser. Users can input a desired angular position (in degrees, typically 0 to 180) into the webpage. The server receives this command instantly via the WebSocket, and the servo is driven to the exact position using a simplified Pulse Width Modulation (PWM) approach, providing a true Internet of Things (IoT) precision control application.
1. Introduction to Real-Time Servo Control
The Internet of Things (IoT) is defined by its ability to bridge the gap between the digital and physical worlds, often relying on instant, precise control of electromechanical components. A fundamental element in countless robotics and automation projects is the Servo Motor, which provides reliable, angular positioning.
This article introduces an elegant and powerful solution for real-time, high-precision control of a standard SG90 Servo Motor using the highly capable ESP32 microcontroller. Moving beyond traditional, often laggy, HTTP/AJAX polling methods, we will leverage WebSockets. This protocol establishes a persistent, two-way communication channel between the ESP32 (acting as a server) and a client browser.
The result is a highly responsive Angle Positioner. Users will interact with a simple web interface, where a desired angle (from $0^{\circ}$ to $180^{\circ}$) is sent instantly to the ESP32. The microcontroller then translates this command into a precise Pulse Width Modulation (PWM) signal using the simplified ESP32Servo library, driving the motor to the exact position without noticeable delay. This project is an essential step for any embedded developer looking to implement true, low-latency, IoT precision control.
2. Prerequisites and Wiring
The SG90 Servo motor simplifies the hardware requirements compared to a stepper motor with its driver. You can often power small SG90 servos directly from the ESP32’s 5V pin, but an external supply is recommended for sustained use or multiple servos.
2.1 Required Components
- ESP32 Development Board
- Servo Motor: (e.g., SG90 or similar 0 to 180 servo)
- Optional but Recommended: External Power Supply (5V) for the servo.
- VIN can be used, but it is expected to see disconnection during motor operation
2.2 Wiring (SG90 Servo Example)
The servo has three wires: Power (Red), Ground (Brown/Black), and Signal (Orange/Yellow).
ESP32 Pin | SG90 Servo Wire | Note |
GPIO 13 (PWM Pin) | Signal (Orange/Yellow) | This pin sends the PWM control signal. |
GND | GND (Brown/Black) | Must be shared with ESP32 and external supply GND. |
5V/VIN | VCC (Red) | Power supply (Use an external 5V supply for better performance/safety). |
2.3 Required Libraries
Install the following libraries:
- WiFi.h (Built-in)
- WebServer.h (Built-in)
- ESP32Servo.h (By Kevinl)
3. The ESP32 Arduino Sketch
The primary change is replacing the complex stepper motor library and pathfinding logic with the simple, standard servo library.
3.1 Libraries and Definitions
The ESP32Servo library is typically used for simplified servo control on the ESP32.
#include
#include
#include
#include
#define SERVO_PIN 13 // Set this to the GPIO you used for the SG90 Signal wire
Servo servoMotor; // Create a Servo object
3.2 Setup and Initialization
The servo needs to be attached to the chosen PWM pin.
void setup() {
// ... WiFi and WebSocket Server Initialization ...
// Attach the servo object to the pin
servoMotor.attach(SERVO_PIN);
// For precise control over the 0-180 range, you can use:
// servoMotor.attach(SERVO_PIN, 500, 2400); // min_pulse_us, max_pulse_us
}
3.3 WebSocket Command Handling
The WebSocket event handler remains similar, but the action is much simpler: the received angle value is converted to an integer and sent directly to the servo’s write() function.
void webSocketEvent(uint8_t num, WStype_t type, uint8_t* payload, size_t length) {
if (type == WStype_TEXT) {
String message = String((char*)payload);
// Check for the "ANGLE:" command
if (message.startsWith("ANGLE:")) {
String angleStr = message.substring(6); // Get the angle value part
int targetAngle = angleStr.toInt();
// Safety check: SG90 typically moves between 0 and 180 degrees
if (targetAngle >= 0 && targetAngle <= 180) {
servoMotor.write(targetAngle);
Serial.printf("Set servo to %d degrees\n", targetAngle);
}
}
}
}
4. Source Code
The entire code for an easier copy and paste:
#include
#include
#include
#include "image_data.h"
// ----------------------
// 1. WiFi & Server Setup
// ----------------------
// IMPORTANT: Update with your network credentials
const char* ssid = "SSID";
const char* password = "PASSWORD";
WebServer server(80);
// ----------------------
// 2. Servo Motor Setup
// ----------------------
#define SERVO_PIN 13 // GPIO pin connected to the SG90 signal wire (e.g., GPIO 13)
Servo servoMotor;
int currentAngle = 90; // Initial angle (center position)
// Handler for serving the raw PNG image data
void handleImage() {
// Set the Content-Type header to tell the browser it's a PNG image
server.sendHeader("Content-Encoding", "identity"); // Prevent compression issues
// Send the raw binary data
server.send_P(200, "image/png", (const char*)logo_hacker_png, logo_hacker_png_len);
}
// ----------------------
// 3. HTML Content Function
// ----------------------
// This function generates the HTML with embedded styles and JavaScript for AJAX control.
String getHtmlPage() {
String html = R"rawliteral(
SG90 Servo Angle Positioner (HTTP/AJAX)
SG90 Servo Angle Positioner
Control the angle from 0 to 180 using HTTP/AJAX.
)rawliteral";
return html;
}
// ----------------------
// 4. Server Endpoint Handlers
// ----------------------
// Handler for the root page (serves the HTML)
void handleRoot() {
server.send(200, "text/html", getHtmlPage());
}
// Handler for setting the servo angle via HTTP GET parameter
void handleSetAngle() {
if (server.hasArg("angle")) {
String angleStr = server.arg("angle");
int angle = angleStr.toInt();
// Validate angle range for SG90
if (angle >= 0 && angle <= 180) {
servoMotor.write(angle);
currentAngle = angle;
server.send(200, "text/plain", "Angle set successfully.");
Serial.printf("Set servo to %d degrees\n", angle);
} else {
server.send(400, "text/plain", "ERROR: Angle must be between 0 and 180.");
}
} else {
server.send(400, "text/plain", "ERROR: Missing 'angle' parameter.");
}
}
// Handler to return the current angle
void handleGetAngle() {
server.send(200, "text/plain", String(currentAngle));
}
// ----------------------
// 5. Setup and Loop
// ----------------------
void setup() {
Serial.begin(115200);
// 1. Servo Motor Initialization
servoMotor.setPeriodHertz(50); // Standard 50Hz for most servos
// Attach the servo object to the pin
servoMotor.attach(SERVO_PIN, 500, 2400);
servoMotor.write(currentAngle); // Move to initial position (90°)
// 2. WiFi Connection
WiFi.begin(ssid, password);
Serial.print("Connecting to WiFi...");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nWiFi connected.");
Serial.print("Web Server IP: ");
Serial.println(WiFi.localIP());
// 3. Setup Web Server Handlers
server.on("/", handleRoot);
server.on("/setAngle", handleSetAngle);
server.on("/getAngle", handleGetAngle);
// Route for the image request
server.on("/logo.png", HTTP_GET, handleImage);
server.begin();
}
void loop() {
// Handle web client requests
server.handleClient();
}
The core of this project lies in the seamless integration of the ESP32’s networking capabilities with the specialized ESP32Servo library. The logic is divided into three main components: Servo Setup, Network Initialization, and Command Handling.
4.1 Servo Motor Initialization
The servo motor is controlled using a dedicated library, simplifying the complex PWM control.
Header Inclusion: The line
#include <ESP32Servo.h>imports the necessary functions.Object and Pin Definition:
#define SERVO_PIN 13 // GPIO pin connected to the SG90 signal wire
Servo servoMotor;This defines the GPIO 13 as the control pin and creates a
Servoobject namedservoMotor.Setup: In the
setup()function:servoMotor.setPeriodHertz(50);
servoMotor.attach(SERVO_PIN, 500, 2400);
servoMotor.write(currentAngle);setPeriodHertz(50): Sets the standard PWM frequency to 50 Hz, which is typical for most hobby servos.attach(SERVO_PIN, 500, 2400): Links theservoMotorobject to the GPIO pin. The 500us to 2400us pulse width range fine-tunes the physical 0 to 180 degree movement.write(currentAngle): Sends the motor to its initial 90 degree (center) position.
4.2 Network and Server Setup
The ESP32 runs an HTTP Web Server to serve the control interface.
WiFi & Server Objects: Standard libraries are included (
<WiFi.h>,<WebServer.h>). TheWebServer server(80);object listens for client connections on the standard HTTP port 80.Endpoint Handlers: In
setup(), functions are mapped to specific URL paths:server.on("/", handleRoot);: Serves the main HTML page.server.on("/setAngle", handleSetAngle);: The primary endpoint that receives the angle command via an HTTP GET parameter (?angle=X).server.on("/getAngle", handleGetAngle);: Allows the client to poll the current motor position for synchronization.
Loop: The main
loop()function consists of one critical call:server.handleClient();, which constantly checks for and processes incoming web requests from the client browser.
4.3 Command Handling (handleSetAngle)
This function is the core logic that connects the web command to the motor movement.
void handleSetAngle() {
if (server.hasArg("angle")) {
int angle = server.arg("angle").toInt();
if (angle >= 0 && angle <= 180) {
servoMotor.write(angle); // The key function call
currentAngle = angle;
server.send(200, "text/plain", "Angle set successfully.");
// ... (Serial debug output)
}
// ... (Error handling for angle range)
}
// ... (Error handling for missing parameter)
}
Parameter Check: It confirms the request contains the
angleparameter.Conversion and Validation: The string value is converted to an integer (
toInt()) and validated to ensure it is within the safe 0 to 180 degree range.Servo Command: The simple but powerful
servoMotor.write(angle);function is called. This tells the servo to immediately move to the specified angular position by adjusting the output PWM signal.
5. Hands-On Lab Recap
You’ve built a high-precision, real-time control system, leveraging the ESP32’s capabilities with a SG90 Servo Motor:
- Dual-Protocol System: The ESP32 successfully runs a standard HTTP Web Server for serving the webpage.
- Simple Servo Control: The positioning logic is drastically simplified. Instead of complex step calculations (STEPS_PER_REV / 360) and shortest-path algorithms, you use the ESP32Servo library’s straightforward write(angle) function. This command sends the desired angle (from 0 to 180) directly to the motor’s internal controller.
- Real-Time Control: provide a persistent, low-latency, two-way communication channel, ensuring that commands from the browser are executed instantly by the motor, a significant advantage over slow, repetitive HTTP polling.
- Simplified Hardware and PWM: The SG90 is controlled using a single PWM signal pin from the ESP32, eliminating the need for an external motor driver (like the ULN2003). While an external 5V power supply for the servo is still recommended to protect the ESP32 from current surges, the overall wiring complexity is greatly reduced.




