Transfer pictures from ESP32-CAM to ESP32 via serial

Hi, I have run out of GPIO pins on my ESP32 project but still need to capture a picture.
For that I am trying to use an ESP32-Cam.
Cameras need a lot of pins and I just do not have them available.
The ESP-Cam takes the picture and sends to ESP32, I would like to get the image and send it to FTP but I am facing a lot of problems most related with the content of the image to arrive in a format that can be used by the FTP client to send it to the net.

That is the first time I am using serial to transfer a lot of data.
The picture is in its native format when it is written to the serial port of the ESP32-CAM but on the receiving side the only thing I could make work was Serial2.readString();
So things start to take a bad route from here because I did not find a way to convert String to const unsigned char that successfully compiles so I hope there is another way to accomplish it.

The code on the ESP32-Cam side is as below:

/* Take picture when requested and sends via serial to Master ESP32*/

#include "esp_camera.h"
#include "Arduino.h"
#include "soc/soc.h"           // Disable brownour problems
#include "soc/rtc_cntl_reg.h"  // Disable brownour problems
#include "driver/rtc_io.h"

// Pin definition for 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

#include <HardwareSerial.h>
HardwareSerial Comm(1);

void setup() {
  WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); //disable brownout detector
 
  Serial.begin(115200);                     // Define and start serial monitor
  Comm.begin(962100, SERIAL_8N1,12,13);     //, Comm_Txd_pin, Comm_Rxd_pin); // Define and start Comm serial port
  Serial.println("Listening...");
  
  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; 
  
  if(psramFound()){
    config.frame_size = FRAMESIZE_UXGA; // FRAMESIZE_ + QVGA|CIF|VGA|SVGA|XGA|SXGA|UXGA
    config.jpeg_quality = 10;
    config.fb_count = 2;
  } else {
    config.frame_size = FRAMESIZE_SVGA;
    config.jpeg_quality = 12;
    config.fb_count = 1;
  }
  
  // Init Camera
  esp_err_t err = esp_camera_init(&config);
  if (err != ESP_OK) {
    Serial.printf("Camera init failed with error 0x%x", err);
    return;
  }
}

void loop() {
  bool takePicture=0;
  while (Comm.available()) {                         // Wait for the Receiver to get the characters
    takePicture = Comm.parseInt();                // Display the Receivers characters  
    if(takePicture == 1){
      Serial.print("Received: ");
      Serial.print(takePicture);      
      camera_fb_t * fb = NULL;
      // Take Picture with Camera
      fb = esp_camera_fb_get(); 
      Comm.write(fb->buf, fb->len);               // payload (image), payload length   SENT Via Serial to ESP32
      Serial.print(" - Pic Size: ");   
      Serial.println(fb->len);     
      esp_camera_fb_return(fb);
    }      
    break;
  } 
}

The code that is to receive the image and forward it to the FTP server is as below:

/*Receives picture via serial and saves to ftp*/

#include <WiFi.h>
// WiFi credentials.
char ssid[] = "myWiFi;
char pass[] = "myWFPass";

// Define NTP Client to get time
#include <NTPClient.h>
#include <WiFiUdp.h>
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP);

// Variables to save date and time
String formattedDate;
String dayStamp;
String timeStamp;

// FTP Client Lib
#include "ESP32_FTPClient.h"

// FTP Server credentials
char ftp_server[] = "ftp.mysite.com";
char ftp_user[]   = "esp32@mysite.com";
char ftp_pass[]   = "mypass";
ESP32_FTPClient ftp (ftp_server, ftp_user, ftp_pass);


// Camera, URL and picture name
String picPrefix ="";
String pic_name;  
String pic_url   = "";
String imageContent="";

// Connection timeout;
#define CON_TIMEOUT   10*1000                     // milliseconds

void setup(){
  //Serial.begin(Baud Rate, Data Protocol, Txd pin, Rxd pin);
  Serial.begin(115200);                                                     // Define and start serial monitor
  Serial2.begin(962100, SERIAL_8N1); //Receiver_Txd_pin, Receiver_Rxd_pin); // Define and start Receiver serial port

  WiFi.begin( ssid, pass );
  Serial.println("\nConnecting to WiFi");
  while ( WiFi.status() != WL_CONNECTED && millis() < CON_TIMEOUT )  {
    delay(500);
    Serial.print(".");
  }
}

