Introduction: Tim's Little R.O.V.

About: Retired due to health. Oldish. My background is in Structural Engineering. Also smith of many trades. The majority of my project will be what can be made sat in a chair within arm's reach, on a plotter, 3D pri…

This is a little ROV I have designed and printed on my 3D Printer.

Because I wanted visual feedback it has the ESP32-CAM Module as the main control unit.

Because it has the ESP32-CAM Module, it can be controlled from any device that has Wi-Fi and a Web Browser.

I wanted to keep it a simple as possible, yet have it able to do something.

There was an earlier version of this that was as simple as it gets for making an ROV with an ESP32-CAM. This one has a couple of upgrades, it has a simple track system to give it some grip on uneven terrain and has an arm which can hook on to things.

There are a few like this on the web, I feel that I have made some nice improvements to what there is.

The skill set required (Some assumptions)

  • 3D printer: You know how to use it.
  • Arduino: You have some experience of making and uploading Sketches. I have put links to help in the Instruction.

Supplies

As this is a 3D Printed project a 3D Printer will be needed.

I have attached all the STL Files needed.

  • The difference between Tread A and Tread B are the hole sizes, I wanted the screw heads all to be on the outside.

For those that don't have a 3D Printer, I am sure that most of this can be mounted to a simple card platform. (no tracks)

Purchased parts needed are as follows:

Step 1: ESP32-CAM and ESP32-CAM-MB

The main module for this is an ESP32-CAM Module.

You can now get an ESP32-CAM-MB (Mother Board) to easaly program this module.

Take a look at what you are buying when getting Mother Board, one of the above MB has only one switch, or the ESP32-CAM for that matter, there are several manufacturers of these Modules.

Check what camera comes with the module, the cameras orientation cannot be changed with firmware, it is fixed. I have done a blog on how to change the cameras orientation if needed.

About the Motherboards, I have found that I can program and run the Serial Monitor with the ESP32-CAM Modules that came with the Motherboards, the older ESP32-CAM Modules that did not come with the Motherboards, I can only program, I cannot see the Serial from the ESP32-CAM Modules and the program does not run until I remove the ESP32-CAM Module from the Motherboard.

Step 2: FTDI Programmer

Before the release of the Mother Board, you needed an FTDI programmer to upload your code.

Before I continue I want to talk about power to the ESP32-CAM, when the Camera is on it needs a lot of power. Depending on which FTDI Programmer you have to program the ESP32-CAM, you may need to use a separate Power supply for the ESP32-CAM, this ensures no brownouts after programming.

The Programmer I have has a jumper to switch the VCC between 3.3v and 5v, this does not change the voltage on the Data Pins, these remain at 3.3v.

On this Programmer the 3.3v comes from the FTDI Chip and does not have enough current to run the Camera.

So the jumper needs to be on 5v, this comes from the USB so the VCC is connected to the 5v on the ESP32-CAM.

The new Motherboard supplies USB 5v to the 5v Pin..

To program the ESP32-CAM, GPIO Pin IO0 needs to be connected to GND when Reset.

Some Motherboards have a switch for this.

I have done a small Modification to make it easy for those that don't have the switch.

I have soldered a small SMD Switch across the two pins.

I used to just put a jumper on these two pins when using the FTDI Programmer, but that cant be done with the Motherboard with only one switch. Without the modification you have to unplug the USB and plug it back in.

Step 3: Code

Also in this Zip file is a Library I wrote for the Servo, this will need copying into your Arduino Library's Folder.

There is a ESP32 library for Servos on the web but I though it was a bit over complicated. Mine I think is a bit simpler to use and it's the one used in this Sketch.

The Arduino Sketch has some options for how you want to Connect with Wi-Fi and which Camera Module you use.

Read the comments inside the code to set the correct choices.

I have put lots of comments in the code so you can see what does what.

