ESP32-CAM RC Car with Camera and Mobile Phone Controller

ESP32-CAM-RC-Car-With-Camera.jpg

Retro-fitting a toy radio control car with an ESP32-CAM to enable Wi-Fi control and video streaming on a mobile phone

This tutorial is fairly advanced and more of an experiment than a finished project. Hopefully there are some ideas and code to learn from that might help with a similar projects.

I bought a cheap radio control car from a local toy shop with the intention of converting the normal radio control elements to a system using an ESP32 micro-controller communicating over WiFi. The ESP32-CAM used in this tutorial features WiFi and a camera so a video stream can be sent from the car.

Unfortunately the ESP32-CAM suffers from a lack of usable pins. I have only found two pins that can be used as outputs so some workarounds and compromises have been made in the design. One pin is used for steering and the other for forward speed control.

Conventionally to control an RC car you need to be able to set four different things, steer left, steer right, go forward, go reverse. The RC car I used doesn’t have analogue steering, it’s just left or right so I used servo motor to push limit switches to turn current on and off to the steering motor.

A motor controller needs two free pins so a reverse signal can be sent in addition to a PWM signal to control speed. However after using one pin for steering there is just one pin left on the ESP32-CAM so while the forward speed can be controlled there’s no way to tell the motor to go the opposite direction.

A possible solution that I haven’t investigated is to add another micro-controller to the system. This could receive data from the ESP32-CAM via the two available pins and parse this data in another Sketch that outputs over multiple pins . For example the ESP32-CAM could output data on a pin in the range 0 to 100, a Sketch on the second microcontroller would interpret the data in the range 0 to 50 to be reverse speeds and 51 – 100 to be forward speeds and activate output pins appropriately.

RC Car Hardware Components

These are the components installed in the RC car. I used 3.3v volts to power the ESP32 with a step down module to make the voltage from the 4 AA cells more stable . The steering is controlled by the two limit switches which make one or other side of the motor live when activated. The limit switches are pushed closed by a servo motor. The motor controller receives a PWM signal from the ESP32 and controls the motor speed.

ESP32-CAM
ESP32-CAM
3.3v Step Down
3.3v Step Down
Motor Controller
Motor Controller
Servo Motor
Servo Motor
Limit Switches
Limit Switches

A closer view of the steering system with the servo and limit switches. When the switches are in the position below, both sides of the steering motor are connected to negative side of the battery. When the servo pushes a limit switch closed, that side of the steering motor is connected to the positive side of the battery.

Servo and Limit Switches

I used a fish-eye lens with a longer cable for the camera. There are various replacement cameras available from here: https://es.aliexpress.com/item/32981773363.html

Longer Camera Cable
Fish-Eye Camera in Hood

This is how it looks assembled. The red and black wires top left are normally connected to the power connector block next to the steering motor.

RC Car Internals Labelled

This is how I wired it all together:

ESP32-CAM WiFi RC Car Wiring Diagram

The car’s speed and steering are controlled using a touch interface on a mobile phone or tablet. Below the the touch interface is a live view from the camera.

Wi-Fi Car Touch Interface

RC Car Sketch

If you’ve not used the ESP32-CAM before you will need to read through this tutorial first – https://robotzero.one/esp32-cam-arduino-ide/ to get familiar with it.

The ArduinoWebsockets and ESPAsyncWebServer libraries need to be installed for this Sketch…

Library Manager ArduinoWebsockets

ESPAsyncWebServer: I’m not sure whether this library has to be used but during the process of developing this project I appeared to have problems with other web server libraries so I ended up with this one. You have to install it manually by copying it to the Arduino libraries folder. https://github.com/me-no-dev/ESPAsyncWebServer

The index_html_gz variable below is the code for the controller interface. If you want to edit this code see this tutorial: https://robotzero.one/esp32-cam-custom-html/

#include <ArduinoWebsockets.h>
#include <WiFi.h>
#include <ESPAsyncWebServer.h>
#include "esp_camera.h"

const char* ssid = "NSA"; //Enter SSID
const char* password = "Orange"; //Enter Password

