Arduino - Control Car via Web

This tutorial instructs you on how to control a robot car wirelessly using an Arduino from a web browser on your smartphone or PC via WiFi. The control is managed through a graphical web user interface that utilizes WebSocket technology, enabling smooth and dynamic operation of the car.

Arduino controls robot car via Web

Hardware Required

1×Arduino UNO R4 WiFi
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 and Car)
1×Jumper Wires
1×(Recommended) Screw Terminal Block Shield for Arduino Uno
1×(Optional) Transparent Acrylic Enclosure For Arduino Uno

Or you can buy the following sensor kits:

1×DIYables Sensor Kit (30 sensors/displays)
1×DIYables Sensor Kit (18 sensors/displays)
Please note: These are Amazon affiliate links. If you buy the components through these links, We will get a commission at no extra cost to you. We appreciate it.

About 2WD RC Car and WebSocket

Now, why choose WebSocket? Here are the reasons:

  • Without WebSocket, you would have to reload the page every time you want to change the car’s direction. This is not efficient!
  • With WebSocket, a special connection is established between the webpage and the Arduino. This allows you to send commands to the Arduino in the background, without the need to reload the page. The outcome? The robot car responds smoothly and instantly. Quite impressive, right?

In summary, WebSocket facilitates 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 Arduino, Arduino code... Learn more about them at the following links:

How It Works

The Arduino code sets up both a web server and a WebSocket server. Here’s how it functions:

  • When you type the Arduino's IP address into a web browser, it sends a request for the webpage (User Interface) from the Arduino.
  • The Arduino’s web server replies by delivering the content of the webpage (HTML, CSS, JavaScript).
  • Your web browser then displays this webpage.
  • Inside the webpage, the JavaScript code initiates a WebSocket connection with the WebSocket server on the Arduino.
  • Once this WebSocket connection is active, any button presses or releases on the webpage cause the JavaScript code to discreetly send commands to the Arduino via this connection.
  • The WebSocket server on the Arduino receives these commands and directs the robot car accordingly.

Below is a table showing the list of commands that the webpage sends to the Arduino based on user actions:

User's Action Button Command Car Action

Wiring Diagram between 2WD RC Car and Arduino

Arduino 2WD RC Car Wiring Diagram

This image is created using Fritzing. Click to enlarge image

Normally, you would need two separate power sources:

  • One for the motor.
  • Another for both the Arduino board and the L298N module (which serves as the motor driver).

However, you can simplify this setup by using just one power source – four 1.5V batteries to make a total of 6V. Here's how to do it:

  • Connect the batteries to the L298N module as directed.
  • Remove the two jumpers from the ENA and ENB pins to the 5 volts on the L298N module.
  • Insert a jumper labeled 5VEN (indicated by a yellow circle on the diagram).
  • Connect the 12V pin on the L298N module to the Vin pin on the Arduino. This connection will power the Arduino directly from the batteries.

The 2WD RC car includes an on/off switch, providing you with the option to connect the battery through the switch. This setup allows you to turn the power for the car on and off as needed. If you prefer a simpler arrangement, you can choose to bypass the switch altogether.

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

Quick Steps

  • If this is the first time you use Arduino Uno R4, see how to setup environment for Arduino Uno R4 on Arduino IDE.
  • Do the wiring as above image.
  • Connect the Arduino board to your PC via a micro USB cable
  • Open Arduino IDE on your PC.
  • Select the right Arduino board (Arduino Uno R4 WiFi) and COM port.
  • Open the Library Manager by clicking on the Library Manager icon on the left navigation bar of Arduino IDE.
  • Search “mWebSockets”, then find the mWebSockets created by Dawid Kurek.
  • Click Install button to install mWebSockets library.
Arduino mWebSockets library
  • On Arduino IDE, create new sketch, Give it a name, for example,
  • Copy the below code and open with Arduino IDE