void loop(){
  Serial.print("Requesting Pic: "); 
  Serial.println(1); 
  Serial2.println(1);
  delay(100); 
  while (Serial2.available()) {                         // Wait for the Receiver to get the characters
    imageContent = Serial2.readString();         // Saves the Received characters
    if(imageContent.length()>0){
      Serial.print("Recebida: ");
      Serial.println(imageContent);                  // Display the result on the serial monitor
      pic_name  = picPrefix;
      pic_name += getDateTime() + ".jpg";
      //FTP_upload();
      break;    
    }
  }
  Serial.println("");
  delay(10000);
}


void FTP_upload(){
  
  Serial.print("Uploading via FTP");
  ftp.OpenConnection();
  
  //Create a file and write the image data to it;
  ftp.InitFile("Type I");
  const char *f_name = pic_name.c_str();
  ftp.NewFile( f_name );
  const char *buf = imageContent.c_str();
  //ftp.WriteData(buf, sizeof(buf));                     //***invalid conversion from 'const char*' to 'unsigned char*' [-fpermissive]
  ftp.CloseFile();
  // Breath, withouth delay URL failed to update.
  Serial.println("Done... Waiting next shot...");
  delay(100);
}

String getDateTime(){
  String curDateTime="";

  while(!timeClient.update()) {
    timeClient.forceUpdate();
  }
  // The formattedDate comes with the following format:
  // 2018-05-28T16:00:13Z
  // We need to extract date and time
  formattedDate = timeClient.getFormattedDate();

  #ifdef DEGUB_ESP
    // Extract date
    int splitT = formattedDate.indexOf("T");
    dayStamp = formattedDate.substring(0, splitT);
    // Extract time
    timeStamp = formattedDate.substring(splitT+1, formattedDate.length()-1);
    Serial.println(formattedDate);  
    Serial.print("DATE: ");
    Serial.println(dayStamp);
    Serial.print("HOUR: ");
    Serial.println(timeStamp);    
  #endif 
  
  curDateTime  = formattedDate.substring(8,10);  //day
  curDateTime += formattedDate.substring(5,7);   //month
  curDateTime += formattedDate.substring(2,4);   //year
  curDateTime += formattedDate.substring(11,13); //hour
  curDateTime += formattedDate.substring(14,16); //minute
  return curDateTime;
}

The idea is that when the ESP32 script needs a picture it will request to ESP32-Cam sending a true, otherwise the ESP32-Cam should stay idle waiting for a new request. In this test code ESP32 keeps requesting pictures for diagnostic purposes.
Please notice my main difficulty is to receive the picture in a format that can be accepted by the upload routine.
Compile error line is indicated on the code.

Thanks in Advance.
Paulo

1 Like

You can use SerialTransfer.h to automatically packetize and parse your data for inter-Arduino communication without the headace. The library is installable through the Arduino IDE and includes many examples.

Here are the library’s features:

This library:

  • can be downloaded via the Arduino IDE‚Äôs Libraries Manager (search ‚ÄúSerialTransfer.h‚ÄĚ)
  • works with ‚Äúsoftware-serial‚ÄĚ libraries
  • is non blocking
  • uses packet delimiters
  • uses consistent overhead byte stuffing
  • uses CRC-8 (Polynomial 0x9B with lookup table)
  • allows the use of dynamically sized packets (packets can have payload lengths anywhere from 1 to 255 bytes)
  • can transfer bytes, ints, floats, and even structs!!

Example TX Arduino Sketch:

#include "SerialTransfer.h"

SerialTransfer myTransfer;

void setup()
{
  Serial.begin(115200);
  Serial1.begin(115200);
  myTransfer.begin(Serial1);
}

void loop()
{
  char buff[] = "hi";

  myTransfer.txObj(buff, sizeof(buff));
  myTransfer.sendData(sizeof(buff));
  delay(100);
}

Example RX Arduino Sketch:

#include "SerialTransfer.h"

SerialTransfer myTransfer;

void setup()
{
  Serial.begin(115200);
  Serial1.begin(115200);
  myTransfer.begin(Serial1);
}

void loop()
{
  if(myTransfer.available())
  {
    char buff[40];
    
    myTransfer.rxObj(buff, sizeof(buff));
    
    Serial.println("New Data: ");
    Serial.write(buff, sizeof(buff));
    Serial.println();
  }
  else if(myTransfer.status < 0)
  {
    Serial.print("ERROR: ");

    if(myTransfer.status == -1)
      Serial.println(F("CRC_ERROR"));
    else if(myTransfer.status == -2)
      Serial.println(F("PAYLOAD_ERROR"));
    else if(myTransfer.status == -3)
      Serial.println(F("STOP_BYTE_ERROR"));
  }
}

For theory behind robust serial communication, check out the tutorials Serial Input Basics and Serial Input Advanced.

