Wireless Servo Control with ESP32

Published: November 23, 2022

Time to read: 7 min

Wireless Servo Control with ESP32

Published: November 23, 2022

Time to read: 7 min

Introduction

In this tutorial, we will learn how to control up to four servo motors wirelessly using the ESP32 development board. This board is known for its low cost and powerful features, which include Wi-Fi and Bluetooth. We will create a web server on the ESP32 that we can access through any device on the same network to control the servo motors.

I'll be using the F() macro for prints

Here is the code. We will go through it step by step, explaining its functionality

Code

code block is c
#include <WiFi.h>
#include <Servo.h>

// Define constants for pin numbers, network credentials, and other fixed parameters
#define SERVO_PIN 13
#define SSID ""
#define PASSWORD ""
#define SERVER_PORT 80
#define TIMEOUT_TIME 2000

// Create a Servo object
Servo myservo;

// Create a WiFi server object
WiFiServer server(SERVER_PORT);

// Variables to keep track of time for timeout purposes
unsigned long currentTime = millis();
unsigned long previousTime = 0;

void setup() {
  Serial.begin(115200);  // Begin the Serial communication at 115200 baud rate
  
	myservo.attach(SERVO_PIN);

  connectToWiFi(SSID, PASSWORD); // Connect to your WiFi network
  
	server.begin();
}

void loop(){
  WiFiClient client = server.available();
  if (client) {
    currentTime = millis();
    previousTime = currentTime;
    Serial.println(F("New Client."));
    handleClient(client);
  }
}

void connectToWiFi(const char* ssid, const char* password) {
  Serial.print(F("Connecting to "));
  Serial.println(ssid);
  WiFi.begin(ssid, password);
	
	 // Wait until connected
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(F("."));
  }
  Serial.println(F("\nWiFi connected."));
  Serial.println(F("IP address: "));
  Serial.println(WiFi.localIP());
}

void handleClient(WiFiClient& client) {
  String currentLine = "";
  String header = "";
  while (client.connected() && currentTime - previousTime <= TIMEOUT_TIME) {
    currentTime = millis();
    if (client.available()) {
      char c = client.read();
      Serial.write(c);
      header += c;

     // If the byte is a newline character
      if (c == '\n') {
        if (currentLine.length() == 0) {
          sendHttpResponse(client);
          if(header.indexOf("GET /?value=")>=0) {
            handleGetRequest(header);
          }
          client.println();
          break;
        } else {
          currentLine = "";
        }
      } else if (c != '\r') {
        currentLine += c;
      }
    }
  }
  header = "";
  client.stop();
  Serial.println(F("Client disconnected."));
  Serial.println("");
}

void sendHttpResponse(WiFiClient& client) {
  // Continue below
}

void handleGetRequest(const String& header) {
  int valueStartPos = header.indexOf('=');
	int valueEndPos = header.indexOf('&');
	String valueString = header.substring(valueStartPos+1, valueEndPos);
	myservo.write(valueString.toInt());
	Serial.println(valueString);
}

This code allows you to control a servo wirelessly by creating a web server on the ESP32. The servo's position can be adjusted using a slider on the web page. The position of the slider is sent as a GET request to the ESP32, which then adjusts the servo's position accordingly.

Let's take a closer look at the different sections of this code to understand how it works.

This function handleGetRequest extracts the servo position from the HTTP header and writes it to the servo. This function is called from handleClient whenever there's a GET request that includes a servo position.

Let's also create the sendHttpResponse function that was referenced in handleClient. This function sends the HTTP response to the client, which includes the HTML, CSS, and JavaScript that make up the web interface for controlling the servo.