/*
ESP32-CAM R.O.V.
By: Tim Jacson.1960

Give credit where it is due.
Credits:
2015-2016 Espressif Systems (Shanghai) PTE LTD
Arduino

Use Arduino "Board Manager" to install files for the ESP32.
May need to add: https://dl.espressif.com/dl/package_esp32_index.json
To Additional Bords Manager URLs, in Arduino IDE preferences.

If Module: ESP32-CAM Cheap China Clone.(no brand) Probably clone of AI Thinker ESP32-CAM.

----------------------
- SD -
5v - - 3.3v
GND - - GPIO 16
Motor GPIO 12 - - GPIO 0 Cam clk on AI Thinker
Motor GPIO 13 - - GND
Motor GPIO 15 - cam - 3.3/5v
Motor GPIO 14 - - GPIO 3 Serial
Servo GPIO 2 - - GPIO 1 Serial
LED GPIO 4 - - GND
- -
- -
- GPIO 4 - LED -
- -
----------------------

Arduino IDE Settings: (May be differen if you have a branded Module)
Tools -> Board -> ESP32 Arduino -> AI Thinker ESP32-CAM
Tools -> Port -> user choice. (Use device manager to find correct COM-Port)

Header "Index_Page.h" is the Index_Page.html inserted into a char array as rawliteral.

Servo signal on GPIO 2.

The motor driver I am using is a DRV8833.

----------------
IN4 - -Sleep
IN3 - - OUT1
GND - - OUT2
VM* - - OUT3
IN2 - - OUT4
IN1 - - Fault
----------------
*Voltage to Motor 2.7 to 10.8 volt.

There is no enable pin to use.
So the drive (+side) pin is sent pwm while the other is kept low.

The L298N may need different code. (Use if you need more that 9 volt to the motors)
If you try an L298N, put jumpers on the enable pins (make them permantly HIGH) and just use 4 wires like with the DRV8833.

Pins used on the ESP32-CAM module:
ESP32-CAM IO12 ----- DRV8833 IN1
ESP32-CAM IO13 ----- DRV8833 IN2
ESP32-CAM IO15 ----- DRV8833 IN3
ESP32-CAM IO14 ----- DRV8833 IN4

I assume you have learned to program the ESP32-CAM for instructions on the internet.

You will need to set your WiFi's ssid and password in the code if using your LOCAL_NETWORK.
*/

#include <WiFi.h>
#include "esp_camera.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"
#include "IndexHTML.h"
#include <TimsESP32_Servo.h>

/*
Type of connection?
"Access Point" or "Local Network"

"Access Point"
This is not connected to your local network, you connect to the Network of the ESP32-CAM.
This means you can connect to it with your Moble Phone anywhare you are.
You change the WiFi your Phone is connected to, to the ESP32-CAM WiFi.
Then open browser to the IP of the ESP32-CAM control page. (may be 192.168.4.1)
Have your serial monitor connected when you re-set the module to confirm correct IP Address.

"Access Point Pasword"
This is same as above, bvut you need a password to connect to the WiFi. (it currently is: 2468)

"Local Network"
This is, both you and the ESP32-CAM is connected to your local network.
You will need to give the ssid (name of your network) and the password required to gain access to your network.
You can connect to the ESP32-CAM from any devices borwser that is connected to your network.
Open a browser to the IP of the ESP32-CAM control page. (may be 192.168.0.46)
Have your serial monitor connected when you re-set the module to confirm correct IP Address. (Using a local Network, it may be alocated a different IP)

Un-Comment which one of the three you want to connect.
*/
#define ACCESS_POINT
//#define ACCESS_POINT_PW
//#define LOCAL_NETWORK

/*
Replace with your network credentials
*/
const char* ssid = "your netwok"; // You need to change to yours if LOCAL_NETWORK is used.
const char* password = "your netwok password"; // You need to change to yours if LOCAL_NETWORK is used.
/*
The name of network of the ESP32-CAM
The Password if used
*/
const char* ESP32_CAMssid = "ESP32-CAM R.O.V.";
const char* ESP32_CAMpassword = "2468";

#define PART_BOUNDARY "123456789000000000000987654321"
/*
Chose the model of your ESP32-CAM module.
Un-Comment the one you have. (If you have a branded Module)
If you have a cheep Cinese Clone, try the CAMERA_MODEL_AI_THINKER
*/
#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