Do note that the max number of bytes of payload per packet will be 255. Because of this, you will need to reserve a few bytes in the payload of each packet for identifying the packet number.

For instance, if the photo requires you to send 100 packets of 253 bytes of pixel data, you would use the remaining 2 bytes to number each packet from 1 to 100. Then you can identify if any data is missing and reconstruct the photo on the second ESP. If any data is missing, you will need a way to re-request that data (or set the missing pixel values to 0).

Dear Power_Broker, thanks for the proposed alternative.

Just a quick question:
As you may see on the receiving sketch above the big problem of the method I am using is of data types.

//ftp.WriteData(buf, sizeof(buf));
//*invalid conversion from 'const char' to 'unsigned char' [-fpermissive]

The ftp.WriteData function requites unsigned char to work.

Any idea how will I recompose and convert the arriving data into this data format?

Thanks
Paulo

You can probably type-cast the pointer while sending it as an argument and get it to work that way:

ftp.WriteData((unsigned char*)buf, sizeof(buf));

How about using ESPNow ESP-Now Overview | Espressif Systems, then sending the data between devices using, either, a freeRTOS stream or ring buffer.

Dear Power_Broker,
ftp.WriteData((unsigned char*)buf, sizeof(buf));
Compiles sucessfully.
Will try if the whole thing works in my code.
Thanks.

Idahowalker
That is also a good idea.
I wasn't even remembering this possibility.
Never used it but will give it a try as well.
Thanks.
Paulo

Hi, I did some tests expanding my own code and there is progress although it is not working yet.

On the ESP32-Cam image sender I updated the code as below:

void loop() {
  bool takePicture=0;
  while (Comm.available()) {              // Wait for the Receiver to get the characters
    takePicture = Comm.parseInt();        // Display the Receivers characters  
    if(takePicture == 1){
      Serial.print("Received: ");
      Serial.print(takePicture);      
      camera_fb_t * fb = NULL;
      // Take Picture with Camera
      fb = esp_camera_fb_get(); 
      Comm.write(fb->buf, fb->len);        // payload (image), payload length   
      Serial.print(" - Pic Size: ");   
      Serial.println(fb->len);     
      esp_camera_fb_return(fb);
    }      
  } 
}

On the receiving side the change was as below:

void loop(){
  Serial.print("Requesting Pic: "); 
  Serial.println(1); 
  Serial2.println(1);
  delay(100); 
  while (Serial2.available()) {                  // Wait for the Receiver to get the characters
    imageContent += Serial2.readString();        // Saves the Received characters
  }
  if(imageContent.length()>0){
    Serial.print("Recebida: ");
    Serial.println(imageContent);                // Display the result on the serial monitor
    Serial.println("");
    pic_name  = picPrefix;
    pic_name += getDateTime() + ".jpg";
    FTP_upload();
  }
  imageContent = "";
  delay(10000);
}

void FTP_upload(){
  Serial.print("Uploading via FTP");
  ftp.OpenConnection();
  //Create a file and write the image data to it;
  ftp.InitFile("Type I");
  const char *f_name = pic_name.c_str();
  ftp.NewFile( f_name );
  ftp.WriteData((unsigned char *)imageContent.c_str(), imageContent.length());       
  ftp.CloseFile();
  // Breath, withouth delay URL failed to update.
  Serial.println("Done... Waiting next shot...");
  delay(100);
}

The image is captured and sent via serial to the ESP32 that then posts it to the FTP server.
The image arrives as .jpg as intended but…
The image is distorted.
I have added some samples for clarification.
These are pictures of my wall and ceiling, most should look like the very top of the image.
As marked on the image, the top 10% is ok but then it gets scrambled.

Assistance highly welcome.
Thanks
Paulo

You didn’t seem to take anybody’s advice…

Did you use an ESPNow or SerialTransfer.h?

Hi, Power_Broker, see I have to sort out the main problem that is how to transfer the image data from ESP32-Cam to ESP32Master.

The image is captured by ESP32-Cam into fb. fb seems to be some sort of object (I am not well versed on how to manipulate this sort of variable)
Then this fb->buf has to be transfered to ESP32Master.

It is still not clear to me how to handle the way fb->buf is transferred into some sort of data structure (variable) that needs, in my case, to be compatible with ftp.WriteData(unsigned char,int ) .

So before investing time on the transfer mechanism I was investigating if, with my current code, I could get the image data properly transferred to the ESP32Master.

I believe that when I get the way to properly transfer the image data I can then try your recommendation and also Idahowalker’s to see witch one better fit my project that will be battery powered (recharged by solar panels).

