This tutorial shows how to use the Arduino IDE to operate a servo motor with an ESP32 over a web server. You’ll learn how to use the ESP32 to drive a servo motor and how to build a simple web server with a slider to adjust the servo motor’s position in both directions. A slider with a position ranging from 0-180 makes up the web server. A user can control the shaft position with this slider.

Parts Required

For this tutorial we’ll use the following parts:

  • ESP32 DOIT DEVKIT V1 Board-The Esp32 DevKit v1 is one of the development boards created to evaluate the ESP-WROOM-32 module. It is based on the ESP32 microcontroller that boasts Wifi, Bluetooth, Ethernet and Low Power support all in a single chip.
  • Micro Servo Motor – S0009 or Servo Motor – S0003
  • Jumper wires


In our examples we’ll connect the signal wire to GPIO 13. So, you can follow the next schematic diagram to wire your servo motor.

Installing the ESP32_Arduino_Servo_Library

The ESP32 Arduino Servo Library makes it easier to control a servo motor with your ESP32, using the Arduino IDE. Follow the next steps to install the library in your Arduino IDE:

  1. Click here to download the ESP32_Arduino_Servo_Library. You should have a .zip folder in your Downloads folder
  2. Unzip the .zip folder and you should get ESP32-Arduino-Servo-Library-Master folder
  3. Rename your folder from ESP32-Arduino-Servo-Library-Master to ESP32_Arduino_Servo_Library
  4. Move the ESP32_Arduino_Servo_Library folder to your Arduino IDE installation libraries folder
  5. Finally, re-open your Arduino IDE

Testing an Example

After installing the library, go to your Arduino IDE. Make sure you have the ESP32 board selected, and then, go to File > Examples > ServoESP32 > Simple Servo.

#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() {
  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 ");
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
  // Print local IP address and start web server
  Serial.println("WiFi connected.");
  Serial.println("IP address: ");
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 =;             // 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("Connection: close");
            // 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=\"\"></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>");
            //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
            // The HTTP response ends with another blank line
            // Break out of the while loop
          } 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
    Serial.println("Client disconnected.");

Understanding the code

This sketch rotates the servo 180 degrees to one side, and 180 degrees to the other. Let’s see how it works.

First, you need to include the Servo library:

#include <Servo.h>

Then, you need to create a servo object. In this case it is called myservo.

Servo myservo;


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

void setup() {  




In the loop(), we change the motor’s shaft position from 0 to 180 degrees, and then from 180 to 0 degrees. To set the shaft to a particular position, you just need to use the write() method in the servo object. You pass as an argument, an integer number with the position in degrees.


Testing the Sketch

Upload the code to your ESP32. After uploading the code, you should see the motor’s shaft rotating to one side and then, to the other.

Creating the ESP32 Web Server

Now that you know how to control a servo with the ESP32, let’s create the web server to control it (learn more about building an ESP32 Web Server). The web server we’ll build:

  • Contains a slider that can be adjusted from 0 to 180 to control the servo’s shaft position
  •  the current slider value, as well as the shaft position, is immediately updated in the web page without the need to refresh the web page. We do this by sending HTTP queries to the ESP32 in the background using AJAX
  •  refreshing the web page has no effect on the slider value or shaft position.

Creating the HTML Page

<!DOCTYPE html>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon" href="data:,">
    body {
      text-align: center;
      font-family: "Trebuchet MS", Arial;
    .slider {
      width: 300px;
  <script src=""></script>
  <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)"/>
    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;
    function servo(pos) {
      $.get("/?value=" + pos + "&");
      {Connection: close};

Creating a Slider

This project’s HTML page includes the creation of a slider. In HTML, the <input> tag is used to build a slider. The< input> tag designates a field into which the user can enter information.

There are numerous input kinds to choose from. Use the “type” attribute with the “range” value to define a slider. The “min” and “max” attributes are used to define the minimum and maximum ranges of a slider.

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

You also need to define other attributes like:

  • the class to style the slider
  • the id to update the current position displayed on the web page
  • And finally, the onchange attribute to call the servo function to send an HTTP request to the ESP32 when the slider moves.

Adding JavaScript to the HTML File

Next, you need to add some JavaScript code to your HTML file using the <script> and </script> tags. This snippet of the code 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;


And the next lines make an HTTP GET request on the ESP IP address in this specific URL path /?value=[SLIDER_POSITION]&.


function servo(pos) {

  $.get(“/?value=” + pos + “&”);


For example, when the slider is at 0, you make an HTTP GET request on the following URL:

And when the slider is at 180 degrees, you’ll have something as follows:

This way, when the ESP32 receives the GET request, it can retrieve the value parameter in the URL and move the servo motor to the right position.


Now, we need to include the previous HTML text in the sketch and rotate the servo accordingly. This next sketch does precisely that.

#include <Servo.h>
Servo myservo;  // create servo object to control a servo
// twelve servo objects can be created on most boards
int pos = 0;    // variable to store the servo position
void setup() {
  myservo.attach(13);  // attaches the servo on pin 13 to the servo object
void loop() {
  for (pos = 0; pos <= 180; pos += 1) { // goes from 0 degrees to 180 degrees
    // in steps of 1 degree
    myservo.write(pos);              // tell servo to go to position in variable 'pos'
    delay(15);                       // waits 15ms for the servo to reach the position
  for (pos = 180; pos >= 0; pos -= 1) { // goes from 180 degrees to 0 degrees
    myservo.write(pos);              // tell servo to go to position in variable 'pos'
    delay(15);                       // waits 15ms for the servo to reach the position

How the Code Works

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

#include <Servo.h> 

Servo myservo; // create servo object to control a servo

We also create a variable to hold the GPIO number the servo is connected to. In this case, GPIO 13.

const int servoPin = 13;

Don’t forget that you need to modify the following two lines to include your network credentials.

// Replace with your network credentials const char* ssid = “”; 

const char* password = “”;

Then, create a couple of variables that will be used to extract the slider position from the HTTP request.

// Decode HTTP GET value 

String valueString = String(5); 

int pos1 = 0; 

int pos2 = 0;


In the setup(), you need to attach the servo to the GPIO it is connected to, with myservo.attach().

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


The first part of the loop() creates the web server and sends the HTML text to display the web page. The following part 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 move the slider, you make an HTTP request on the following URL, that contains the slider position between the = and & signs.


The slider position value is saved in the valueString variable.

Then, with the valueString variable as a parameter, we use myservo.write() to set the servo to that specific position. Because the valueString variable is a string, we must convert it to an integer number using the toInt() method, which is the data type that the write() method accepts.


Testing the Web Server

Now you can upload the code to your ESP32 – make sure you have the right board and COM port selected. Also don’t forget to modify the code to include your network credentials.

  • After uploading the code, open the Serial Monitor at a baud rate of 115200.
  • Press the ESP32 “Enable” button to restart the board, and copy the ESP32 IP address that shows up on the Serial Monitor.
  • Open your browser, paste the ESP IP address, and you should see the web page you’ve created previously. Move the slider to control the servo motor.
  • In the Serial Monitor, you can also see the HTTP requests you’re sending to the ESP32 when you move the slider.
  • Experiment with your web server for a while to see if it’s working properly


In summary, in this tutorial you’ve learned how to control a servo motor with the ESP32 and how to create a web server with a slider to control its position.