/*
Module Pins.
I have used the same Pin Values for the PWM Channels.
(Doing this will only work for the first 16 pins, there is only 16 PWM channels)

Pins 1 and 3 used for serial.
*/
#define MOTOR_1_PIN_1 13
#define MOTOR_1_PIN_2 12
#define MOTOR_2_PIN_1 14
#define MOTOR_2_PIN_2 15
#define LED_PIN 4
#define SERVO_2 2

/*
Servo 2 attaced to Pin 2 using channel 2
*/
ESP32_Servo Servo02(SERVO_2, SERVO_2);
/*
Setting for PWM properties
*/
const int pwmFreq = 30000;
const int pwmResolution = 8;
int dutyCycle = 200;

String sliderValue = "0";
byte MotorSpeed = 200;
byte LED_Power = 0;
byte ServoAngle = 0;
boolean Toggle = true;

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 esp_err_t index_handler(httpd_req_t* req) {
httpd_resp_set_type(req, "text/html");
httpd_resp_set_hdr(req, "Content-Encoding", "gzip");
return httpd_resp_send(req, (const char*)index_Tims_Little_ROV_html, INDEX_TIMS_LITTLE_ROV_GZ_LEN);
}

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 if (httpd_query_key_value(buf, "Speed_value", variable, sizeof(variable)) == ESP_OK) {
sliderValue = variable;
MotorSpeed = sliderValue.toInt();
Serial.print("MotorSpeed: ");
Serial.println(MotorSpeed);
}
else if (httpd_query_key_value(buf, "LED_value", variable, sizeof(variable)) == ESP_OK) {
sliderValue = variable;
LED_Power = sliderValue.toInt();
ledcWrite(LED_PIN, LED_Power);
Serial.print("LED_Power: ");
Serial.println(LED_Power);
}
else if (httpd_query_key_value(buf, "Servo_value", variable, sizeof(variable)) == ESP_OK) {
sliderValue = variable;
ServoAngle = sliderValue.toInt();
Servo02.Angle(ServoAngle);
Serial.print("Servo_value: ");
Serial.println(ServoAngle);
}
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;
/*
Motor control
To control the forward Pin of a motor you alter the dutyclcle of the LED Channel.
The use "LED Channel", "ledcWrite" is a little confusing, it is a pwm function to control LED's.
The same PWM can be used with a Motor driver.
ledcWrite(Channel, dutyCycle);
*/
if (!strcmp(variable, "forward")) {
Serial.println("Forward");
AllMotorOff();
ledcWrite(MOTOR_1_PIN_2, MotorSpeed);
ledcWrite(MOTOR_2_PIN_2, MotorSpeed);
}
else if (!strcmp(variable, "left")) {
Serial.println("Left");
AllMotorOff();
ledcWrite(MOTOR_1_PIN_2, MotorSpeed);
ledcWrite(MOTOR_2_PIN_1, MotorSpeed);
}
else if (!strcmp(variable, "right")) {
Serial.println("Right");
AllMotorOff();
ledcWrite(MOTOR_1_PIN_1, MotorSpeed);
ledcWrite(MOTOR_2_PIN_2, MotorSpeed);
}
else if (!strcmp(variable, "backward")) {
Serial.println("Backward");
AllMotorOff();
ledcWrite(MOTOR_1_PIN_1, MotorSpeed);
ledcWrite(MOTOR_2_PIN_1, MotorSpeed);
}
else if (!strcmp(variable, "stop")) {
Serial.println("Stop");
AllMotorOff();
}
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 AllMotorOff() {
/*
Set all Motor Pins to 0 (off/GND)
*/
ledcWrite(MOTOR_1_PIN_1, 0);
ledcWrite(MOTOR_1_PIN_2, 0);
ledcWrite(MOTOR_2_PIN_1, 0);
ledcWrite(MOTOR_2_PIN_2, 0);
}
void setup() {
/*
Set Pin Direction.
*/
pinMode(MOTOR_1_PIN_1, OUTPUT);
pinMode(MOTOR_1_PIN_2, OUTPUT);
pinMode(MOTOR_2_PIN_1, OUTPUT);
pinMode(MOTOR_2_PIN_2, OUTPUT);
pinMode(LED_PIN, OUTPUT);
/*
Configure LED PWM functionalitites.
I have used same channel as pin number.
ledcSetup(Channel, Frequency, Resolution);
*/
ledcSetup(MOTOR_1_PIN_1, pwmFreq, pwmResolution);
ledcSetup(MOTOR_1_PIN_2, pwmFreq, pwmResolution);
ledcSetup(MOTOR_2_PIN_1, pwmFreq, pwmResolution);
ledcSetup(MOTOR_2_PIN_2, pwmFreq, pwmResolution);
ledcSetup(LED_PIN, pwmFreq, pwmResolution);
/*
Attach the channel to the GPIO to be controlled
I have used same channel as pin number.
ledcAttachPin(Pin, pwmChannel);
*/
ledcAttachPin(MOTOR_1_PIN_1, MOTOR_1_PIN_1);
ledcAttachPin(MOTOR_1_PIN_2, MOTOR_1_PIN_2);
ledcAttachPin(MOTOR_2_PIN_1, MOTOR_2_PIN_1);
ledcAttachPin(MOTOR_2_PIN_2, MOTOR_2_PIN_2);
ledcAttachPin(LED_PIN, LED_PIN);

AllMotorOff();
/*
LED Off
To control the LED you alter the dutyclcle of the LED Channel.
ledcWrite(Channel, dutyCycle);
*/
ledcWrite(LED_PIN, 0);
/*
Disable brownout detector
*/
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0);

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