const uint8_t index_html_gz[] = {
  0x1f,0x8b,0x08,0x08,0x22,0x16,0x3b,0x5d,0x00,0xff,0x69,0x6e,0x64,0x65,0x78,0x2e,0x68,0x74,0x6d,0x6c,0x2e,0x67,0x7a,0x00,0x95,0x58,0x7b,0x8f,0xdb,0x36,0x12,0xff,0x7b,0xfd,0x29,0x58,0x07,0x57,0xdb,0x17,0x5b,0xf2,0xda,0x49,0x2f,0x71,0xec,0x3d,0x5c,0xba,0x01,0x5a,0xa0,0x45,0x83,0xdb,0xbd,0x26,0xc1,0xe1,0xb0,0xa0,0xa5,0x91,0xcd,0x44,0x12,0x75,0x24,0xe5,0xc7,0x5d,0xf7,0xbb,0x77,0x86,0xa2,0x24,0xca,0xeb,0x5d,0x24,0xfe,0xc3,0x96,0xc8,0xf9,0xcd,0xf3,0xc1,0xa1,0x97,0x5b,0x93,0xa5,0x57,0xbd,0xe5,0x16,0x78,0x8c,0x3f,0x19,0x18,0xce,0x72,0x9e,0xc1,0x6a,0xb0,0x13,0xb0,0x2f,0xa4,0x32,0x03,0x56,0x7f,0x22,0x99,0x1b,0xc8,0xcd,0x6a,0xb0,0x17,0xb1,0xd9,0xae,0x62,0xd8,0x89,0x08,0x26,0xf6,0x65,0xdc,0x10,0x89,0x5c,0x18,0xc1,0xd3,0x89,0x8e,0x78,0x0a,0xab,0xcb,0x60,0xda,0x6e,0x95,0x1a,0x94,0x5d,0xe7,0x6b,0xdc,0xca,0xe5,0x80,0x85,0x28,0xd2,0x08,0x93,0xc2,0xd5,0x7b,0x9e,0x33,0x9e,0xc7,0xec,0x56,0xa4,0x86,0xbd,0xbb,0x79,0x3f,0x9f,0x4d,0x7e,0xfc,0xc7,0xaf,0xcb,0xb0,0xda,0xed,0x2d,0xb5,0x39,0xe2,0x2f,0xeb,0x3d,0x23,0x25,0xb8,0xc8,0x41,0xb1,0xff,0xf7,0x2c,0x57,0x2b,0x7f,0xc1,0x2e,0xa7,0xd3,0xbf,0xbc,0xa9,0x56,0xb6,0x20,0x36,0x5b,0xb3,0x60,0x2f,0x5e,0xef,0xb6,0x6e,0x69,0xcd,0xa3,0x2f,0x1b,0x25,0xcb,0x3c,0x9e,0x44,0x32,0x95,0x6a,0xc1,0x9e,0xcd,0xe7,0x73,0xb7,0x19,0x0b,0x5d,0xa4,0xfc,0xb8,0x60,0x49,0x0a,0x07,0xb7,0xc6,0x53,0xb1,0xc9,0x27,0xc2,0x40,0xa6,0x17,0x2c,0x42,0xab,0x41,0xb9,0x9d,0xcf,0xa5,0x36,0x22,0x39,0x22,0x1f,0xeb,0x8d,0x93,0x5d,0xb9,0x03,0x95,0xa4,0x72,0xbf,0x60,0x5b,0x11,0xc7,0x90,0xd7,0xf2,0xa5,0x8a,0xd1,0x78,0xc5,0x63,0x51,0x22,0xc3,0xbf,0x15,0xb5,0x1c,0x23,0xcb,0x68,0x3b,0xe1,0x91,0x11,0x32,0x5f,0xb0,0x5c,0xe6,0xf0,0xa6,0x77,0xdf,0x63,0xcf,0x48,0xf2,0x43,0x0b,0x1b,0x58,0x6d,0xa2,0xbf,0xf6,0xd0,0x46,0xb5,0x59,0x0f,0x67,0x2f,0x5e,0x8e,0xd9,0x6c,0x8e,0x61,0x78,0xfd,0x7a,0xd4,0xd1,0x86,0xd0,0xc5,0x81,0x69,0x99,0x8a,0x98,0x48,0xf9,0xf0,0x72,0xfe,0xc3,0x98,0xb5,0x5f,0xc1,0xcb,0x2e,0xa0,0x51,0xff,0x65,0xe3,0xea,0x73,0xea,0x7b,0xb1,0x86,0x14,0x22,0xd4,0xb2,0x6b,0xd6,0x82,0xc8,0x77,0x50,0x5b,0x77,0x56,0x6d,0xd4,0xe5,0x87,0x57,0xa8,0xf7,0x25,0x7d,0xcd,0x50,0x79,0x4c,0xa4,0x29,0x6a,0xd3,0xf0,0xd8,0x92,0xa3,0x6b,0x16,0x51,0xa9,0x34,0x01,0x0b,0x29,0xbc,0x50,0x38,0xa5,0x9d,0xf7,0x66,0xd6,0x51,0x84,0xe7,0x0a,0x78,0x8d,0x2c,0xa4,0xc6,0x74,0x25,0xdd,0x13,0x71,0x80,0xd8,0x21,0x55,0xe5,0xdc,0x69,0x63,0x64,0x61,0x5f,0xee,0x7b,0xbd,0x44,0x6c,0x4a,0x05,0x0e,0x7c,0x26,0xcf,0x0a,0x1e,0xc7,0x22,0xdf,0x2c,0x6a,0x68,0xc6,0xd5,0x46,0xe4,0xcd,0xeb,0x64,0x0f,0xeb,0x2f,0xc2,0x4c,0xaa,0xe5,0xc9,0x1a,0x12,0xa9,0xe0,0x84,0x78,0xb2,0x4e,0x65,0xf4,0x65,0xa2,0x0d,0x57,0xe6,0x31,0x20,0x4f,0xd0,0xcc,0xf3,0x38,0xc8,0xe3,0xc7,0x50,0x5d,0x96,0x6e,0x51,0xe4,0x29,0x56,0xd3,0xd3,0xe2,0x7c,0x9e,0x5d,0x98,0xdd,0x61,0xe8,0x99,0xca,0x31,0x4c,0x64,0x1b,0xe7,0x9c,0xba,0xa8,0xac,0x56,0x0e,0x5b,0x85,0x82,0x97,0x46,0x76,0xd3,0xd8,0x2f,0xd4,0x4e,0xa6,0xbd,0x68,0x92,0xdb,0x89,0xa5,0x50,0xbc,0xc2,0xa4,0xa5,0x58,0x2c,0xc3,0xaa,0x29,0xe0,0x83,0xeb,0x5e,0x6b,0x19,0x1f,0xf1,0x47,0xe4,0x45,0x69,0x98,0x39,0x16,0xd8,0xc5,0x0c,0x1c,0xd0,0x32,0xe0,0x03,0x26,0xe2,0xd5,0xa0,0x7a,0x42,0xd5,0xa8,0x03,0xc5,0xb6,0xfd,0xc4,0x62,0x67,0xb7,0x64,0x89,0x2e,0xfd,0xb1,0x6e,0x2e,0x83,0x2b,0x14,0xdb,0xec,0x35,0x3d,0xc7,0x2e,0x7b,0x1b,0x94,0x8a,0x83,0x2b,0xb6,0x0c,0x71,0xc1,0x22,0x9a,0x07,0xe7,0x0f,0xab,0xe1,0xaa,0xef,0xd9,0xd9,0xef,0xb2,0xe8,0x6b,0x83,0x3a,0x65,0xb6,0x9b,0x58,0x11,0x7d,0x16,0xa5,0x5c,0xeb,0x55,0x5f,0x64,0x7c,0x03,0xde,0x3a,0x8a,0x41,0xef,0x7a,0x98,0x3e,0xd3,0x2a,0x5a,0xf5,0x69,0xa3,0x95,0x5f,0xc9,0x25,0x9f,0xd8,0xa5,0xa5,0x8e,0x94,0x28,0xcc,0x55,0x0f,0xf9,0x68,0xc3,0xa8,0xa7,0xb3,0x15,0x8b,0x65,0x54,0x66,0xd8,0xb5,0x82,0x0d,0x98,0x77,0x29,0xd0,0xe3,0xdb,0xe3,0xcf,0xf1,0x70,0x50,0x31,0x1e,0x60,0xa1,0x55,0xf4,0x1f,0x6e,0xee,0xfe,0xf5,0xcf,0x5f,0x10,0xd1,0xdf,0xeb,0x45,0x18,0xf6,0xd9,0x73,0x8c,0x61,0x1e,0xcb,0x7d,0x80,0x51,0xe5,0x54,0x38,0xc1,0x56,0x22,0xdd,0x73,0xd6,0x5f,0xbc,0x9a,0xf5,0x6b,0xd8,0x5e,0x23,0x24,0x47,0x51,0x1f,0x60,0x7d,0x83,0xe1,0x07,0x33,0xac,0x38,0x21,0xe3,0x8b,0x8b,0xde,0x5e,0x07,0x32,0xcf,0x40,0x6b,0x34,0x10,0x09,0x9b,0xa7,0x2b,0xac,0xca,0x0b,0x91,0xb0,0xa1,0x5b,0x09,0x62,0x8e,0xa7,0x91,0x40,0x8e,0x3c,0x8f,0x40,0x26,0xec,0x6d,0x2a,0xd7,0x23,0x22,0x62,0x6c,0xc7,0x15,0x2b,0x55,0xfa,0xdb,0xfa,0x33,0x76,0x18,0x64,0x82,0xcc,0x83,0x08,0x95,0x37,0x50,0x2d,0xe1,0x7b,0x87,0x0d,0x49,0x46,0x14,0xda,0x1f,0xa0,0xd7,0x10,0xd0,0x80,0x71,0xe3,0xbe,0x77,0xff,0xa6,0xd7,0x23,0x96,0xb1,0xe2,0x9b,0x9f,0xa9,0xf5,0x7a,0x4e,0xfa,0x6f,0x09,0xea,0x78,0x63,0x5b,0x99,0x54,0xc3,0x81,0xed,0x3f,0xe4,0x22,0xa2,0x6f,0x4f,0xa3,0x27,0x00,0x6d,0xfa,0x9c,0xa2,0x3e,0x50,0x41,0x20,0xb4,0x59,0x08,0x64,0x92,0x68,0x30,0x76,0xfd,0x84,0xf6,0x27,0x9b,0x43,0x67,0x88,0xab,0x8d,0x8a,0x3a,0xe3,0x87,0x6b,0x34,0xe1,0x27,0xa9,0xc4,0xff,0x88,0x2c,0xf5,0xe9,0x2b,0x69,0x21,0x9b,0x75,0x68,0x7f,0x07,0x65,0x04,0x1e,0xca,0x3e,0xa5,0x93,0x65,0x49,0x1f,0x4d,0x16,0x5b,0x4c,0xa3,0x60,0xc7,0xd3,0x92,0xc2,0x58,0x4d,0x05,0x0b,0x36,0xc0,0x6c,0x78,0xa8,0xc6,0x73,0x5c,0xaf,0xfb,0xa5,0x47,0x51,0x0b,0xaf,0x34,0x72,0x07,0xc3,0x8a,0x25,0x3c,0xd5,0x78,0x62,0x58,0xfb,0x4b,0xa5,0x50,0xe4,0xc7,0xce,0xdb,0xa7,0xea,0xcd,0xcd,0x1a,0x6e,0xcf,0xbd,0xb9,0xbd,0xc3,0x6f,0xd6,0x39,0xc8,0x0c,0x3b,0x17,0x2d,0x1c,0x4f,0x17,0xb0,0xcc,0xcc,0x2d,0xb6,0x87,0xb1,0x7d,0xba,0xc1,0x56,0x36,0x66,0x1a,0xbf,0x6f,0x45,0x06,0xd8,0x0d,0x6c,0x32,0x3b,0x47,0x63,0x57,0x7f,0xb7,0x43,0xb9,0xbf,0x08,0x8d,0x67,0x3e,0x60,0x50,0xed,0xc1,0x67,0x9b,0xe6,0x60,0x6c,0xb3,0xe6,0x86,0x9e,0xc7,0x95,0xe6,0x55,0xfd,0x3c,0x8d,0x45,0x41,0x0e,0xf9,0x8e,0x04,0x7f,0x35,0x2e,0xc3,0x83,0xcf,0x01,0xbf,0x0e,0x95,0x49,0x3c,0x8b,0xb1,0x64,0xf3,0x6f,0x57,0xd4,0x42,0xcb,0xe2,0x5b,0xf5,0xb4,0xb0,0xb3,0x7a,0xf6,0x92,0x32,0xb7,0xb3,0x42,0xab,0xc9,0x10,0xa8,0xa2,0xed,0xe4,0x88,0x95,0x0f,0x01,0xb5,0x6d,0xb6,0x5a,0x61,0x3e,0x79,0x2e,0xae,0x49,0xbc,0x01,0xf3,0x23,0x06,0x12,0xa9,0xad,0x2b,0xf5,0xbf,0xa7,0xff,0x09,0xa2,0x54,0x50,0x9e,0xb0,0x49,0x1d,0x7a,0x77,0x72,0xb4,0x90,0x4f,0xe7,0x21,0x9f,0x10,0xe2,0x92,0xa3,0x82,0xdc,0x33,0x40,0x75,0x1f,0x13,0xf9,0xb5,0x72,0x1e,0x63,0xee,0xdb,0x8a,0x87,0x1a,0x65,0x24,0x5a,0x5b,0x37,0x1e,0xdf,0xd2,0xa6,0x1c,0x8c,0x2a,0xdd,0x5c,0x85,0x9d,0xea,0xc4,0x89,0x18,0x15,0xcf,0x85,0xad,0xa2,0x6d,0xdd,0x9c,0x68,0xd6,0x96,0x50,0x47,0x86,0x2b,0xb9,0x53,0xf6,0xdd,0xf0,0x54,0xd4,0xbe,0x8e,0x10,0x14,0x0a,0x28,0xfc,0xd7,0x90,0xf0,0x32,0x35,0xc3,0x7a,0x64,0x7c,0x2c,0xa0,0x36,0x2f,0x7c,0x0e,0x6e,0x80,0xb3,0xca,0x3e,0x1e,0xd3,0xb6,0xd4,0xcf,0xe0,0x9e,0x08,0x6c,0xdb,0x14,0x6a,0xcc,0x83,0xe8,0x3e,0x50,0xe0,0x9b,0xa4,0x3e,0x29,0xaa,0x79,0x6a,0xbb,0x51,0x37,0x2e,0xf4,0x69,0x1b,0x53,0x37,0x34,0xb5,0x0b,0x7f,0xe5,0x66,0x1b,0xf0,0xb5,0x1e,0xd6,0xdb,0x23,0xb6,0x7c,0xd0,0xbc,0xbf,0xff,0x9e,0x9d,0xd2,0x7d,0xf4,0xe8,0xda,0x4e,0x7c,0xea,0x7a,0x94,0x7c,0xab,0x78,0xae,0x53,0x3c,0x3a,0x1b,0xe4,0xb8,0x51,0xa5,0xaa,0x60,0x9b,0x99,0xe7,0xec,0xfa,0xea,0xc3,0xe1,0x63,0xd5,0xf6,0x1b,0x3f,0xd3,0x79,0xf0,0xa9,0xb3,0xe6,0xac,0xb6,0x19,0x1e,0x86,0x2c,0x15,0x99,0x30,0xb6,0x1f,0xe3,0x3c,0x8d,0x23,0x38,0xc3,0x3b,0x44,0x33,0x2a,0x60,0xc6,0xa9,0x23,0x9b,0x4f,0x59,0xa6,0x89,0x76,0x6b,0x4c,0x41,0xf3,0xc9,0x46,0x98,0x6d,0xb9,0x0e,0x22,0x99,0x85,0x39,0xc8,0x5c,0x60,0x1f,0x0a,0xf1,0x2e,0xf6,0x59,0xdf,0x81,0x2e,0xe6,0xb3,0x3b,0x38,0xf0,0xac,0x48,0x41,0x87,0x38,0x94,0xae,0xc3,0x0c,0xbb,0x3e,0xa8,0x86,0xf2,0x0e,0x05,0x84,0x11,0xa4,0x69,0xb1,0xc5,0xa7,0x3b,0x6a,0x70,0x4a,0xa6,0x38,0x26,0xde,0xa9,0xe8,0x2e,0xe2,0x2a,0xdc,0xef,0xf7,0x21,0x0e,0x3f,0x70,0x08,0xe8,0xce,0xdc,0x96,0x09,0xe9,0x38,0x34,0x07,0x53,0x7b,0x96,0xce,0x96,0x5c,0xd2,0x9c,0x45,0x23,0xd0,0x35,0xf9,0x75,0x44,0xfe,0xa1,0x73,0xa5,0x2e,0x0f,0x91,0x0c,0xeb,0x43,0xc7,0xd6,0x06,0xde,0x7e,0x20,0xc1,0x76,0x1a,0xb3,0x3f,0xfe,0xb0,0xe0,0x49,0x73,0x28,0xb1,0xab,0x15,0x5a,0xea,0x87,0xcd,0xa0,0xed,0xdd,0x20,0xe2,0x44,0xd5,0x68,0xd1,0xcd,0xd5,0x56,0xca,0x13,0xda,0xd4,0x1f,0x05,0xa6,0x54,0xf5,0x95,0xd5,0x96,0x0a,0x0e,0x79,0xd1,0xb6,0xed,0x01,0xde,0x3f,0x01,0x78,0x6d,0x04,0x9c,0x02,0xa9,0x41,0x9c,0x26,0x46,0xf5,0x5d,0x1f,0xaf,0xd4,0xbf,0x0e,0xae,0xf9,0xa1,0xd9,0xdf,0x79,0x47,0xac,0xcf,0xd6,0x4e,0x24,0x34,0x37,0x36,0x1a,0x7f,0xd7,0xf1,0xcb,0xdf,0x29,0xdc,0x13,0x36,0xec,0x3a,0x67,0xc4,0x16,0xb8,0xee,0xd7,0xcb,0x10,0x99,0x2c,0xd9,0x74,0xd4,0xd1,0xd7,0x32,0xf6,0xc8,0x3c,0x1d,0x70,0x83,0x4a,0xa0,0x7a,0x19,0x0e,0x47,0xd5,0x18,0xda,0xad,0x10,0x9f,0x38,0x2f,0x53,0x9c,0x56,0x4e,0xf7,0x6d,0x34,0xc9,0x5a,0xdf,0x19,0x63,0x94,0xeb,0xde,0xbb,0x7d,0xbb,0x53,0x73,0x87,0xf7,0x52,0x8f,0xd9,0xd1,0x7e,0x43,0x53,0x9e,0x90,0x06,0xf6,0xee,0x10,0x18,0x22,0xc4,0x6b,0x22,0x8d,0xa4,0x03,0xfb,0x42,0xa8,0x39,0x96,0x18,0x96,0x0e,0x61,0xa9,0x94,0x8a,0xc3,0xd8,0x96,0x12,0x71,0xa9,0xdf,0xa7,0xa3,0x41,0x25,0x9b,0x3c,0x5b,0xf0,0xfc,0x1a,0x36,0x0a,0x80,0x1c,0x61,0x51,0x7f,0x65,0xaf,0xa7,0x38,0xe0,0x3d,0xe8,0x0f,0x2d,0xc6,0xe0,0x5f,0x31,0x2d,0xc8,0xb2,0xee,0x82,0xda,0xe1,0xed,0xc2,0xfa,0xc0,0x13,0x82,0x3a,0x8c,0x49,0x21,0x8f,0x87,0xbd,0xbd,0xe3,0x9d,0xcd,0x5d,0x48,0x96,0xa1,0xbb,0xad,0x85,0xf6,0x1f,0xa8,0x3f,0x01,0x69,0xb6,0x7a,0xb0,0x88,0x12,0x00,0x00
};