I have been trying not to use WiFi precisely because of battery consumption.

But my idea is, once the the way to transfer the image data is known (at variable level) I can then try transfer methods to see which one is better in my case.

I have tried many things, Serial.read(), Serial.readBytes() but only Serial.readString() worked to the point of at least get the image to the FTP although scrambled.

What do you think?
Thanks

pcborges:
It is still not clear to me how to handle the way fb->buf is transferred into some sort of data structure (variable) that needs, in my case, to be compatible with ftp.WriteData(unsigned char,int ) .

This isn't clear, can you expand on this? What does "fb->buf" mean? Is fb a pointer to buf? If you know the input arguments and the argument types of ftp.WriteData(), then what what's confusing about making your code compatible with it? What exactly is your question?

OK, step by step.

That is how the picture is captured on the ESP32-Cam:

fb = esp_camera_fb_get(); //this fb is some sort of frame buffer but it is nit a simple variable

In the original sketch I downloaded from the internet that is the way the image is saved to SD memory:

file.write(fb->buf, fb->len);

That is the way I am trying to transfer the image taken via serial:

Comm.write(fb->buf, fb->len); // payload (image), payload length SENT Via Serial to ESP32

I do not know if this is the proper way to send image data via serial, I just tried it, it compiles and runs.

On the receiving side I use:

while (Serial2.available()) { // Wait for the Receiver to get the characters
imageContent += Serial2.readString(); // Saves the Received characters
}

The image is sent as char but is received as String. That might not be the correct way.

I have no idea if this is the correct way to get the image data that is, I hope, been transferred from ESP32-Cam with the Comm.write(fb->buf, fb->len); that I do not really know is the way to handle this transfer.

And then this is the way the image data is sent to the ftp server:

ftp.WriteData((unsigned char *)imageContent.c_str(), imageContent.length());

My code is a sort of Frankenstein of pieces of code that I am not sure cam be associating the way I am associating.

You see, I get ‚Äúinspiration‚ÄĚ on code I find here and there but there are things I do not understand and right now I do not understand why the image arrives scrambled at the destination.

All I know now is that is compiles and run but the image arrives scrambled at the destination.

Hope it is clear enough.
Regards

pcborges:
fb = esp_camera_fb_get(); //this fb is some sort of frame buffer but it is nit a simple variable

‚Äúfb‚ÄĚ is a pointer to a struct of type camera_fb_t. The source code for this struct can be found in this header:

/**
 * @brief Data structure of camera frame buffer
 */
typedef struct {
    uint8_t * buf;              /*!< Pointer to the pixel data */
    size_t len;                 /*!< Length of the buffer in bytes */
    size_t width;               /*!< Width of the buffer in pixels */
    size_t height;              /*!< Height of the buffer in pixels */
    pixformat_t format;         /*!< Format of the pixel data */
    struct timeval timestamp;   /*!< Timestamp since boot of the first DMA buffer of the frame */
} camera_fb_t;

pcborges:
I do not know if this is the proper way to send image data via serial, I just tried it, it compiles and runs.

Then how do you know the information you’re trying to send over FTP is even a full, un-corrupted image?! There’s no robustness to how your transferring the data, so if any issues arise during transmission, you won’t be able to recover the lost data/image. AND that makes debugging the FTP transmission completely useless.

What you really need to do is one of the following:

1.) Test FTP transmission of an example image without any serial connections - use only one ESP32. Once you work the bugs out and verify your FTP transmission is reliable, then you can focus on testing the serial connection.
2.) Temporarily forget about the FTP portion of the project and focus on transferring an entire image over serial. Once you verify you can reliably do this, then you can test the FTP transmission.

In short, you’re currently testing two features where one is dependent on the other. This means that when a problem arises (i.e. a scrambled image), you don’t know what part of the process caused the bug. You need to test each feature one at a time (if at all possible).

Thanks for the clarification, but there is some previous knowledge on the subject.

The original code is tested with ESP32-Cam both to write the image to SD and to send it to FTP via WiFi.
The images at the SD have been tested on a PC.
Then I modified the code to instead of writing to SD send it to FTP and it works, I also modified the code so it both write to SD and send to FTP. The images have been checked on the FTP and downloaded to my PC and they are fine.
That was ESP32-Cam alone.

My project with the ESP32Master:
Weather station, no WiFi available, running on batteries, internet connection via GPRS, GPS positioning and timing, barometer, rain sensor.
No GPIOs are left to add a camera.

