October 19, 2024

About

In the former article (https://eye.kohei-kevin.com/2024/05/24/esp32-cam-image-webserver/), I wrote the code to enable the ESP32 to function as an HTTP server, allowing clients to retrieve images from the server. Although the code worked fine, this way is a bit heavy task for ESP32 devices. So, I implemented the code that allows the ESP32 to function as an HTTP client to post images.

Code

The code uses ArudinoHttpClient to post an image to Http Server. Since simple HTTPClient can be seen in internet frequently.

https://github.com/kevin-tofu/esp32-camera-httppost

#include "Arduino.h"
#include <FS.h>
#include <ArduinoHttpClient.h>
#include <SPI.h>
#include <WiFi.h>
#include "esp_camera.h"
#include "settings.h"

/////// WiFi Settings ///////
char ssid[] = SECRET_SSID;
char password[] = SECRET_PASS;
// const char* password = "ri0rubc2ahj2";

char serverAddress[] = SERVER_IP;
int port = int(SERVER_PORT);

void wifi_setup() {
  // Connect to Wi-Fi
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);

  Serial.println(String("Connecting to ") + ssid);
  while ( WiFi.status() != WL_CONNECTED) {
    delay(3000);
    Serial.print("Attempting to connect to Network named: ");
    Serial.println(ssid);
    Serial.println(WiFi.status());
  }
  Serial.println("\nConnected, IP address: ");
  Serial.print("SSID: ");
  Serial.println(WiFi.SSID());

  // print your WiFi shield's IP address:
  IPAddress ip = WiFi.localIP();
  Serial.print("IP Address: ");
  Serial.println(ip);

  WiFiClient wifi;
  HttpClient client = HttpClient(wifi, serverAddress, port);
}

void camera_setup() {
  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_sccb_sda = SIOD_GPIO_NUM;
  config.pin_sccb_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;
  config.frame_size   = FRAMESIZE_QVGA; // 320x240
//    config.frame_size   = FRAMESIZE_UXGA; // 1600x1200
  config.jpeg_quality = 10;
  config.fb_count     = 2;
  config.fb_location = CAMERA_FB_IN_DRAM;

  // Camera init
  esp_err_t err = esp_camera_init(&config);
  if (err != ESP_OK) {
    Serial.printf("Camera init failed with error 0x%x", err);
    ESP.restart();
  }
}

void setup() {
  // Serial.begin(9600);
  Serial.begin(115200);
  delay(5000);
  Serial.println("Setup start");

  // 
  Serial.print("Camera  SetUp Begin");
  camera_setup();
  Serial.print("Camera  SetUp Finished");
  // 
  Serial.print("Wifi SetUp Begin");
  wifi_setup();
  Serial.print("Wifi SetUp Finished");
}

void postImage(const uint8_t *imageData, size_t imageSize) {

  if (WiFi.status() == WL_CONNECTED) {
    Serial.println("POST request");

    WiFiClient wifi;
    HttpClient client = HttpClient(wifi, serverAddress, port);
    // Create header/footer
    // Measure content and header-footer length
    String boundary = "Boundary123";
    String header = "--" + boundary + "\r\n"
                  + "Content-Disposition: form-data; name=\"file\"; filename=\"image.jpg\"\r\n"
                  + "Content-Type: image/jpeg\r\n\r\n";
    String footer = "\r\n--" + boundary + "--\r\n";
    size_t totalLength = header.length() + imageSize + footer.length();

    // Begin Http Post Request
    client.beginRequest();
    client.post("/image");
    client.sendHeader("Content-Type", "multipart/form-data; boundary=" + boundary);
    client.sendHeader("Content-Length", totalLength);

    // write footer
    client.print(header);

    // write image file
    client.write(imageData, imageSize);

    // write footer
    client.print(footer);

    // End Request
    client.endRequest();

    // Check Response
    int statusCode = client.responseStatusCode();
    String response = client.responseBody();
    Serial.print("Status code: ");
    Serial.println(statusCode);
    Serial.print("Response: ");
    Serial.println(response);
  }
  else {
    Serial.println("Error in WiFi connection");
  }
  delay(500);
}

void loop() {

  unsigned long startTime = millis();

  // capture Image
  camera_fb_t* fb = esp_camera_fb_get();
  if (!fb) {
    Serial.println("Camera capture failed");
    return;
  }
  Serial.printf("Captured image with size %d bytes\n", fb->len);

  postImage(fb->buf, fb->len);
  
  // release buffer
  esp_camera_fb_return(fb);

  unsigned long endTime = millis();

  // elapsedTime
  unsigned long elapsedTime = endTime - startTime;
  Serial.print("Task took ");
  Serial.print(elapsedTime);
  Serial.println("milliseconds.");

  // wait 5 sec
  delay(CAMERA_TIMEINTERVAL_MSEC);
}

Setting and Experimental Result

As I showed the setting for camera on my code, the image quality is like this

config.pixel_format = PIXFORMAT_JPEG;
config.frame_size   = FRAMESIZE_QVGA; // 320x240
config.jpeg_quality = 10;

In my case, the ESP32 required approximately 1.8 to 3.3 seconds to capture an image, and post the image to Http Server (https://github.com/kevin-tofu/fastapi-image-reciever) in local network.