FTP over 4/5G-GPRS

Been working on a project for some time and hit a roadblock.

ESP32Cam32 serially sends to the Master and then Master uploads images via wireless to FTP site.

Original code uploads image to FTP via wireless, I wont have wireless and need to do via 4/5G to FTP site

Tx ESP32Cam (Slave)

#define DEBUG_ESP              //comment out to deactivate debug console

#ifdef DEBUG_ESP
  #define pDBGln(x) Serial.println(x)
  #define pDBG(x)   Serial.print(x)
#else 
  #define pDBG(...)
  #define pDBGln(...)
#endif

#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"
#include "SerialTransfer.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

SerialTransfer myTransfer;
HardwareSerial Comm(1);
struct img_meta_data{
  uint16_t counter;
  uint16_t imSize;
  uint16_t numLoops;
  uint16_t sizeLastLoop;
} ImgMetaData;
const uint16_t PIXELS_PER_PACKET = MAX_PACKET_SIZE - sizeof(ImgMetaData);

void setup(){
  WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); //disable brownout detector
 
  Serial.begin(115200);                     // Define and start serial monitor
  // 115200,256000,512000,962100
  Comm.begin(962100, SERIAL_8N1,15,14);     //, 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;
//  }

    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(){
  uint16_t startIndex = 0;
  camera_fb_t * fb = NULL;
  //Take Picture with Camera
  fb = esp_camera_fb_get(); 
   
  ImgMetaData.imSize       = fb->len;                             //sizeof(myFb);
  ImgMetaData.numLoops     = (fb->len / PIXELS_PER_PACKET) + 1;   //(sizeof(myFb)/PIXELS_PER_PACKET) + 1; 
  ImgMetaData.sizeLastLoop = fb->len % PIXELS_PER_PACKET;         //(sizeof(myFb)%PIXELS_PER_PACKET);

  for(ImgMetaData.counter=1; ImgMetaData.counter<=ImgMetaData.numLoops; ImgMetaData.counter++){
    myTransfer.txObj(ImgMetaData, sizeof(ImgMetaData));
    //printStructBuf();
    stuffPixels(fb->buf, startIndex, sizeof(ImgMetaData), PIXELS_PER_PACKET);
    //stuffPixels(myFb, startIndex, sizeof(ImgMetaData), PIXELS_PER_PACKET);
    //printStructBuf();   
    myTransfer.sendData(MAX_PACKET_SIZE);
     
    pDBGln(F("Sent:"));
    pDBG(F("img.counter: "));      pDBGln((uint16_t)((myTransfer.txBuff[1] << 8) | myTransfer.txBuff[0]));
    pDBG(F("img.imSize: "));       pDBGln((uint16_t)((myTransfer.txBuff[3] << 8) | myTransfer.txBuff[2]));
    pDBG(F("img.numLoops: "));     pDBGln((uint16_t)((myTransfer.txBuff[5] << 8) | myTransfer.txBuff[4]));
    pDBG(F("img.sizeLastLoop: ")); pDBGln((uint16_t)((myTransfer.txBuff[7] << 8) | myTransfer.txBuff[6]));

    startIndex += PIXELS_PER_PACKET;    
    delay(100);
    //printBuf();  
  }
  esp_camera_fb_return(fb);   //clear camera memory
  delay(10000);
}

void printStructBuf(){
  pDBG(F("Internal Struct: { "));
  for (uint16_t k=0; k<sizeof(ImgMetaData); k++){
    pDBG(myTransfer.txBuff[k]);
    if (k<(sizeof(ImgMetaData)-1))
      pDBG(F(", "));
    else
      pDBGln(F(" }"));
  }
}

void printBuf(){
  pDBG(F("Pixel Values: { "));
  for (uint16_t k=8; k<MAX_PACKET_SIZE; k++){
    pDBG(myTransfer.txBuff[k]);
    if (k < (MAX_PACKET_SIZE - 1))
      pDBG(F(", "));
    else
      pDBGln(F(" }"));
  }
  pDBGln();
}

void stuffPixels(const uint8_t * pixelBuff, const uint16_t &bufStartIndex, const uint16_t &txStartIndex, const uint16_t &len){
  uint16_t txi = txStartIndex;
  for (uint16_t i=bufStartIndex; i<(bufStartIndex + len); i++)  {
    myTransfer.txBuff[txi] = pixelBuff[i];
    txi++;
  }
}

RX Master

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

#define DEBUG_ESP              //comment out to deactivate debug console

#ifdef DEBUG_ESP
  #define pDBGln(x) Serial.println(x)
  #define pDBG(x)   Serial.print(x)
