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.

ESP32-CAM Remote Controlled Car Robot Web Server 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.

ESP32-CAM board 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.

ESP32-CAM Remote Controlled Robot Web Server Arduino IDE

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.

Smart Robot Chassis Kit Arduino kit ESP32-CAM

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.

L298N Motor Driver DC ESP32-CAM

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.

Portable power bank to power ESP32-CAM Robot

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:

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() {
  
}

View raw code

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.

ESP32-CAM Getting IP Address Serial Monitor

Open a browser and type the ESP IP address. A similar web page should load:

ESP32-CAM Web Server Remote Controlled Robot 2 Demonstration

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.

ESP32-CAM Remote Controlled Robot Serial Monitor Commands

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.

ESP32-CAM Remote Controlled Robot Diagram DC Motors Wiring Circuit

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 DriverESP32-CAM
IN1GPIO 14
IN2GPIO 15
IN3GPIO 13
IN4GPIO 12

We assembled all the connections on a mini stripboard as shown below.

ESP32-CAM Remote Controlled Robot Stripboard for Circuit

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:

ESP32-CAM Remote Controlled Card Robot Assembled

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.

ESP32-CAM Remote Controlled Robot Web Server Demonstration

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:



Learn how to build a home automation system and we’ll cover the following main subjects: Node-RED, Node-RED Dashboard, Raspberry Pi, ESP32, ESP8266, MQTT, and InfluxDB database DOWNLOAD »
Learn how to build a home automation system and we’ll cover the following main subjects: Node-RED, Node-RED Dashboard, Raspberry Pi, ESP32, ESP8266, MQTT, and InfluxDB database DOWNLOAD »

Enjoyed this project? Stay updated by subscribing our newsletter!

