MiUbi Systems
Published © MIT

Video Survillance Car Using ESP32-CAM

This project combines three exciting DIY builds—an ESP32-CAM video camcorder, IP-controlled robotic car, and PT (Pan-Tilt) system—into one

AdvancedFull instructions provided12 hours35
Video Survillance Car Using ESP32-CAM

Things used in this project

Hardware components

ESP32 Camera Module Development Board
M5Stack ESP32 Camera Module Development Board
×1
Pan-Tilt HAT
Pimoroni Pan-Tilt HAT
×1
Pmod USBUART
Digilent Pmod USBUART
×1
9V battery (generic)
9V battery (generic)
×1

Software apps and online services

Arduino IDE
Arduino IDE

Story

Read more

Custom parts and enclosures

Step-FIle

Schematics

Schematic-ESP32-CAM

Helpful in Reference

Code

ArudinoApp

Arduino
Just Keep this a main file
#define CAMERA_MODEL_AI_THINKER // Your Board Info

#include "esp_camera.h" // Contains Declarations of Function Calls releated to ESP-IDF Library for camera acess
#include "camera_pins.h" // Defines the GPIO Pin Numbers of ESP32 for which OV2640 Camera Module is interfaced
void startCameraServer(); // External Function present in Application  

#include <WiFi.h> // Contains Declarations of Function Calls releated to ESP-IDF and Arduino HookUps for Wifi Acess 
const char* ssid = "TPLINK2020";// Access Point User Name
const char* password = "2020TPLINK";//Password of the AccessPoint

 
void setup() { //First Function That would Run when any Arduino Based Board Starts
 
  Serial.begin(115200);//Set Baud Rate For Serial Communication
  Serial.setDebugOutput(true);// Let Debug Messages Appear
  Serial.println();//Just Prints a NewLine

  static camera_config_t config; 
  // Config Object is created to fill settings and Pin numbers. These Macros are part of camera_pins.h
  // In NutShell its the Object Created to Configure the Sensor
  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;//SPI Clock Frequency set to 20MHz
  config.pixel_format       = PIXFORMAT_JPEG;//Compression format from Sensor is selected as JPEG
  config.frame_size         = FRAMESIZE_SVGA;// Resolution SVGA 
  config.jpeg_quality       = 12;//lowest is better, lowest possible is 10
  config.fb_count           = 1;//Number of Frame Buffers at a time

  // camera init with required settings and pin numbers 
  esp_err_t err = esp_camera_init(&config);
  if (err != ESP_OK) {//This Flag is Set Internally by Init Function in case of failure
    Serial.printf("Camera init failed with error 0x%x", err);//error number would tell us type of error
    return;
  }
  #if 1
  //Acquire Access to the sensor
  sensor_t * s = esp_camera_sensor_get();// Get Acess to tweak camera settings 
  s->set_brightness(s, 0);     // -2 to 2
  s->set_contrast(s, 0);       // -2 to 2
  s->set_saturation(s, 0);     // -2 to 2
  s->set_special_effect(s, 0); // 0 to 6 (0 - No Effect, 1 - Negative, 2 - Grayscale, 3 - Red Tint, 4 - Green Tint, 5 - Blue Tint, 6 - Sepia)
  s->set_whitebal(s, 1);       // 0 = disable , 1 = enable
  s->set_awb_gain(s, 1);       // 0 = disable , 1 = enable
  s->set_wb_mode(s, 0);        // 0 to 4 - if awb_gain enabled (0 - Auto, 1 - Sunny, 2 - Cloudy, 3 - Office, 4 - Home)
  s->set_exposure_ctrl(s, 1);  // 0 = disable , 1 = enable
  s->set_aec2(s, 1);           // 0 = disable , 1 = enable
  s->set_ae_level(s, 0);       // -2 to 2
  s->set_aec_value(s, 1200);    // 0 to 1200
  s->set_gain_ctrl(s, 1);      // 0 = disable , 1 = enable
  s->set_agc_gain(s, 30);       // 0 to 30
  s->set_gainceiling(s, (gainceiling_t)6);  // 0 to 6
  s->set_bpc(s, 1);            // 0 = disable , 1 = enable
  s->set_wpc(s, 1);            // 0 = disable , 1 = enable
  s->set_raw_gma(s, 1);        // 0 = disable , 1 = enable
  s->set_lenc(s, 1);           // 0 = disable , 1 = enable
  s->set_hmirror(s, 0);        // 0 = disable , 1 = enable
  s->set_vflip(s,0);          // 0 = disable , 1 = enable
  s->set_dcw(s, 1);            // 0 = disable , 1 = enable
  s->set_colorbar(s, 0);       // 0 = disable , 1 = enable
  #endif

  #if 1
  // Set your Static IP address of ESP32-CAM Node / Station.
  IPAddress STATIC_IP(192, 168, 0, 108);
  // Set your Gateway IP address
  IPAddress gway(192, 168, 0, 1);//Gateway Address, in our case the Gate way is router, this IP is common for any routers
  IPAddress sNet(255, 255, 0, 0);//Sub Net Mask, Default as we have no network divisions
  IPAddress pDNS(8, 8, 8, 8); //Primary DNS - This is Google's
  IPAddress sDNS(8, 8, 4, 4);//Secondary DNS - This is Google's
  
  if(!WiFi.config(STATIC_IP, gway, sNet, pDNS, sDNS)) {// Attempt for Configuration of Wifi Hardware with Settings
  Serial.println("Configuration with Static IP Failed");
  }
  #endif
  
  WiFi.begin(ssid, password);// Attempt for Wifi Connection Establishment with uname and password of Access Point

  while (WiFi.status() != WL_CONNECTED) {// Result of Wifi Connection Attempt
    delay(500);
    Serial.print(".");
  }
  
  Serial.println("");
  Serial.println("WiFi connected");

  startCameraServer();// Start the Camera Server / Streamer Expalined in Application File / Lecture.

  Serial.print("Camera Ready! Use 'http://");
  Serial.print(WiFi.localIP());// This will give you the IP Assigned to Device in General, here it should return the Static IP you have demanded
  Serial.println("' to connect");
}