Separately the two projects are already working.
As power is a constraint I have to be economic on what I add to the final project.
The idea is that the final project will hibernate and only wake up from time to time, transfer its data payload and go back to sleep to save battery.

In the current concept ESP32Master will wake ESP32-Cam up (Uses 1 GPIO) and request it takes a picture via the same serial connection ESP32-Cam will use to transfer the image to ESP32Master.

The code I am trying to debug now is not the complete code, just a scaled down sketch to accomplish the task of taking the picture on ESP32-Cam, transfer it to ESP32Master using whatever method that works reliably and transfer the image to FTP using WiFi.
Once it works I will take the idea to the main code.

Considering all that I think that the problem is in the image transmission from ESP32-Cam to ESP32Master.

Thanks again
Paulo

Hi, Just modified the code to display fb->len (on the sender side) and imageContent.length() (on the receiving side) and only about half of the size displayed on fb->len is arriving at imageContent.length().

Trying to understand why.
Paulo

I’m having a difficult piecing together what you’re exactly doing in the sender sketch. However, I think the better approach would be to save memory by cutting out the usage of imgBuff and decrease clock cycles by removing the loops:

TX:

#include "SerialTransfer.h"


SerialTransfer myTransfer;
uint16_t counter = 0;


void setup()
{
  Serial.begin(115200);
  Serial1.begin(115200);
  myTransfer.begin(Serial1);
}


void loop()
{
  const char buff[252] = "Hello there... Here we are ok!******************************************************************************************************************************************************************************************************************************";
  uint16_t sendSize = 0;

  myTransfer.txObj(counter, sizeof(counter));
  sendSize += sizeof(counter);
  
  myTransfer.txObj(buff, sizeof(buff), sendSize);
  sendSize += sizeof(buff);
  
  myTransfer.sendData(sendSize);

  Serial.print(F("Sending: "));
  Serial.print(counter);
  Serial.write(myTransfer.txBuff, sendSize);
  Serial.println();
  
  delay(500);
  counter++;
}

RX:

#include "SerialTransfer.h"


SerialTransfer myTransfer;
uint16_t counter = 0;


void setup()
{
  Serial.begin(115200);
  Serial1.begin(115200);
  myTransfer.begin(Serial1);
}


void loop()
{
  if(myTransfer.available())
  {
    char buff[252];
    uint16_t recSize = 0;

    myTransfer.rxObj(counter, sizeof(counter));
    recSize += sizeof(counter);
    
    myTransfer.rxObj(buff, sizeof(buff), recSize);
    recSize += sizeof(buff);

    Serial.print(F("Received: Count- "));
    Serial.print(counter);
    Serial.print(F(" Buff- "));
    Serial.write(buff, sizeof(buff));
    Serial.println();
  }
  else if(myTransfer.status < 0)
  {
    Serial.print("ERROR: ");

    if(myTransfer.status == -1)
      Serial.println(F("CRC_ERROR"));
    else if(myTransfer.status == -2)
      Serial.println(F("PAYLOAD_ERROR"));
    else if(myTransfer.status == -3)
      Serial.println(F("STOP_BYTE_ERROR"));
  }
}

Note that I had to change the serial port to Serial2 at 115200 for testing on both sketches. I tested the sketches successfully on 2 of my Megas.

Hi, thanks again for the support.
I spent the whole afternoon trying to make it work and with some more info on your previous post I could adjust my code and it is now working although it needs some fine tuning.

One thing is the image size, currently VGA every picture is about 24K in size so it requires looping through both at sending and at arriving. I am working on that now.

Agree with the imgBuffer, I will have to optimize some things and that is sure one to look for.
May be I will transfer picture data straight to packetBuff instead of this two steps I currently using.

I have to admit that I am finding it rather difficult to adjust sizes so I do not cut out or overlap bytes from the image.

The current code takes the picture, segments it in packets, add sequence numbers to be tested on arrival and sends the packets.
At the receiving end I can check and print packet by packet but it is still not posting to the FTP server.

Somehow I have to be more sure there are no mixed or lost pixels in the process of segmentation and reassembly.

But perhaps you could take a look and see if looks ok.
Thanks again for your support, I would not have been able to get that far without your assistance.

Sender:

/* Take picture when requested and sends via serial to MasterESP32*/
#include "SerialTransfer.h"
SerialTransfer myTransfer;
HardwareSerial Comm(1);

#include "esp_camera.h"
#include "Arduino.h"
#include "soc/soc.h"           // Disable brownour problems
#include "soc/rtc_cntl_reg.h"  // Disable brownour problems
#include "driver/rtc_io.h"

// Pin definition for 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

