{"id":101469,"date":"2021-06-24T10:25:54","date_gmt":"2021-06-24T10:25:54","guid":{"rendered":"https:\/\/randomnerdtutorials.com\/?p=101469"},"modified":"2021-06-24T10:38:14","modified_gmt":"2021-06-24T10:38:14","slug":"esp32-cam-car-robot-web-server","status":"publish","type":"post","link":"https:\/\/randomnerdtutorials.com\/esp32-cam-car-robot-web-server\/","title":{"rendered":"ESP32-CAM Remote Controlled Car Robot Web Server"},"content":{"rendered":"\n<p>Build a Wi-Fi remote controlled car robot with the ESP32-CAM. You\u2019ll be able to control the robot using a web server that displays a video streaming of what the robot \u201csees\u201d. You can control your robot remotely even if it\u2019s out of your sight. The ESP32-CAM will be programmed using Arduino IDE.<\/p>\n\n\n\n<div class=\"wp-block-image is-style-default\"><figure class=\"aligncenter size-large\"><img data-recalc-dims=\"1\" fetchpriority=\"high\" decoding=\"async\" width=\"1024\" height=\"576\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/01\/ESP32-CAM-Remote-Controlled-Car-Robot-Web-Server.jpg?resize=1024%2C576&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32-CAM Remote Controlled Car Robot Web Server Arduino IDE\" class=\"wp-image-101494\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/01\/ESP32-CAM-Remote-Controlled-Car-Robot-Web-Server.jpg?resize=1024%2C576&amp;quality=100&amp;strip=all&amp;ssl=1 1024w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/01\/ESP32-CAM-Remote-Controlled-Car-Robot-Web-Server.jpg?resize=300%2C169&amp;quality=100&amp;strip=all&amp;ssl=1 300w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/01\/ESP32-CAM-Remote-Controlled-Car-Robot-Web-Server.jpg?resize=768%2C432&amp;quality=100&amp;strip=all&amp;ssl=1 768w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/01\/ESP32-CAM-Remote-Controlled-Car-Robot-Web-Server.jpg?w=1280&amp;quality=100&amp;strip=all&amp;ssl=1 1280w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure><\/div>\n\n\n\n<p class=\"rntbox rntclblue\"><strong>Boards compatibility<\/strong>: this project requires 4 GPIOs to control the DC motors. So, you can use any ESP32 camera board with 4 available GPIOs like the <a href=\"https:\/\/makeradvisor.com\/esp32-cam-ov2640-camera\/\" target=\"_blank\" rel=\"noreferrer noopener\">ESP32-CAM Ai-Thinker board<\/a> or the <a href=\"https:\/\/makeradvisor.com\/ttgo-t-journal-esp32-camera-board-review\/\" target=\"_blank\" rel=\"noreferrer noopener\">TTGO T-Journal<\/a>. <\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Project Overview<\/h2>\n\n\n\n<p>Before starting the project, we\u2019ll highlight the most important features and components used to build the robot.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Wi-Fi<\/h3>\n\n\n\n<p>The robot will be controlled via Wi-Fi using your ESP32-CAM. We\u2019ll create a web-based interface to control the robot, that can be accessed in any device inside your local network. <\/p>\n\n\n\n<p>The web page also shows a video streaming of what the robot \u201csees\u201d. For good results with video streaming, we recommend using an <a href=\"https:\/\/randomnerdtutorials.com\/esp32-cam-connect-external-antenna\/\">ESP32-CAM with external antenna<\/a>. <\/p>\n\n\n\n<div class=\"wp-block-image is-style-default\"><figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" decoding=\"async\" width=\"750\" height=\"424\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/03\/ESP32-CAM-External-Antenna.jpg?resize=750%2C424&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32-CAM board external antenna\" class=\"wp-image-94889\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/03\/ESP32-CAM-External-Antenna.jpg?w=750&amp;quality=100&amp;strip=all&amp;ssl=1 750w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/03\/ESP32-CAM-External-Antenna.jpg?resize=300%2C170&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 750px) 100vw, 750px\" \/><\/figure><\/div>\n\n\n\n<p class=\"rntbox rntcred\"><strong>Important: <\/strong> without an external antenna the video stream lags and the web server is extremely slow to control the robot. <\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Robot Controls<\/h3>\n\n\n\n<p>The web server has 5 controls: <strong>Forward<\/strong>, <strong>Backward<\/strong>, <strong>Left<\/strong>, <strong>Right<\/strong>, and <strong>Stop<\/strong>.<\/p>\n\n\n\n<div class=\"wp-block-image is-style-default\"><figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" decoding=\"async\" width=\"300\" height=\"610\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/01\/ESP32-CAM-Remote-Controlled-Robot-Web-Server-Smartphone.jpg?resize=300%2C610&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32-CAM Remote Controlled Robot Web Server Arduino IDE\" class=\"wp-image-101487\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/01\/ESP32-CAM-Remote-Controlled-Robot-Web-Server-Smartphone.jpg?w=300&amp;quality=100&amp;strip=all&amp;ssl=1 300w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/01\/ESP32-CAM-Remote-Controlled-Robot-Web-Server-Smartphone.jpg?resize=148%2C300&amp;quality=100&amp;strip=all&amp;ssl=1 148w\" sizes=\"(max-width: 300px) 100vw, 300px\" \/><\/figure><\/div>\n\n\n\n<p>The robot moves as long as you\u2019re pressing the buttons. When you release any button, the robot stops. However, we\u2019ve included the <strong>Stop <\/strong>button that can be useful in case the ESP32 doesn\u2019t receive the stop command when you release a button. <\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Smart Robot Chassis Kit<\/h3>\n\n\n\n<p>We\u2019re going to use the <a href=\"https:\/\/makeradvisor.com\/2wd-smart-robot-chassis-kit\/\" target=\"_blank\" rel=\"noreferrer noopener\">Smart Robot Chassis Kit<\/a>. You can find it in most online stores. The kit costs around $10 and it\u2019s easy to assemble &#8211; <a href=\"https:\/\/www.youtube.com\/watch?v=KYAwjSB9nYc\" target=\"_blank\" rel=\"noreferrer noopener\">watch this video to see how to assemble the robot chassis kit<\/a>. <\/p>\n\n\n\n<p>You can use any other chassis kit as long as it comes with two DC motors. <\/p>\n\n\n\n<div class=\"wp-block-image is-style-default\"><figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"700\" height=\"393\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2017\/04\/assembling_robot_chassis_13.png?resize=700%2C393&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"Smart Robot Chassis Kit Arduino kit ESP32-CAM\" class=\"wp-image-36693\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2017\/04\/assembling_robot_chassis_13.png?w=700&amp;quality=100&amp;strip=all&amp;ssl=1 700w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2017\/04\/assembling_robot_chassis_13.png?resize=150%2C84&amp;quality=100&amp;strip=all&amp;ssl=1 150w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2017\/04\/assembling_robot_chassis_13.png?resize=300%2C168&amp;quality=100&amp;strip=all&amp;ssl=1 300w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2017\/04\/assembling_robot_chassis_13.png?resize=80%2C45&amp;quality=100&amp;strip=all&amp;ssl=1 80w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2017\/04\/assembling_robot_chassis_13.png?resize=220%2C124&amp;quality=100&amp;strip=all&amp;ssl=1 220w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2017\/04\/assembling_robot_chassis_13.png?resize=178%2C100&amp;quality=100&amp;strip=all&amp;ssl=1 178w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2017\/04\/assembling_robot_chassis_13.png?resize=267%2C150&amp;quality=100&amp;strip=all&amp;ssl=1 267w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2017\/04\/assembling_robot_chassis_13.png?resize=424%2C238&amp;quality=100&amp;strip=all&amp;ssl=1 424w\" sizes=\"(max-width: 700px) 100vw, 700px\" \/><\/figure><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">L298N Motor Driver <\/h3>\n\n\n\n<p>There are many ways to control DC motors. We\u2019ll use the L298N motor driver that provides an easy way to control the speed and direction of 2 DC motors.<\/p>\n\n\n\n<div class=\"wp-block-image is-style-default\"><figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"750\" height=\"500\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2018\/05\/L298N-label.jpg?resize=750%2C500&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"L298N Motor Driver DC ESP32-CAM\" class=\"wp-image-61999\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2018\/05\/L298N-label.jpg?w=750&amp;quality=100&amp;strip=all&amp;ssl=1 750w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2018\/05\/L298N-label.jpg?resize=300%2C200&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 750px) 100vw, 750px\" \/><\/figure><\/div>\n\n\n\n<p>We won\u2019t explain how the L298N motor driver works. You can read the following article for an in-depth tutorial about the L298N motor driver:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><a href=\"https:\/\/randomnerdtutorials.com\/esp32-dc-motor-l298n-motor-driver-control-speed-direction\/\">ESP32 with DC Motor and L298N Motor Driver \u2013 Control Speed and Direction<\/a> <\/li><\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Power<\/h3>\n\n\n\n<p>To keep the circuitry simple, we&#8217;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.<\/p>\n\n\n\n<div class=\"wp-block-image is-style-default\"><figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"750\" height=\"421\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/01\/Portable-power-bank.jpg?resize=750%2C421&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"Portable power bank to power ESP32-CAM Robot\" class=\"wp-image-101477\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/01\/Portable-power-bank.jpg?w=750&amp;quality=100&amp;strip=all&amp;ssl=1 750w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/01\/Portable-power-bank.jpg?resize=300%2C168&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 750px) 100vw, 750px\" \/><\/figure><\/div>\n\n\n\n<p class=\"rntbox rntclblue\"><strong>Note: <\/strong> 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.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Parts Required<\/h2>\n\n\n\n<p>For this project, we\u2019ll use the following parts:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><a href=\"https:\/\/makeradvisor.com\/tools\/esp32-cam-external-antenna\/\" target=\"_blank\" rel=\"noreferrer noopener\">ESP32-CAM AI-Thinker with external antenna<\/a><\/li><li><a href=\"https:\/\/makeradvisor.com\/tools\/l298n-motor-driver\/\" target=\"_blank\" rel=\"noreferrer noopener\">L298N Motor Driver<\/a><\/li><li><a href=\"https:\/\/makeradvisor.com\/tools\/2wd-smart-robot-chassis-kit\/\" target=\"_blank\" rel=\"noreferrer noopener\">Robot Car Chassis Kit<\/a><\/li><li>Power bank or other 5V power supply<\/li><li><a href=\"https:\/\/makeradvisor.com\/tools\/prototyping-circuit-board-stripboard\/\" target=\"_blank\" rel=\"noreferrer noopener\">Prototyping circuit board<\/a> (optional)<\/li><\/ul>\n\n\n<p>You can use the preceding links or go directly to <a href=\"https:\/\/makeradvisor.com\/tools\/?utm_source=rnt&utm_medium=post&utm_campaign=post\" target=\"_blank\">MakerAdvisor.com\/tools<\/a> to find all the parts for your projects at the best price!<\/p><p style=\"text-align:center;\"><a href=\"https:\/\/makeradvisor.com\/tools\/?utm_source=rnt&utm_medium=post&utm_campaign=post\" target=\"_blank\"><img data-recalc-dims=\"1\" decoding=\"async\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2017\/10\/header-200.png?w=1200&#038;quality=100&#038;strip=all&#038;ssl=1\"><\/a><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Code<\/h2>\n\n\n\n<p>Copy the following code to your Arduino IDE.<\/p>\n\n\n<pre style=\"max-height: 40em; margin-bottom: 20px;\"><code class=\"language-c\">\/*********\r\n  Rui Santos &amp; Sara Santos - Random Nerd Tutorials\r\n  Complete instructions at https:\/\/RandomNerdTutorials.com\/esp32-cam-projects-ebook\/\r\n  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.\r\n  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\r\n*********\/\r\n\r\n#include &quot;esp_camera.h&quot;\r\n#include &lt;WiFi.h&gt;\r\n#include &quot;esp_timer.h&quot;\r\n#include &quot;img_converters.h&quot;\r\n#include &quot;Arduino.h&quot;\r\n#include &quot;fb_gfx.h&quot;\r\n#include &quot;soc\/soc.h&quot;             \/\/ disable brownout problems\r\n#include &quot;soc\/rtc_cntl_reg.h&quot;    \/\/ disable brownout problems\r\n#include &quot;esp_http_server.h&quot;\r\n\r\n\/\/ Replace with your network credentials\r\nconst char* ssid = &quot;REPLACE_WITH_YOUR_SSID&quot;;\r\nconst char* password = &quot;REPLACE_WITH_YOUR_PASSWORD&quot;;\r\n\r\n#define PART_BOUNDARY &quot;123456789000000000000987654321&quot;\r\n\r\n#define CAMERA_MODEL_AI_THINKER\r\n\/\/#define CAMERA_MODEL_M5STACK_PSRAM\r\n\/\/#define CAMERA_MODEL_M5STACK_WITHOUT_PSRAM\r\n\/\/#define CAMERA_MODEL_M5STACK_PSRAM_B\r\n\/\/#define CAMERA_MODEL_WROVER_KIT\r\n\r\n#if defined(CAMERA_MODEL_WROVER_KIT)\r\n  #define PWDN_GPIO_NUM    -1\r\n  #define RESET_GPIO_NUM   -1\r\n  #define XCLK_GPIO_NUM    21\r\n  #define SIOD_GPIO_NUM    26\r\n  #define SIOC_GPIO_NUM    27\r\n  \r\n  #define Y9_GPIO_NUM      35\r\n  #define Y8_GPIO_NUM      34\r\n  #define Y7_GPIO_NUM      39\r\n  #define Y6_GPIO_NUM      36\r\n  #define Y5_GPIO_NUM      19\r\n  #define Y4_GPIO_NUM      18\r\n  #define Y3_GPIO_NUM       5\r\n  #define Y2_GPIO_NUM       4\r\n  #define VSYNC_GPIO_NUM   25\r\n  #define HREF_GPIO_NUM    23\r\n  #define PCLK_GPIO_NUM    22\r\n\r\n#elif defined(CAMERA_MODEL_M5STACK_PSRAM)\r\n  #define PWDN_GPIO_NUM     -1\r\n  #define RESET_GPIO_NUM    15\r\n  #define XCLK_GPIO_NUM     27\r\n  #define SIOD_GPIO_NUM     25\r\n  #define SIOC_GPIO_NUM     23\r\n  \r\n  #define Y9_GPIO_NUM       19\r\n  #define Y8_GPIO_NUM       36\r\n  #define Y7_GPIO_NUM       18\r\n  #define Y6_GPIO_NUM       39\r\n  #define Y5_GPIO_NUM        5\r\n  #define Y4_GPIO_NUM       34\r\n  #define Y3_GPIO_NUM       35\r\n  #define Y2_GPIO_NUM       32\r\n  #define VSYNC_GPIO_NUM    22\r\n  #define HREF_GPIO_NUM     26\r\n  #define PCLK_GPIO_NUM     21\r\n\r\n#elif defined(CAMERA_MODEL_M5STACK_WITHOUT_PSRAM)\r\n  #define PWDN_GPIO_NUM     -1\r\n  #define RESET_GPIO_NUM    15\r\n  #define XCLK_GPIO_NUM     27\r\n  #define SIOD_GPIO_NUM     25\r\n  #define SIOC_GPIO_NUM     23\r\n  \r\n  #define Y9_GPIO_NUM       19\r\n  #define Y8_GPIO_NUM       36\r\n  #define Y7_GPIO_NUM       18\r\n  #define Y6_GPIO_NUM       39\r\n  #define Y5_GPIO_NUM        5\r\n  #define Y4_GPIO_NUM       34\r\n  #define Y3_GPIO_NUM       35\r\n  #define Y2_GPIO_NUM       17\r\n  #define VSYNC_GPIO_NUM    22\r\n  #define HREF_GPIO_NUM     26\r\n  #define PCLK_GPIO_NUM     21\r\n\r\n#elif defined(CAMERA_MODEL_AI_THINKER)\r\n  #define PWDN_GPIO_NUM     32\r\n  #define RESET_GPIO_NUM    -1\r\n  #define XCLK_GPIO_NUM      0\r\n  #define SIOD_GPIO_NUM     26\r\n  #define SIOC_GPIO_NUM     27\r\n  \r\n  #define Y9_GPIO_NUM       35\r\n  #define Y8_GPIO_NUM       34\r\n  #define Y7_GPIO_NUM       39\r\n  #define Y6_GPIO_NUM       36\r\n  #define Y5_GPIO_NUM       21\r\n  #define Y4_GPIO_NUM       19\r\n  #define Y3_GPIO_NUM       18\r\n  #define Y2_GPIO_NUM        5\r\n  #define VSYNC_GPIO_NUM    25\r\n  #define HREF_GPIO_NUM     23\r\n  #define PCLK_GPIO_NUM     22\r\n\r\n#elif defined(CAMERA_MODEL_M5STACK_PSRAM_B)\r\n  #define PWDN_GPIO_NUM     -1\r\n  #define RESET_GPIO_NUM    15\r\n  #define XCLK_GPIO_NUM     27\r\n  #define SIOD_GPIO_NUM     22\r\n  #define SIOC_GPIO_NUM     23\r\n  \r\n  #define Y9_GPIO_NUM       19\r\n  #define Y8_GPIO_NUM       36\r\n  #define Y7_GPIO_NUM       18\r\n  #define Y6_GPIO_NUM       39\r\n  #define Y5_GPIO_NUM        5\r\n  #define Y4_GPIO_NUM       34\r\n  #define Y3_GPIO_NUM       35\r\n  #define Y2_GPIO_NUM       32\r\n  #define VSYNC_GPIO_NUM    25\r\n  #define HREF_GPIO_NUM     26\r\n  #define PCLK_GPIO_NUM     21\r\n\r\n#else\r\n  #error &quot;Camera model not selected&quot;\r\n#endif\r\n\r\n#define MOTOR_1_PIN_1    14\r\n#define MOTOR_1_PIN_2    15\r\n#define MOTOR_2_PIN_1    13\r\n#define MOTOR_2_PIN_2    12\r\n\r\nstatic const char* _STREAM_CONTENT_TYPE = &quot;multipart\/x-mixed-replace;boundary=&quot; PART_BOUNDARY;\r\nstatic const char* _STREAM_BOUNDARY = &quot;\\r\\n--&quot; PART_BOUNDARY &quot;\\r\\n&quot;;\r\nstatic const char* _STREAM_PART = &quot;Content-Type: image\/jpeg\\r\\nContent-Length: %u\\r\\n\\r\\n&quot;;\r\n\r\nhttpd_handle_t camera_httpd = NULL;\r\nhttpd_handle_t stream_httpd = NULL;\r\n\r\nstatic const char PROGMEM INDEX_HTML[] = R&quot;rawliteral(\r\n&lt;html&gt;\r\n  &lt;head&gt;\r\n    &lt;title&gt;ESP32-CAM Robot&lt;\/title&gt;\r\n    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1&quot;&gt;\r\n    &lt;style&gt;\r\n      body { font-family: Arial; text-align: center; margin:0px auto; padding-top: 30px;}\r\n      table { margin-left: auto; margin-right: auto; }\r\n      td { padding: 8 px; }\r\n      .button {\r\n        background-color: #2f4468;\r\n        border: none;\r\n        color: white;\r\n        padding: 10px 20px;\r\n        text-align: center;\r\n        text-decoration: none;\r\n        display: inline-block;\r\n        font-size: 18px;\r\n        margin: 6px 3px;\r\n        cursor: pointer;\r\n        -webkit-touch-callout: none;\r\n        -webkit-user-select: none;\r\n        -khtml-user-select: none;\r\n        -moz-user-select: none;\r\n        -ms-user-select: none;\r\n        user-select: none;\r\n        -webkit-tap-highlight-color: rgba(0,0,0,0);\r\n      }\r\n      img {  width: auto ;\r\n        max-width: 100% ;\r\n        height: auto ; \r\n      }\r\n    &lt;\/style&gt;\r\n  &lt;\/head&gt;\r\n  &lt;body&gt;\r\n    &lt;h1&gt;ESP32-CAM Robot&lt;\/h1&gt;\r\n    &lt;img src=&quot;&quot; id=&quot;photo&quot; &gt;\r\n    &lt;table&gt;\r\n      &lt;tr&gt;&lt;td colspan=&quot;3&quot; align=&quot;center&quot;&gt;&lt;button class=&quot;button&quot; onmousedown=&quot;toggleCheckbox('forward');&quot; ontouchstart=&quot;toggleCheckbox('forward');&quot; onmouseup=&quot;toggleCheckbox('stop');&quot; ontouchend=&quot;toggleCheckbox('stop');&quot;&gt;Forward&lt;\/button&gt;&lt;\/td&gt;&lt;\/tr&gt;\r\n      &lt;tr&gt;&lt;td align=&quot;center&quot;&gt;&lt;button class=&quot;button&quot; onmousedown=&quot;toggleCheckbox('left');&quot; ontouchstart=&quot;toggleCheckbox('left');&quot; onmouseup=&quot;toggleCheckbox('stop');&quot; ontouchend=&quot;toggleCheckbox('stop');&quot;&gt;Left&lt;\/button&gt;&lt;\/td&gt;&lt;td align=&quot;center&quot;&gt;&lt;button class=&quot;button&quot; onmousedown=&quot;toggleCheckbox('stop');&quot; ontouchstart=&quot;toggleCheckbox('stop');&quot;&gt;Stop&lt;\/button&gt;&lt;\/td&gt;&lt;td align=&quot;center&quot;&gt;&lt;button class=&quot;button&quot; onmousedown=&quot;toggleCheckbox('right');&quot; ontouchstart=&quot;toggleCheckbox('right');&quot; onmouseup=&quot;toggleCheckbox('stop');&quot; ontouchend=&quot;toggleCheckbox('stop');&quot;&gt;Right&lt;\/button&gt;&lt;\/td&gt;&lt;\/tr&gt;\r\n      &lt;tr&gt;&lt;td colspan=&quot;3&quot; align=&quot;center&quot;&gt;&lt;button class=&quot;button&quot; onmousedown=&quot;toggleCheckbox('backward');&quot; ontouchstart=&quot;toggleCheckbox('backward');&quot; onmouseup=&quot;toggleCheckbox('stop');&quot; ontouchend=&quot;toggleCheckbox('stop');&quot;&gt;Backward&lt;\/button&gt;&lt;\/td&gt;&lt;\/tr&gt;                   \r\n    &lt;\/table&gt;\r\n   &lt;script&gt;\r\n   function toggleCheckbox(x) {\r\n     var xhr = new XMLHttpRequest();\r\n     xhr.open(&quot;GET&quot;, &quot;\/action?go=&quot; + x, true);\r\n     xhr.send();\r\n   }\r\n   window.onload = document.getElementById(&quot;photo&quot;).src = window.location.href.slice(0, -1) + &quot;:81\/stream&quot;;\r\n  &lt;\/script&gt;\r\n  &lt;\/body&gt;\r\n&lt;\/html&gt;\r\n)rawliteral&quot;;\r\n\r\nstatic esp_err_t index_handler(httpd_req_t *req){\r\n  httpd_resp_set_type(req, &quot;text\/html&quot;);\r\n  return httpd_resp_send(req, (const char *)INDEX_HTML, strlen(INDEX_HTML));\r\n}\r\n\r\nstatic esp_err_t stream_handler(httpd_req_t *req){\r\n  camera_fb_t * fb = NULL;\r\n  esp_err_t res = ESP_OK;\r\n  size_t _jpg_buf_len = 0;\r\n  uint8_t * _jpg_buf = NULL;\r\n  char * part_buf[64];\r\n\r\n  res = httpd_resp_set_type(req, _STREAM_CONTENT_TYPE);\r\n  if(res != ESP_OK){\r\n    return res;\r\n  }\r\n\r\n  while(true){\r\n    fb = esp_camera_fb_get();\r\n    if (!fb) {\r\n      Serial.println(&quot;Camera capture failed&quot;);\r\n      res = ESP_FAIL;\r\n    } else {\r\n      if(fb-&gt;width &gt; 400){\r\n        if(fb-&gt;format != PIXFORMAT_JPEG){\r\n          bool jpeg_converted = frame2jpg(fb, 80, &amp;_jpg_buf, &amp;_jpg_buf_len);\r\n          esp_camera_fb_return(fb);\r\n          fb = NULL;\r\n          if(!jpeg_converted){\r\n            Serial.println(&quot;JPEG compression failed&quot;);\r\n            res = ESP_FAIL;\r\n          }\r\n        } else {\r\n          _jpg_buf_len = fb-&gt;len;\r\n          _jpg_buf = fb-&gt;buf;\r\n        }\r\n      }\r\n    }\r\n    if(res == ESP_OK){\r\n      size_t hlen = snprintf((char *)part_buf, 64, _STREAM_PART, _jpg_buf_len);\r\n      res = httpd_resp_send_chunk(req, (const char *)part_buf, hlen);\r\n    }\r\n    if(res == ESP_OK){\r\n      res = httpd_resp_send_chunk(req, (const char *)_jpg_buf, _jpg_buf_len);\r\n    }\r\n    if(res == ESP_OK){\r\n      res = httpd_resp_send_chunk(req, _STREAM_BOUNDARY, strlen(_STREAM_BOUNDARY));\r\n    }\r\n    if(fb){\r\n      esp_camera_fb_return(fb);\r\n      fb = NULL;\r\n      _jpg_buf = NULL;\r\n    } else if(_jpg_buf){\r\n      free(_jpg_buf);\r\n      _jpg_buf = NULL;\r\n    }\r\n    if(res != ESP_OK){\r\n      break;\r\n    }\r\n    \/\/Serial.printf(&quot;MJPG: %uB\\n&quot;,(uint32_t)(_jpg_buf_len));\r\n  }\r\n  return res;\r\n}\r\n\r\nstatic esp_err_t cmd_handler(httpd_req_t *req){\r\n  char*  buf;\r\n  size_t buf_len;\r\n  char variable[32] = {0,};\r\n  \r\n  buf_len = httpd_req_get_url_query_len(req) + 1;\r\n  if (buf_len &gt; 1) {\r\n    buf = (char*)malloc(buf_len);\r\n    if(!buf){\r\n      httpd_resp_send_500(req);\r\n      return ESP_FAIL;\r\n    }\r\n    if (httpd_req_get_url_query_str(req, buf, buf_len) == ESP_OK) {\r\n      if (httpd_query_key_value(buf, &quot;go&quot;, variable, sizeof(variable)) == ESP_OK) {\r\n      } else {\r\n        free(buf);\r\n        httpd_resp_send_404(req);\r\n        return ESP_FAIL;\r\n      }\r\n    } else {\r\n      free(buf);\r\n      httpd_resp_send_404(req);\r\n      return ESP_FAIL;\r\n    }\r\n    free(buf);\r\n  } else {\r\n    httpd_resp_send_404(req);\r\n    return ESP_FAIL;\r\n  }\r\n\r\n  sensor_t * s = esp_camera_sensor_get();\r\n  int res = 0;\r\n  \r\n  if(!strcmp(variable, &quot;forward&quot;)) {\r\n    Serial.println(&quot;Forward&quot;);\r\n    digitalWrite(MOTOR_1_PIN_1, 1);\r\n    digitalWrite(MOTOR_1_PIN_2, 0);\r\n    digitalWrite(MOTOR_2_PIN_1, 1);\r\n    digitalWrite(MOTOR_2_PIN_2, 0);\r\n  }\r\n  else if(!strcmp(variable, &quot;left&quot;)) {\r\n    Serial.println(&quot;Left&quot;);\r\n    digitalWrite(MOTOR_1_PIN_1, 0);\r\n    digitalWrite(MOTOR_1_PIN_2, 1);\r\n    digitalWrite(MOTOR_2_PIN_1, 1);\r\n    digitalWrite(MOTOR_2_PIN_2, 0);\r\n  }\r\n  else if(!strcmp(variable, &quot;right&quot;)) {\r\n    Serial.println(&quot;Right&quot;);\r\n    digitalWrite(MOTOR_1_PIN_1, 1);\r\n    digitalWrite(MOTOR_1_PIN_2, 0);\r\n    digitalWrite(MOTOR_2_PIN_1, 0);\r\n    digitalWrite(MOTOR_2_PIN_2, 1);\r\n  }\r\n  else if(!strcmp(variable, &quot;backward&quot;)) {\r\n    Serial.println(&quot;Backward&quot;);\r\n    digitalWrite(MOTOR_1_PIN_1, 0);\r\n    digitalWrite(MOTOR_1_PIN_2, 1);\r\n    digitalWrite(MOTOR_2_PIN_1, 0);\r\n    digitalWrite(MOTOR_2_PIN_2, 1);\r\n  }\r\n  else if(!strcmp(variable, &quot;stop&quot;)) {\r\n    Serial.println(&quot;Stop&quot;);\r\n    digitalWrite(MOTOR_1_PIN_1, 0);\r\n    digitalWrite(MOTOR_1_PIN_2, 0);\r\n    digitalWrite(MOTOR_2_PIN_1, 0);\r\n    digitalWrite(MOTOR_2_PIN_2, 0);\r\n  }\r\n  else {\r\n    res = -1;\r\n  }\r\n\r\n  if(res){\r\n    return httpd_resp_send_500(req);\r\n  }\r\n\r\n  httpd_resp_set_hdr(req, &quot;Access-Control-Allow-Origin&quot;, &quot;*&quot;);\r\n  return httpd_resp_send(req, NULL, 0);\r\n}\r\n\r\nvoid startCameraServer(){\r\n  httpd_config_t config = HTTPD_DEFAULT_CONFIG();\r\n  config.server_port = 80;\r\n  httpd_uri_t index_uri = {\r\n    .uri       = &quot;\/&quot;,\r\n    .method    = HTTP_GET,\r\n    .handler   = index_handler,\r\n    .user_ctx  = NULL\r\n  };\r\n\r\n  httpd_uri_t cmd_uri = {\r\n    .uri       = &quot;\/action&quot;,\r\n    .method    = HTTP_GET,\r\n    .handler   = cmd_handler,\r\n    .user_ctx  = NULL\r\n  };\r\n  httpd_uri_t stream_uri = {\r\n    .uri       = &quot;\/stream&quot;,\r\n    .method    = HTTP_GET,\r\n    .handler   = stream_handler,\r\n    .user_ctx  = NULL\r\n  };\r\n  if (httpd_start(&amp;camera_httpd, &amp;config) == ESP_OK) {\r\n    httpd_register_uri_handler(camera_httpd, &amp;index_uri);\r\n    httpd_register_uri_handler(camera_httpd, &amp;cmd_uri);\r\n  }\r\n  config.server_port += 1;\r\n  config.ctrl_port += 1;\r\n  if (httpd_start(&amp;stream_httpd, &amp;config) == ESP_OK) {\r\n    httpd_register_uri_handler(stream_httpd, &amp;stream_uri);\r\n  }\r\n}\r\n\r\nvoid setup() {\r\n  WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); \/\/disable brownout detector\r\n  \r\n  pinMode(MOTOR_1_PIN_1, OUTPUT);\r\n  pinMode(MOTOR_1_PIN_2, OUTPUT);\r\n  pinMode(MOTOR_2_PIN_1, OUTPUT);\r\n  pinMode(MOTOR_2_PIN_2, OUTPUT);\r\n  \r\n  Serial.begin(115200);\r\n  Serial.setDebugOutput(false);\r\n  \r\n  camera_config_t config;\r\n  config.ledc_channel = LEDC_CHANNEL_0;\r\n  config.ledc_timer = LEDC_TIMER_0;\r\n  config.pin_d0 = Y2_GPIO_NUM;\r\n  config.pin_d1 = Y3_GPIO_NUM;\r\n  config.pin_d2 = Y4_GPIO_NUM;\r\n  config.pin_d3 = Y5_GPIO_NUM;\r\n  config.pin_d4 = Y6_GPIO_NUM;\r\n  config.pin_d5 = Y7_GPIO_NUM;\r\n  config.pin_d6 = Y8_GPIO_NUM;\r\n  config.pin_d7 = Y9_GPIO_NUM;\r\n  config.pin_xclk = XCLK_GPIO_NUM;\r\n  config.pin_pclk = PCLK_GPIO_NUM;\r\n  config.pin_vsync = VSYNC_GPIO_NUM;\r\n  config.pin_href = HREF_GPIO_NUM;\r\n  config.pin_sccb_sda = SIOD_GPIO_NUM;\r\n  config.pin_sccb_scl = SIOC_GPIO_NUM;\r\n  config.pin_pwdn = PWDN_GPIO_NUM;\r\n  config.pin_reset = RESET_GPIO_NUM;\r\n  config.xclk_freq_hz = 20000000;\r\n  config.pixel_format = PIXFORMAT_JPEG; \r\n  \r\n  if(psramFound()){\r\n    config.frame_size = FRAMESIZE_VGA;\r\n    config.jpeg_quality = 10;\r\n    config.fb_count = 2;\r\n  } else {\r\n    config.frame_size = FRAMESIZE_SVGA;\r\n    config.jpeg_quality = 12;\r\n    config.fb_count = 1;\r\n  }\r\n  \r\n  \/\/ Camera init\r\n  esp_err_t err = esp_camera_init(&amp;config);\r\n  if (err != ESP_OK) {\r\n    Serial.printf(&quot;Camera init failed with error 0x%x&quot;, err);\r\n    return;\r\n  }\r\n  \/\/ Wi-Fi connection\r\n  WiFi.begin(ssid, password);\r\n  while (WiFi.status() != WL_CONNECTED) {\r\n    delay(500);\r\n    Serial.print(&quot;.&quot;);\r\n  }\r\n  Serial.println(&quot;&quot;);\r\n  Serial.println(&quot;WiFi connected&quot;);\r\n  \r\n  Serial.print(&quot;Camera Stream Ready! Go to: http:\/\/&quot;);\r\n  Serial.println(WiFi.localIP());\r\n  \r\n  \/\/ Start streaming web server\r\n  startCameraServer();\r\n}\r\n\r\nvoid loop() {\r\n  \r\n}\r\n<\/code><\/pre>\n\t<p style=\"text-align:center\"><a class=\"rntwhite\" href=\"https:\/\/github.com\/RuiSantosdotme\/ESP32-CAM-eBook\/raw\/master\/Code\/Module_4\/Car_Robot_Camera_Web_Server\/Car_Robot_Camera_Web_Server.ino\" target=\"_blank\">View raw code<\/a><\/p>\n\n\n\n<p>Insert your network credentials and the code should work straight away.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>const char* ssid = \"REPLACE_WITH_YOUR_SSID\"; \nconst char* password = \"REPLACE_WITH_YOUR_PASSWORD\";<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">How the Code Works<\/h3>\n\n\n\n<p>Let\u2019s 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.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>#define MOTOR_1_PIN_1 14\n#define MOTOR_1_PIN_2 15\n#define MOTOR_2_PIN_1 13\n#define MOTOR_2_PIN_2 12<\/code><\/pre>\n\n\n\n<p>When you click the buttons, you make a request on a different URL.<\/p>\n\n\n\n<pre class=\"wp-block-code language-html\"><code>&lt;table&gt;\n    &lt;tr&gt;&lt;td colspan=\"3\" align=\"center\"&gt;&lt;button class=\"button\" onmousedown=\"toggleCheckbox('forward');\" ontouchstart=\"toggleCheckbox('forward');\" onmouseup=\"toggleCheckbox('stop');\" ontouchend=\"toggleCheckbox('stop');\"&gt;Forward&lt;\/button&gt;&lt;\/td&gt;&lt;\/tr&gt;\n    &lt;tr&gt;&lt;td align=\"center\"&gt;&lt;button class=\"button\" onmousedown=\"toggleCheckbox('left');\" ontouchstart=\"toggleCheckbox('left');\" onmouseup=\"toggleCheckbox('stop');\" ontouchend=\"toggleCheckbox('stop');\"&gt;Left&lt;\/button&gt;&lt;\/td&gt;&lt;td align=\"center\"&gt;&lt;button class=\"button\" onmousedown=\"toggleCheckbox('stop');\" ontouchstart=\"toggleCheckbox('stop');\"&gt;Stop&lt;\/button&gt;&lt;\/td&gt;&lt;td align=\"center\"&gt;&lt;button class=\"button\" onmousedown=\"toggleCheckbox('right');\" ontouchstart=\"toggleCheckbox('right');\" onmouseup=\"toggleCheckbox('stop');\" ontouchend=\"toggleCheckbox('stop');\"&gt;Right&lt;\/button&gt;&lt;\/td&gt;&lt;\/tr&gt;\n    &lt;tr&gt;&lt;td colspan=\"3\" align=\"center\"&gt;&lt;button class=\"button\" onmousedown=\"toggleCheckbox('backward');\" ontouchstart=\"toggleCheckbox('backward');\" onmouseup=\"toggleCheckbox('stop');\" ontouchend=\"toggleCheckbox('stop');\"&gt;Backward&lt;\/button&gt;&lt;\/td&gt;&lt;\/tr&gt;                   \n&lt;\/table&gt;\n&lt;script&gt;\n    function toggleCheckbox(x) {\n      var xhr = new XMLHttpRequest();\n      xhr.open(\"GET\", \"\/action?go=\" + x, true);\n      xhr.send();\n    }\n   window.onload = document.getElementById(\"photo\").src =     window.location.href.slice(0, -1) + \":81\/stream\";\n&lt;\/script&gt;<\/code><\/pre>\n\n\n\n<p>Here\u2019s the requests made depending on the button that is being pressed:<\/p>\n\n\n\n<p><strong>Forward:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>&lt;ESP_IP_ADDRESS&gt;\/action?go=forward<\/code><\/pre>\n\n\n\n<p><strong>Backward:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/action?go=backward<\/code><\/pre>\n\n\n\n<p><strong>Left:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/action?go=left<\/code><\/pre>\n\n\n\n<p><strong>Right:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/action?go=right<\/code><\/pre>\n\n\n\n<p><strong>Stop:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/action?go=stop<\/code><\/pre>\n\n\n\n<p>When you release the button, a request is made on the <span class=\"rnthl rntliteral\">\/action?go=stop<\/span> URL. The robot only moves as long as you\u2019re pressing the buttons.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Handle Requests<\/h3>\n\n\n\n<p>To handle what happens when we get requests on those URLs, we use these if\u2026 else statements:<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>if(!strcmp(variable, \"forward\")) {\n    Serial.println(\"Forward\");\n    digitalWrite(MOTOR_1_PIN_1, 1);\n    digitalWrite(MOTOR_1_PIN_2, 0);\n    digitalWrite(MOTOR_2_PIN_1, 1);\n    digitalWrite(MOTOR_2_PIN_2, 0);\n }\n else if(!strcmp(variable, \"left\")) {\n   Serial.println(\"Left\");\n   digitalWrite(MOTOR_1_PIN_1, 0);\n   digitalWrite(MOTOR_1_PIN_2, 1);\n   digitalWrite(MOTOR_2_PIN_1, 1);\n   digitalWrite(MOTOR_2_PIN_2, 0);\n}\nelse if(!strcmp(variable, \"right\")) {\n   Serial.println(\"Right\");\n   digitalWrite(MOTOR_1_PIN_1, 1);\n   digitalWrite(MOTOR_1_PIN_2, 0);\n   digitalWrite(MOTOR_2_PIN_1, 0);\n   digitalWrite(MOTOR_2_PIN_2, 1);\n}\nelse if(!strcmp(variable, \"backward\")) {\n   Serial.println(\"Backward\");\n   digitalWrite(MOTOR_1_PIN_1, 0);\n   digitalWrite(MOTOR_1_PIN_2, 1);\n   digitalWrite(MOTOR_2_PIN_1, 0);\n   digitalWrite(MOTOR_2_PIN_2, 1);\n}\nelse if(!strcmp(variable, \"stop\")) {\n   Serial.println(\"Stop\");\n   digitalWrite(MOTOR_1_PIN_1, 0);\n   digitalWrite(MOTOR_1_PIN_2, 0);\n   digitalWrite(MOTOR_2_PIN_1, 0);\n   digitalWrite(MOTOR_2_PIN_2, 0);\n}<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Testing the Code<\/h2>\n\n\n\n<p>After inserting your network credentials, you can upload the code to your ESP32-CAM board. If you don&#8217;t know how to upload code to the board, follow the next tutorial:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><a href=\"https:\/\/randomnerdtutorials.com\/program-upload-code-esp32-cam\/\">How to Program \/ Upload Code to ESP32-CAM AI-Thinker (Arduino IDE)<\/a><\/li><\/ul>\n\n\n\n<p>After uploading, open the Serial Monitor to get its IP address.<\/p>\n\n\n\n<div class=\"wp-block-image is-style-default\"><figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"742\" height=\"433\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/01\/ESP32-CAM-Getting-IP-Address-Serial-Monitor.png?resize=742%2C433&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32-CAM Getting IP Address Serial Monitor\" class=\"wp-image-101481\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/01\/ESP32-CAM-Getting-IP-Address-Serial-Monitor.png?w=742&amp;quality=100&amp;strip=all&amp;ssl=1 742w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/01\/ESP32-CAM-Getting-IP-Address-Serial-Monitor.png?resize=300%2C175&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 742px) 100vw, 742px\" \/><\/figure><\/div>\n\n\n\n<p>Open a browser and type the ESP IP address. A similar web page should load:<\/p>\n\n\n\n<div class=\"wp-block-image is-style-default\"><figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1036\" height=\"834\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/01\/ESP32-CAM-Remote-Controlled-Robot-Web-Server-Video-Streaming-2.jpg?resize=1036%2C834&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32-CAM Web Server Remote Controlled Robot 2 Demonstration\" class=\"wp-image-101482\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/01\/ESP32-CAM-Remote-Controlled-Robot-Web-Server-Video-Streaming-2.jpg?w=1036&amp;quality=100&amp;strip=all&amp;ssl=1 1036w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/01\/ESP32-CAM-Remote-Controlled-Robot-Web-Server-Video-Streaming-2.jpg?resize=300%2C242&amp;quality=100&amp;strip=all&amp;ssl=1 300w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/01\/ESP32-CAM-Remote-Controlled-Robot-Web-Server-Video-Streaming-2.jpg?resize=1024%2C824&amp;quality=100&amp;strip=all&amp;ssl=1 1024w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/01\/ESP32-CAM-Remote-Controlled-Robot-Web-Server-Video-Streaming-2.jpg?resize=768%2C618&amp;quality=100&amp;strip=all&amp;ssl=1 768w\" sizes=\"(max-width: 1036px) 100vw, 1036px\" \/><\/figure><\/div>\n\n\n\n<p>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.<\/p>\n\n\n\n<div class=\"wp-block-image is-style-default\"><figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"769\" height=\"541\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/01\/ESP32-CAM-Robot-Receice-Commands-Serial-Monitor.png?resize=769%2C541&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32-CAM Remote Controlled Robot Serial Monitor Commands\" class=\"wp-image-101483\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/01\/ESP32-CAM-Robot-Receice-Commands-Serial-Monitor.png?w=769&amp;quality=100&amp;strip=all&amp;ssl=1 769w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/01\/ESP32-CAM-Robot-Receice-Commands-Serial-Monitor.png?resize=300%2C211&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 769px) 100vw, 769px\" \/><\/figure><\/div>\n\n\n\n<p>If everything is working properly, it\u2019s time to assemble the circuit.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Circuit<\/h2>\n\n\n\n<p>After assembling the robot chassis, you can wire the circuit by following the next schematic diagram.<\/p>\n\n\n\n<div class=\"wp-block-image is-style-default\"><figure class=\"aligncenter size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"780\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/01\/ESP32-CAM-Remote-Controlled-Robot-Diagram.png?resize=1024%2C780&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32-CAM Remote Controlled Robot Diagram DC Motors Wiring Circuit\" class=\"wp-image-101471\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/01\/ESP32-CAM-Remote-Controlled-Robot-Diagram.png?resize=1024%2C780&amp;quality=100&amp;strip=all&amp;ssl=1 1024w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/01\/ESP32-CAM-Remote-Controlled-Robot-Diagram.png?resize=300%2C228&amp;quality=100&amp;strip=all&amp;ssl=1 300w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/01\/ESP32-CAM-Remote-Controlled-Robot-Diagram.png?resize=768%2C585&amp;quality=100&amp;strip=all&amp;ssl=1 768w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/01\/ESP32-CAM-Remote-Controlled-Robot-Diagram.png?w=1358&amp;quality=100&amp;strip=all&amp;ssl=1 1358w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure><\/div>\n\n\n\n<p>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.<\/p>\n\n\n\n<p>The following table shows the connections between the ESP32-CAM and the L298N Motor Driver.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><tbody><tr><td><strong>L298N Motor Driver<\/strong><\/td><td><strong>ESP32-CAM<\/strong><\/td><\/tr><tr><td>IN1<\/td><td>GPIO 14<\/td><\/tr><tr><td>IN2<\/td><td>GPIO 15<\/td><\/tr><tr><td>IN3<\/td><td>GPIO 13<\/td><\/tr><tr><td>IN4<\/td><td>GPIO 12<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>We assembled all the connections on a mini stripboard as shown below.<\/p>\n\n\n\n<div class=\"wp-block-image is-style-default\"><figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"750\" height=\"421\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/01\/ESP32-CAM-Remote-Controlled-Robot-Wiring-Stripboard-f.jpg?resize=750%2C421&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32-CAM Remote Controlled Robot Stripboard for Circuit\" class=\"wp-image-101473\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/01\/ESP32-CAM-Remote-Controlled-Robot-Wiring-Stripboard-f.jpg?w=750&amp;quality=100&amp;strip=all&amp;ssl=1 750w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/01\/ESP32-CAM-Remote-Controlled-Robot-Wiring-Stripboard-f.jpg?resize=300%2C168&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 750px) 100vw, 750px\" \/><\/figure><\/div>\n\n\n\n<p>After that, wire each motor to its terminal block.<\/p>\n\n\n\n<p class=\"rntbox rntclgray\"><strong>Note: <\/strong> 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.<\/p>\n\n\n\n<p>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.<\/p>\n\n\n\n<p class=\"rntbox rntclgray\"><strong>Note: <\/strong> 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. <\/p>\n\n\n\n<p>Your robot should look similar to the following figure:<\/p>\n\n\n\n<div class=\"wp-block-image is-style-default\"><figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"750\" height=\"421\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/01\/ESP32-CAM-Remote-Controlled-Robot-Assembled.jpg?resize=750%2C421&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32-CAM Remote Controlled Card Robot Assembled\" class=\"wp-image-101484\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/01\/ESP32-CAM-Remote-Controlled-Robot-Assembled.jpg?w=750&amp;quality=100&amp;strip=all&amp;ssl=1 750w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/01\/ESP32-CAM-Remote-Controlled-Robot-Assembled.jpg?resize=300%2C168&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 750px) 100vw, 750px\" \/><\/figure><\/div>\n\n\n\n<p>Don\u2019t forget that you should use an external antenna with the ESP32-CAM, otherwise the web server might be extremely slow.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Demonstration<\/h2>\n\n\n\n<p>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. <\/p>\n\n\n\n<div class=\"wp-block-image is-style-default\"><figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"750\" height=\"510\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/01\/ESP32-CAM-Remote-Controlled-Robot-Web-Server-Smartphone-Demonstration.jpg?resize=750%2C510&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32-CAM Remote Controlled Robot Web Server Demonstration\" class=\"wp-image-101489\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/01\/ESP32-CAM-Remote-Controlled-Robot-Web-Server-Smartphone-Demonstration.jpg?w=750&amp;quality=100&amp;strip=all&amp;ssl=1 750w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/01\/ESP32-CAM-Remote-Controlled-Robot-Web-Server-Smartphone-Demonstration.jpg?resize=300%2C204&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 750px) 100vw, 750px\" \/><\/figure><\/div>\n\n\n\n<p><strong>You can only have the web server open in one device\/tab at a time.<\/strong><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Wrapping Up<\/h2>\n\n\n\n<p>In this tutorial you&#8217;ve learned how to build a remote controlled robot using the ESP32-CAM and how to control it using a web server. <\/p>\n\n\n\n<p>Controlling DC motors with the ESP32-CAM is the same as controlling them using a &#8220;regular&#8221; ESP32. Read this tutorial to learn more: <a href=\"https:\/\/randomnerdtutorials.com\/esp32-dc-motor-l298n-motor-driver-control-speed-direction\/\" target=\"_blank\" rel=\"noreferrer noopener\">ESP32 with DC Motor and L298N Motor Driver \u2013 Control Speed and Direction<\/a>.<\/p>\n\n\n\n<p>If you want to control your robot outside the range of your local network, you might consider <a href=\"https:\/\/randomnerdtutorials.com\/esp32-cam-access-point-ap-web-server\/\">setting the ESP32-CAM as an access point<\/a>. This way, the ESP32-CAM doesn&#8217;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.<\/p>\n\n\n\n<p>For more projects and tutorials with the ESP32-CAM:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><a href=\"https:\/\/randomnerdtutorials.com\/esp32-cam-projects-ebook\/\">Build ESP32-CAM Projects using Arduino IDE eBook<\/a><\/li><li><a href=\"https:\/\/randomnerdtutorials.com\/projects-esp32-cam\/\">More ESP32-CAM Projects and Tutorials&#8230;<\/a><\/li><\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Build a Wi-Fi remote controlled car robot with the ESP32-CAM. You\u2019ll be able to control the robot using a web server that displays a video streaming of what the robot &#8230; <\/p>\n<p class=\"read-more-container\"><a title=\"ESP32-CAM Remote Controlled Car Robot Web Server\" class=\"read-more button\" href=\"https:\/\/randomnerdtutorials.com\/esp32-cam-car-robot-web-server\/#more-101469\" aria-label=\"Read more about ESP32-CAM Remote Controlled Car Robot Web Server\">CONTINUE READING \u00bb<\/a><\/p>\n","protected":false},"author":5,"featured_media":101494,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[276,281,319,264],"tags":[],"class_list":["post-101469","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-esp32","category-esp32-project","category-esp32-cam","category-project"],"aioseo_notices":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/01\/ESP32-CAM-Remote-Controlled-Car-Robot-Web-Server.jpg?fit=1280%2C720&quality=100&strip=all&ssl=1","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/posts\/101469","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/users\/5"}],"replies":[{"embeddable":true,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/comments?post=101469"}],"version-history":[{"count":3,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/posts\/101469\/revisions"}],"predecessor-version":[{"id":104801,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/posts\/101469\/revisions\/104801"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/media\/101494"}],"wp:attachment":[{"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/media?parent=101469"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/categories?post=101469"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/tags?post=101469"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}