#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

camera_fb_t * fb = NULL;

using namespace websockets;
WebsocketsServer server;
AsyncWebServer webserver(80);

const int fwdPin = 2;  //Forward Motor Pin
const int turnPin = 12;  //Steering Servo Pin

// Arduino like analogWrite
// value has to be between 0 and valueMax
void fwdAnalogWrite(uint8_t channel, uint32_t value, uint32_t valueMax = 4000) {
  // calculate duty, 8191 from 2 ^ 13 - 1
  uint32_t duty = (8191 / valueMax) * min(value, valueMax);
  ledcWrite(channel, duty);
}
void steeringAnalogWrite(uint8_t channel, uint32_t value, uint32_t valueMax = 180) {
  // calculate duty, 8191 from 2 ^ 13 - 1
  uint32_t duty = (8191 / valueMax) * min(value, valueMax);
  ledcWrite(channel, duty);
}

void setup()
{
  Serial.begin(115200);

  // forward motor PWM
  ledcSetup(2, 200, 12); //channel, freq, resolution
  ledcAttachPin(fwdPin, 2); // pin, channel

  // steering servo PWM
  ledcSetup(4, 50, 16); //channel, freq, resolution
  ledcAttachPin(turnPin, 4); // pin, channel

  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;
  //init with high specs to pre-allocate larger buffers
  if (psramFound()) {
    config.frame_size = FRAMESIZE_UXGA;
    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(&amp;config);
  if (err != ESP_OK) {
    Serial.printf("Camera init failed with error 0x%x", err);
    return;
  }

  sensor_t * s = esp_camera_sensor_get();
  s->set_framesize(s, FRAMESIZE_SVGA);

  // Connect to wifi
  WiFi.begin(ssid, password);

  // Wait some time to connect to wifi
  for (int i = 0; i < 15 &amp;&amp; WiFi.status() != WL_CONNECTED; i++) {
    Serial.print(".");
    delay(1000);
  }

  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());   // You can get IP address assigned to ESP

  webserver.on("/", HTTP_GET, [](AsyncWebServerRequest * request) {
    AsyncWebServerResponse *response = request->beginResponse_P(200, "text/html", index_html_gz, sizeof(index_html_gz));
    response->addHeader("Content-Encoding", "gzip");
    request->send(response);
  });

  webserver.begin();
  server.listen(82);
  Serial.print("Is server live? ");
  Serial.println(server.available());

}

