Introduction: DC Motor and Servo Motor Control Via Web Server - ESP8266 HTTP GET Request

About: Maker 101; Beginner and intermediate level Maker projects! You can find projects such as "How to" and "DIY" on programmable boards such as Arduino, ESP8266, ESP32 and Raspberry Pi on this channel. The projects…

In this project you will see how to build a web server with ESP that controls dual DC motor directions and one servo motor position using four buttons and a slider. I will use the my last design ESP8266 based DRV8848 Motor Driver Development Board, but the source codes of the project are compatible for motor drivers such as L298N and L293D.

Supplies

The components listed below are purely optional options. An ESP8266 board, a motor driver, 5V DC motor and a mini servo motor are required to experience the project.

Step 1: About the ESP8266 Based DRV88xx Motor Driver Board

Followers remember, in a previous project I designed a board that includes the ESP8266 (ESP-12F) WiFi module, allowing you to control sensors such as HC-SR04 ultrasonic and infrared (IR), a servo and dual DC motors (DRV8848).

If you need both a WiFi module like the ESP8266 and a dual motor driver in your projects, this board has exactly the features you are looking for. If you want to avoid a complicated circuit with jumpers, you can opt for this board. There is a video tutorial for the board, Instructables and a PCBWay page with all the details about the circuit.

Step 2: Understanding the Source Codes (Arduino IDE Side)

Let's take a look at the Arduino IDE side from the source codes that allow controlling DC motor directions and Servo motor position via Web Server.

Added the necessary WiFi and Servo libraries to use the Arduino IDE:

#include <ESP8266WiFi.h> 
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <Servo.h>

A Servo object named “My Servo” has been created:

Servo myServo;

GPIO pins to which servo and DC motors are connected are defined:

// GPIO the servo is attached to 
static const int servoPin = 0;
// GPIO the motors are attached to
static const int M1forwardPin = 14;
static const int M1backwardPin = 12;
static const int M2forwardPin = 5;
static const int M2backwardPin = 4;
static const int sleepPin = 16;

If your drive doesn't include a "sleep" pin, change the "sleep mode" lines to comment lines:

//static const int sleepPin = 16;

Motor speeds are defined and can be changed according to demand.

#define M1SPEED 200 
#define M2SPEED 200

Replace your network details with these lines:

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

A variable was created to store the header of the HTTP request:

String header;

Then a few more variables were created to be used to extract the slider position from the HTTP request:

String valueString = String(5);
int pos1 = 0;
int pos2 = 0;

In the "Setup" section, serial communication is started and the modes of the connected pins are specified. If "sleep pin" is not used, you can remove the line:

Serial.begin(115200);
pinMode(M1forwardPin, OUTPUT);
pinMode(M1backwardPin, OUTPUT);
pinMode(M2forwardPin, OUTPUT);
pinMode(M2backwardPin, OUTPUT);
pinMode(sleepPin, OUTPUT);
myServo.attach(servoPin);

digitalWrite(sleepPin, HIGH);

Then the Wi-Fi connection is started and the IP address is printed on the serial monitor:

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();

Finally before moving on to the loop part, functions were created for the move and direction of the motors:

void forward() {
analogWrite(M1forwardPin, M1SPEED);
analogWrite(M2forwardPin, M2SPEED);
analogWrite(M1backwardPin, 0);
analogWrite(M2backwardPin, 0);
}
void backward() {
analogWrite(M1backwardPin, M1SPEED);
analogWrite(M2backwardPin, M2SPEED);
analogWrite(M1forwardPin, 0);
analogWrite(M2forwardPin, 0);
}
void turnLeft() {
analogWrite(M1backwardPin, M1SPEED);
analogWrite(M2forwardPin, M2SPEED);
analogWrite(M1forwardPin, 0);
analogWrite(M2backwardPin, 0);
}
void turnRight() {
analogWrite(M1forwardPin, M1SPEED);
analogWrite(M2backwardPin, M2SPEED);
analogWrite(M1backwardPin, 0);
analogWrite(M2forwardPin, 0);
}
void stopMotors() {
analogWrite(M1forwardPin, 0);
analogWrite(M2forwardPin, 0);
analogWrite(M1backwardPin, 0);
analogWrite(M2backwardPin, 0);
}