/* * Created by * * This example code is in the public domain * * Tutorial page: */ #include <WiFiS3.h> #include <WebSocketServer.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 7 // The Arduino pin connected to the ENA pin L298N #define IN1_PIN 6 // The Arduino pin connected to the IN1 pin L298N #define IN2_PIN 5 // The Arduino pin connected to the IN2 pin L298N #define IN3_PIN 4 // The Arduino pin connected to the IN3 pin L298N #define IN4_PIN 3 // The Arduino pin connected to the IN4 pin L298N #define ENB_PIN 2 // The Arduino pin connected to the ENB pin L298N const char *ssid = "YOUR_WIFI_SSID"; // CHANGE IT const char *password = "YOUR_WIFI_PASSWORD"; // CHANGE IT using namespace net; WebSocketServer webSocket(81); WiFiServer server(80); int status = WL_IDLE_STATUS; 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 //Initialize serial and wait for port to open: Serial.begin(9600); String fv = WiFi.firmwareVersion(); if (fv < WIFI_FIRMWARE_LATEST_VERSION) Serial.println("Please upgrade the firmware"); // attempt to connect to WiFi network: while (status != WL_CONNECTED) { Serial.print("Attempting to connect to SSID: "); Serial.println(password); // Connect to WPA/WPA2 network. Change this line if using open or WEP network: status = WiFi.begin(ssid, password); // wait 4 seconds for connection: delay(4000); } // print your board's IP address: Serial.print("IP Address: "); Serial.println(WiFi.localIP()); server.begin(); webSocket.onConnection([](WebSocket &ws) { const auto protocol = ws.getProtocol(); if (protocol) { Serial.print(F("Client protocol: ")); Serial.println(protocol); } ws.onMessage([](WebSocket &ws, const WebSocket::DataType dataType, const char *message, uint16_t length) { String cmd_str = String((char *)message); int command = cmd_str.toInt(); Serial.print("command: "); Serial.println(command); switch (dataType) { case WebSocket::DataType::TEXT: 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; case WebSocket::DataType::BINARY: Serial.println(F("Received binary data")); break; } }); ws.onClose([](WebSocket &, const WebSocket::CloseCode, const char *, uint16_t) { Serial.println(F("Disconnected")); }); Serial.print(F("New WebSocket Connnection from client: ")); Serial.println(ws.getRemoteIP()); }); webSocket.begin(); } void loop() { webSocket.listen(); // listen for incoming clients WiFiClient client = server.available(); if (client) { // read the HTTP request header line by line while (client.connected()) { if (client.available()) { String HTTP_header = client.readStringUntil('\n'); // read the header line of HTTP request if (HTTP_header.equals("\r")) // the end of HTTP request break; Serial.print("<< "); Serial.println(HTTP_header); // print HTTP request to Serial Monitor } } // send the HTTP response header client.println("HTTP/1.1 200 OK"); client.println("Content-Type: text/html"); client.println("Connection: close"); // the connection will be closed after completion of the response client.println(); // the separator between HTTP header and body String html = String(HTML_CONTENT); client.println(html); client.flush(); // give the web browser time to receive the data delay(100); // close the connection: client.stop(); } } 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:
    • 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.
    /* * Created by * * This example code is in the public domain * * Tutorial page: */ const char *HTML_CONTENT = R"=====( <!DOCTYPE html> <html> <head> <title>Arduino 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('') no-repeat; background-size: contain; left: 200px; top: 0px; transform: translateX(-50%); } .button_down { background: url('') no-repeat; background-size: contain; left:200px; bottom: 0px; transform: translateX(-50%); } .button_right { background: url('') no-repeat; background-size: contain; right: 0px; top: 200px; transform: translateY(-50%); } .button_left { background: url('') no-repeat; background-size: contain; left:0px; top: 200px; transform: translateY(-50%); } .button_stop { background: url('') 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 : " +; } 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://" + + ":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.currentTarget) { var id =; send_command(id); = "url('" + img_name_lookup[id] + "_active.png')"; } event.stopPropagation(); event.preventDefault(); } function mouse_up(event) { if ( !== event.currentTarget) { var id =; send_command(CMD_STOP); = "url('" + 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 - 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="">DIYables</a></div> </body> </html> )=====";
    • Now you have the code in two files: and index.h
    • Click Upload button on Arduino IDE to upload code to Arduino

    You will see an error like below:

    In file included from c:\Users\YOU_ACCOUNT\Documents\Arduino\libraries\mWebSockets\src/utility.h:3:0, from c:\Users\YOU_ACCOUNT\Documents\Arduino\libraries\mWebSockets\src/WebSocket.h:5, from c:\Users\YOU_ACCOUNT\Documents\Arduino\libraries\mWebSockets\src/WebSocketServer.h:5, from C:\Users\YOU_ACCOUNT\Documents\Arduino\\ C:\Users\YOU_ACCOUNT\Documents\Arduino\libraries\mWebSockets\src/platform.h:54:12: fatal error: Ethernet.h: No such file or directory # include <Ethernet.h> ^~~~~~~~~~~~ compilation terminated. exit status 1

    To fix this error:

    • Go to C:\Users\YOU_ACCOUNT\Documents\Arduino\libraries\mWebSockets\src/ directory.
    • Find the config.h file and open it by a text editor.
    • Look at the line 26, you will see it like below:
    • Change this line to the below and save it:
    • Click Upload button on Arduino IDE to upload code to Arduino.
    • Open the Serial Monitor
    • Check out the result on Serial Monitor.
    Connecting to WiFi... Connected to WiFi Arduino Web Server's IP address IP address:
    Autoscroll Show timestamp
    Clear output
    9600 baud  
    • 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 controls car via web browser
    • Click the CONNECT button to connect the webpage to Arduino via WebSocket.
    • Now you can control the car to turn left/right, move forward/backward via the web interface.

    To save the memory of Arduino, the images of the control buttons are NOT stored on Arduino. 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 file, when you compile and upload code to Arduino, Arduino IDE will not update the HTML content.
    • To make Arduino IDE update the HTML content in this case, make a change in the file (e.g. adding empty line, add a comment....)

    Line-by-line Code Explanation

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

The Best Arduino Starter Kit