void handle_message(WebsocketsMessage msg) {
  int commaIndex = msg.data().indexOf(',');
  int steerValue = msg.data().substring(0, commaIndex).toInt();
  int forwardValue = msg.data().substring(commaIndex + 1).toInt();

  if (steerValue > 5 ) {
    steeringAnalogWrite(4, 112);
  }
  else if (steerValue < -5 ) {
    steeringAnalogWrite(4, 65);
  }
  else {
    steeringAnalogWrite(4, 85); // center steering
  }

  forwardValue = map(forwardValue, 0, -90, 500, 2000);

  if (forwardValue > 600 ) {
    fwdAnalogWrite(2, forwardValue);
  }
  else {
    fwdAnalogWrite(2, 0); // stop
  }
}

void loop()
{
  auto client = server.accept();
  client.onMessage(handle_message);
  while (client.available()) {
    client.poll();
        fb = esp_camera_fb_get();
        client.sendBinary((const char *)fb->buf, fb->len);
        esp_camera_fb_return(fb);
        fb = NULL;
  }

}

The code uses Web Sockets to stream the camera data to the browser:

client.sendBinary((const char *)fb->buf, fb->len)

and also receive control data from the touch interface on the mobile or tablet:

void handle_message(WebsocketsMessage msg) { // do stuff}

Project Demonstration Video

In there’s a lot of interest in this project (coffees!) I’ll work on a simpler version with a 3D printed chassis with servo control for steering and foreward/reverse and space for the ESP32-CAM to be located without bending pins etc.

You can also find this project on YouTube here: https://youtu.be/BwsH1kLxDmQ

References

Touch Interface: https://www.kirupa.com/html5/drag.htm

3 Replies to “ESP32-CAM RC Car with Camera and Mobile Phone Controller”

  1. rob says:

    Nice project. Have you ever tried the DRV8871 DC motor drivers? They are MOSFET based and have a lower voltage drop and better performance , price wise very similar.

    1. WordBot says:

      Maybe for another project but it looks like the DRV8871 needs a minimum of 6.5v. The ones I used are a quarter of the price and work from 2v – https://aliexpress.com/item/32920407851.html

      1. rob says:

        Ahh. Yes, you are right about that, there is also a low voltage dual channel version , DRV8833.
        Thanks for the great write up.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

scroll to top