#else 
  #define pDBG(...)
  #define pDBGln(...)
#endif

#include "SerialTransfer.h"
SerialTransfer myTransfer;
struct img_meta_data{
  uint16_t counter;
  uint16_t imSize;
  uint16_t numLoops;
  uint16_t sizeLastLoop;
} ImgMetaData;
uint16_t packetCounter=1;
uint16_t bufferPointer=0;
char tempImageBuffer[32000];

#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[] = "x.x.x.x";
char ftp_user[]   = "user"; 
char ftp_pass[]   = "pass";

// picture name
String picPrefix ="";
String pic_name;  

// 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
  // 115200,256000,512000,962100
  Serial2.begin(962100, SERIAL_8N1); //Receiver_Txd_pin, Receiver_Rxd_pin); // Define and start Receiver serial port
  myTransfer.begin(Serial2);

  WiFi.begin(ssid, pass);
  pDBGln("Connecting Wifi...");
  while (WiFi.status() != WL_CONNECTED) {
      delay(500);
      pDBG(".");
  }
  pDBGln("");
  pDBG("IP address: ");
  pDBGln(WiFi.localIP());
  pDBGln();

  // Initialize a NTPClient to get time
  timeClient.begin();
  // Set offset time in seconds to adjust for your timezone, for example:
  // GMT +1 = 3600, GMT +8 = 28800, GMT -1 = -3600, GMT 0 = 0
  timeClient.setTimeOffset(3600*-3);  
}

void loop(){
  if(myTransfer.available())  {
    myTransfer.rxObj(ImgMetaData, sizeof(ImgMetaData));
    pDBG("Struct Data: ");
    pDBG(ImgMetaData.counter);
    pDBG(", ");
    pDBG(ImgMetaData.imSize);
    pDBG(", ");  
    pDBG(ImgMetaData.numLoops);
    pDBG(", ");
    pDBG(ImgMetaData.sizeLastLoop);
    pDBG(", PacketCounter: ");
    pDBGln(packetCounter);  

    if(ImgMetaData.counter==1){  
      copyToImageBuff(MAX_PACKET_SIZE-sizeof(ImgMetaData));
    }else{
      if(ImgMetaData.counter==packetCounter){  
        if(packetCounter<ImgMetaData.numLoops){        
          copyToImageBuff(MAX_PACKET_SIZE-sizeof(ImgMetaData));
        }else if(ImgMetaData.counter==packetCounter){
          copyToImageBuff(ImgMetaData.sizeLastLoop);     
        }
      }
    }
 
    if(packetCounter>ImgMetaData.numLoops){  
      pic_name  = picPrefix;
      pic_name += getDateTime() + ".jpg";  
      FTP_upload();     
      packetCounter=1;
      bufferPointer=0;
      delay(2000);
      //while(1){}
    }  

  }else if(myTransfer.status < 0) { 
    pDBG("ERROR: ");
    if(myTransfer.status == -1)
      pDBGln(F("CRC_ERROR"));
    else if(myTransfer.status == -2)
      pDBGln(F("PAYLOAD_ERROR"));
    else if(myTransfer.status == -3)
      pDBGln(F("STOP_BYTE_ERROR"));
  }
}
  

void copyToImageBuff(uint16_t dataLenght){
  for(int y=0;y<dataLenght;y++){
    tempImageBuffer[bufferPointer+y] = myTransfer.rxBuff[y+sizeof(ImgMetaData)];
  } 
  bufferPointer+=dataLenght;
  packetCounter++;  

  pDBG("dataLenght: ");
  pDBG(dataLenght);  
  pDBG(", bufferPointer: ");
  pDBGln(bufferPointer);
}

void printBuf(char localBuff){
  pDBG(F("Pixel Values: { "));
  for (uint16_t k=0; k<sizeof(myTransfer.rxBuff); k++){
    pDBG(myTransfer.rxBuff[k]);
    if (k < (sizeof(myTransfer.rxBuff) - 1))
      pDBG(F(", "));
    else
      pDBGln(F(" }"));
  }
}

void FTP_upload(){
  pDBG("Uploading via FTP: "); 
  ftp.OpenConnection();
  //ftp.ChangeWorkDir("/public_html/zyro/");
  //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 ); 
  pDBGln( f_name );    
  ftp.WriteData((unsigned char*)tempImageBuffer, ImgMetaData.imSize);
  ftp.CloseFile();
  // Breath, withouth delay URL failed to update.
  pDBGln("Done... Waiting next transfer...");
  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);
    pDBGln(formattedDate);  
    pDBG("DATE: ");
    pDBGln(dayStamp);
    pDBG("HOUR: ");
    pDBGln(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
  curDateTime += formattedDate.substring(17,19); //seconds
  return curDateTime;
}

