ESP32-CAM saves to SPIFFS fails to FTP

Hi, my app has two tasks:
Task 1- Serves to capture an external condition that will trigger the picture and store it on a SPIFFS partition.
Task 2- Constantly scan the SPIFFS partition and posts images it finds to a FTP server.

Condition:
Task 1 runs successfully and saves images to SPIFFS without a hint.
Task 2 crashes as on the following example condition below:

FILE: /_20210208135805.jpg SIZE: 19065
Uploading via FTP: /4c99bac40a24_20210208135805.jpg, Size= 19065 Kb
Guru Meditation Error: Core 1 panic’ed (LoadStoreError). Exception was unhandled.

The problem is located at: postPicture();

Source code as below:

const char* mySSID[]   = {"WiFi1","WiFi2","WiFi3"};
const char* myPASS[]   = {"pass1","pass2","pass3"}; 
static byte goodWiFi=0;

#include <WiFi.h>
#include <WiFiClientSecure.h>
WiFiClientSecure webClient;

#include <NTPClient.h>
#include <WiFiUdp.h>
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP);

String devid;
String newLine  = "\r\n";
const char* filePath = "/";

//VideoWebServer
#include "esp_timer.h"
#include "img_converters.h"
#include "fb_gfx.h"
#include "esp_http_server.h"
#include "soc/soc.h"            //disable brownout problems
#include "soc/rtc_cntl_reg.h"   //disable brownout problems
#define PART_BOUNDARY "123456789000000000000987654321"
static const char* _STREAM_CONTENT_TYPE = "multipart/x-mixed-replace;boundary=" PART_BOUNDARY;
static const char* _STREAM_BOUNDARY = "\r\n--" PART_BOUNDARY "\r\n";
static const char* _STREAM_PART = "Content-Type: image/jpeg\r\nContent-Length: %u\r\n\r\n";
httpd_handle_t stream_httpd = NULL;
byte enebleVideoWeb    = 16;

#include "esp_camera.h"
#include "FS.h"
#include "SPI.h"
#include "SPIFFS.h"
#include "driver/rtc_io.h"
bool formatSpiffs = false;

// FTP Client Lib
#include "ESP32_FTPClient.h"
// FTP Server credentials
char ftp_server[] = "ftp.myserver.com";
char ftp_user[]   = "user@myserver.com";
char ftp_pass[]   = "ftpPass";

// Select camera model
#define CAMERA_MODEL_AI_THINKER
#include "camera_pins.h"

#define TIME_TO_SLEEP  20           //time ESP32 will go to sleep (in seconds)
#define uS_TO_S_FACTOR 1000000ULL   //conversion factor for micro seconds to seconds */

int postOrder = 0;  //0=FIFO, 1=LIFO

void picTrigger( void * parameter );
void postPicture( void * parameter );
void takePicture();
void listFilesInDir(File dir, int numTabs);

void setup() {
  Serial.begin(115200);
  Serial.println("Booting...");
  pinMode(enebleVideoWeb,INPUT);
  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;
  config.frame_size = FRAMESIZE_SVGA;
  config.jpeg_quality = 10;
  config.fb_count = 1;

#if defined(CAMERA_MODEL_ESP_EYE)
  pinMode(13, INPUT_PULLUP);
  pinMode(14, INPUT_PULLUP);
#endif

  //initialize camera
  esp_err_t err = esp_camera_init(&config);
  if (err != ESP_OK)   {
    Serial.printf("Camera init failed with error 0x%x", err);
    return;
  }
  
  if(digitalRead(enebleVideoWeb)==0){
    connectWiFi();
    Serial.print("Camera Stream Ready! Go to: http://");
    Serial.print(WiFi.localIP());
    // Start streaming web server
    startCameraServer();    
  }else{
    if (!SPIFFS.begin(true)) {
      Serial.println("An Error has occurred while mounting SPIFFS");
      delay(2000);
      ESP.restart();
    }else if(formatSpiffs){
      if ( SPIFFS.format() ) {
        Serial.println("SPIFFS formatted successfully");
      } else {
        Serial.println("Error formatting");
      }
    }
  
xTaskCreatePinnedToCore(
    picTrigger,     /* Function to implement the task */
    "picTrigger",   /* Name of the task */
    10000,          /* Stack size in words */
    NULL,           /* Task input parameter */
    1,              /* Priority of the task */
    NULL,           /* Task handle. */
    0);             /* Core where the task should run */      


xTaskCreatePinnedToCore(
    postPicture,        /* Function to implement the task */
    "postPicture",      /* Name of the task */
    10000,              /* Stack size in words */
    (void*)&postOrder,  /* Task input parameter */
    1,                  /* Priority of the task */
    NULL,               /* Task handle. */
    1);                 /* Core where the task should run */         
  }
}