112 thoughts on “ESP32-CAM Remote Controlled Car Robot Web Server”

  1. 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…

    Reply
    • 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

      Reply
    • “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 */

      Reply
  2. 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!

    Reply
  3. 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.

    Reply
    • 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

      Reply
      • 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

        Reply
  4. 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

    Reply
  5. 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

    Reply
  6. 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

    Reply
      • 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

        Reply
        • 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

          Reply
  7. 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.

    Reply
  8. 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

    Reply
  9. 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

    Reply
    • 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

      Reply
  10. 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…

    Reply
  11. 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.

    Reply
  12. 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.

    Reply
  13. 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

    Reply
  14. 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

    Reply
    • 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

      Reply
  15. 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

    Reply
  16. 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

    Reply
  17. 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

    Reply
    • 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.

      Reply
  18. 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

    Reply
  19. 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.

    Reply
  20. Hi,
    very good tutorial, thank you.
    One question:
    how can i display and refresh status messages on the html web screen ?

    Reply
  21. 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

    Reply
  22. 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.

    Reply
  23. 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

    “ESP32-CAM Remote Controlled Car Robot Web Server”
    and
    ” Pan and Tilt Stand and Motors”

    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);

    Reply
    • 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

      Reply
  24. 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

    Reply
  25. 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

    Reply
    • 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();
      }

      Reply
      • Sorry the html part was broken.
        replace it by:

        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();
        }

        Reply
        • 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

          Reply
          • 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

  26. 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.

    Reply
  27. 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.

    Reply
    • 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

      Reply
  28. 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!

    Reply
  29. 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

    Reply
    • 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.

      Reply
  30. 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

    Reply
  31. 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!

    Reply
  32. 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?

    Reply
  33. 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?

    Reply
    • 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

      Reply
  34. 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

    Reply
    • 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:

      // ledPin refers to ESP32-CAM GPIO 4 (flashlight)
      const int ledPin = 4;
      void setup() {
        // initialize digital pin ledPin as an output.
        pinMode(ledPin, OUTPUT);
      }
      void loop() {
        digitalWrite(ledPin, HIGH);
        delay(2000);
        digitalWrite(ledPin, LOW);
        delay(2000);
      } 

      Regards,
      Sara

      Reply
  35. Hi,
    control and streaming is working fine in my local network.
    now, i was trying to access the camera server from the internet with FritzBox and Port forwarding (IP4). I can access the web screen and it seemed to be working fine.
    But: what i see is the web screen with the buttons but NO stream.
    it only shows the broken picture symbol.
    Any idea?

    Reply
    • Are you forwarding just one port? The buttons are on port 80 and the video stream is port 81.
      Look at the code very carefully and you will see there are really 2 servers. I send the video to it’s own page so I can have room on the control page for sensor data. A work in progress.

      Reply
      • I’m forwarding both ports. 80 and 81. On port 81/stream i can get the stream. But the stream does not work in the web interface. In my local network it is working

        Reply
      • I got it!
        I had to modify the forwarding ports for the stream to be (port+1) for the stream:

        function getStreamSrc(aref) {
        var href = aref.split(“:”);
        var port = 80; // default port
        if (href.length > 2) {
        if (href[2].length > 0) port = +(href[2]);
        }
        port += 1; // increment port
        return href[0] + “:” + href[1] + “:” + port + “/stream”;
        }
        // instead of: window.onload = document.getElementById(“photo”).src = window.location.href.slice(0, -1) +”:81/stream”;
        window.onload = document.getElementById(“photo”).src = getStreamSrc(window.location.href.slice(0, -1));

        I had to move the

        function toggleCheckbox(x)

        to the header, otherwise strange things happend.

        Reply
  36. @Sara
    How do i post code snippets here correctly?
    i tried

    <

    pre> tag but it does not work. (see my last post above)
    the ” double quote is displayed in the wrong character set, it will cause an error when pasted in arduino IDE.

    Reply
    • Hi mrt, this is a different Chapter.
      I can try to explain how i did it:
      (If you do not understand what i’m talking about, Google can help you 😉
      1) To access your local network over IP4 protocol, first you need a “Dual Stack” connection to the Internet, ask your provider to get one.
      2) Then you need a “DynDNS” Adress to access your local network from outside.
      3) In your router, you have to enable “port forwarding” for your ESP32-Cam.
      Example: https://www.cfos.de/en/cfos-personal-net/port-forwarding/avm-fritzbox-7490.htm
      You have to forward port 80 – 81.
      4) If all settings are correct, you can access your ESP32-Cam from outside with:
      “your-dyndns-address.80” for the web interface and “your-dyndns-address.81/stream” if you only want to access the video stream.
      5) If your router changes the forwarding port number (because port 80 is already occupied) then you have to follow my instruction above of April 11, 2023.
      In this case your address will be “your-dyndns-address.forwarded-port”
      Let me know if it works 🙂

      Reply
  37. Hi. Maybe you can help me. I have a smart robot car v4.0 and as we know it is alredy programmed and i can control it from it’s app. The problem is i must connect my phone to wifi AP of that car. I’d like to make this car to be able to connect my LAN and i do not need each time switch between APs. I found some code for that but the problem is during upload it says there is not #include <WiFi.h>. I copied libralries from here: https://github.com/espressif/arduino-esp32 but this time it says other liblraries missed. And each time it finds new missed file. Any idea?

    Reply
  38. Hi,
    Great tutorial, thanks!
    In relation to the code, I have a doubt:
    I don’t understand the logic of the use of the variable “res” in this function:
    “static esp_err_t cmd_handler(httpd_req_t *req)”
    in particular I don’t understand how it can ever be changed from “0” (its initial value) or “-1”, so that this test:
    “if(res){
    return httpd_resp_send_500(req);
    }”
    can ever be true.

    Can you tell me how it works?

    Again thank you for this tutorial, it is very interesting and useful.

    Reply
  39. Hi Sara, I found your code is the most stable and it use the original esp_httpd library.
    I have tried it using both AP mode and Client of the ESP32 and got stable video frames.

    I also tried many websocket video stream code and the best one is only the case doing as an AP itself, when I use the same wifi router configuration (hardware as above) and ESP32 acting as an client, the video had many delays and lost frame.

    I search other method of video stream such as websocket is that it is easier to make two way communication between the ESP32 and the web page, but it is very depressing no code in all the web sites I found worked in both AP & Client mode.

    So, I back to the origin and wish to find efficient two way communications using http server only (modify your code), I found there is a method using ajax to refrash the web page, however, it jam the code and could not work easily, do you have successful two way communication using the code in this project? any hint?

    Reply
  40. I’ve installed code and every works fine when using edge browser on laptop. But when I use safari or chrome on my iPhone, I get an extra tap.
    For instance if I press forward on my iPhone, in the serial output I get forward, stop and forward again.
    Any idea what’s wrong?

    Reply
  41. hi~
    the event will be like this, you can try without touchstart and touchend event.
    the event trigger sequence will be
    touchstart => touchend => mousemove => mousedown => mouseup => click
    medium.com/frochu/touch-and-mouse-together-76fb69114c04

    Reply
  42. HGi, very useful tutorial.
    I just start my first project with ESP32.

    Could you please help with few issues
    1) I choose AI thinker ESP32 CAM board from menu, because I use ESP32-CAN , and it does not work , what board I I need to choose?
    2) I have got big message in Serial monitor:

    st:0x1(POWEO_EET,oot:0x13(SP_AS_LSH_OOT)
    cnfgip ,SPIW:0xee
    cl_r:00,qdrv:000,ddv00,s0_dv:0x0,h_r:x0wp_dv:0x0
    oeDO lock dv:1
    od03ff030,len1344
    la:x407800,len:194
    od0x4080400,ln:60
    nry 0x00805f
    What does it mean?

    Thanks in advance

    Reply

Leave a Comment

Download Our Free eBooks and Resources

Get instant access to our FREE eBooks, Resources, and Exclusive Electronics Projects by entering your email address below.