void setup() {
  WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); //disable brownout detector
 
  Serial.begin(115200);                     // Define and start serial monitor
  Comm.begin(962100, SERIAL_8N1,12,13);     //, Comm_Txd_pin, Comm_Rxd_pin); // Define and start Comm serial port
  myTransfer.begin(Comm);
  
  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; 
  
//  if(psramFound()){
//    config.frame_size = FRAMESIZE_UXGA; // FRAMESIZE_ + QVGA|CIF|VGA|SVGA|XGA|SXGA|UXGA
//    config.jpeg_quality = 10;
//    config.fb_count = 2;
//  } else {
    config.frame_size = FRAMESIZE_SVGA;
    config.jpeg_quality = 12;
    config.fb_count = 1;
//  }
  
  // Init Camera
  esp_err_t err = esp_camera_init(&config);
  if (err != ESP_OK) {
    Serial.printf("Camera init failed with error 0x%x", err);
    return;
  }
}

void loop(){
  char imgBuffer[249];
  char packetBuff[254];
  int  packetCounter=0;
  int  byteCounter=0;
  
  camera_fb_t * fb = NULL;
  // Take Picture with Camera
  fb = esp_camera_fb_get(); 

  Serial.print("imgFull: ");
  Serial.println(fb->len);

  for(int i=0;i<fb->len;i++){
    imgBuffer[byteCounter] = fb->buf[i];
    byteCounter++;
    if(byteCounter==sizeof(imgBuffer)-1){

      sprintf(packetBuff,"%05d",packetCounter);
      strcat(packetBuff,imgBuffer);

      myTransfer.txObj(packetBuff, sizeof(packetBuff));
      myTransfer.sendData(sizeof(packetBuff));

      delay(100);
/*
      Serial.print("Packet: ");
      Serial.print(packetCounter);
      Serial.print("- ");
      Serial.write((const uint8_t*)packetBuff, sizeof(packetBuff));
      Serial.println();
*/
      packetCounter++;
      byteCounter=0;       
    }  
  }
  delay(10000);
}

Receiver:

/*Receives picture via serial and saves to ftp*/

#include "SerialTransfer.h"
SerialTransfer myTransfer;
int counter=0;

#include <WiFi.h>
#include <NTPClient.h>
#include <WiFiUdp.h>
// Define NTP Client to get time
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP);
// Variables to save date and time
String formattedDate;
String dayStamp;
String timeStamp;

// FTP Client Lib
#include "ESP32_FTPClient.h"

// Your WiFi credentials.
char ssid[] = "mySSID";
char pass[] = "myPass";

// FTP Server credentials
char ftp_server[] = "ftp.myserver.com";
char ftp_user[]   = "name@myserver.com";
char ftp_pass[]   = "mypass";

// Camera, URL and picture name
String picPrefix ="";
String pic_name;  
String pic_url   = "";
String imageContent="";

// Connection timeout;
#define CON_TIMEOUT   10*1000                     // milliseconds

ESP32_FTPClient ftp (ftp_server, ftp_user, ftp_pass);

void setup(){
  //Serial.begin(Baud Rate, Data Protocol, Txd pin, Rxd pin);
  Serial.begin(115200);                                                     // Define and start serial monitor
  Serial2.begin(962100, SERIAL_8N1); //Receiver_Txd_pin, Receiver_Rxd_pin); // Define and start Receiver serial port
  myTransfer.begin(Serial2);

  WiFi.begin(ssid, pass);
  Serial.println("\nConnecting to WiFi");
  while ( WiFi.status() != WL_CONNECTED && millis() < CON_TIMEOUT )  {
    delay(500);
    Serial.print(".");
  }
  Serial.println();
}

void loop(){
  if(myTransfer.available()){
    char buff[254];
    
    myTransfer.rxObj(buff, sizeof(buff));

    counter++;
    Serial.print("New Data: ");
    Serial.println(counter);
    Serial.write((const uint8_t*)buff, sizeof(buff));
    Serial.println();
  }
  else if(myTransfer.status < 0)
  {
    Serial.print("ERROR: ");

    if(myTransfer.status == -1)
      Serial.println(F("CRC_ERROR"));
    else if(myTransfer.status == -2)
      Serial.println(F("PAYLOAD_ERROR"));
    else if(myTransfer.status == -3)
      Serial.println(F("STOP_BYTE_ERROR"));
  }
}