void loop() {}

void picTrigger( void * parameter ){
  while(1){
    takePicture();
    vTaskDelay(20000);
  }   
}

void postPicture( void * parameter ){
  while(1){
    Serial.print("Post Method: ");
    Serial.println(*((int*)parameter));
    Serial.printf("Listing directory: %s\n", filePath);
    File root = SPIFFS.open(filePath,FILE_READ);
    File file = root.openNextFile();
    if(WiFi.status() != WL_CONNECTED){
      connectWiFi();
    }
    while(file){
      if(file.size()>0){
        Serial.print("FILE: ");
        Serial.print(file.name());
        Serial.print("  SIZE: ");
        Serial.println(file.size());

        String imageBuffer;
        while (file.available()){
          imageBuffer += char(file.read());
        }

        //test file buffer OK
        //File myFile = fs.open("/myImage.jpg", FILE_WRITE);
        //myFile.write((const uint8_t*)imageBuffer.c_str(),file.size());     
        //myFile.close();  
      
        if(WiFi.status() == WL_CONNECTED){
          Serial.print("Uploading via FTP: ");
          ESP32_FTPClient ftp (ftp_server, ftp_user, ftp_pass);
          ftp.OpenConnection();

          if(ftp.isConnected()){
            //Create a file and write the image data to it;
            ftp.InitFile("Type I");
            ftp.ChangeWorkDir("/"); // change it to reflect your directory
            String fName = file.name();
            fName = "/" + getMAC() + fName.substring(1);
            const char *f_name = fName.c_str();
            ftp.NewFile( f_name );
            Serial.print(f_name);
            Serial.print(", Size= ");
            Serial.print(file.size());
            Serial.println(" Kb");
            const char *buf = imageBuffer.c_str();
            ftp.WriteData((uint8_t*)buf,file.size());    //(uint8_t*)imageBuffer.c_str()
            ftp.CloseFile();

            // Breath, withouth delay URL failed to update.
            Serial.println("Done... Standing by for next shot...");
            vTaskDelay(100); 
          
            ftp.CloseConnection();
            deleteFile(SPIFFS,file.name());
          }
        }else{
          Serial.println(" Failed.");  
        }
        break;  
      }
      file = root.openNextFile();
    }
    file.close();

    // Get all information of SPIFFS
 
    unsigned int totalBytes = SPIFFS.totalBytes();
    unsigned int usedBytes = SPIFFS.usedBytes();
 
    Serial.println("===== File system info =====");
 
    Serial.print("Total space:      ");
    Serial.print(totalBytes);
    Serial.println("byte");
 
    Serial.print("Total space used: ");
    Serial.print(usedBytes);
    Serial.println("byte");
 
    Serial.println();
 
    // Open dir folder
    File dir = SPIFFS.open("/");
    // List file at root
    listFilesInDir(dir,1);
    dir.close(); 
  }
}

void takePicture(){
  //take new image
  camera_fb_t * fb = NULL;
  //obtain camera frame buffer
  fb = esp_camera_fb_get();
  if (!fb) {
    Serial.println("Camera capture failed");
    Serial.println("Exiting now"); 
    while(1);   //wait here as something is not right
  }

  //generate file path
  String path = String(filePath) + getGlobalPicName();  

  //create new file
  File file = SPIFFS.open(path.c_str(), FILE_WRITE);
  if(!file){
    Serial.println("Failed to create file");
    Serial.println("Exiting now"); 
    while(1);   //wait here as something is not right    
  } else {
    file.write(fb->buf, fb->len); 
    Serial.printf("Image saved: %s\n", path.c_str());
  }
  file.close();
  //return camera frame buffer
  esp_camera_fb_return(fb); 
}

Due to text size restrictions of the forum I had to remove most of non relevant code.
I will post whatever remaining code is required on request.

There is no code that synchronizes the two threads. Ensure that the upload doesn’t happen while the other thread saves the image.

Hi, I thought about that as a potential source for the problem as well.
I tested this scenario by taking pictures faster than they can be posted so pictures accumulate.
As the current implementation postPicture() always choose the oldest and the problem happens anyway I understood that was not the source for the problem.

Eventually I solved that specific Guru Meditation changing the line below:

From: ftp.WriteData((uint8_t*)imageBuffer.c_str(),file.size());
To: ftp.WriteData((uint8_t*)imageBuffer.c_str(),imageBuffer.length());

It was trial and error but somehow it solved the problem.
I also devised some code to avoid the postPicture() task to try to post a picture at the same time it is been saved, still testing this one.
Thanks for your contribution.

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