#ifdef ACCESS_POINT
/*
Connect to ESP32-CAM Wi-Fi network with no password
*/
Serial.println("Setting AP (Access Point)…");
WiFi.softAP(ESP32_CAMssid);
Serial.print("Connect you device to network '");
Serial.print(ESP32_CAMssid);
Serial.println("'\r\nThere is no Password.");
Serial.print("Open browser to ");
Serial.println(WiFi.softAPIP());

#endif // ACCESS_POINT
#ifdef ACCESS_POINT_PW
/*
Connect to ESP32-CAM Wi-Fi network with a password
*/
Serial.println("Setting AP (Access Point)…");
WiFi.softAP(ESP32_CAMssid, ESP32_CAMpassword);
Serial.print("Connect you device to network '");
Serial.print(ESP32_CAMssid);
Serial.println("'");
Serial.print("Use password '");
Serial.print(ESP32_CAMpassword);
Serial.println("'");
Serial.print("Open browser to ");
Serial.println(WiFi.softAPIP());

#endif // ACCESS_POINT_PW
#ifdef LOCAL_NETWORK
/*
Connect to ESP32-CAM to Local Wi-Fi network
*/
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected to Local Network");
Serial.print("Camera Stream Ready! Go to: http://");
Serial.println(WiFi.localIP());

#endif // LOCAL_NETWORK

/*
Start server
*/
startCameraServer();
}
void loop() {
delay(1);
}

The "IndexHTML.h" file is the Web page that will be shown in the Web Browser.

  • It is compressed using gzip and converted to 8bit to save memory. It needs to be in the same folder as the Sketch.

I assume that if you have an ESP32-CAM you have experimented with it and have been to Arduino.cc site to learn things about it.

If this is your first time using a Device with the Arduino Architecture, then first go here: Arduino IDE 2 Tutorials

  • Here you can download the Arduino IDE and there are tutorials from the very people who created Arduino.
  • The tutorial show how to upload a sketch to a device.

Step 4: Motors and Diver

The motors are the cheep Motor with Gears found by searching for: intelligent smart robot car.

Add about 70mm of cable with Female Dupont connectors on the end to the Motors.

I find it some times cheaper to by a kit, (one may be on offer) rather than by individual motors.

