Managing large size char array (for image data) in arduino mega

Hello, I'm still new to the process of managing char array data types.
I have a project that uses esp32cam - arduino mega. the flow is esp32cam takes a picture then it is converted to base64 and then base64 data is sent to arduino mega via serial communication.

i have tried the way from here but the problem is the arduino mega is not enough memory due to the large size of the char array variable. Is there any other solution or method I can use to solve this problem? Thanks in advance
https://forum.arduino.cc/t/transfer-pictures-from-esp32-cam-to-esp32-via-serial/647488

Here is my code:
Esp32Cam (Sender) based on fustyles Base64 lib

#include "soc/soc.h"
#include "soc/rtc_cntl_reg.h"
#include "Base64.h"
#include "esp_camera.h"
#include "SerialTransfer.h"

SerialTransfer myTransfer;

// WARNING!!! Make sure that you have either selected ESP32 Wrover Module,
//            or another board which has PSRAM enabled

//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

String command, phrase;
char chara;

char img_64[20000];
 
void setup() {
    WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0);
    
    Serial.begin(115200);
    Serial.println("Wellcam");
    
    myTransfer.begin(Serial);
    Serial.setDebugOutput(true);
    
     
    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_JPEG;
    //init with high specs to pre-allocate larger buffers
    if(psramFound()){
      config.frame_size = FRAMESIZE_UXGA;
      config.jpeg_quality = 10;  //0-63 lower number means higher quality
      config.fb_count = 2;
    } else {
      config.frame_size = FRAMESIZE_SVGA;
      config.jpeg_quality = 12;  //0-63 lower number means higher quality
      config.fb_count = 1;
    }
    
    // camera init
    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();
    }
  
    //drop down frame size for higher initial frame rate
    sensor_t * s = esp_camera_sensor_get();
    s->set_framesize(s, FRAMESIZE_QVGA);  // VGA|CIF|QVGA|HQVGA|QQVGA
    
}
 
void loop() {
   while(Serial.available()){
      delay(10);
      chara = Serial.read();
      if(chara == '\n'){break;}
    
      phrase += chara; 
   }

   if(phrase.length()>0){
      Serial.println(phrase);
      if (phrase == "Data"){
          CapaturedPhoto64();
          delay(200);
        }
      else{
         Serial.println("Data Palsu");
      }
      phrase ="";
   }    
}

void CapaturedPhoto64() {
  Serial.println("Take Photo -----");
  uint16_t sendSize = 0;
    //capture 1
    for (int i = 0; i <= 3; i++) {
      camera_fb_t * fb = NULL;
      fb = esp_camera_fb_get();
       if(!fb) {
          Serial.println("Camera capture failed");
          delay(1000);
          ESP.restart();
          return;
        } 
      esp_camera_fb_return(fb);
      delay(200);
    }
    camera_fb_t * fb = NULL;
    fb = esp_camera_fb_get();
    char *input = (char *)fb->buf;
    char output[base64_enc_len(3)];
    String imageFile = "data:image/jpeg;base64,";
    for (int i=0;i<fb->len;i++) {
      base64_encode(output, (input++), 3);
      if (i%3==0) imageFile += urlencode(String(output));
    }
    
    esp_camera_fb_return(fb);
    int file_len = imageFile.length()+1;
    
    imageFile.toCharArray(img_64, file_len);
    
    sendSize = myTransfer.txObj(img_64, sendSize);

    ///////////////////////////////////////// Send buffer
    myTransfer.sendData(sendSize);
    delay(500);
    //Serial.println(imageFile); 
    
    //Serial.println("####Image size = " + String(imageFile.length()));  
    
}

//https://github.com/zenmanenergy/ESP8266-Arduino-Examples/
String urlencode(String str)
{
    String encodedString="";
    char c;
    char code0;
    char code1;
    char code2;
    for (int i =0; i < str.length(); i++){
      c=str.charAt(i);
      if (c == ' '){
        encodedString+= '+';
      } else if (isalnum(c)){
        encodedString+=c;
      } else{
        code1=(c & 0xf)+'0';
        if ((c & 0xf) >9){
            code1=(c & 0xf) - 10 + 'A';
        }
        c=(c>>4)&0xf;
        code0=c+'0';
        if (c > 9){
            code0=c - 10 + 'A';
        }
        code2='\0';
        encodedString+='%';
        encodedString+=code0;
        encodedString+=code1;
        //encodedString+=code2;
      }
      yield();
    }
    return encodedString;
}

Arduino mega (Receiver)

#include "SerialTransfer.h"

SerialTransfer myTransfer;
String command, data_read;
char img_64[20000];    

void setup() {
    Serial.begin(115200); 
    Serial2.begin(115200); 
    Serial.println("Wellcam .... ");
    myTransfer.begin(Serial2);
}
 
void loop() {

    if(Serial.available()){
        command = Serial.readStringUntil('\n');
         
        if(command.equals("Data")){
            Serial.println(F("Req data...."));
            Serial2.print(F("Data\n"));
        }
        else{
            Serial.println(F("Invalid command"));
        }
    }

    if(myTransfer.available()>0){
      uint16_t recSize = 0;
  
      recSize = myTransfer.rxObj(img_64, recSize); 
      Serial.println(F("#######"));
      Serial.println(img_64);
      Serial.print(F("Total size:"));
      Serial.println(sizeof(img_64));
    }
}

what do you need the MEGA for?

Best to use the ESP32 itself (or a larger computer) for anything to do with images.

The Mega is better for collecting data from sensors, etc.

store image data to mysql via ethernet.

you're right. but conditions in the field do not support this (not using a wifi connection).

Parcel the image into suitably small packets, and send.

There is at least one example of that in the SerialTransfer library, but it has been some time since I've looked.

why don't you send them directly from the ESP ?? (connect an ethernet module to your ESP)

what do you need the MEGA for?

store image data to mysql via ethernet.

Unplug the Ethernet cable from the MEGA and plug it into a WiFi access point/router. Then the ESP32cam can upload directly. :slight_smile:

1 Like

These "conditions in the field" somehow include wired ethernet?

this is what i think but i don't understand how to implement it in arduino program.

I'll keep it as a last resort :smiling_face_with_tear: :smiling_face_with_tear:

Implement what?

If you want help, you need to clearly describe the problem, show what you have tried and tell us where you are stuck.

on the ESP side you have a buffer with size X for your 320x240 pixels (size in JPEG will depend on the picture)

You decide of a fixed size buffer that is compatible with the MEGA's memory and a small protocol to send that over - say 1KB

for example

0xFF00FF00        STARTFRAME (4 bytes start marker)
0x400             PAYLOAD SIZE (2 bytes)
0x0000             CHUNK ID (2 bytes)
0x0030             CHUNK TOTAL (2 bytes)
PAYLOAD           your 1024 bytes
CKSUM             to be calculated

the MEGA listens and awaits the start marker, then read the data into a 1KB buffer until the payload size is reached, verifies the checksum. if all is OK it sends that over ethernet and notifies the ESP through aSerial answer that it's ready for the next chunk or that something went wrong and data needs to be transferred again (you can use the chunk ID and total chunk to know exactly where you are in the upload) ➜ that protocol needs to be written

the last chunk of course won't have 1024 bytes, but that's OK because the protocol lets you define the transferred size.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.