ESP32 CAM send jpg image from sd cart to browser

Hello everyboby,
I'm new on this Forum I'm also new in a lot of subject regarding ARDUINO IDE, ESP32 CAM and C language so please, be friendly with me even if my post seems to be confused or simple many thanks by advance for that!

I'm programming a CCTV installation based on ESP32 CAM. I used Eloquent vision libraries, the web server is esp_http_server and everything is almost working:

  • video streaming (mjpeg) OK
  • pan/tilt (servo) OK
  • image capture (sd card) OK
  • directory image list (via browser) OK
  • delete image (via browser) OK

But I'm facing a problem to get jpg file from sd card and sending it to the browser.
I tried a lot a formulation but no one is working Here after is one of them this is the method handling the view function:

static esp_err_t action_handler(httpd_req_t *req){
  //  result http header manipulation
  esp_err_t res = ESP_OK;
  
  uint8_t * fb;
  size_t fb_len;
  // Buffer http request
  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, "get", variable, sizeof(variable)) == ESP_OK) {
        res = httpd_resp_set_type(req, "image/jpeg");
        if (res == ESP_OK)
        {
          res = httpd_resp_set_hdr(req, "Content-Disposition", "inline; filename=image.jpg");
        }
        if (res == ESP_OK) {
          File image = SD_MMC.open("/cctv/capture" + String(variable),FILE_READ);
          if (!image) {
             Serial.println("Opening file to read failed");
             return httpd_resp_send_500(req);
          }
          fb_len = image.size();
          
          Serial.println("Before read");
          Serial.println(fb_len);
          
          image.read((uint8_t *)fb, fb_len);
          
          Serial.println("Afters read");
          Serial.println(fb_len);
          Serial.println(size_t(fb));
    
          image.close();
          
          free(buf);
          return httpd_resp_send(req, (const char *)fb, fb_len);
       }
        else {
          free(buf);
          httpd_resp_send_500(req);
          return ESP_FAIL;
        }

I get the right size for file size (fb_len), but buffer size (size_t(fb)) is zero!

If any one can help me ...

Cordially

Welcome,

You do uint8_t * fb; then image.read((uint8_t *)fb, fb_len);

But fb is not initialized so it points to nothing, when it should point to an array of size at least fb_len. You must either use malloc to create this array (as you did with buf, and don't forget to free it after use), or, a better idea is to use an array that is declared globally (or static), with a size big enough for the biggest image.

Dynamic memory allocation is not really a good idea on small microcontrollers..

And about size_t(fb), did you meant to use sizeof instead ?

Hello GUIX
First of all many thanks for the speed, courtesy and relevance of your response.
I functionnaly well understood your reply and tried to modify my code to take your suggestion into account.

I 've added a statement to declare an array in global

uint8_t tampon[50000]={0};

But I got this compilation error:

"cannot convert 'uint8_t ()[50000] {aka unsigned char ()[50000]}' to 'uint8_t* {aka unsigned char*}' in initialization"

Involved statement is:

uint8_t* fb = &tampon;

My code is:

static esp_err_t action_handler(httpd_req_t *req){
  // Result  HTTP header manipulation
  esp_err_t res = ESP_OK;
  
  // Read buffer from SD card
  uint8_t* fb = &tampon;
  size_t fb_len;
  
  // Request buffer
  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, "get", variable, sizeof(variable)) == ESP_OK) {
        res = httpd_resp_set_type(req, "image/jpeg");
        if (res == ESP_OK)
        {
          res = httpd_resp_set_hdr(req, "Content-Disposition", "inline; filename=image.jpg");
        }
        if (res == ESP_OK) {
          File image = SD_MMC.open("/cctv/capture" + String(variable),FILE_READ);
          if (!image) {
             Serial.println("Opening file to read failed");
             return httpd_resp_send_500(req);
          }
          fb_len = image.size();
          
          Serial.println("Before read");
          Serial.println(fb_len);
          
          image.read((uint8_t *)fb, fb_len);
          
          Serial.println("After read");
          Serial.println(fb_len);
          Serial.println(sizeof(fb));
    
          image.close();
          
          free(buf);
          return httpd_resp_send(req, (uint8_t *)fb, fb_len);
         }
        else {
          free(buf);
          httpd_resp_send_500(req);
          return ESP_FAIL;
        }
      }
      else {
        // Unknow ACTION
        free(buf);
        httpd_resp_send_404(req);
        return ESP_FAIL;
      }
    } else {
      // Failed to get URI parameters
      free(buf);
      httpd_resp_send_404(req);
      return ESP_FAIL;
    }
  } else {
    // URI parameters error
    httpd_resp_send_404(req);
    return ESP_FAIL;
  }
}

Where did I mistake and/or misunderstood your proposal ?

Cordially