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.
Or you can buy the following sensor kits:
Disclosure: Some links in this section are Amazon affiliate links. If you make a purchase through these links, we may earn a commission at no extra cost to you.
Additionally, some links direct to products from our own brand, DIYables.
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:
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 |
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
Normally, you would need two separate power sources:
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.
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.
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.
On Arduino IDE, create new sketch, Give it a name, for example, ArduinoGetStarted.com.ino
Copy the below code and open with Arduino IDE
#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
#define IN1_PIN 6
#define IN2_PIN 5
#define IN3_PIN 4
#define IN4_PIN 3
#define ENB_PIN 2
const char *ssid = "YOUR_WIFI_SSID";
const char *password = "YOUR_WIFI_PASSWORD";
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);
digitalWrite(ENB_PIN, HIGH);
Serial.begin(9600);
String fv = WiFi.firmwareVersion();
if (fv < WIFI_FIRMWARE_LATEST_VERSION)
Serial.println("Please upgrade the firmware");
while (status != WL_CONNECTED) {
Serial.print("Attempting to connect to SSID: ");
Serial.println(password);
status = WiFi.begin(ssid, password);
delay(4000);
}
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();
WiFiClient client = server.available();
if (client) {
while (client.connected()) {
if (client.available()) {
String HTTP_header = client.readStringUntil('\n');
if (HTTP_header.equals("\r"))
break;
Serial.print("<< ");
Serial.println(HTTP_header);
}
}
client.println("HTTP/1.1 200 OK");
client.println("Content-Type: text/html");
client.println("Connection: close");
client.println();
String html = String(HTML_CONTENT);
client.println(html);
client.flush();
delay(100);
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:
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('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>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="https://amazon.com/diyables">DIYables</a></div>
</body>
</html>
)=====";
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\ArduinoGetStarted.com\ArduinoGetStarted.com.ino:2:
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:
#define NETWORK_CONTROLLER ETHERNET_CONTROLLER_W5X00
#define NETWORK_CONTROLLER NETWORK_CONTROLLER_WIFI
Connecting to WiFi...
Connected to WiFi
Arduino 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 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 ArduinoGetStarted.com.ino 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 ArduinoGetStarted.com.ino file (e.g. adding empty line, add a comment....)
The above Arduino code contains line-by-line explanation. Please read the comments in the code!
※ OUR MESSAGES
You can share the link of this tutorial anywhere. Howerver, please do not copy the content to share on other websites. We took a lot of time and effort to create the content of this tutorial, please respect our work!