For example:

  • At the time of writing, two motors where advertised at £5.50 (no wheels), the kit was only £7.90 (has wheels and battery holder).
  • If you don't have a 3D printer, you could use this kit as a base for this type of project.

The driver for the motors will be a DRV8833 Module.

This is a 2 Channel DC Motor Driver Module 1.5A 3V-10V

I am using this driver because it uses less control pins (only 4) from the ESP32-CAM.

I like to colour-code the header pins, this helps me get it right when connecting things together.

Step 5: Battery

I use Li-Po Batteries for my project, I love them, they come in all shapes and sizes.

In this project I am using one 1200mAh battery.

The physical size is: Height 9mm, Width 27mm, Length 56mm.

Anything similar will do:

  • Two AA batteries in a battery holder like in the kit I previously mentioned.
  • The voltage does not want to be more than 4v, as I am using a DC-DC Step-up module in this project.

Step 6: Boost

I will be using a Step-Up DC-DC Boost to increase the voltage of the battery to 5v.

I found this Small DC-DC Step-Up Adjustable SX1308 2A module.

I have soldered wires direct to this module.

The easiest way to show how the wires are attached, is to do a fritzing of the this part of the circuit.

When soldering the battery wires to it, I kept the top right hole as it is shown open, so that I can fix the module in place with a small screw.

I have placed the cables in the Breadboard layout out like it should be made. There is about 50mm of cable both sides of SX1308 Boost.

Step 7: Assembly

I will assume you have made/attached all the necessary cables and connectors to all the electronic modules as shown previously.

All screws will be M1.7x6mm Self tapping Screws unless stated.

Step 8: Fit the Camera Mount Vertical to the Base, With Two Screws.

Step 9: Fit Two Battery Support, With Four Screws.

Step 10: Fit Hook Mount, With Two Screws.

Step 11: Fit Servo to Servo Mount, With Two Screws.

Step 12: Fit Horn to Servo, With One Screw (The One That Came With It).

Step 13: Fit Servo Arm to Horn of Servo, With Two Screws and Washers.

Step 14: Fit Servo Assembly to Base, With Two Screws.

Step 15: Fit Both Wheel Brackets to the Motors, With Two M3x25mm Allen Cap Head Set Screws. One Mirrored to the Other.

Step 16: Fit Both Wheel Brackets With Motors to the Base, Use Four Screws.

Step 17: Fit Axle Bracket to Underside of Base, Use Two Screws.

There is two versions of this:

  • One has mount holes central.
  • One has holes off-set.

Using the off-set version enables the slack to be taken out of the track when it loosens with use.

Step 18: Fit the Two Bearings to Both of the Idle Wheels. (One of Each in Each Wheel)

Step 19: Fit Both Idle Wheels to the Rear Axle, Use Two M3x10mm Self Tapping Screws.

Step 20: Fit the Rear Axle Assembly to the Axle Bracket, Use Two Screws.

Step 21: Fit the Two Drive Wheels, I Used the Two Screws That Came With the Motors.

If you didn't get screws then two M1.7 may do.

Step 22: Fit a Weight to the Underside Using the Weight Support, Use Four Screws.

Step 23: Fit the Hook to the Hook Support and the Servo Arm, Use Three Screws.

Step 24: Fit the DRV8833 Module With the Motor Driver Bracket, Use Two Screws.

Step 25: Fit the SX1308 Module, Using One Screw.

Step 26: Fit the Re-Set Bar. This Just Slides Over the Camera Mount Vertical.

Depending on the orientation of the ESP32-CAM, this aids pushing the re-set button if the re-set button ends up at the bottom.

Step 27: Fit the ESP32-CAM Module.

The ESP32-CAM Module should just slide inside the two slots of the Camera Mount Vertical.

Step 28: Make Up Two Sets of Track.

Each Track is made up of eight Tread A and eight Tread B, with sixteen screws.

  • Make sure the Treads pivot around the screws, don't over tighten the screws.
  • If you want to add foam pads to the treads, it is a good time to do this now.

Step 29: Fit the Tracks to the R.O.V.

Easiest method is to remove the Drive Wheels, assemble and re-fit the Drive Wheels