void FTP_upload(){
  Serial.print("Uploading via FTP");
  ftp.OpenConnection();
  //Create a file and write the image data to it;
  ftp.InitFile("Type I");
  const char *f_name = pic_name.c_str();
  ftp.NewFile( f_name );
  ftp.WriteData((unsigned char *)imageContent.c_str(), imageContent.length());       
  ftp.CloseFile();
  // Breath, withouth delay URL failed to update.
  Serial.println("Done... Waiting next shot...");
  delay(100);
}

String getDateTime(){
  String curDateTime="";

  while(!timeClient.update()) {
    timeClient.forceUpdate();
  }
  // The formattedDate comes with the following format:
  // 2018-05-28T16:00:13Z
  // We need to extract date and time
  formattedDate = timeClient.getFormattedDate();

  #ifdef DEGUB_ESP
    // Extract date
    int splitT = formattedDate.indexOf("T");
    dayStamp = formattedDate.substring(0, splitT);
    // Extract time
    timeStamp = formattedDate.substring(splitT+1, formattedDate.length()-1);
    Serial.println(formattedDate);  
    Serial.print("DATE: ");
    Serial.println(dayStamp);
    Serial.print("HOUR: ");
    Serial.println(timeStamp);    
  #endif 
  
  curDateTime  = formattedDate.substring(8,10);  //day
  curDateTime += formattedDate.substring(5,7);   //month
  curDateTime += formattedDate.substring(2,4);   //year
  curDateTime += formattedDate.substring(11,13); //hour
  curDateTime += formattedDate.substring(14,16); //minute
  return curDateTime;
}

Once woke up the sender will keep sending pictures.
The receiver will look for packet number 0 as a sign of a new picture.
It will remove sequencing and append image data to some variable to be created.
When packet number returns to zero it means the previous picture was received and can be sent to FTP.
Still have to find a way to put the ESP32CAM to sleep. May be the same GPIO pin I will use to wake it up.

Thanks again
Paulo

pcborges:
Hi, thanks again for the support.

Glad to help!

pcborges:
Agree with the imgBuffer, I will have to optimize some things and that is sure one to look for.
May be I will transfer picture data straight to packetBuff instead of this two steps I currently using.

Working with 24K bytes at a time will require your code to be very, very memory and CPU-cycle efficient. The less copying and fewer the loops the better!

As for the code, I see a few things:

1.) Don't convert the counter int into a string - if you send counter as a uint16_t, you'll only use up 2 bytes of the packet. This leaves you a maximum of 252 bytes of pixel data (max number of bytes per packet is 254).
2.) Don't use a middle-man buffer like packetBuff. The library already has it's own version of a packet payload buffer that's accessed when you call myTransfer.txObj().
3.) Don't use strcat() for the same reasons as #2

Basically, take whatever data you need directly from their respective sources and input them into the library via myTransfer.txObj() for each datum without middle-men.

After you edit your code with the above in mind, post your new code and then we can see what the problem might be.

Hi, here I Am again.
I decided to proceed with the current code and make it work first before optimizations.
So I implemented the packet reassembly but are facing a CRC_ERROR.
The code of the sender only had the serial speed reduced to 115200 and the code for the receiver is now as below:

/*Receives picture via serial and saves to ftp*/

#include "SerialTransfer.h"
SerialTransfer myTransfer;
int counter=0;

#include <WiFi.h>
#include <NTPClient.h>
#include <WiFiUdp.h>
// Define NTP Client to get time
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP);
// Variables to save date and time
String formattedDate;
String dayStamp;
String timeStamp;

// FTP Client Lib
#include "ESP32_FTPClient.h"

// Your WiFi credentials.
char ssid[] = "Skynet";
char pass[] = "babacabaca";

// FTP Server credentials
char ftp_server[] = "ftp.hidroflux.com";
char ftp_user[]   = "esp32@hidroflux.com";
char ftp_pass[]   = "gen99ius";

// Camera, URL and picture name
String picPrefix ="";
String pic_name;  
String pic_url   = "";
String imageContent="";
String fullImage;
char   byteCounter[4];

// Connection timeout;
#define CON_TIMEOUT   10*1000                     // milliseconds

ESP32_FTPClient ftp (ftp_server, ftp_user, ftp_pass);

void setup(){
  //Serial.begin(Baud Rate, Data Protocol, Txd pin, Rxd pin);
  Serial.begin(115200);                                                     // Define and start serial monitor
  Serial2.begin(115200, SERIAL_8N1); //Receiver_Txd_pin, Receiver_Rxd_pin); // Define and start Receiver serial port
  myTransfer.begin(Serial2);

  WiFi.begin(ssid, pass);
  Serial.println("\nConnecting to WiFi");
  while ( WiFi.status() != WL_CONNECTED && millis() < CON_TIMEOUT )  {
    delay(500);
    Serial.print(".");
  }
  Serial.println();
}

