Arduino Nano ESP32 - Control Car via Web

This guide shows you how to use the Arduino Nano 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.

Arduino Nano ESP32 controls robot car via Web

Hardware Preparation

1×Arduino Nano ESP32
1×USB Cable Type-C
1×2WD RC Car
1×L298N Motor Driver Module
1×IR Remote Controller Kit
1×CR2025 Battery (for IR Remote controller)
1×1.5V AA Battery (for Arduino Nano ESP32 and Car)
1×Jumper Wires
1×Breadboard
1×(Recommended) Screw Terminal Adapter for Arduino Nano

Or you can buy the following sensor kit:

1×DIYables Sensor Kit 30 types, 69 units
Disclosure: Some of the links provided in this section are Amazon affiliate links. We may receive a commission for any purchases made through these links at no additional cost to you. We appreciate your support.

Overview of 2WD RC Car and WebSocket

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 Arduino Nano ESP32. This enables sending commands to the Arduino Nano 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, Arduino Nano ESP32 code... Learn more about them at the following links:

How It Works

The Arduino Nano 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 Arduino Nano 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 Arduino Nano 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 Arduino Nano 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 Arduino Nano 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

Wiring Diagram between 2WD RC Car and Arduino Nano ESP32

The wiring diagram between Arduino Nano ESP32 and 2WD RC Car

This image is created using Fritzing. Click to enlarge image

Typically, you need two power sources:

  • One for the motor via the L298N module.
  • Another for the Arduino Nano ESP32 board, L298N module (Motor driver).

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 all of three jumpers from ENA and ENB pins from 5 volts on the L298N module.
  • Do the wiring as the above diagram.

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.

Arduino Nano ESP32 Code

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 Arduino Nano ESP32 code, which creates a web sever and WebSocket Server, and controls car
  • An .h file, which contains the webpage's content.

Detailed Instructions

To get started with Arduino Nano ESP32, follow these steps:

  • If you are new to Arduino Nano ESP32, refer to the tutorial on how to set up the environment for Arduino Nano ESP32 in the Arduino IDE.
  • Wire the components according to the provided diagram.
  • Connect the Arduino Nano ESP32 board to your computer using a USB cable.
  • Launch the Arduino IDE on your computer.
  • Select the Arduino Nano ESP32) board and its corresponding 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.
Arduino Nano ESP32 ESPAsyncWebServer library
  • You will be asked to install the dependency. Click Install All button.
Arduino Nano ESP32 ESPAsyncWebServer dependencies library
  • Search “WebSockets”, then find the WebSockets created by Markus Sattler.
  • Click Install button to install WebSockets library.
Arduino Nano ESP32 WebSockets library
  • On Arduino IDE, create new sketch, Give it a name, for example, newbiely.com.ino
  • Copy the below code and open with Arduino IDE
/* * This Arduino Nano ESP32 code was developed by newbiely.com * * This Arduino Nano ESP32 code is made available for public use without any restriction * * For comprehensive instructions and wiring diagrams, please visit: * https://newbiely.com/tutorials/arduino-nano-esp32/arduino-nano-esp32-controls-car-via-web */ #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 D7 // The Arduino Nano ESP32 pin connected to the ENA pin L298N #define IN1_PIN D6 // The Arduino Nano ESP32 pin connected to the IN1 pin L298N #define IN2_PIN D5 // The Arduino Nano ESP32 pin connected to the IN2 pin L298N #define IN3_PIN D4 // The Arduino Nano ESP32 pin connected to the IN3 pin L298N #define IN4_PIN D3 // The Arduino Nano ESP32 pin connected to the IN4 pin L298N #define ENB_PIN D2 // The Arduino Nano ESP32 pin connected to the ENB pin L298N const char* ssid = "YOUR_WIFI_SSID"; // CHANGE IT const char* password = "YOUR_WIFI_PASSWORD"; // CHANGE IT AsyncWebServer server(80); WebSocketsServer webSocket = WebSocketsServer(81); // WebSocket server on port 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: //Serial.printf("[%u] Received text: %s\n", num, payload); 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); // set full speed digitalWrite(ENB_PIN, HIGH); // set full speed // Connect to Wi-Fi WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(1000); Serial.println("Connecting to WiFi..."); } Serial.println("Connected to WiFi"); // Initialize WebSocket server webSocket.begin(); webSocket.onEvent(webSocketEvent); // Serve a basic HTML page with JavaScript to create the WebSocket connection server.on("/", HTTP_GET, [](AsyncWebServerRequest* request) { Serial.println("Web Server: received a web page request"); String html = HTML_CONTENT; // Use the HTML content from the servo_html.h file request->send(200, "text/html", html); }); server.begin(); Serial.print("Arduino Nano ESP32 Web Server's IP address: "); Serial.println(WiFi.localIP()); } void loop() { webSocket.loop(); // TO DO: Your code here } 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); }
  • Create the index.h file On Arduino IDE by:
    • Either click on the button just below the serial monitor icon and choose New Tab, or use Ctrl+Shift+N keys.
    Arduino IDE 2 adds file
    • Give the file's name index.h and click OK button
    Arduino IDE 2 adds file index.h
    • Copy the below code and paste it to the index.h.
    /* * This Arduino Nano ESP32 code was developed by newbiely.com * * This Arduino Nano ESP32 code is made available for public use without any restriction * * For comprehensive instructions and wiring diagrams, please visit: * https://newbiely.com/tutorials/arduino-nano-esp32/arduino-nano-esp32-controls-car-via-web */ const char *HTML_CONTENT = R"=====( <!DOCTYPE html> <html> <head> <title>Arduino Nano 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; // MessageEvent //alert("msg : " + e_msg.data); } 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://" + window.location.host + ":81"); 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>Arduino Nano 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: newbiely.com.ino and index.h
    • Click Upload button on Arduino IDE to upload code to Arduino Nano ESP32
    • Open the Serial Monitor
    • Check out the result on Serial Monitor.
    COM6
    Send
    Connecting to WiFi... Connected to WiFi Arduino Nano ESP32 Web Server's IP address IP address: 192.168.0.2
    Autoscroll Show timestamp
    Clear output
    9600 baud  
    Newline  
    • 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:
    Arduino Nano ESP32 controls car via web browser
    • The JavaScript code of the webpage automatically creates the WebSocket connection to Arduino Nano ESP32.
    • 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 Arduino Nano 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 newbiely.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 newbiely.com.ino file (e.g. adding empty line, add a comment....)

    Line-by-line Code Explanation

    The above Arduino Nano ESP32 code contains line-by-line explanation. Please read the comments in the code!