Step 30: Time to Connect Some Cables.

It should be just a matter of plug things together if the cables shown previously have been made.

Reference: Tims_ESP32-CAM_ROV_Power_Module.fzz, while connecting the cables from the SX1308 Module.

Bring the cables from the Motors up through the hole between the two Battery Supports, plug them onto the DRV8833 Module.

From the SX1308 Module, take the Cable and plug it onto the DRV8833 Module.

  • Make sure of the correct polarity.

Take the Cable DRV8833 IN1 & IN2, also the Cable DRV8833 IN3 & IN4 and plug these onto the DRV8833 Module.

Next fit the Power Cable that goes to the ESP32-CAM.

Then fit the Cables for POI 2, PIO 14, PIO 15 and PIO 13 on the ESP32-CAM.

The Servo Plug goes to the Pins for the Servo.

  • It is best left unplugged until the first time it is switched on and has been adjusted.
  • Instruction comes later.

Step 31: Now Fit the Battery.

I haven't fitted a switch, to turn it on and off, I dis-connect the battery when not in use.

  • I recommend removing Li-Po batteries from projects when not in use.
  • Unless of course you have designed the project to have them remain permanent and have added some protection against draining the power below there minimum threshold.


There are some checks to do the first time you connect it up.

First you will need to connect to the ESP32-CAM with your Wi-Fi device.

  • A Wi-Fi Device = Mobile Phone, Laptop, Tablet, PC or similar.

Depending on you choice of Wi-Fi connection in the Arduino Firmware.

The choices of Wi-Fi connection are:

  • Access Point
  • Local Network

"Access Point"

  • This is not connected to your local network, you connect to the Network of the ESP32-CAM.
  • This means you can connect to it with your Mobile Wi-Fi Device any ware you are.
  • You change the Wi-Fi your Device is connected to, to the ESP32-CAM Wi-Fi, then open a browser on your Device to the IP of the ESP32-CAM control page. (may be 192.168.0.46)

Have your serial monitor connected when you re-set the ESP32-CAM module after uploading the firmware to confirm correct IP Address.

"Access Point Password"

  • This is same as above, but you need a password to connect to the Wi-Fi.
  • It currently is: 2468

"Local Network"

  • This is, both you and the ESP32-CAM, connects to your local network.
  • You will need to give the SSID (name of your network) and the password required to gain access to your network.
  • You can connect to the ESP32-CAM from any devices browser that is connected to your network.
  • Open a browser to the IP of the ESP32-CAM control page. (may be 192.168.0.46)

Have your serial monitor connected when you re-set the ESP32-CAM module after uploading the firmware to confirm correct IP Address.

When connected with your web browser, the web page should look like the shown, not the view of my ling room though haha.

The LED Slider turns on the LED at a percentage of it's full power.

  • It does not go full power there is no need.
  • The LED is designed as a flash, so it should only be on full for brief amount of time.

The Buttons do the movement.

  • So the first test is to check that the motors go in the right direction.
  • Push the Forward Button, check that both motors rotate forward.
  • If any of the Motor run in the wrong direction, then that motors plug needs to turned around. This is the plug that is on the end of the cables from the Motor.

The Speed Slider sets the speed at which it moves.

The Servo Slider moves the Arm up and down.

  • The Servo will probably need to be adjusted the first use.

Step 32: Servo Adjustment

  • Unscrew the screw holding the Servo Horn in place.
  • Pull the Servo Horn away from the servo.
  • Plug in the Servo Cables to the Servo Connector.
  • Connect to the ESP32-CAM with your browser.
  • Try the Slider for the Servo, the servo should move.
  • Set the Servo to Zero Degrees.
  • Re-Fit the Servo Horn so that the Servo Arm is in the shown position.

You should be all set to go roving about with your Remotely Operated Vehicle (R.O.V.)

Step 33: To Open and Get Through Doors

To open and get through doors I made a little ring that can be fixed to the bottom of the doors.

  • I use double sided self adhesive foam pads.
Remote Control Contest

First Prize in the
Remote Control Contest