Tele-operated Surveillance Bot
Published:
Description
I collaborated with AFEME students to assemble a tele-operated bot using ESP32 CAM, Arduino Uno, L298n motor driver, Lipo batteries, and DC motors. Programmed the bot’s functionality in C++ via Arduino IDE, with Firebase for real-time communication and React.js for a user-friendly software interface.
Hardware
- ESP32 CAM
- Arduino Uno
- Ultrasonic Sensor
- L298n motor driver
- Lipo batteries
- DC motors
Software
- Arduino IDE (C++)
- Firebase
- React.js
ESP32-CAM Code
#include <WiFi.h>
#include "esp_camera.h"
#include <Firebase_ESP_Client.h>
#include "addons/TokenHelper.h"
#include "addons/RTDBHelper.h"
#include <base64.h>
// Replace with your network credentials
#define WIFI_SSID "********"
#define WIFI_PASSWORD "********"
// Replace with your Firebase project credentials
#define API_KEY "********"
#define STORAGE_BUCKET "********.appspot.com"
#define DATABASE_URL "https://********.firebaseio.com/"
// Camera configuration (based on your ESP32-CAM module)
#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
// Firebase objects
FirebaseData fbdo;
FirebaseAuth auth;
FirebaseConfig config;
bool signupOK = false;
unsigned long previousMillis = 0;
const long interval = 100; // 1 second
void setup() {
Serial.begin(115200);
// Connect to Wi-Fi
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
Serial.print("Connecting to Wi-Fi");
while (WiFi.status() != WL_CONNECTED) {
Serial.print(".");
delay(300);
}
Serial.println();
Serial.print("Connected with IP: ");
Serial.println(WiFi.localIP());
Serial.println();
// Firebase configuration
config.api_key = API_KEY;
config.database_url = DATABASE_URL;
if (Firebase.signUp(&config, &auth, "", "")){
Serial.println("Firebase Sign Up OK");
signupOK = true;
} else {
Serial.printf("Firebase Sign Up Failed: %s\n", config.signer.signupError.message.c_str());
}
config.token_status_callback = tokenStatusCallback;
Firebase.begin(&config, &auth);
Firebase.reconnectWiFi(true);
// Initialize camera
camera_config_t camera_config;
camera_config.ledc_channel = LEDC_CHANNEL_0;
camera_config.ledc_timer = LEDC_TIMER_0;
camera_config.pin_d0 = Y2_GPIO_NUM;
camera_config.pin_d1 = Y3_GPIO_NUM;
camera_config.pin_d2 = Y4_GPIO_NUM;
camera_config.pin_d3 = Y5_GPIO_NUM;
camera_config.pin_d4 = Y6_GPIO_NUM;
camera_config.pin_d5 = Y7_GPIO_NUM;
camera_config.pin_d6 = Y8_GPIO_NUM;
camera_config.pin_d7 = Y9_GPIO_NUM;
camera_config.pin_xclk = XCLK_GPIO_NUM;
camera_config.pin_pclk = PCLK_GPIO_NUM;
camera_config.pin_vsync = VSYNC_GPIO_NUM;
camera_config.pin_href = HREF_GPIO_NUM;
camera_config.pin_sccb_sda = SIOD_GPIO_NUM;
camera_config.pin_sccb_scl = SIOC_GPIO_NUM;
camera_config.pin_pwdn = PWDN_GPIO_NUM;
camera_config.pin_reset = RESET_GPIO_NUM;
camera_config.xclk_freq_hz = 20000000;
camera_config.pixel_format = PIXFORMAT_JPEG;
camera_config.frame_size = FRAMESIZE_SVGA;
camera_config.jpeg_quality = 12;
camera_config.fb_count = 1;
// Camera init
esp_err_t err = esp_camera_init(&camera_config);
if (err != ESP_OK) {
Serial.printf("Camera init failed with error 0x%x", err);
return;
}
}
void loop() {
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
String base64Photo = capturePhoto();
if (!base64Photo.isEmpty()) {
uploadPhoto(base64Photo);
}
}
}
String capturePhoto() {
camera_fb_t * fb = esp_camera_fb_get();
if (!fb) {
Serial.println("Camera capture failed");
return "";
}
String imageFile = "data:image/jpeg;base64,";
imageFile += base64::encode((uint8_t *)fb->buf, fb->len);
esp_camera_fb_return(fb);
return imageFile;
}
void uploadPhoto(const String& base64Photo) {
String photoPath = "/esp32-cam/photo"; // Single entry path
if (Firebase.RTDB.setString(&fbdo, photoPath, base64Photo)) {
Serial.println("Photo uploaded successfully");
} else {
Serial.println("Failed to upload photo");
Serial.println(fbdo.errorReason());
}
}
ESP32 Dev. Board Code
#include <WiFi.h>
#include <FirebaseESP32.h>
#include "addons/TokenHelper.h"
#include "addons/RTDBHelper.h"
#include <base64.h>
// Replace with your network credentials
#define WIFI_SSID "********"
#define WIFI_PASSWORD "********"
// Replace with your Firebase project credentials
#define API_KEY "********"
#define STORAGE_BUCKET "********.appspot.com"
#define DATABASE_URL "https://********.firebaseio.com"
// Firebase objects
FirebaseData fbdo;
FirebaseAuth auth;
FirebaseConfig config;
bool signupOK = false;
unsigned long previousMillis = 0;
const long interval = 1000; // 1 second
// Define control variable
String control = "";
String lastCommandState = "stop"; // previous state of the command
// Ultrasonic sensor
const int trigPin = 12; // Example GPIO pin for trig
const int echoPin = 14; // Example GPIO pin for echo
float duration, distance;
unsigned long lastDebounceTime = 0; // For debouncing the sensor readings
unsigned long debounceDelay = 50; // 50ms debounce delay
// Motor Driver
int m1p1 = 27; // Example GPIO pin
int m1p2 = 26; // Example GPIO pin
int m2p1 = 33; // Example GPIO pin
int m2p2 = 25; // Example GPIO pin
// LEDs
int wifiLED = 4; // Changed to a suitable GPIO pin for output
int firebaseLED = 2; // Changed to a suitable GPIO pin for output
int obstacleLED = 32; // red (unchanged)
void updateDistance() {
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
duration = pulseIn(echoPin, HIGH);
distance = (duration * 0.0343) / 2;
}
void stop() {
digitalWrite(m1p1, LOW);
digitalWrite(m1p2, LOW);
digitalWrite(m2p1, LOW);
digitalWrite(m2p2, LOW);
}
void forward() {
digitalWrite(m1p1, HIGH);
digitalWrite(m1p2, LOW);
digitalWrite(m2p1, HIGH);
digitalWrite(m2p2, LOW);
}
void backward() {
digitalWrite(m1p1, LOW);
digitalWrite(m1p2, HIGH);
digitalWrite(m2p1, LOW);
digitalWrite(m2p2, HIGH);
}
void right() {
digitalWrite(m1p1, HIGH);
digitalWrite(m1p2, LOW);
digitalWrite(m2p1, LOW);
digitalWrite(m2p2, HIGH);
}
void left() {
digitalWrite(m1p1, LOW);
digitalWrite(m1p2, HIGH);
digitalWrite(m2p1, HIGH);
digitalWrite(m2p2, LOW);
}
void executeCommand(String command) {
if (command == "backward") {
backward();
} else if (command == "forward") {
if (distance < 10) {
Serial.println("Obstacle detected ahead! " + String(distance) + "cm");
// stop();
} else {
forward();
}
} else if (command == "right") {
if (distance < 10) {
Serial.println("Obstacle detected ahead! " + String(distance) + "cm");
// stop();
} else {
right();
}
} else if (command == "left") {
if (distance < 10) {
Serial.println("Obstacle detected ahead! " + String(distance) + "cm");
// stop();
} else {
left();
}
} else if (command == "stop") {
stop();
} else {
stop();
}
}
void setup() {
Serial.begin(115200); // Initialize the hardware serial port for debugging
pinMode(wifiLED, OUTPUT);
pinMode(firebaseLED, OUTPUT);
pinMode(obstacleLED, OUTPUT);
// Connect to Wi-Fi
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
Serial.print("Connecting to Wi-Fi");
while (WiFi.status() != WL_CONNECTED) {
Serial.print(".");
digitalWrite(wifiLED, HIGH);
delay(300);
digitalWrite(wifiLED, LOW);
delay(300);
}
digitalWrite(wifiLED, HIGH);
Serial.println();
Serial.print("Connected with IP: ");
Serial.println(WiFi.localIP());
Serial.println();
// Firebase configuration
config.api_key = API_KEY;
config.database_url = DATABASE_URL;
// Option 2: Use Firebase email and password
auth.user.email = "fatoyejoseph@gmail.com"; // Replace with your Firebase email
auth.user.password = "fatoyejoseph@gmail.com"; // Replace with your Firebase password
if (Firebase.signUp(&config, &auth, auth.user.email.c_str(), auth.user.password.c_str())) {
digitalWrite(firebaseLED, HIGH);
Serial.println("Firebase Sign Up OK");
signupOK = true;
} else {
Serial.printf("Firebase Sign Up Failed: %s\n", config.signer.signupError.message.c_str());
}
config.token_status_callback = tokenStatusCallback;
Firebase.begin(&config, &auth);
Firebase.reconnectWiFi(true);
// Ultrasonic sensor
pinMode(trigPin, OUTPUT);
pinMode(echoPin, INPUT);
// Motor Driver
pinMode(m1p1, OUTPUT);
pinMode(m1p2, OUTPUT);
pinMode(m2p1, OUTPUT);
pinMode(m2p2, OUTPUT);
}
void loop() {
// Ultrasonic sensor with debouncing
if (millis() - lastDebounceTime > debounceDelay) {
lastDebounceTime = millis();
updateDistance();
}
if (distance < 10) {
// Check if all motors are stopped (i.e., all pins are LOW)
if (digitalRead(m1p1) == LOW && digitalRead(m1p2) == LOW && digitalRead(m2p1) == LOW && digitalRead(m2p2) == LOW) {
// All motors are stopped
// pass
}
// Check if the robot is moving backward
else if (digitalRead(m1p1) == LOW && digitalRead(m1p2) == HIGH && digitalRead(m2p1) == LOW && digitalRead(m2p2) == HIGH) {
// Moving backward
// pass
}
else {
// stop();
Serial.println("Obstacle detected ahead!: " + String(distance) + "cm");
}
}
// Read control command from Firebase and execute it directly
if (Firebase.getString(fbdo, F("/control"))) {
control = fbdo.stringData(); // Update the control variable
if (control != lastCommandState) {
Serial.println(control);
executeCommand(control); // Execute the control command
lastCommandState = control
}
} else {
Serial.println("Failed to get control command: " + fbdo.errorReason());
}
}
Challenges Faced
- Software debugging and updating during integration of real-time tele-operation and communication.
- Hardware issues with ESP32 Development Board
- Availability of electronics components
Acknowledgements
- AFEME Instructors
- AFEME Group members
- Adebisi Covenant