Slave (ESP32Cam) serially sends its photo to the Master ESP32
Original coder used FTP library in my case I wont have wifi at the location and have the SIM 4/5G hence here is the issue to solve.

I need to convert this code block to use 4/5G rather than wifi/FTP.

void FTP_upload() {
 pDBG("Uploading via FTP: ");
  ftp.OpenConnection();
  ftp.ChangeWorkDir("/domain.com/Camera");
  //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 );
  pDBGln( f_name );
  ftp.WriteData((unsigned char*)tempImageBuffer, ImgMetaData.imSize); //void WriteData (unsigned char * data, int dataLength);
  ftp.CloseFile();
  // Breath, withouth delay URL failed to update.
  pDBGln("Done... Waiting next transfer...");
  delay(100);
}

My 4/5G GPRS FTP Upload Code Block

void FTP4G_upload() { 
  String res; 
  Serial.println("Uploading via FTP: ");

//FTP Start
  modem.sendAT("+CFTPSSTART"); //used to start FTP
  modem.waitResponse(1000L);
  pDBG(res);
  if (modem.waitResponse(10000L) != 0) { //x is not equal to y
     pDBGln("+CFTPSSTART failed to start");
     delay(500);
     pDBGln("+CFTPSSTART retrying...");
     modem.sendAT("+CFTPSSTART"); //try again
     modem.waitResponse(10000L);
     pDBG(res);
     return;
  }
  else {
    (modem.waitResponse() == 0); 
    pDBG(res);
    pDBGln("+CFTPSSTART started");
  }

  
//FTP Login
  modem.sendAT("+CFTPSLOGIN=\"", ftp_serverIP, ("\","), 21,(",\""), ftp_user,("\",\""), ftp_pass,("\","),0); 
  if (modem.waitResponse(2000L) != 1) {
     pDBGln("+CFTPSLOGIN Fail");
  }
  //modem.waitResponse(4000L, res);
  //pDBG(res);
  
//Set FTP Type to binary
  modem.sendAT("+CFTPTYPE=", ftp_type);
  modem.waitResponse(1000L, res);
  pDBG(res);
  if (modem.waitResponse(10000L) != 0) {
     pDBGln("+CFTPSTYPE Fail");
  }

//Change Directory
  //modem.sendAT("+CFTPSCWD\"", ftp_pwd,("\"")); //e.g. AT+CFTPSCWD=”/lu.liu/TEST7600”
  modem.sendAT("+CFTPSCWD=\"", ftp_pwd,("\"")); //e.g. AT+CFTPSCWD /domain.com/Camera
  modem.waitResponse(1000L, res);
  pDBG(res);
  if (modem.waitResponse(10000L) != 0) {
     pDBGln("+CFTPSLIST Fail");
  }

//List Files
  modem.sendAT("+CFTPSLIST=\"", ftp_list,("\"")); //AT+CFTPSLIST=”/”
  modem.waitResponse(1000L, res);
  pDBG(res);
  if (modem.waitResponse(10000L) != 0) {
     pDBGln("+CFTPSLIST Fail");
  }

//Write File
  const char *f_name = pic_name.c_str();
  //ftp.NewFile( f_name );
  pDBGln( f_name );
  //ftp.WriteData((unsigned char*)tempImageBuffer, ImgMetaData.imSize);
  char ftp_data = ((unsigned char*)tempImageBuffer, ImgMetaData.imSize); //e.,g AT+CFTPSPUT=”<filepath>”[,<data_len>[,<rest_size>]]
  //modem.sendAT("+CFTPSPUT=\"", ftp_data,("\""));  //e.g. AT+CFTPSPUT=”/LK/LM/LO.TXT”
  //modem.sendAT("+CFTPSPUT=\"", ftp_pwd,("/"), f_name, ftp_data,("\""));
 
  //modem.sendAT("+CFTPSPUT=\"", ftp_pwd,("/"), f_name, ("\""));
  // pDBG(res);
  //if (modem.waitResponse(10000L) != 0) {
  //   pDBGln("+CFTPSPUT Fail");
  //}

  //modem.sendAT("+CFTPSPUTFILE=\"", ftp_pwd,("/"), f_name,("\","),1);  //e.g. AT+CFTPSPUT=”/LK/LM/LO.TXT” correct but there is no file to FTP

//Logout of the FTP server
  modem.sendAT("+CFTPSLOGOUT"); //used to stop FTP AT+CFTPSLOGOUT
  modem.waitResponse(10000L);
  pDBG(res);
  if (modem.waitResponse(1000L) != 0) { //x is not equal to y
     pDBGln("+CFTPSLOGOUT Failed to logout");
     delay(500);
     pDBGln("+CFTPSLOGOUT retrying...");
     modem.sendAT("+CFTPSLOGOUT"); //try again
     modem.waitResponse(10000L);
     pDBG(res);
     return;
  }
  else {
    (modem.waitResponse() == 0); 
    pDBG(res);
    pDBGln("+CFTPSLOGOUT logged out successfully");
  }
  
//Stop the FTP service
  modem.sendAT("+CFTPSSTOP");  //Check if the FTP service is running
  modem.waitResponse(1000L);
  pDBG(res);
  if (modem.waitResponse() != 0) {
     pDBG(res);
     pDBGln("+CFTPSSTOP failed to stop ");
     modem.sendAT("+CFTPSSTOP");
     modem.waitResponse(10000L);
     pDBG(res);
     return;
  }
  else {
   (modem.waitResponse() == 0); 
    pDBG(res);
    pDBGln("+CFTPSSTOP service stopped successfully");
  }
}