void loop() {
  // put your main code here, to run repeatedly:
  delay(10000);
}

Cpp-App

C/C++
Keep it in same folder as arduino file
#include "esp_http_server.h" // Contains Declarations of Function Calls releated to ESP-IDF Library for HTTP Server Creation
#include "esp_camera.h" // Contains Declarations of Function Calls releated to ESP-IDF Library for camera acess
#include "Arduino.h" // Contains Declarations of Function Calls releated to Serial Port 

#define PART_BOUNDARY "123456789000000000000987654321" // A Unique Header to sperate payloads , which are JPEG Images in our case
// String which would passed to HTML Browser for indicating the stream as multipart.
static const char* _STREAM_CONTENT_TYPE = "multipart/x-mixed-replace;boundary=" PART_BOUNDARY; 
// Unique Header discussed will replaced in place of macro
static const char* _STREAM_BOUNDARY = "\r\n--" PART_BOUNDARY "\r\n";
// Strig which would passed to HTML Browser for indicating the stream as image and format as JPEG
static const char* _STREAM_PART = "Content-Type: image/jpeg\r\nContent-Length: %u\r\n\r\n"; 
httpd_handle_t stream_httpd = NULL;


// Stream Handler Function mainly, takes images from camera and streams it over HTTP Protocol to the Client 
static esp_err_t stream_handler(httpd_req_t *req){
    camera_fb_t * fb = NULL; // Initialize the Frame Buffer
    esp_err_t res = ESP_OK; // Initialize the result variable
    size_t _jpg_buf_len = 0; // variable which shall hold the buffer length of jpeg compressed image
    uint8_t * _jpg_buf = NULL; // pointer to JPEG buffer
    char * part_buf[64]; // Temperory buffer for string creation 
    
    res = httpd_resp_set_type(req, _STREAM_CONTENT_TYPE); // Whe the browser opens the URL, it should know that the content which is going to come is a jpeg image steam.
    if(res != ESP_OK){
        return res;
    }

    while(true){
        fb = esp_camera_fb_get(); // Get the JPEG Frame from Image Sensor
        if (!fb) {
            Serial.println("Camera capture failed");
            res = ESP_FAIL;
        } else {
             _jpg_buf_len = fb->len;// Fill the Buffer length variable with size of compressed image
             _jpg_buf = fb->buf;// Pointer to the frame buffer is assigned to variable
        }	
        if(res == ESP_OK){
            size_t hlen = snprintf((char *)part_buf, 64, _STREAM_PART, _jpg_buf_len); // Create a string to communicate to client about length of buffer which is going to come
            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);// Send the JPEG Image 
        }
        if(res == ESP_OK){
            res = httpd_resp_send_chunk(req, _STREAM_BOUNDARY, strlen(_STREAM_BOUNDARY));// Seperate each JPEG Image with a Unique Boundary string
        }
        if(fb){
            esp_camera_fb_return(fb);// Relinquish the Frame buffer memory allocation
            fb = NULL; // re-initialize the frame buffer variable
            _jpg_buf = NULL; // re initiazie the jpeg frame buffer variable
        } else if(_jpg_buf){
            free(_jpg_buf);// release the memory allotted to the buffer
            _jpg_buf = NULL;
        }
        if(res != ESP_OK){
            break;
        }
        
    }
    return res;
}

void startCameraServer(){
  httpd_config_t config = HTTPD_DEFAULT_CONFIG(); // Start with Configurating HTTP Server with default settings
	config.server_port = 80; // What is Special about Port 80, its the standard of HTTP where the browser interacts to server through this port
	httpd_uri_t stream_uri = {
        .uri       = "/",//Just Root, no sub folder or nodes
        .method    = HTTP_GET,// Request which come to server from client have the method as HTTP_GET, GET is used by clients to request data.
        .handler   = stream_handler,// Function that shall be executed for GET request from clients
        .user_ctx  = NULL
    };

  if (httpd_start(&stream_httpd, &config) == ESP_OK) {
       httpd_register_uri_handler(stream_httpd, &stream_uri);// Register the Hadler Created above in HTTP Server Library, so that request will be addressed by handler.
  }
}

HeaderFIle_Camera

C Header File
Keep this file in same folder as cpp and .ino file
#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_ESP_EYE)
#define PWDN_GPIO_NUM    -1
#define RESET_GPIO_NUM   -1
#define XCLK_GPIO_NUM    4
#define SIOD_GPIO_NUM    18
#define SIOC_GPIO_NUM    23

#define Y9_GPIO_NUM      36
#define Y8_GPIO_NUM      37
#define Y7_GPIO_NUM      38
#define Y6_GPIO_NUM      39
#define Y5_GPIO_NUM      35
#define Y4_GPIO_NUM      14
#define Y3_GPIO_NUM      13
#define Y2_GPIO_NUM      34
#define VSYNC_GPIO_NUM   5
#define HREF_GPIO_NUM    27
#define PCLK_GPIO_NUM    25

#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_WIDE)
#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

#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

#else
#error "Camera model not selected"
#endif

Credits

MiUbi Systems
2 projects • 0 followers

Comments