Search

Step-by-Step Guide: ESP32 Servo Motor Control via Web Server

In this instructional guide, you will discover the process of constructing a web server using the ESP32 to regulate the position of a servo motor via a slider. Initially, we’ll provide a brief overview of controlling a servo with the ESP32, followed by the steps to create the web server.

Connecting the Servo Motor to the ESP32

Servo motors typically come with three wires: power, ground, and signal. The power wire is commonly red, while the ground (GND) wire is usually black or brown, and the signal wire tends to be yellow, orange, or white.

WireColor
PowerRed
GNDBlack, or brown
SignalYellow, orange, or white

For a small servo such as the S0009 depicted in the figure below, you can directly supply power from the ESP32.

However, if you’re utilizing more than one servo or a different model, it’s likely that you’ll require an external power source to energize your servos.

If you’re employing a small servo like the S0009, the connections should be as follows:

  • GND -> ESP32 GND pin;
  • Power -> ESP32 VIN pin;
  • Signal -> GPIO 13 (or any PWM pin).
Note: In this scenario, any ESP32 GPIO can be used since all GPIOs are capable of generating a PWM signal. Nonetheless, we advise against using GPIOs 9, 10, and 11, as they are linked to the integrated SPI flash and are not recommended for other purposes.

Schematic

In our demonstrations, we’ll connect the signal wire to GPIO 13. You can refer to the following schematic diagram to wire your servo motor accordingly.

How to Operate a Servo Motor?

You can adjust the position of the servo shaft within a range of 0 to 180 degrees. Servos are managed through pulse width modulation (PWM) signals. The duration of the PWM signal dispatched to the motor dictates the position of the shaft.

To regulate the motor, you can utilize the PWM capabilities of the ESP32 by sending a 50Hz signal with the appropriate pulse width. Alternatively, you can employ a library to streamline this process.

Testing an Example

Once you’ve installed the library, navigate to your Arduino IDE. Ensure that you’ve selected the ESP32 board, then proceed to File > Examples > ServoESP32 > Simple Servo. Subsequently, adjust the code to utilize GPIO 13.

static const int servoPin = 13;
#include <Servo.h>

static const int servoPin = 13;

Servo servo1;

void setup() {
  Serial.begin(115200);
  servo1.attach(servoPin);
}

void loop() {
  for(int posDegrees = 0; posDegrees <= 180; posDegrees++) {
    servo1.write(posDegrees);
    Serial.println(posDegrees);
    delay(20);
  }

  for(int posDegrees = 180; posDegrees >= 0; posDegrees--) {
    servo1.write(posDegrees);
    Serial.println(posDegrees);
    delay(20);
  }
}

Understanding the code

This code snippet rotates the servo 180 degrees to one side and then 180 degrees to the other side. Let’s delve into how it functions.

Firstly, you must include the Servo library:

#include <Servo.h>

Define the pin linked to the servo data pin. In this instance, we’re using GPIO 13, but you can opt for any other suitable pins.

static const int servoPin = 13;

Subsequently, you need to instantiate a Servo object, named servo1 in this case.

Servo servo1;

setup()

Within the setup(), you initialize a serial communication for debugging purposes and attach GPIO 13 to the servo object.

void setup() {
    Serial.begin(115200);
    servo1.attach(servoPin);
}

loop()

In the loop(), the motor’s shaft position shifts from 0 to 180 degrees, and then from 180 to 0 degrees. To adjust the shaft to a specific position, you simply utilize the write() method on the Servo object. You provide an integer argument representing the position in degrees.

myservo.write(pos);

Testing the Sketch

Upload the code to your ESP32 servo. Upon completion of the upload, you should observe the motor’s shaft rotating to one side and then to the other.

Creating the ESP32 Web Server

Now that you’ve learned how to control a servo with the ESP32, let’s proceed with creating the web server to manage it. The web server we’re going to construct will have the following features:

  • It will feature a slider ranging from 0 to 180, allowing you to adjust and control the servo’s shaft position.
  • The current value of the slider will be automatically updated on the web page without requiring a manual refresh. To achieve this, we’ll employ AJAX to send HTTP requests to the ESP32 in the background.
  • Refreshing the web page won’t alter the slider value or the position of the shaft.

Creating the HTML Page

To begin, let’s examine the HTML code that the ESP32 needs to transmit to your web browser:

<!DOCTYPE html>
<html>
<head>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon" href="data:,">
  <style>
    body {
      text-align: center;
      font-family: "Trebuchet MS", Arial;
      margin-left:auto;
      margin-right:auto;
    }
    .slider {
      width: 300px;
    }
  </style>
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
</head>
<body>
  <h1>ESP32 with Servo</h1>
  <p>Position: <span id="servoPos"></span></p>
  <input type="range" min="0" max="180" class="slider" id="servoSlider" onchange="servo(this.value)"/>
  <script>
    var slider = document.getElementById("servoSlider");
    var servoP = document.getElementById("servoPos");
    servoP.innerHTML = slider.value;
    slider.oninput = function() {
      slider.value = this.value;
      servoP.innerHTML = this.value;
    }
    $.ajaxSetup({timeout:1000});
    function servo(pos) {
      $.get("/?value=" + pos + "&");
      {Connection: close};
    }
  </script>
</body>
</html>

Creating a Slider

To implement a slider in the HTML page for this project, you utilize the <input> tag. The <input> tag defines an area where the user can input data.

There are various input types available. To specify a slider, you use the “type” attribute with the value “range”. Within the slider, you also need to set the minimum and maximum range using the “min” and “max” attributes.

<input type="range" min="0" max="180" class="slider" id="servoSlider" onchange="servo(this.value)"/>

Additionally, you need to define other attributes such as:

  • the class for styling the slider
  • the id to update the current position displayed on the web page
  • Finally, the onchange attribute calls the servo function to send an HTTP request to the ESP32 whenever the slider moves.

Adding JavaScript to the HTML File

Next, you incorporate JavaScript code into your HTML file using the <script> and </script> tags. The following code snippet updates the web page with the current slider position:

var slider = document.getElementById("servoSlider");
    var servoP = document.getElementById("servoPos");
    servoP.innerHTML = slider.value;
    slider.oninput = function() {
      slider.value = this.value;
      servoP.innerHTML = this.value;
    }

The subsequent lines initiate an HTTP GET request to the ESP’s IP address, targeting the specific URL path /?value=[SLIDER_POSITION]&.

$.ajaxSetup({timeout:1000});
function servo(pos) {
  $.get("/?value=" + pos + "&");
}

For instance, when the slider is set to 0, an HTTP GET request is made to the following URL:

http://192.168.1.135/?value=0&

And when the slider is positioned at 180 degrees, the URL would resemble:

http://192.168.1.135/?value=180&

Upon receiving the GET request, the ESP32 can extract the value parameter from the URL and adjust the servo motor accordingly.

Code

Now, let’s incorporate the previous HTML text into the sketch and adjust the servo accordingly. The following code accomplishes this task:

#include <WiFi.h>
#include <Servo.h>

Servo myservo;  // create servo object to control a servo
// twelve servo objects can be created on most boards

// GPIO the servo is attached to
static const int servoPin = 13;

// Replace with your network credentials
const char* ssid     = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

// Set web server port number to 80
WiFiServer server(80);

// Variable to store the HTTP request
String header;

// Decode HTTP GET value
String valueString = String(5);
int pos1 = 0;
int pos2 = 0;

// Current time
unsigned long currentTime = millis();
// Previous time
unsigned long previousTime = 0; 
// Define timeout time in milliseconds (example: 2000ms = 2s)
const long timeoutTime = 2000;

void setup() {
  Serial.begin(115200);

  myservo.attach(servoPin);  // attaches the servo on the servoPin to the servo object

  // Connect to Wi-Fi network with SSID and password
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  // Print local IP address and start web server
  Serial.println("");
  Serial.println("WiFi connected.");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
  server.begin();
}