In the loop part, it was defined what happens when a new client first establishes a connection with the web server. When a request is received from a client, the incoming data is saved. The while loop that follows will be running as long as the client stays connected.

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");

Then the web server is generated and some HTML text is sent to display the web page. The web page is sent to the client using the "client print" function. You must enter what you want to send to the client as an argument.

// Display the HTML web page
client.println("<!DOCTYPE html><html><head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">");
// Style section for buttons and slider. You can change customizations such as color and so on.
client.println("<style>");
client.println(".container {font: caption; display: block; flex-direction: column; align-items: center;}");
client.println(".slider {width: 275px; accent-color: #ede100; cursor: pointer;}");
client.println(".button-container {display: flex; flex-direction: column; align-items: center;}");
client.println(".row-container {display: flex; flex-direction: row; justify-content: center; align-items: center;}");
client.println(".button {width: 135px; border: 0; background-color: #ede100; box-shadow: inset 2px 2px 3px #b2b2b2, inset -2px -2px 3px #000; padding: 10px 25px; text-align: center; display: inline-block; border-radius: 5px; font-size: 16px; margin: 10px 25px; cursor: pointer;}");
client.println(".button:hover {background-color: #fff200;}");
client.println(".button:active{box-shadow: inset 2px 2px 3px #000, inset -2px -2px 3px #b2b2b2;}");
client.println("footer {text-align: center; margin: 50px; width: 400px; background-color: #ede100;}");
client.println("</style><script src=\"https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js\"></script></head>");
// Body section. Contains functions of buttons and slider.
client.println("<body>");
client.println("<div class=\"container\">");
client.println("<div class=\"row-container\"><h2>ESP8266 Motor Control Panel</h2></div>");
client.println("<div><div class=\"button-container\">");
client.println("<div class=\"row-container\"><button class=\"button\" id=\"forward-btn\" onmousedown=\"btnVal('forward')\" onmouseup=\"btnVal('stop')\">Forward</button></div>");
client.println("<div class=\"row-container\"><button class=\"button\" id=\"left-btn\" onmousedown=\"btnVal('left')\" onmouseup=\"btnVal('stop')\">Left</button>");
client.println("<button class=\"button\" id=\"right-btn\" onmousedown=\"btnVal('right')\" onmouseup=\"btnVal('stop')\">Right</button></div>");
client.println("<div class=\"row-container\"><button class=\"button\" id=\"backward-btn\" onmousedown=\"btnVal('backward')\" onmouseup=\"btnVal('stop')\">Backward</button></div></div>");
client.println("</div>");
client.println("<div class=\"row-container\"><p>Servo Position: <span id=\"servoPos\"></span></p></div>");
client.println("<div class=\"row-container\"><input type=\"range\" min=\"0\" max=\"180\" class=\"slider\" id=\"servoSlider\" onchange=\"servo(this.value)\" value="+valueString+"/></div>");
client.println("<div class=\"row-container\"><footer><p>@Maker101io</p></footer></div></div>");
// Script section. Get the button and slider values and executes functions.
client.println("<script>");
client.println("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});");
client.println("function btnVal(dir) { $.get(\"/?btnVal=\" + dir, true);}");
client.println("function servo(pos) { $.get(\"/?value=\" + pos + \"&\");}");
client.println("{Connection: close};</script>");
client.println("</body></html>");

Step 3: Web Page Side (HTML Codes)

Let's take a look at the HTML codes that the ESP sends to the browser.

If you want to make changes on the web page, I will share these HTML codes together with the source codes. Remember, you must update the HTML lines you make changes to the HTML lines found in the shared source code for the Arduino IDE. The point you should pay attention to when making changes is that you need to add the "Backslash" symbol before the "double quotes" symbols in the HTML code.


The web page has some CSS for the buttons and slider. If you are sure of what you are doing, you can change some properties defined in this section, such as colors and sizes.

<!DOCTYPE html><html><head><meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Style section for buttons and slider. You can change customizations such as color and so on. -->
<style>
.container {font: caption; display: block; flex-direction: column; align-items: center;}
.slider {width: 275px; accent-color: #ede100; cursor: pointer;}
.button-container {display: flex; flex-direction: column; align-items: center;}
.row-container {display: flex; flex-direction: row; justify-content: center; align-items: center;}
.button {width: 135px; border: 0; background-color: #ede100; box-shadow: inset 2px 2px 3px #b2b2b2, inset -2px -2px 3px #000; padding: 10px 25px; text-align: center; display: inline-block; border-radius: 5px; font-size: 16px; margin: 10px 25px; cursor: pointer;}
.button:hover {background-color: #fff200;}
.button:active{box-shadow: inset 2px 2px 3px #000, inset -2px -2px 3px #b2b2b2;}
footer {text-align: center; margin: 50px; width: 400px; background-color: #ede100;}
</style><script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script></head>

Four button classes were added and motor directions were defined for each button. As long as the button is kept pressed, the "direction" value of the button will be taken and when the button is released, the "stop" value will be taken. Then a slider is added, minimum and maximum range is defined to the slider. Created a "value string" variable to hold the slider value.

<!-- Body section. Contains functions of buttons and slider. -->
<body>
<div class="container">
<div class="row-container"><h2>ESP8266 Motor Control Panel</h2></div>
<div><div class="button-container">
<div class="row-container"><button class="button" id="forward-btn" onmousedown="btnVal('forward')" onmouseup="btnVal('stop')">Forward</button></div>
<div class="row-container"><button class="button" id="left-btn" onmousedown="btnVal('left')" onmouseup="btnVal('stop')">Left</button>
<button class="button" id="right-btn" onmousedown="btnVal('right')" onmouseup="btnVal('stop')">Right</button></div>
<div class="row-container"><button class="button" id="backward-btn" onmousedown="btnVal('backward')" onmouseup="btnVal('stop')">Backward</button></div></div>
</div>
<div class="row-container"><p>Servo Position: <span id="servoPos"></span></p></div>
<div class="row-container"><input type="range" min="0" max="180" class="slider" id="servoSlider" onchange="servo(this.value)" value="+valueString+"/></div>
<div class="row-container"><footer><p>@Maker101io</p></footer></div></div>

Finally, you make an HTTP GET request to get the values. Two functions are executed that process the button direction values and the slider value.

<!-- Script section. Get the button and slider values and executes functions. -->
<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 btnVal(dir) { $.get("/?btnVal=" + dir);}
function servo(pos) { $.get("/?value=" + pos + "&");}
{Connection: close};
</script></body></html>

Step 4: Upload the Code and Control It Via Web Server

Let's upload the source code and test the functions. After uploading the code, open Serial Monitor. Copy the IP address that appears on the Serial Monitor. Open your browser, paste the IP address and you will see the previously created web page. Press and hold the motor direction buttons and release. In the Serial Monitor you can see the HTTP requests you are sending to the ESP.

The “If” and “Else” statements are a condition. It controls which button is pressed and runs the function determined according to the received data. This data contains the variables defined for the buttons. When you move the slider, the slider position between the two symbol is taken and stored in the "value string" variable. Then this string value is converted to an integer value. This converted value is written to the servo motor position. The servo motor moves to this position value.

if(header.indexOf("GET /?value=")>=0) {
pos1 = header.indexOf('=');
pos2 = header.indexOf('&');
valueString = header.substring(pos1+1, pos2);
myServo.write(valueString.toInt()); // Rotate the servo
Serial.println(valueString);
} else if (header.indexOf("GET /?btnVal=forward")!= -1) {
Serial.println("FORWARD");
forward(); // Turn forward
} else if (header.indexOf("GET /?btnVal=left")!= -1) {
Serial.println("LEFT");
turnLeft(); // Turn left
} else if (header.indexOf("GET /?btnVal=right")!= -1) {
Serial.println("RIGHT");
turnRight(); // Turn right
} else if (header.indexOf("GET /?btnVal=backward")!= -1) {
Serial.println("BACK");
backward(); // Turn backward
} else if (header.indexOf("GET /?btnVal=stop")!= -1) {
Serial.println("STOP");
stopMotors(); // Stopped
}
// 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("");
}
}

Thanks for your interest and reading. If you have any questions or suggestions, please let me know in the comments.