void loop(){
  imageCapture(false);
  Serial.print("Lenght: ");
  Serial.println(fullImage.length());
  delay(10000);
}


void FTP_upload(){
  Serial.print("Uploading via FTP");
  ftp.OpenConnection();
  //Create a file and write the image data to it;
  ftp.InitFile("Type I");
  const char *f_name = pic_name.c_str();
  ftp.NewFile( f_name );
  ftp.WriteData((unsigned char *)imageContent.c_str(), imageContent.length());       
  ftp.CloseFile();
  // Breath, withouth delay URL failed to update.
  Serial.println("Done... Waiting next shot...");
  delay(100);
}

String getDateTime(){
  String curDateTime="";

  while(!timeClient.update()) {
    timeClient.forceUpdate();
  }
  // The formattedDate comes with the following format:
  // 2018-05-28T16:00:13Z
  // We need to extract date and time
  formattedDate = timeClient.getFormattedDate();

  #ifdef DEGUB_ESP
    // Extract date
    int splitT = formattedDate.indexOf("T");
    dayStamp = formattedDate.substring(0, splitT);
    // Extract time
    timeStamp = formattedDate.substring(splitT+1, formattedDate.length()-1);
    Serial.println(formattedDate);  
    Serial.print("DATE: ");
    Serial.println(dayStamp);
    Serial.print("HOUR: ");
    Serial.println(timeStamp);    
  #endif 
  
  curDateTime  = formattedDate.substring(8,10);  //day
  curDateTime += formattedDate.substring(5,7);   //month
  curDateTime += formattedDate.substring(2,4);   //year
  curDateTime += formattedDate.substring(11,13); //hour
  curDateTime += formattedDate.substring(14,16); //minute
  return curDateTime;
} 

void imageCapture(bool capturing){
  fullImage="";
  int localCounter=0, frameCounter=0;
  while (true){
    if(myTransfer.available()){
      char buff[254];
    
      myTransfer.rxObj(buff, sizeof(buff));

      counter++;
//    Serial.print("New Data: ");
//    Serial.println(counter);
//    Serial.write((const uint8_t*)buff, sizeof(buff));
//    Serial.println();

      for(int i=0;i<5;i++){
        byteCounter[i] = buff[i];
      }
      frameCounter = String(byteCounter).toInt();
      if(frameCounter==0){
        capturing = !capturing;
        if(!capturing){
          break;
        }
      }
      if(capturing){
        Serial.print("localCounter: ");
        Serial.print(localCounter);
        Serial.print(" - frameCounter: ");
        Serial.println(frameCounter);
        if(frameCounter == localCounter){
          localCounter++;  
          for(int i=5;i<sizeof(buff);i++){
            fullImage += buff[i]; 
          }
        }else{
          Serial.println("Reassembly Sequence Error...");
          break;
        }
      }
    } else if(myTransfer.status < 0) {
      Serial.print("ERROR: ");
      if(myTransfer.status == -1){
        Serial.println(F("CRC_ERROR"));
        break;
      }else if(myTransfer.status == -2){
        Serial.println(F("PAYLOAD_ERROR"));
        break;
      }else if(myTransfer.status == -3){
        Serial.println(F("STOP_BYTE_ERROR"));
        break;
      }
    }
  }  
}

I believe that, as the sender is, at every 10 seconds, sending one image in a sequence of several independent packets, as the receiver starts it may get half a packet.
Ideally the receiver would ignore whatever it receives till it can identify packet 00000.
What is the behavior of myTransfer.available() when it gets a transmission at the middle?
Say, I switch on ESP32CAM and it starts sending packets: 00, 01, 02, 03…
Then I switch on ESP32Master and it starts getting packet 04 at the middle of it.
What will happen?

Below the console of the receiver:

Connecting to WiFi
.
ERROR: CRC_ERROR
Lenght: 0
ERROR: CRC_ERROR
Lenght: 0
ERROR: CRC_ERROR
Lenght: 0
ERROR: CRC_ERROR
Lenght: 0
ERROR: CRC_ERROR
Lenght: 0
ERROR: CRC_ERROR
Lenght: 0
ERROR: CRC_ERROR
Lenght: 0
ERROR: CRC_ERROR
Lenght: 0
localCounter: 0 - frameCounter: 0
ERROR: CRC_ERROR
Lenght: 249
ERROR: CRC_ERROR
Lenght: 0
ERROR: CRC_ERROR
Lenght: 0

Thanks
Paulo