code block is c
void sendHttpResponse(WiFiClient& client) {
  client.println(F("HTTP/1.1 200 OK"));
  client.println(F("Content-type:text/html"));
  client.println(F("Connection: close"));
  client.println();

  client.println(F("<!DOCTYPE html><html>"));
  client.println(F("<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">"));
  client.println(F("<link rel=\"icon\" href=\"data:,\">"));
  client.println(F("<style>body { text-align: center; margin:auto;}"));
  client.println(F(".slider { width: 300px; }</style>"));
  client.println(F("<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/3.6.4/jquery.min.js\"></script>"));

  client.println(F("</head><body><h1>Demo wireless servo</h1>"));
  client.println(F("<p>Position: <span id=\"servoPos\"></span></p>"));
  client.println(F("<input type=\"range\" min=\"0\" max=\"180\" class=\"slider\" id=\"servoSlider\" onchange=\"servo(this.value)\"/>"));

  client.println(F("<script>var slider = document.getElementById(\"servoSlider\");"));
  client.println(F("var servoP = document.getElementById(\"servoPos\"); servoP.innerHTML = slider.value;"));
  client.println(F("slider.oninput = function() { slider.value = this.value; servoP.innerHTML = this.value; }"));
  client.println(F("$.ajaxSetup({timeout:1000}); function servo(pos) { "));
  client.println(F("$.get(\"/?value=\" + pos + \"&\"); {Connection: close};}</script>"));

  client.println(F("</body></html>"));
}

Essentials

Here's the list of components you would need to accomplish wireless control of up to four servos using the ESP32:

ESP32 Board: This is the microcontroller board that will be running your code. It has built-in Wi-Fi capabilities, which makes it perfect for this wireless control project.

I have used these 2 before.

Servo Motors (x4): These are the devices that you will be controlling. Servo motors are a type of motor that can move to a specified position based on an input signal. You'll need four for this project.

I've used these before

Jumper Wires: These are used to connect the servo motors to the ESP32 board. You'll need three per servo (for power, ground, and signal), so a total of twelve for this project.

Micro USB Cable: This is used to connect the ESP32 board to your computer for programming.

Breadboard (optional): This can be used to make connections between the ESP32 and the servos easier and more organized.

Power Supply: Depending on the type of servo motors you are using, you may need an external power supply. Some servo motors require more power than the ESP32 board can provide, so an external power supply (like a battery pack or wall adapter) may be necessary.

Please note that working with electronics involves risks. Always be careful and make sure you understand the requirements of your specific components. For example, providing too much power to a servo motor could damage it, and connecting components incorrectly could damage your ESP32 board. Always double-check your connections and the specifications of your components.

PS

Depending on your machine, it the SRAM is large enough you'll be able to do more in your markup. Arduino's F() macro stores the string in flash memory instead of SRAM.

code block is c
void sendHttpResponse(WiFiClient& client) {
  client.println(F(
    "HTTP/1.1 200 OK\n"
    "Content-type:text/html\n"
    "Connection: close\n"
    "\n"
    "<!DOCTYPE html><html>\n"
    "<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n"
    "<link rel=\"icon\" href=\"data:,\">\n"
    "<style>body { text-align: center; margin:auto;}\n"
    ".slider { width: 300px; }</style>\n"
    "<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/3.6.4/jquery.min.js\"></script>\n"
    "</head><body><h1>Demo wireless servo</h1>\n"
    "<p>Position: <span id=\"servoPos\"></span></p>\n"
    "<input type=\"range\" min=\"0\" max=\"180\" class=\"slider\" id=\"servoSlider\" onchange=\"servo(this.value)\"/>\n"
    "<script>var slider = document.getElementById(\"servoSlider\");\n"
    "var servoP = document.getElementById(\"servoPos\"); servoP.innerHTML = slider.value;\n"
    "slider.oninput = function() { slider.value = this.value; servoP.innerHTML = this.value; }\n"
    "$.ajaxSetup({timeout:1000}); function servo(pos) { \n"
    "$.get(\"/?value=\" + pos + \"&\"); {Connection: close};}</script>\n"
    "</body></html>\n"
  ));
}