ESP32 - Control Car via Web
This guide shows you how to use the ESP32 to wirelessly control a robot car from a Web browser on your smartphone or PC using WiFi. The control is facilitated through a graphical web user interface using something called WebSocket, allowing for smooth and dynamic control of the car.
Or you can buy the following sensor kits:
Disclosure: some of these links are affiliate links. We may earn a commission on your purchase at no extra cost to you. We appreciate it.
Now, why go for WebSocket? Here's the scoop:
Without WebSocket, changing the car's direction would require reloading the page every time. Not ideal!
However, with WebSocket, we establish a special connection between the webpage and the ESP32. This enables sending commands to the ESP32 in the background, without needing to reload the page. The result? The robot car moves seamlessly and in real-time. Pretty cool, right?
In a nutshell, the WebSocket connection enables the smooth, real-time control of the robot.
We have specific tutorials about 2WD RC Car and WebSocket. Each tutorial contains detailed information and step-by-step instructions about hardware pinout, working principle, wiring connection to ESP32, ESP32 code... Learn more about them at the following links:
The ESP32 code creates both a web server and a WebSocket Server. Here's how it works:
When you enter the ESP32's IP address in a web browser, it requests the webpage (User Interface) from the ESP32.
The ESP32's web server responds by sending the webpage's content (HTML, CSS, JavaScript).
Your web browser then displays the webpage.
The JavaScript code within the webpage establishes a WebSocket connection to the WebSocket server on the ESP32.
Once this WebSocket connection is established, if you press/release the buttons on the webpage, the JavaScript code quietly sends the commands to the ESP32 through this WebSocket connection in the background.
The WebSocket server on the ESP32, upon receiving the commands, controls the robot car accordingly.
The below table show commands list that the webpage sends to ESP32 based on the user's actions:
User's Action | Button | Command | Car Action |
PRESS | UP | 1 | MOVE FORWARD |
PRESS | DOWN | 2 | MOVE BACKWARD |
PRESS | LEFT | 4 | TURN LEFT |
PRESS | RIGHT | 8 | TURN RIGHT |
PRESS | STOP | 0 | STOP |
RELEASE | UP | 0 | STOP |
RELEASE | DOWN | 0 | STOP |
RELEASE | LEFT | 0 | STOP |
RELEASE | RIGHT | 0 | STOP |
RELEASE | STOP | 0 | STOP |
This image is created using Fritzing. Click to enlarge image
Typically, you need two power sources:
But, you can simplify it using just one power source for everything – four 1.5V batteries (totaling 6V). Here's how:
Connect the batteries to the L298N module as shown.
Remove two jumpers from ENA and ENB pins to 5 volts on the L298N module.
Add a jumper labeled 5VEN (yellow circle on the diagram).
Connect the 12V pin on the L298N module to the Vin pin on the ESP32 to power it directly from batteries.
Since the 2WD RC car has an on/off switch, you can optionally connect the battery via the switch to enable turning on/off power for the car. If you want to make it simple, just ignore the switch.
If you're unfamiliar with how to supply power to the ESP32 and other components, you can find guidance in the following tutorial: How to Power ESP32.
The webpage's content (HTML, CSS, JavaScript) are stored separately on an index.h file. So, we will have two code files on Arduino IDE:
An .ino file that is ESP32 code, which creates a web sever and WebSocket Server, and controls car
An .h file, which contains the webpage's content.
Do the wiring as above image.
Connect the ESP32 board to your PC via a micro USB cable
Open Arduino IDE on your PC.
Select the right ESP32 board (e.g. ESP32 Dev Module) and COM port.
Open the Library Manager by clicking on the Library Manager icon on the left navigation bar of Arduino IDE.
Search “ESPAsyncWebServer”, then find the ESPAsyncWebServer created by lacamera.
Click Install button to install ESPAsyncWebServer library.
Search “WebSockets”, then find the WebSockets created by Markus Sattler.
Click Install button to install WebSockets library.
On Arduino IDE, create new sketch, Give it a name, for example, esp32io.com.ino
Copy the below code and open with Arduino IDE
#include <WiFi.h>
#include <ESPAsyncWebServer.h>
#include <WebSocketsServer.h>
#include "index.h"
#define CMD_STOP 0
#define CMD_FORWARD 1
#define CMD_BACKWARD 2
#define CMD_LEFT 4
#define CMD_RIGHT 8
#define ENA_PIN 14
#define IN1_PIN 27
#define IN2_PIN 26
#define IN3_PIN 25
#define IN4_PIN 33
#define ENB_PIN 32
const char* ssid = "YOUR_WIFI_SSID";
const char* password = "YOUR_WIFI_PASSWORD";
AsyncWebServer server(80);
WebSocketsServer webSocket = WebSocketsServer(81);
void webSocketEvent(uint8_t num, WStype_t type, uint8_t* payload, size_t length) {
switch (type) {
case WStype_DISCONNECTED:
Serial.printf("[%u] Disconnected!\n", num);
break;
case WStype_CONNECTED:
{
IPAddress ip = webSocket.remoteIP(num);
Serial.printf("[%u] Connected from %d.%d.%d.%d\n", num, ip[0], ip[1], ip[2], ip[3]);
}
break;
case WStype_TEXT:
String angle = String((char*)payload);
int command = angle.toInt();
Serial.print("command: ");
Serial.println(command);
switch (command) {
case CMD_STOP:
Serial.println("Stop");
CAR_stop();
break;
case CMD_FORWARD:
Serial.println("Move Forward");
CAR_moveForward();
break;
case CMD_BACKWARD:
Serial.println("Move Backward");
CAR_moveBackward();
break;
case CMD_LEFT:
Serial.println("Turn Left");
CAR_turnLeft();
break;
case CMD_RIGHT:
Serial.println("Turn Right");
CAR_turnRight();
break;
default:
Serial.println("Unknown command");
}
break;
}
}
void setup() {
Serial.begin(9600);
pinMode(ENA_PIN, OUTPUT);
pinMode(IN1_PIN, OUTPUT);
pinMode(IN2_PIN, OUTPUT);
pinMode(IN3_PIN, OUTPUT);
pinMode(IN4_PIN, OUTPUT);
pinMode(ENB_PIN, OUTPUT);
digitalWrite(ENA_PIN, HIGH);
digitalWrite(ENB_PIN, HIGH);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("Connecting to WiFi...");
}
Serial.println("Connected to WiFi");
webSocket.begin();
webSocket.onEvent(webSocketEvent);
server.on("/", HTTP_GET, [](AsyncWebServerRequest* request) {
Serial.println("Web Server: received a web page request");
String html = HTML_CONTENT;
request->send(200, "text/html", html);
});
server.begin();
Serial.print("ESP32 Web Server's IP address: ");
Serial.println(WiFi.localIP());
}
void loop() {
webSocket.loop();
}
void CAR_moveForward() {
digitalWrite(IN1_PIN, HIGH);
digitalWrite(IN2_PIN, LOW);
digitalWrite(IN3_PIN, HIGH);
digitalWrite(IN4_PIN, LOW);
}
void CAR_moveBackward() {
digitalWrite(IN1_PIN, LOW);
digitalWrite(IN2_PIN, HIGH);
digitalWrite(IN3_PIN, LOW);
digitalWrite(IN4_PIN, HIGH);
}
void CAR_turnLeft() {
digitalWrite(IN1_PIN, HIGH);
digitalWrite(IN2_PIN, LOW);
digitalWrite(IN3_PIN, LOW);
digitalWrite(IN4_PIN, LOW);
}
void CAR_turnRight() {
digitalWrite(IN1_PIN, LOW);
digitalWrite(IN2_PIN, LOW);
digitalWrite(IN3_PIN, HIGH);
digitalWrite(IN4_PIN, LOW);
}
void CAR_stop() {
digitalWrite(IN1_PIN, LOW);
digitalWrite(IN2_PIN, LOW);
digitalWrite(IN3_PIN, LOW);
digitalWrite(IN4_PIN, LOW);
}
Modify the WiFi information (SSID and password) in the code to match your own network credentials.
Create the index.h file On Arduino IDE by:
const char *HTML_CONTENT = R"=====(
<!DOCTYPE html>
<html>
<head>
<title>ESP32 Control Car via Web</title>
<meta name="viewport" content="width=device-width, initial-scale=0.7, maximum-scale=1, user-scalable=no">
<style type="text/css">
body { text-align: center; font-size: 24px;}
button { text-align: center; font-size: 24px;}
#container {
margin-right: auto;
margin-left: auto;
width: 400px;
height: 400px;
position: relative;
margin-bottom: 10px;
}
div[class^='button'] { position: absolute; }
.button_up, .button_down { width:214px; height:104px;}
.button_left, .button_right { width:104px; height:214px;}
.button_stop { width:178px; height:178px;}
.button_up {
background: url('https://esp32io.com/images/tutorial/up_inactive.png') no-repeat;
background-size: contain;
left: 200px;
top: 0px;
transform: translateX(-50%);
}
.button_down {
background: url('https://esp32io.com/images/tutorial/down_inactive.png') no-repeat;
background-size: contain;
left:200px;
bottom: 0px;
transform: translateX(-50%);
}
.button_right {
background: url('https://esp32io.com/images/tutorial/right_inactive.png') no-repeat;
background-size: contain;
right: 0px;
top: 200px;
transform: translateY(-50%);
}
.button_left {
background: url('https://esp32io.com/images/tutorial/left_inactive.png') no-repeat;
background-size: contain;
left:0px;
top: 200px;
transform: translateY(-50%);
}
.button_stop {
background: url('https://esp32io.com/images/tutorial/stop_inactive.png') no-repeat;
background-size: contain;
left:200px;
top: 200px;
transform: translate(-50%, -50%);
}
</style>
<script>
var CMD_STOP = 0;
var CMD_FORWARD = 1;
var CMD_BACKWARD = 2;
var CMD_LEFT = 4;
var CMD_RIGHT = 8;
var img_name_lookup = {
[CMD_STOP]: "stop",
[CMD_FORWARD]: "up",
[CMD_BACKWARD]: "down",
[CMD_LEFT]: "left",
[CMD_RIGHT]: "right"
}
var ws = null;
function init()
{
var container = document.querySelector("#container");
container.addEventListener("touchstart", mouse_down);
container.addEventListener("touchend", mouse_up);
container.addEventListener("touchcancel", mouse_up);
container.addEventListener("mousedown", mouse_down);
container.addEventListener("mouseup", mouse_up);
container.addEventListener("mouseout", mouse_up);
}
function ws_onmessage(e_msg)
{
e_msg = e_msg || window.event;
}
function ws_onopen()
{
document.getElementById("ws_state").innerHTML = "OPEN";
document.getElementById("wc_conn").innerHTML = "Disconnect";
}
function ws_onclose()
{
document.getElementById("ws_state").innerHTML = "CLOSED";
document.getElementById("wc_conn").innerHTML = "Connect";
console.log("socket was closed");
ws.onopen = null;
ws.onclose = null;
ws.onmessage = null;
ws = null;
}
function wc_onclick()
{
if(ws == null)
{
ws = new WebSocket("ws:
document.getElementById("ws_state").innerHTML = "CONNECTING";
ws.onopen = ws_onopen;
ws.onclose = ws_onclose;
ws.onmessage = ws_onmessage;
}
else
ws.close();
}
function mouse_down(event)
{
if (event.target !== event.currentTarget)
{
var id = event.target.id;
send_command(id);
event.target.style.backgroundImage = "url('https://esp32io.com/images/tutorial/" + img_name_lookup[id] + "_active.png')";
}
event.stopPropagation();
event.preventDefault();
}
function mouse_up(event)
{
if (event.target !== event.currentTarget)
{
var id = event.target.id;
send_command(CMD_STOP);
event.target.style.backgroundImage = "url('https://esp32io.com/images/tutorial/" + img_name_lookup[id] + "_inactive.png')";
}
event.stopPropagation();
event.preventDefault();
}
function send_command(cmd)
{
if(ws != null)
if(ws.readyState == 1)
ws.send(cmd + "\r\n");
}
window.onload = init;
</script>
</head>
<body>
<h2>ESP32 - RC Car via Web</h2>
<div id="container">
<div id="0" class="button_stop"></div>
<div id="1" class="button_up"></div>
<div id="2" class="button_down"></div>
<div id="8" class="button_right"></div>
<div id="4" class="button_left"></div>
</div>
<p>
WebSocket : <span id="ws_state" style="color:blue">closed</span><br>
</p>
<button id="wc_conn" type="button" onclick="wc_onclick();">Connect</button>
<br>
<br>
<div class="sponsor">Sponsored by <a href="https://amazon.com/diyables">DIYables</a></div>
</body>
</html>
)=====";
Now you have the code in two files: esp32io.com.ino and index.h
Click Upload button on Arduino IDE to upload code to ESP32
Open the Serial Monitor
Check out the result on Serial Monitor.
Connecting to WiFi...
Connected to WiFi
ESP32 Web Server's IP address IP address: 192.168.0.2
Take note of the IP address displayed, and enter this address into the address bar of a web browser on your smartphone or PC.
You will see the webpage it as below:
Click the CONNECT button to connect the webpage to ESP32 via WebSocket.
Now you can control the car to turn left/right, move forward/backward via the web interface.
To save the memory of ESP32, the images of the control buttons are NOT stored on ESP32. Instead, they are stored on the internet, so, your phone or PC need to have internet connection to load images for the web control page.
※ NOTE THAT:
If you modify the HTML content in the index.h and does not touch anything in esp32io.com.ino file, when you compile and upload code to ESP32, Arduino IDE will not update the HTML content.
To make Arduino IDE update the HTML content in this case, make a change in the esp32io.com.ino file (e.g. adding empty line, add a comment....)
The above ESP32 code contains line-by-line explanation. Please read the comments in the code!