ESP32-CAM Remote Controlled Car Robot Web Server
Build a Wi-Fi remote controlled car robot with the ESP32-CAM. You’ll be able to control the robot using a web server that displays a video streaming of what the robot “sees”. You can control your robot remotely even if it’s out of your sight. The ESP32-CAM will be programmed using Arduino IDE.
Boards compatibility: this project requires 4 GPIOs to control the DC motors. So, you can use any ESP32 camera board with 4 available GPIOs like the ESP32-CAM Ai-Thinker board or the TTGO T-Journal.
Project Overview
Before starting the project, we’ll highlight the most important features and components used to build the robot.
Wi-Fi
The robot will be controlled via Wi-Fi using your ESP32-CAM. We’ll create a web-based interface to control the robot, that can be accessed in any device inside your local network.
The web page also shows a video streaming of what the robot “sees”. For good results with video streaming, we recommend using an ESP32-CAM with external antenna.
Important: without an external antenna the video stream lags and the web server is extremely slow to control the robot.
Robot Controls
The web server has 5 controls: Forward, Backward, Left, Right, and Stop.
The robot moves as long as you’re pressing the buttons. When you release any button, the robot stops. However, we’ve included the Stop button that can be useful in case the ESP32 doesn’t receive the stop command when you release a button.
Smart Robot Chassis Kit
We’re going to use the Smart Robot Chassis Kit. You can find it in most online stores. The kit costs around $10 and it’s easy to assemble – watch this video to see how to assemble the robot chassis kit.
You can use any other chassis kit as long as it comes with two DC motors.
L298N Motor Driver
There are many ways to control DC motors. We’ll use the L298N motor driver that provides an easy way to control the speed and direction of 2 DC motors.
We won’t explain how the L298N motor driver works. You can read the following article for an in-depth tutorial about the L298N motor driver:
Power
To keep the circuitry simple, we’ll power the robot (motors) and the ESP32 using the same power source. We used a power bank/portable charger (like the ones used to charge your smartphone) and it worked well.
Note: the motors draw a lot of current, so if you feel your robot is not moving properly, you may need to use an external power supply for the motors. This means you need two different power sources. One to power the DC motors, and the other to power the ESP32.
Parts Required
For this project, we’ll use the following parts:
- ESP32-CAM AI-Thinker with external antenna
- L298N Motor Driver
- Robot Car Chassis Kit
- Power bank or other 5V power supply
- Prototyping circuit board (optional)
You can use the preceding links or go directly to MakerAdvisor.com/tools to find all the parts for your projects at the best price!
Code
Copy the following code to your Arduino IDE.
/*********
Rui Santos
Complete instructions at https://RandomNerdTutorials.com/esp32-cam-projects-ebook/
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*********/
#include "esp_camera.h"
#include <WiFi.h>
#include "esp_timer.h"
#include "img_converters.h"
#include "Arduino.h"
#include "fb_gfx.h"
#include "soc/soc.h" // disable brownout problems
#include "soc/rtc_cntl_reg.h" // disable brownout problems
#include "esp_http_server.h"
// Replace with your network credentials
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";
#define PART_BOUNDARY "123456789000000000000987654321"
#define CAMERA_MODEL_AI_THINKER
//#define CAMERA_MODEL_M5STACK_PSRAM
//#define CAMERA_MODEL_M5STACK_WITHOUT_PSRAM
//#define CAMERA_MODEL_M5STACK_PSRAM_B
//#define CAMERA_MODEL_WROVER_KIT
#if defined(CAMERA_MODEL_WROVER_KIT)
#define PWDN_GPIO_NUM -1
#define RESET_GPIO_NUM -1
#define XCLK_GPIO_NUM 21
#define SIOD_GPIO_NUM 26
#define SIOC_GPIO_NUM 27
#define Y9_GPIO_NUM 35
#define Y8_GPIO_NUM 34
#define Y7_GPIO_NUM 39
#define Y6_GPIO_NUM 36
#define Y5_GPIO_NUM 19
#define Y4_GPIO_NUM 18
#define Y3_GPIO_NUM 5
#define Y2_GPIO_NUM 4
#define VSYNC_GPIO_NUM 25
#define HREF_GPIO_NUM 23
#define PCLK_GPIO_NUM 22
#elif defined(CAMERA_MODEL_M5STACK_PSRAM)
#define PWDN_GPIO_NUM -1
#define RESET_GPIO_NUM 15
#define XCLK_GPIO_NUM 27
#define SIOD_GPIO_NUM 25
#define SIOC_GPIO_NUM 23
#define Y9_GPIO_NUM 19
#define Y8_GPIO_NUM 36
#define Y7_GPIO_NUM 18
#define Y6_GPIO_NUM 39
#define Y5_GPIO_NUM 5
#define Y4_GPIO_NUM 34
#define Y3_GPIO_NUM 35
#define Y2_GPIO_NUM 32
#define VSYNC_GPIO_NUM 22
#define HREF_GPIO_NUM 26
#define PCLK_GPIO_NUM 21
#elif defined(CAMERA_MODEL_M5STACK_WITHOUT_PSRAM)
#define PWDN_GPIO_NUM -1
#define RESET_GPIO_NUM 15
#define XCLK_GPIO_NUM 27
#define SIOD_GPIO_NUM 25
#define SIOC_GPIO_NUM 23
#define Y9_GPIO_NUM 19
#define Y8_GPIO_NUM 36
#define Y7_GPIO_NUM 18
#define Y6_GPIO_NUM 39
#define Y5_GPIO_NUM 5
#define Y4_GPIO_NUM 34
#define Y3_GPIO_NUM 35
#define Y2_GPIO_NUM 17
#define VSYNC_GPIO_NUM 22
#define HREF_GPIO_NUM 26
#define PCLK_GPIO_NUM 21
#elif defined(CAMERA_MODEL_AI_THINKER)
#define PWDN_GPIO_NUM 32
#define RESET_GPIO_NUM -1
#define XCLK_GPIO_NUM 0
#define SIOD_GPIO_NUM 26
#define SIOC_GPIO_NUM 27
#define Y9_GPIO_NUM 35
#define Y8_GPIO_NUM 34
#define Y7_GPIO_NUM 39
#define Y6_GPIO_NUM 36
#define Y5_GPIO_NUM 21
#define Y4_GPIO_NUM 19
#define Y3_GPIO_NUM 18
#define Y2_GPIO_NUM 5
#define VSYNC_GPIO_NUM 25
#define HREF_GPIO_NUM 23
#define PCLK_GPIO_NUM 22
#elif defined(CAMERA_MODEL_M5STACK_PSRAM_B)
#define PWDN_GPIO_NUM -1
#define RESET_GPIO_NUM 15
#define XCLK_GPIO_NUM 27
#define SIOD_GPIO_NUM 22
#define SIOC_GPIO_NUM 23
#define Y9_GPIO_NUM 19
#define Y8_GPIO_NUM 36
#define Y7_GPIO_NUM 18
#define Y6_GPIO_NUM 39
#define Y5_GPIO_NUM 5
#define Y4_GPIO_NUM 34
#define Y3_GPIO_NUM 35
#define Y2_GPIO_NUM 32
#define VSYNC_GPIO_NUM 25
#define HREF_GPIO_NUM 26
#define PCLK_GPIO_NUM 21
#else
#error "Camera model not selected"
#endif
#define MOTOR_1_PIN_1 14
#define MOTOR_1_PIN_2 15
#define MOTOR_2_PIN_1 13
#define MOTOR_2_PIN_2 12
static const char* _STREAM_CONTENT_TYPE = "multipart/x-mixed-replace;boundary=" PART_BOUNDARY;
static const char* _STREAM_BOUNDARY = "\r\n--" PART_BOUNDARY "\r\n";
static const char* _STREAM_PART = "Content-Type: image/jpeg\r\nContent-Length: %u\r\n\r\n";
httpd_handle_t camera_httpd = NULL;
httpd_handle_t stream_httpd = NULL;
static const char PROGMEM INDEX_HTML[] = R"rawliteral(
<html>
<head>
<title>ESP32-CAM Robot</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
body { font-family: Arial; text-align: center; margin:0px auto; padding-top: 30px;}
table { margin-left: auto; margin-right: auto; }
td { padding: 8 px; }
.button {
background-color: #2f4468;
border: none;
color: white;
padding: 10px 20px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 18px;
margin: 6px 3px;
cursor: pointer;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
-webkit-tap-highlight-color: rgba(0,0,0,0);
}
img { width: auto ;
max-width: 100% ;
height: auto ;
}
</style>
</head>
<body>
<h1>ESP32-CAM Robot</h1>
<img src="" id="photo" >
<table>
<tr><td colspan="3" align="center"><button class="button" onmousedown="toggleCheckbox('forward');" ontouchstart="toggleCheckbox('forward');" onmouseup="toggleCheckbox('stop');" ontouchend="toggleCheckbox('stop');">Forward</button></td></tr>
<tr><td align="center"><button class="button" onmousedown="toggleCheckbox('left');" ontouchstart="toggleCheckbox('left');" onmouseup="toggleCheckbox('stop');" ontouchend="toggleCheckbox('stop');">Left</button></td><td align="center"><button class="button" onmousedown="toggleCheckbox('stop');" ontouchstart="toggleCheckbox('stop');">Stop</button></td><td align="center"><button class="button" onmousedown="toggleCheckbox('right');" ontouchstart="toggleCheckbox('right');" onmouseup="toggleCheckbox('stop');" ontouchend="toggleCheckbox('stop');">Right</button></td></tr>
<tr><td colspan="3" align="center"><button class="button" onmousedown="toggleCheckbox('backward');" ontouchstart="toggleCheckbox('backward');" onmouseup="toggleCheckbox('stop');" ontouchend="toggleCheckbox('stop');">Backward</button></td></tr>
</table>
<script>
function toggleCheckbox(x) {
var xhr = new XMLHttpRequest();
xhr.open("GET", "/action?go=" + x, true);
xhr.send();
}
window.onload = document.getElementById("photo").src = window.location.href.slice(0, -1) + ":81/stream";
</script>
</body>
</html>
)rawliteral";
static esp_err_t index_handler(httpd_req_t *req){
httpd_resp_set_type(req, "text/html");
return httpd_resp_send(req, (const char *)INDEX_HTML, strlen(INDEX_HTML));
}
static esp_err_t stream_handler(httpd_req_t *req){
camera_fb_t * fb = NULL;
esp_err_t res = ESP_OK;
size_t _jpg_buf_len = 0;
uint8_t * _jpg_buf = NULL;
char * part_buf[64];
res = httpd_resp_set_type(req, _STREAM_CONTENT_TYPE);
if(res != ESP_OK){
return res;
}
while(true){
fb = esp_camera_fb_get();
if (!fb) {
Serial.println("Camera capture failed");
res = ESP_FAIL;
} else {
if(fb->width > 400){
if(fb->format != PIXFORMAT_JPEG){
bool jpeg_converted = frame2jpg(fb, 80, &_jpg_buf, &_jpg_buf_len);
esp_camera_fb_return(fb);
fb = NULL;
if(!jpeg_converted){
Serial.println("JPEG compression failed");
res = ESP_FAIL;
}
} else {
_jpg_buf_len = fb->len;
_jpg_buf = fb->buf;
}
}
}
if(res == ESP_OK){
size_t hlen = snprintf((char *)part_buf, 64, _STREAM_PART, _jpg_buf_len);
res = httpd_resp_send_chunk(req, (const char *)part_buf, hlen);
}
if(res == ESP_OK){
res = httpd_resp_send_chunk(req, (const char *)_jpg_buf, _jpg_buf_len);
}
if(res == ESP_OK){
res = httpd_resp_send_chunk(req, _STREAM_BOUNDARY, strlen(_STREAM_BOUNDARY));
}
if(fb){
esp_camera_fb_return(fb);
fb = NULL;
_jpg_buf = NULL;
} else if(_jpg_buf){
free(_jpg_buf);
_jpg_buf = NULL;
}
if(res != ESP_OK){
break;
}
//Serial.printf("MJPG: %uB\n",(uint32_t)(_jpg_buf_len));
}
return res;
}
static esp_err_t cmd_handler(httpd_req_t *req){
char* buf;
size_t buf_len;
char variable[32] = {0,};
buf_len = httpd_req_get_url_query_len(req) + 1;
if (buf_len > 1) {
buf = (char*)malloc(buf_len);
if(!buf){
httpd_resp_send_500(req);
return ESP_FAIL;
}
if (httpd_req_get_url_query_str(req, buf, buf_len) == ESP_OK) {
if (httpd_query_key_value(buf, "go", variable, sizeof(variable)) == ESP_OK) {
} else {
free(buf);
httpd_resp_send_404(req);
return ESP_FAIL;
}
} else {
free(buf);
httpd_resp_send_404(req);
return ESP_FAIL;
}
free(buf);
} else {
httpd_resp_send_404(req);
return ESP_FAIL;
}
sensor_t * s = esp_camera_sensor_get();
int res = 0;
if(!strcmp(variable, "forward")) {
Serial.println("Forward");
digitalWrite(MOTOR_1_PIN_1, 1);
digitalWrite(MOTOR_1_PIN_2, 0);
digitalWrite(MOTOR_2_PIN_1, 1);
digitalWrite(MOTOR_2_PIN_2, 0);
}
else if(!strcmp(variable, "left")) {
Serial.println("Left");
digitalWrite(MOTOR_1_PIN_1, 0);
digitalWrite(MOTOR_1_PIN_2, 1);
digitalWrite(MOTOR_2_PIN_1, 1);
digitalWrite(MOTOR_2_PIN_2, 0);
}
else if(!strcmp(variable, "right")) {
Serial.println("Right");
digitalWrite(MOTOR_1_PIN_1, 1);
digitalWrite(MOTOR_1_PIN_2, 0);
digitalWrite(MOTOR_2_PIN_1, 0);
digitalWrite(MOTOR_2_PIN_2, 1);
}
else if(!strcmp(variable, "backward")) {
Serial.println("Backward");
digitalWrite(MOTOR_1_PIN_1, 0);
digitalWrite(MOTOR_1_PIN_2, 1);
digitalWrite(MOTOR_2_PIN_1, 0);
digitalWrite(MOTOR_2_PIN_2, 1);
}
else if(!strcmp(variable, "stop")) {
Serial.println("Stop");
digitalWrite(MOTOR_1_PIN_1, 0);
digitalWrite(MOTOR_1_PIN_2, 0);
digitalWrite(MOTOR_2_PIN_1, 0);
digitalWrite(MOTOR_2_PIN_2, 0);
}
else {
res = -1;
}
if(res){
return httpd_resp_send_500(req);
}
httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
return httpd_resp_send(req, NULL, 0);
}
void startCameraServer(){
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
config.server_port = 80;
httpd_uri_t index_uri = {
.uri = "/",
.method = HTTP_GET,
.handler = index_handler,
.user_ctx = NULL
};
httpd_uri_t cmd_uri = {
.uri = "/action",
.method = HTTP_GET,
.handler = cmd_handler,
.user_ctx = NULL
};
httpd_uri_t stream_uri = {
.uri = "/stream",
.method = HTTP_GET,
.handler = stream_handler,
.user_ctx = NULL
};
if (httpd_start(&camera_httpd, &config) == ESP_OK) {
httpd_register_uri_handler(camera_httpd, &index_uri);
httpd_register_uri_handler(camera_httpd, &cmd_uri);
}
config.server_port += 1;
config.ctrl_port += 1;
if (httpd_start(&stream_httpd, &config) == ESP_OK) {
httpd_register_uri_handler(stream_httpd, &stream_uri);
}
}
void setup() {
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); //disable brownout detector
pinMode(MOTOR_1_PIN_1, OUTPUT);
pinMode(MOTOR_1_PIN_2, OUTPUT);
pinMode(MOTOR_2_PIN_1, OUTPUT);
pinMode(MOTOR_2_PIN_2, OUTPUT);
Serial.begin(115200);
Serial.setDebugOutput(false);
camera_config_t config;
config.ledc_channel = LEDC_CHANNEL_0;
config.ledc_timer = LEDC_TIMER_0;
config.pin_d0 = Y2_GPIO_NUM;
config.pin_d1 = Y3_GPIO_NUM;
config.pin_d2 = Y4_GPIO_NUM;
config.pin_d3 = Y5_GPIO_NUM;
config.pin_d4 = Y6_GPIO_NUM;
config.pin_d5 = Y7_GPIO_NUM;
config.pin_d6 = Y8_GPIO_NUM;
config.pin_d7 = Y9_GPIO_NUM;
config.pin_xclk = XCLK_GPIO_NUM;
config.pin_pclk = PCLK_GPIO_NUM;
config.pin_vsync = VSYNC_GPIO_NUM;
config.pin_href = HREF_GPIO_NUM;
config.pin_sscb_sda = SIOD_GPIO_NUM;
config.pin_sscb_scl = SIOC_GPIO_NUM;
config.pin_pwdn = PWDN_GPIO_NUM;
config.pin_reset = RESET_GPIO_NUM;
config.xclk_freq_hz = 20000000;
config.pixel_format = PIXFORMAT_JPEG;
if(psramFound()){
config.frame_size = FRAMESIZE_VGA;
config.jpeg_quality = 10;
config.fb_count = 2;
} else {
config.frame_size = FRAMESIZE_SVGA;
config.jpeg_quality = 12;
config.fb_count = 1;
}
// Camera init
esp_err_t err = esp_camera_init(&config);
if (err != ESP_OK) {
Serial.printf("Camera init failed with error 0x%x", err);
return;
}
// Wi-Fi connection
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
Serial.print("Camera Stream Ready! Go to: http://");
Serial.println(WiFi.localIP());
// Start streaming web server
startCameraServer();
}
void loop() {
}
Insert your network credentials and the code should work straight away.
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";
How the Code Works
Let’s take a look at the relevant parts to control the robot. Define the GPIOs that will control the motors. Each motor is controlled by two pins.
#define MOTOR_1_PIN_1 14
#define MOTOR_1_PIN_2 15
#define MOTOR_2_PIN_1 13
#define MOTOR_2_PIN_2 12
When you click the buttons, you make a request on a different URL.
<table>
<tr><td colspan="3" align="center"><button class="button" onmousedown="toggleCheckbox('forward');" ontouchstart="toggleCheckbox('forward');" onmouseup="toggleCheckbox('stop');" ontouchend="toggleCheckbox('stop');">Forward</button></td></tr>
<tr><td align="center"><button class="button" onmousedown="toggleCheckbox('left');" ontouchstart="toggleCheckbox('left');" onmouseup="toggleCheckbox('stop');" ontouchend="toggleCheckbox('stop');">Left</button></td><td align="center"><button class="button" onmousedown="toggleCheckbox('stop');" ontouchstart="toggleCheckbox('stop');">Stop</button></td><td align="center"><button class="button" onmousedown="toggleCheckbox('right');" ontouchstart="toggleCheckbox('right');" onmouseup="toggleCheckbox('stop');" ontouchend="toggleCheckbox('stop');">Right</button></td></tr>
<tr><td colspan="3" align="center"><button class="button" onmousedown="toggleCheckbox('backward');" ontouchstart="toggleCheckbox('backward');" onmouseup="toggleCheckbox('stop');" ontouchend="toggleCheckbox('stop');">Backward</button></td></tr>
</table>
<script>
function toggleCheckbox(x) {
var xhr = new XMLHttpRequest();
xhr.open("GET", "/action?go=" + x, true);
xhr.send();
}
window.onload = document.getElementById("photo").src = window.location.href.slice(0, -1) + ":81/stream";
</script>
Here’s the requests made depending on the button that is being pressed:
Forward:
<ESP_IP_ADDRESS>/action?go=forward
Backward:
/action?go=backward
Left:
/action?go=left
Right:
/action?go=right
Stop:
/action?go=stop
When you release the button, a request is made on the /action?go=stop URL. The robot only moves as long as you’re pressing the buttons.
Handle Requests
To handle what happens when we get requests on those URLs, we use these if… else statements:
if(!strcmp(variable, "forward")) {
Serial.println("Forward");
digitalWrite(MOTOR_1_PIN_1, 1);
digitalWrite(MOTOR_1_PIN_2, 0);
digitalWrite(MOTOR_2_PIN_1, 1);
digitalWrite(MOTOR_2_PIN_2, 0);
}
else if(!strcmp(variable, "left")) {
Serial.println("Left");
digitalWrite(MOTOR_1_PIN_1, 0);
digitalWrite(MOTOR_1_PIN_2, 1);
digitalWrite(MOTOR_2_PIN_1, 1);
digitalWrite(MOTOR_2_PIN_2, 0);
}
else if(!strcmp(variable, "right")) {
Serial.println("Right");
digitalWrite(MOTOR_1_PIN_1, 1);
digitalWrite(MOTOR_1_PIN_2, 0);
digitalWrite(MOTOR_2_PIN_1, 0);
digitalWrite(MOTOR_2_PIN_2, 1);
}
else if(!strcmp(variable, "backward")) {
Serial.println("Backward");
digitalWrite(MOTOR_1_PIN_1, 0);
digitalWrite(MOTOR_1_PIN_2, 1);
digitalWrite(MOTOR_2_PIN_1, 0);
digitalWrite(MOTOR_2_PIN_2, 1);
}
else if(!strcmp(variable, "stop")) {
Serial.println("Stop");
digitalWrite(MOTOR_1_PIN_1, 0);
digitalWrite(MOTOR_1_PIN_2, 0);
digitalWrite(MOTOR_2_PIN_1, 0);
digitalWrite(MOTOR_2_PIN_2, 0);
}
Testing the Code
After inserting your network credentials, you can upload the code to your ESP32-CAM board. If you don’t know how to upload code to the board, follow the next tutorial:
After uploading, open the Serial Monitor to get its IP address.
Open a browser and type the ESP IP address. A similar web page should load:
Press the buttons and take a look at the Serial Monitor to see if it is streaming without lag and if it is receiving the commands without crashing.
If everything is working properly, it’s time to assemble the circuit.
Circuit
After assembling the robot chassis, you can wire the circuit by following the next schematic diagram.
Start by connecting the ESP32-CAM to the motor driver as shown in the schematic diagram. You can either use a mini breadboard or a stripboard to place your ESP32-CAM and build the circuit.
The following table shows the connections between the ESP32-CAM and the L298N Motor Driver.
L298N Motor Driver | ESP32-CAM |
IN1 | GPIO 14 |
IN2 | GPIO 15 |
IN3 | GPIO 13 |
IN4 | GPIO 12 |
We assembled all the connections on a mini stripboard as shown below.
After that, wire each motor to its terminal block.
Note: we suggest soldering a 0.1 uF ceramic capacitor to the positive and negative terminals of each motor, as shown in the diagram to help smooth out any voltage spikes. Additionally, you can solder a slider switch to the red wire that comes from the power bank. This way, you can turn the power on and off.
Finally, apply power with a power bank as shown in the schematic diagram. You need to strip a USB cable. In this example, the ESP32-CAM and the motors are being powered using the same power source and it works well.
Note: the motors draw a lot of current, so if you feel your robot is not moving fast enough, you may need to use an external power supply for the motors. This means you need two different power sources. One to power the DC motors, and the other to power the ESP32. You can use a 4 AA battery pack to power the motors. When you get your robot chassis kit, you usually get a battery holder for 4 AA batteries.
Your robot should look similar to the following figure:
Don’t forget that you should use an external antenna with the ESP32-CAM, otherwise the web server might be extremely slow.
Demonstration
Open a browser on the ESP32-CAM IP address, and you should be able to control your robot. The web server works well on a laptop computer or smartphone.
You can only have the web server open in one device/tab at a time.
Wrapping Up
In this tutorial you’ve learned how to build a remote controlled robot using the ESP32-CAM and how to control it using a web server.
Controlling DC motors with the ESP32-CAM is the same as controlling them using a “regular” ESP32. Read this tutorial to learn more: ESP32 with DC Motor and L298N Motor Driver – Control Speed and Direction.
If you want to control your robot outside the range of your local network, you might consider setting the ESP32-CAM as an access point. This way, the ESP32-CAM doesn’t need to connect to your router, it creates its own wi-fi network and nearby wi-fi devices like your smartphone can connect to it.
For more projects and tutorials with the ESP32-CAM:
I am always surprised by your funny and motivating projects. This project again. A little hint regarding the human interface: There is a nice JS joystick that can be used by the control of mobile robots (see link). Regards Wijnand…
Hi.
That’s an interesting alternative way of controlling the robot.
Thanks for sharing.
Regards,
Sara
But i cant find a vid (its easier with a vid)
Thank you for the one more interesting project
You’re welcome.
Regards,
Sara
Hello
Very nice project which can follow, I think, to the excellent tutorial “ESP32-CAM Pan and Tilt video streaming web server (2 axes)”
https://randomnerdtutorials.com/esp32-cam-pan-and-tilt-2-axis/#comment-630951
Thanks, that’s great
Hallo…
How could I extend I/O esp32-cam with PCF8574?
Thank and kiss…
Hi.
We don’t have any tutorials about that.
Search for a tutorial with that module and the ESP32. It will be similar for the ESP32-CAM.
Regards,
Sara
have you had any luck with the PCF8574 and the ESP32-CAM . I too need more I/O’s from it.
SIR I AM INTREST FOR FACERECOGNITATION DOOR LOCK SYSTEM.
PLEASE SEND ME CODE .
Thank you
“How could I extend I/O esp32-cam with PCF8574?”
I made a project using an I2C interface:
#define I2C_SDA 13
#define I2C_SCL 15
You may have to disable any pullups on these lines during programming
int SCLpin = I2C_SCL;
int SDApin = I2C_SDA;
/* setup /
Wire.begin(SDApin, SCLpin);
/ code specific to your chip */
Hello
It would be nice to monitor the streamed video on another esp32 (connected to a tft display or likewise). Something like a remote monitor.
Do you have any suggestions? I did not find any tutorial.
Thanks!
Hi, interesting project from different points of view.
I would be interested in your last suggested solution, and that is to set esp as the access point.
Thank you guys.
Where I can find the ESP32-CAM IP address?
Hi.
After uploading the code, remove GPIO 0 from GND, open the Serial Monitor and press the RST button.
The IP address will be printed on the Serial Monitor.
Regards,
Sara
Done but I get only this;
ets Jun 8 2016 00:22:57
rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:1
load:0x3fff0018,len:4
load:0x3fff001c,len:1216
ho 0 tail 12 room 4
load:0x40078000,len:9720
ho 0 tail 12 room 4
load:0x40080400,len:6352
entry 0x400806b8
Testing DC Motor…Moving Forward
Motor stopped
Moving Backwards
Motor stopped
Forward with duty cycle: 200
Forward with duty cycle: 205
Forward with duty cycle: 210
Sorry Sara,
I uploaded the wrong sketch, all Ok following your instructions.
Thanks
Renzo
Great!
Realy very good project.
Iam very happy.
thank you
All runned very well, but now suddenly in my smartphone Android i get only this message:
“header files are too long for server to interpret”.
What happen, what have I to do?
Thanks
Renzo
Thanks for a very nice project but may I ask where can I find esp_camera.h.
Dear Sara,
as I wrote in a my previus post the sketck runned perfectly in my smartphone.
Now I get only this message :“header files are too long for server to interpret”.
On the serial monitor I continue di see all command an the imagine.
I tried to find the solution on the Web bud i didn’t get any reply.
There is someone that can help me.
Thanks
Renzo
Hi.
I’m sorry, but I don’t know how to solve that issue.
See these suggestions:
– https://rntlab.com/question/esp32-camera-headers-and-browser/
– https://stackoverflow.com/questions/67849381/esp32-httpd-header-fields-are-too-long-for-the-server-to-interpret
I’m not sure if this works, but I hope it helps.
Regards,
Sara
Hi Sara,
many thanks, but no success. Smartphon doesn’t run but serial monitor do.
You are always great.
Renzo
Hi Rui and Sara, the ESP32-CAM set Access Point works fine but the Remote Control Car Robot doesn’t connect to Wi.Fi – on the Serial Monitor the IP Address never comes up and
it shows Flash Read err , 1000.
Any suggestions?
Thanks
Hi.
Can you tell me the exact error that you get? Do you get it in the Serial Monitor?
Regards,
Sara
Hi Sara, the serial monitor reads : rst:0x1 (POWERON_RESET), boot:0x13 (SPI_FAST_FLASH_BOOT)
flash read err, 1000
est_main.c 371
rst:0x10 (RICWDI_RTC_RESER),boot:0x13 (SPI_FAST_FLAS_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:1
load:0x3fff0018,len 4
load:0xfff001c,len1216
ho ) tail 12 room 4
load:0x40078000,10944
load:0x40080400,len:6388
entry 0x400806b4
………………………………………………………………………………………………………………………………
so the err is 1000 and it doesn’t give any IP address.
Any idea where I may be going wrong?
Thanks
Hi.
Can you first double-check that you’ve inserted the right network credentials?
Because you have a lot of dots being printed on the Serial Monitor, and that means that it can’t connect to Wi-Fi.
Then, disconnect all circuitry from your ESP32-CAM before uploading the code.
Get your ESP32-CAM IP address.
Then, wire the circuit and restart the ESP32-CAM.
I hope this helps.
Regards,
Sara
Hello, thanks for all the helpful information, I have a similar project that I am working on, only my project bot requires lots of GPIO pins. I added a second ESP32(not a camera board) to accommodate for GPIO pins. But now i have become curious if the esp32 cam could stream the camera data to the ESP32 via ESPNOW ? My esp32 cam does not have an external antenna for the Wi-Fi connection but the ESP32 can connect an antenna using GPIO pins. My bot has a lot going on, and not sure how to get going properly. Thanks again for all the hard work.
Hi.
ESP-NOW only supports sending a small number of bytes. See our ESP-NOW tutorial: https://randomnerdtutorials.com/esp-now-esp32-arduino-ide/
So, you can’t stream the camera data using that communication protocol.
Regards,
Sara
Hi Sara, I use the SSID : “ESP32-CAM” and password :”NULL”
I also changed the password to “123456789” that of course didn’t make any changes.
in the tools I changed the flash mode from “QIO” to “DIO” and also made no changes.
The fact is that if I load the sketch “Camera Web Server Access Point” every thing works as expected and I get the IP Address.
It sounds to me like there is something wrong in the Wi.Fi connection part of the sketch, maybe a miss print I can’t detect?
I hate to be so insistent but I would really like to get this sketch to work as it seems to be working for other people.
Thanks again
Hi.
Did you disconnect all peripherals before uploading the code?
Regards,
Sara
Hi
Yes of course all peripherals were disconnected before uploading the code.
When I upload the Camera Web Server Access IP Address I follow exactly the same steps as when I upload the ESP32-CAM Remote control – the web server access sketch gives me the IP Address while the remote control sketch doesn’t connect to Wi-Fi.
I see you both are very busy and my interest in the project is not important it is only for kids toy.
Valeu a tentativa, obrigado por tudo.
Roberto
Hi again.
I really would like to help.
But I’m not sure what might be causing the issue.
Select the AI-Thinker ESP32-CAM board when uploading the code in Tools>Board.
If your Access point is running appropriately, it has the default IP address: 192.168.4.1
Regards,
Sara
Hi i need to build truck with 4 motor using 2*l298n driver with esp32 no cam I am searching for internet I cant find any code can one help me…
Hi.
At the moment, we don’t have any web server tutorial with DC motors. I’m planning to create a tutorial about that.
We have this getting started guide for DC motors:
– https://randomnerdtutorials.com/esp32-dc-motor-l298n-motor-driver-control-speed-direction/
Because DC motors are controlled with PWM signals, you can try one of our tutorial that control PWM for LEDs and try to adapt to DC motors.
– https://randomnerdtutorials.com/esp32-web-server-websocket-sliders/
– https://randomnerdtutorials.com/esp32-web-server-slider-pwm/
I hope this helps.
Regards,
Sara
Dear Sara
The motors works fine in esp 32 cam survielance robot car but camera not works kindly help
Hi.
Check the antenna and if the board can catch a good wi-fi signal.
Check the power supply.
Regards,
Sara
Hello,
Nice project I try to make it but car not going anyway please help me. Camera work very well
Hi.
Double-check the wiring of the motors.
Regards,
Sara
It is an interesting project and I tried to make it happen. The camera works but the motors don’t. I have checked the wiring several times. I haven’t put the antenna on yet. Could this be the problem? Thanks and congratulations.
Thanks for sharing this great project!
I have lot of cheap servos, there is a very easy mod to run servos in continuous mode, removing a small part inside servo.
Unfortunately I don´t have software skills, do you think is lot of coding adding a servo option for motoring this project?
I mean use two servos controlled by PWM outputs instead of smart robot chassis and L298N driver. This wiil be great and reduce the price of the robot
Thanks in advance
Best regards
Ernest.
Bonjour,
Joli projet fonctionnel. Téléchargement un peu difficil, mais en suivant les instructions, j’y suis parvenu.
Un petit plus en gadget peut-être? Créer un bouton pour allumer et éteindre la Led de l’ESP32 CAM.
Merci
Hello and thanks for the great tutorial!
I am still a novice in the field of micro-controller programming and therefore still have a question of understanding:
Don’t I need to download libraries before I transfer the code using the Arduino IDE?
Kind regards, Alex
Hi.
It depends on the libraries.
There are libraries that are installed by default.
When you need to install external libraries, it is mentioned in our tutorials.
Regards,
Sara
All well, but I haven’t solved an old problem. On my Android smartphone I only see this message:
“header files are too long for server to interpret”.
What can I do?
Thanks
Renzo
Thanks again for the instructions,
it all worked out great (although I’m still a beginner).
Now that my video car is driving around the apartment so nicely, however, I notice that the video image is quite dark.
Isn’t there also a onboard LED on the ESP32Cam? … how could I include it in the code?
Kind regards
Dear All,
As I like to controll this Robotcar Project from the outside world I changed my router settings in port-forwarding to the according ESP32-cam IP- address 192.168.. : myport. and switched my router setting protocol in TCP-UDP and Internal port: 80.
These settings result in just seeing the ESP32 cam- site pushbuttons but no video streaming window….
Can you advise me what to do in the router portforwarding menu to see the complete ESP32-cam site?
Thank you in advance for your reply…!
Alex
Good afternoon,
I had the same problem, I fixed opennig the port 80 and 81, at the same time. It looks like the streaming video go with other port..
Good luck. Miguel.
Hello.
nice job , love your tutorials .
please help me with some suggestions .
in the past i have done with your help , webserver for a relay to open my building door from a web page. all my neighbors love it .
now i am at the point where i want to do a webserver for esp32 cam with button for a relay so i can see who is at the building door and open it .
basically it will need to be like the one from this project but with just 1 button and user login .
thanks in advance
Hi,
How can I add in a bumper switch sensor to indicate collision and display it in the webpage below the backward button?
Thanks in advance.
Hi,
very good tutorial, thank you.
One question:
how can i display and refresh status messages on the html web screen ?
Hello , thanks for your amazing projects,
i have problem
E (125) cam_hal: cam_dma_config(280): frame buffer malloc failed
E (126) cam_hal: cam_config(364): cam_dma_config failed
E (126) camera: Camera config failed with error 0xffffffff
Camera init failed with error 0xffffffff
shows this massage on serial monitor, pls help me
Hi guys, thanks for posting this, was trying to proxy this out via my router to get access from my phone remotely. When I proxy the ESP32-Cam I get the following error Header fields are too long for server to interpret. (it all works great locally, I think it might be when I add a letsencrypt certificate this error shows up.
I have tried to add
config.stack_size = 4096;
config.max_resp_headers = 8;
To no avail, is there any way to get over the header error? Any advice greatly appreciated.
First of all:
Thanks for the great tutorials , thea are awesome, even for me NOOB ,
( jou should add a ” buy me a Coffee ” sponsor link to your page, i would press it
Would it be possible to make it a ” ESP32-ToyTank” when I combine
Could you pick up my idear / or just Comment “should work” or “nonsens”
i cant confirm the function of what i just wrote – i am waiting the Hardware to arrive- but after all- i get a WIFI connection, a CAM screen, all buttons, and serial output- so it COULD? work ?
=> if i add a Servo for “Barrel/ CAM ” > up/down
I am not “THAAAAK PROGRAMMING GUY”
I can copy / paste & Hope it works
– i am working on a ESP32Cam – CAMERA_MODEL_AI_THINKER
and when i include & define:
#include “esp_http_server.h”
#define SERVO_1 16
#define SERVO_STEP 5
Servo servoN1;
Servo servo1;
int servo1Pos = 0;
——————– ad to
<
table>
Up
Down
————- and at the end-continue below >
else if(!strcmp(variable, “stop”)) {
Serial.println(“Stop”);
digitalWrite(MOTOR_1_PIN_1, 0);
digitalWrite(MOTOR_1_PIN_2, 0);
digitalWrite(MOTOR_2_PIN_1, 0);
digitalWrite(MOTOR_2_PIN_2, 0);
}
— if i add there the servo part
else if(!strcmp(variable, “up”)) {
if(servo1Pos <= 170) {
servo1Pos += 10;
servo1.write(servo1Pos);
}
Serial.println(servo1Pos);
Serial.println(“Up”);
}
else if(!strcmp(variable, “down”)) {
if(servo1Pos >= 10) {
servo1Pos -= 10;
servo1.write(servo1Pos);
}
Serial.println(servo1Pos);
Serial.println(“Down”);
}
—————–finaly at >void setup() {
servo1.setPeriodHertz(50); // standard 50 hz servo
servoN1.attach(2, 1000, 2000);
servo1.attach(SERVO_1, 1000, 2000);
Hi.
I think it should work. But in the end, you need to try all the hardware and see if everything actually works together.
Regards,
Sara
Great Tutorial, I have used couple of codes from here and other sites to have the ESP Work fine, Few troubles I have though.
1. ESP resets itself after first connecting to WiFi if i make use of Battery power, It works fine with USB plugged into Laptop though.
2. How do I send a sensor data for example an Ultrasonic sensor to the webpage (Any hint would be really helpful)
Thanks
Hi.
If the ESP is constantly resetting with battery, it probably means that the battery is not supplying enough power to the ESP32-CAM.
We have many web server examples that show how to display sensor data. We don’t have a specific web server for ultrasonic sensor, but you may want to try to combine several tutorials:
– Get started with Ultrasonic sensor ESP32: https://randomnerdtutorials.com/esp32-hc-sr04-ultrasonic-arduino/
– Web Server tutorials (choose a simple one): https://randomnerdtutorials.com/?s=web+server
I hope this helps.
Regards,
Sara
Very Happy and Thanks for the great tutorials.
Please tell me the Code for return status of Robot Car:
if(!strcmp(variable, “forward”)) {
Serial.println(“Forward”);
digitalWrite(MOTOR_1_PIN_1, 1);
digitalWrite(MOTOR_1_PIN_2, 0);
digitalWrite(MOTOR_2_PIN_1, 1);
digitalWrite(MOTOR_2_PIN_2, 0);
-> ESP send back (“Forward”);
}
Many thanks for your help.
Regards,
Tam Thai
I did it this way:
char http_response[50] = “\0”; // < add this
if(!strcmp(variable, “forward”)) {
Serial.println(“Forward”);
digitalWrite(MOTOR_1_PIN_1, 1);
digitalWrite(MOTOR_1_PIN_2, 0);
digitalWrite(MOTOR_2_PIN_1, 1);
digitalWrite(MOTOR_2_PIN_2, 0);
strcpy(http_response, “Forward”); // < add this
}
at the bottom of the function you find:
return httpd_resp_send(req, NULL, 0);
replace it by:
httpd_resp_set_type(req, “text/html”);
return httpd_resp_send(req, http_response, strlen(http_response));
in the html part you find:
<
script>
function toggleCheckbox(x) {
var xhr = new XMLHttpRequest();
xhr.open(“GET”, “/action?go=” + x, true);
xhr.send();
}
replace it by:
<script>
function toggleCheckbox(x) {
var xhr = new XMLHttpRequest();
xhr.open(“GET”, “/action?go=” + x, true);
xhr.addEventListener(‘load’, function(event) {
document.getElementById(‘txtbox’).innerHTML = xhr.responseText;
});
xhr.send();
}
Sorry the html part was broken.
replace it by:
Sorry again, the html does not display correctly.
Try this:
https://drive.google.com/file/d/1LrFolJP9ouR2Twy_XJEa84Kjh1DKN381/view?usp=sharing
Hi Walter.
I did it:
char http_response[50] = “\0”; // < add this
if(!strcmp(variable, “forward”)) {
Serial.println(“Forward”);
digitalWrite(MOTOR_1_PIN_1, 1);
digitalWrite(MOTOR_1_PIN_2, 0);
digitalWrite(MOTOR_2_PIN_1, 1);
digitalWrite(MOTOR_2_PIN_2, 0);
strcpy(http_response, “Forward”); // < add this
}
But Adruino verify show notification:
exit status 1
stray ‘\342’ in program
Thanks for your helps.
Regard,
Tam Thai
Sorry, it was a problem with the character set used in this site.
If you (or me) copy the code of the postings and insert it into Arduino, you get this error message.
I fixed the above file, you should download it again.
Now it should work
Best Regards, Walter
Hi, is it working?
I found an old RC tank track car and used it as a base with a 12v battery bank on board . It works great.I think I’ll have to figure a way to slow it down a bit though ,it rockets around the place
Thanks for the tutorial.
That’s great!
I’m glad this was useful.
Regards,
Sara
I put a buck converter on the input of the L298N and dropped it down to about 8v . I shouldn’t get speeding tickets now
Thanks again for the easy to follow tutorial.
Can I change 16 buttons below vedio streaming in this project
Tengo una pregunta.
Donde descargo las librerías que pide, desde el mismo Arduino IDE no las encuentro, alguien que por favor me las pudiera compartir. Gracias.
Hi.
All the used libraries are automatically included.
You don’t need to install anything. Just make sure you have an ESP32 board selected in Tools > Board.
Regards,
Sara
Olá, vi o código do Arduíno, e fiquei com algumas questões relativamente à primeira parte do código. No inicio aparece “defined(camera_model…”, sei que são definições para a camara, no entanto gostava de saber se era possível descrever o que faz cada bloco. Obrigado!
Hello
First of all great tutorial, it was really easy to build silimar car to yours.
I wanted to add length sensor to avoid crashing into walls and I managed to do that but I also wanted to display the length between car and obsticle on website but i dont know how to that using esp_http_server (in asyncwebserver it is really easy but with this library I find it really difficult). Do you now maybe how to do that? I would be very grateful for your help
Regards
Can you share how you implemented the length sensor to prevent crashing? Are you using Ultrasonic sensing for that? Also how did you incorporate the Code ? Kindly share.. I implemented the reading distance on site once and it was working, ll share it when I get access to my old laptop.
hi i was wondering where i can download the libraries for this code to work. cant seem to find any of the libraries i need. thanks
actually got the libraries working but now i have a compiling issue. getting error message ‘Y2_GPIO_NUM’ was not declared in this scope. dont know how to solve this error.
Hi.
In what line of the code do you get the error?
Regards,
Sara
error codes are gone, thanks
Hello,
Thanks for the tutorial!
However, I have an issue with the video stream: after connection, the image flickers randomly for a few seconds, and then the image is gone. The control buttons work further. If I reload the page, it is flickery again for some time and then the stream stops.
I think it is a software issue because
with the stock webserver code the camera runs fine.Anyway, I like your much simpler user interface and the control buttons.
Do you have any idea what may be wrong?
Thank you in advance!
I built the project and got it to work without major problems, and was able to make it work in both Station Mode and AP mode.
Also added a couple of buttons to the table to control the onboard LED which worked OK.
I did however run into a problem which I was able to solve but I am not certain why the fix worked.
I had the same problem with all of the buttons as follows:
Controlling the robot with my Windows 10 computer everything worked as it should.
When I would click and release my mouse on the Forward Button I would get the following on the Serial Monitor:
Forward
Stop
(Which is how it should work)
However when I controlled the Robot with my Ipad and pressed the button on the screen I got the following on the Serial Monitor
Forward
Stop
Forward
(in order to stop the robot I would have to press the Stop button)
After some experimenting I was able to solve the problem by removing the onmousedown=”toggleCheckbox(‘forward’)
[ or other run commands from the button table.]
Doing this for all of the commands in the button table solved my problem for the Ipad but I am at loss as to why?
Hey! I have an end-of-course project based on an ESP-32CAM. I’m using as a basis the code of “Video Streaming Web Server Sensor Reading”. Any ideas on how to enter code techos to control a robot with gas detection sensors?
Hey, can you add one more button to control a led on another pin, not a esp32-cam led? would love if you can teach me
Great project and tutorials all along the website. Thank you so much ! I had a lot of fun building this robot this year. I would ask the same thing as Jack… I was wondering how to add a button to put the esp-32 led on (and off). Also, if there is a way to use keyboard input to drive the robot instead of mouse ?
Anyway thank you so much ! Take care
Hi Sara, may use any other Pins free for another sensor?
I tried with GPIO 04 or GPIO 02 naming them 2 , 4 or 22 , 24 but nothins run.
Where may I find the corresponce numer of pins, I found only that one above?
Thanks
Renzo
Hi.
You refer to those pins as 4 and 2.
Which sensors are you trying to connect?
Regards,
Sara
Hi Sara, I want use two ultrasonic sensors HR-SR04, so in fact I need four pins.
Thanks
Renzo
Thank you for the great content.
How do I add the ability to turn on and off the led on the esp32cam board?
Hi.
You control it like a regular LED connected to your board.
It is connected to GPIO 4.
So,
declare it as an OUTPUT using pinMode() and then just use digitalWrite() to turn it on and off.
Here’s an example on how to blynk the LED onboard:
Regards,
Sara