Page 1 of 1

Fastest way to send images from ESP32CAM to cloud server

Posted: Thu Jul 18, 2024 9:59 am
by Aswinth
Hi,

My idea is to use ESP32CAM for image processing projects. Since the ESP32 core is not powerful enough, I am planning to send the video (as images) from ESP32CAM into my python flask server and perform image processing over there using openCV.

I am aware that ESP32CAM is not powerful enough to stream video live into my cloud server, so I will be doing it image by image. Right now I have used the below code to get things working

Code: Select all

#include <Arduino.h>
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include "soc/soc.h"
#include "soc/rtc_cntl_reg.h"
#include "esp_camera.h"

const char* ssid = "CircuitDigest";
const char* password = "nnnnnn";
String serverName = "circuitdigestxxxxx.pythonanywhere.com";   // Replace with your PythonAnywhere domain
String serverPath = "/receive_image";     // Adjust path based on your Flask route
const int serverPort = 443; // Server port for HTTPS

int count = 0;

WiFiClientSecure client;

// Camera GPIO pins - adjust based on your ESP32-CAM board
#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

void setup() {
  WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); 
  Serial.begin(115200);

  WiFi.mode(WIFI_STA);
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);  
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    delay(500);
  }
  Serial.println();
  Serial.print("ESP32-CAM IP Address: ");
  Serial.println(WiFi.localIP());

  // Configure camera
  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_GRAYSCALE;

  if(psramFound()){
    config.frame_size = FRAMESIZE_96X96;
    config.jpeg_quality = 20;  // Lower number means higher quality (0-63)
    config.fb_count = 2;
    Serial.printf("PSRAM found");
  } else {
    config.frame_size = FRAMESIZE_CIF;
    config.jpeg_quality = 12;  // Lower number means higher quality (0-63)
    config.fb_count = 1;
  }
  
  // Initialize camera
  esp_err_t err = esp_camera_init(&config);
  if (err != ESP_OK) {
    Serial.printf("Camera init failed with error 0x%x", err);
    delay(1000);
    ESP.restart();
  }

  // Initial photo send
  sendPhoto(); 
}

void loop() {
  // Example: Send photo every 30 seconds
  unsigned long currentMillis = millis();
  static unsigned long previousMillis = 0;
  const int timerInterval = 10000;

  if (currentMillis - previousMillis >= timerInterval) {
    Serial.println("Lets send next image..");
    sendPhoto();
    previousMillis = currentMillis;
  }
}

String sendPhoto() {
  camera_fb_t * fb = NULL;
  fb = esp_camera_fb_get();
  if(!fb) {
    Serial.println("Camera capture failed");
    return "Camera capture failed";
  }
  
  Serial.println("Connecting to server: " + serverName);
  
  client.setInsecure(); // Skip certificate validation (for simplicity, consider security implications in a production environment)
  if (client.connect(serverName.c_str(), serverPort)) {
    Serial.println("Connection successful!");
    count=count+1;
    Serial.println(count);

    // Generate a unique filename using the current timestamp
    //String filename = "esp32-cam-" + String(count) + ".jpg";
    String filename = "esp32-cam-image.jpg";
    
    String head = "--RandomNerdTutorials\r\nContent-Disposition: form-data; name=\"imageFile\"; filename=\"" + filename + "\"\r\nContent-Type: image/jpeg\r\n\r\n";
    String tail = "\r\n--RandomNerdTutorials--\r\n";

    uint32_t imageLen = fb->len;
    uint32_t extraLen = head.length() + tail.length();
    uint32_t totalLen = imageLen + extraLen;

    // Send HTTP POST request with multipart/form-data
    client.println("POST " + serverPath + " HTTP/1.1");
    client.println("Host: " + serverName);
    client.println("Content-Length: " + String(totalLen));
    client.println("Content-Type: multipart/form-data; boundary=RandomNerdTutorials");
    client.println();
    client.print(head);

    // Send image data in chunks
    uint8_t *fbBuf = fb->buf;
    size_t fbLen = fb->len;
    for (size_t n = 0; n < fbLen; n = n + 1024) {
      if (n + 1024 < fbLen) {
        client.write(fbBuf, 1024);
        fbBuf += 1024;
      } else {
        size_t remainder = fbLen % 1024;
        client.write(fbBuf, remainder);
      }
    }

    client.print(tail);
    
    // Clean up
    esp_camera_fb_return(fb);
    
    // Wait for server response
    String response;
    long startTime = millis();
    while (client.connected() && millis() - startTime < 10000) {
      if (client.available()) {
        char c = client.read();
        response += c;
      }
    }
    
    // Print server response
    Serial.println(response);
    client.stop();
    return response;
  } else {
    Serial.println("Connection to server failed");
    return "Connection to server failed";
  }
}

On my flask server side I am saving the image using the below code

Code: Select all

# Ensure the 'mysite/static' directory exists
UPLOAD_FOLDER = 'mysite/static/'
if not os.path.exists(UPLOAD_FOLDER):
    os.makedirs(UPLOAD_FOLDER)

# Path to save the image
IMAGE_FILE_PATH = os.path.join(UPLOAD_FOLDER, 'received_image.jpg')

@app.route('/mysite/static/<filename>')
def uploaded_file(filename):
    return send_from_directory(UPLOAD_FOLDER, filename)


@app.route('/receive_image', methods=['POST'])
def receive_image():
    if 'imageFile' not in request.files:
        return jsonify({'error': 'No image file provided'}), 400

    imageFile = request.files['imageFile']
    if imageFile.filename == '':
        return jsonify({'error': 'No selected file'}), 400

    # Save the file to the specified directory
    file_path = os.path.join(UPLOAD_FOLDER, imageFile.filename)
    imageFile.save(file_path)

    return jsonify({'message': 'Image received and saved successfully'}), 200
I am trying to stitch the images together to get a video so that i can do real time image processing. I am even okay with very slow FPS, something like 3-5. But here in the above code it takes 7-9 seconds for a single image to reach my cloud. How can i speed this up?

I have tried capturing images in grayscale but that did not improve the speed
I also tried reducing image size to 96x96 but then the code stopped working with error shown below

Code: Select all

ESP32-CAM IP Address: 192.168.29.178
PSRAM foundConnecting to server: circuitdigest.pythonanywhere.com
Connection successful!
1
[ 19876][E][ssl_client.cpp:37] _handle_error(): [send_ssl_data():382]: (-80) UNKNOWN ERROR CODE (0050)

Lets send next image..
Connecting to server: circuitdigest.pythonanywhere.com
Connection successful!
2
[ 24205][E][ssl_client.cpp:37] _handle_error(): [send_ssl_data():382]: (-80) UNKNOWN ERROR CODE (0050)

Lets send next image..
Connecting to server: circuitdigest.pythonanywhere.com