void loop(){
  WiFiClient client = server.available();   // Listen for incoming clients

  if (client) {                             // If a new client connects,
    currentTime = millis();
    previousTime = currentTime;
    Serial.println("New Client.");          // print a message out in the serial port
    String currentLine = "";                // make a String to hold incoming data from the client
    while (client.connected() && currentTime - previousTime <= timeoutTime) { // loop while the client's connected
      currentTime = millis();
      if (client.available()) {             // if there's bytes to read from the client,
        char c = client.read();             // read a byte, then
        Serial.write(c);                    // print it out the serial monitor
        header += c;
        if (c == '\n') {                    // if the byte is a newline character
          // if the current line is blank, you got two newline characters in a row.
          // that's the end of the client HTTP request, so send a response:
          if (currentLine.length() == 0) {
            // HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK)
            // and a content-type so the client knows what's coming, then a blank line:
            client.println("HTTP/1.1 200 OK");
            client.println("Content-type:text/html");
            client.println("Connection: close");
            client.println();

            // Display the HTML web page
            client.println("<!DOCTYPE html><html>");
            client.println("<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">");
            client.println("<link rel=\"icon\" href=\"data:,\">");
            // CSS to style the on/off buttons 
            // Feel free to change the background-color and font-size attributes to fit your preferences
            client.println("<style>body { text-align: center; font-family: \"Trebuchet MS\", Arial; margin-left:auto; margin-right:auto;}");
            client.println(".slider { width: 300px; }</style>");
            client.println("<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js\"></script>");
                     
            // Web Page
            client.println("</head><body><h1>ESP32 with Servo</h1>");
            client.println("<p>Position: <span id=\"servoPos\"></span></p>");          
            client.println("<input type=\"range\" min=\"0\" max=\"180\" class=\"slider\" id=\"servoSlider\" onchange=\"servo(this.value)\" value=\""+valueString+"\"/>");
            
            client.println("<script>var slider = document.getElementById(\"servoSlider\");");
            client.println("var servoP = document.getElementById(\"servoPos\"); servoP.innerHTML = slider.value;");
            client.println("slider.oninput = function() { slider.value = this.value; servoP.innerHTML = this.value; }");
            client.println("$.ajaxSetup({timeout:1000}); function servo(pos) { ");
            client.println("$.get(\"/?value=\" + pos + \"&\"); {Connection: close};}</script>");
           
            client.println("</body></html>");     
            
            //GET /?value=180& HTTP/1.1
            if(header.indexOf("GET /?value=")>=0) {
              pos1 = header.indexOf('=');
              pos2 = header.indexOf('&');
              valueString = header.substring(pos1+1, pos2);
              
              //Rotate the servo
              myservo.write(valueString.toInt());
              Serial.println(valueString); 
            }         
            // The HTTP response ends with another blank line
            client.println();
            // Break out of the while loop
            break;
          } else { // if you got a newline, then clear currentLine
            currentLine = "";
          }
        } else if (c != '\r') {  // if you got anything else but a carriage return character,
          currentLine += c;      // add it to the end of the currentLine
        }
      }
    }
    // Clear the header variable
    header = "";
    // Close the connection
    client.stop();
    Serial.println("Client disconnected.");
    Serial.println("");
  }
}

Copy the provided code into your Arduino IDE, but refrain from uploading it just yet. First, let’s briefly examine how it operates.

How the Code Works

First, we include the Servo library and create a Servo object named myservo.

#include <Servo.h> 
Servo myservo; // create servo object to control a servo

We also define a variable to store the GPIO number the servo is connected to, which in this case is GPIO 13.

const int servoPin = 13;

Ensure to update the following two lines with your network credentials.

const char* ssid     = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

Next, we create variables to extract the slider position from the HTTP request.

// Decode HTTP GET value 
String valueString = String(5); 
int pos1 = 0; 
int pos2 = 0;

setup()

In the setup(), the servo is attached to the designated GPIO using myservo.attach().

myservo.attach(servoPin); // attaches the servo on the servoPin to the servo object

loop()

The first part of the loop() initializes the web server and transmits the HTML text to display the web page, utilizing the same method as in this web server project.

The subsequent portion of the code retrieves the slider value from the HTTP request.

//GET /?value=180& HTTP/1.1
if(header.indexOf("GET /?value=")>=0) {
  pos1 = header.indexOf('=');
  pos2 = header.indexOf('&');
  valueString = header.substring(pos1+1, pos2);

When you adjust the slider, you generate an HTTP request with the slider position between the = and & signs in the URL.

http://your-esp-ip-address/?value=[SLIDER_POSITION]&

The slider position value is stored in the valueString variable.

Then, we set the servo to that specific position using myservo.write() with the valueString variable as an argument. Since valueString is a string, we utilize the toInt() method to convert it into an integer number – the data type accepted by the write() method.

myservo.write(valueString.toInt());

Testing the Web Server

You can now upload the code to your ESP32 – ensure you have selected the correct board and COM port. Also, remember to adjust the code to include your network credentials.

After uploading the code, open the Serial Monitor with a baud rate of 115200. Press the ESP32 EN/RST button to restart the board, and note the ESP32 IP address displayed on the Serial Monitor.

Open your browser, paste the ESP IP address, and you should see the previously created web page. Adjust the slider to control the servo motor.

In the Serial Monitor, you can observe the HTTP requests sent to the ESP32 when you adjust the slider.

Spend some time experimenting with your web server to ensure it is functioning correctly.

Wrapping Up

To summarize, in this tutorial, you’ve acquired knowledge on controlling a servo motor using the ESP32 and constructing a web server equipped with a slider for managing its position.

This serves as an illustration of servo motor control. Beyond employing a slider, alternative options include utilizing a text input field, multiple buttons with preset angles, or any other applicable input mechanisms.

Furthermore, we offer tutorials detailing the integration of other motors with the ESP32:

Leave a Comment