I have most of the code sorted output below shows it logging and if needed create a blank jpg.

17:11:29.534 -> Struct Data: 103, 25422, 104, 84, PacketCounter: 103
17:11:29.581 -> dataLenght: 246, bufferPointer: 25338
17:11:29.675 -> Struct Data: 104, 25422, 104, 84, PacketCounter: 104
17:11:29.675 -> dataLenght: 84, bufferPointer: 25422
17:11:29.675 -> FTP Upload
17:11:29.675 -> Uploading via FTP: 
17:11:29.675 -> AT+CFTPSSTART
17:11:29.722 -> +CFTPSSTART: 0
17:11:29.722 -> OK
17:11:40.727 -> +CFTPSSTART started
17:11:40.727 -> AT+CFTPSLOGIN="x.x.x.x",21,"FTPuser","xxxxx",0
17:11:40.727 -> OK
17:11:40.727 -> AT+CFTPTYPE=I
17:11:40.727 -> OK
17:11:44.515 -> +CFTPSLOGIN: 0
17:11:50.744 -> AT+CFTPSCWD="/domain.com/Camera"
17:11:50.744 -> OK
17:11:51.026 -> +CFTPSCWD: 0
17:12:00.747 -> AT+CFTPSLIST="/domain.com/Camera"
17:12:00.794 -> OK
17:12:01.496 -> +CFTPSLIST: DATA,672
17:12:01.544 -> -rw-r--r--   1 FTPuser pg1263480    21703 Oct  3 00:10 test.jpg
17:12:01.730 -> +CFTPSLIST: 0
17:12:10.781 -> 091122161129.jpg
17:12:10.781 -> AT+CFTPSLOGOUT
17:12:10.781 -> OK
17:12:10.828 -> +CFTPSLOGOUT: 0
17:12:12.789 -> OK
17:12:12.789 -> +CFTPSLOGOUT logged out successfully
17:12:12.789 -> AT+CFTPSSTOP
17:12:12.789 -> +CFTPSSTOP: 0
17:12:14.794 -> OK
17:12:14.794 -> +CFTPSSTOP service stopped successfully
17:12:18.808 -> AT+CGREG?
17:12:18.808 -> +CGREG: 0,1
17:12:18.808 -> OK
17:12:18.808 -> AT+CIPRXGET=4,0
17:12:18.808 -> +CIPRXGET: 4,0,80
17:12:18.808 -> OK
17:12:18.808 -> AT+CIPSEND=0,2
17:12:18.853 -> 

The part I dont know how to tackle or even how to start is the that the original code uses wireless/FTP and I need to use 4/5G GPRS and FTP.
Only library I can find is TinyGSM which does eventhing except the FTP side. SIM board supports AT commands and FTPS so again thats all working.

I need some guidance on how to create the file and send the image data to ftp over GPRS 4/5G. I have tried to work out each step above and now need some guidance on how to
take the ftp.WriteData((unsigned char*)tempImageBuffer, ImgMetaData.imSize); and do the same step over GPRS 4/5G / ftp.

Any guidance here, tried searching but not getting far to how I even start this.

  const char *f_name = pic_name.c_str();
  ftp.NewFile( f_name );
  ftp.WriteData((unsigned char*)tempImageBuffer, ImgMetaData.imSize); //void WriteData (unsigned char * data, int dataLength);
  ftp.CloseFile();

perhaps I missed it but what modem are you using?
how is it connected to the ESP32-CAM?

Assigned gpios so TX,RX between the master and slave using ftp all works sim 7600

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