Esp32 cam + sim800l = ftp file upload

Hallo, this is my first message on this thread! From about one year I'm playing around with esp32 and arduino as hobbyist. Searching on the web I have Found this tutorial - use Google translate - that explains a project useful for a stuff I want to do... But
to see the code is needed a pro account and is not possible to buy a plan with a "common" credit card... I have already try to contact them but without results, Any help/suggestions?
https://digispark.ir/create-online-camera-with-esp32-cam-and-sim800l-simcom-module/
Thanks!

hi,

depending on what you are planning to use it for you may find it easier to use a "personal wifi hotspot" with your esp32cam, you can get them pretty cheap on ebay and this way your esp32cam just sees a standard wifi connection and so you do not have to deal with all the added complexities of using gsm. You then also have the option of other devices also using this wifi.

btw - i have a sketch which may be of interest as a starting point for your project

note: if you wish to connect to your esp32cam from the internet most networks do not assign a unique ip address to each device so this is not possible, in the UK Three are the only one I know of who do. you can then use a virtual fixed ip service such as noip.com

For when you are ready ESP32CAM - FTP code.

#include "sdkconfig.h" // used for log printing
#include "esp_system.h"
#include "freertos/FreeRTOS.h" //freeRTOS items to be used
#include "freertos/task.h"
#include "certs.h"
#include "esp_camera.h"
#include "soc/soc.h"           // Disable brownout problems
#include "soc/rtc_cntl_reg.h"  // Disable brownout problems
#include "driver/rtc_io.h"
#include <WiFi.h>
#include <WiFiClient.h>
#include "ESP32_FTPClient.h"
#include <PubSubClient.h>
#include <ESP32Time.h>
//
WiFiClient      wifiClient; // do the WiFi instantiation thing
PubSubClient    MQTTclient( mqtt_server, mqtt_port, wifiClient ); //do the MQTT instantiation thing
ESP32_FTPClient ftp (ftp_server, ftp_user, ftp_pass, 5000, 2);
ESP32Time       rtc;
////
//#define evtDoMQTTParse  ( 1 << 0 ) // declare an event
//EventGroupHandle_t eg; // variable for the event group handle
////
SemaphoreHandle_t sema_MQTT_KeepAlive;
SemaphoreHandle_t sema_mqttOK;
////
QueueHandle_t xQ_Message; // payload and topic queue of MQTT payload and topic
const int payloadSize = 300;
struct stu_message
{
  char payload [payloadSize] = {'\0'};
  String topic ;
} x_message;
////
int  mqttOK = 0;
bool TimeSet = false;
bool FlashMode = false;
//
void IRAM_ATTR WiFiEvent(WiFiEvent_t event)
{
  switch (event) {
    case SYSTEM_EVENT_STA_CONNECTED:
      log_i("Connected to WiFi access point");
      break;
    case SYSTEM_EVENT_STA_DISCONNECTED:
      log_i("Disconnected from WiFi access point");
      break;
    case SYSTEM_EVENT_AP_STADISCONNECTED:
      log_i("WiFi client disconnected");
      break;
    default: break;
  }
} // void IRAM_ATTR WiFiEvent(WiFiEvent_t event)
//
void IRAM_ATTR mqttCallback(char* topic, byte * payload, unsigned int length)
{
  // clear locations
  memset( x_message.payload, '\0', payloadSize );
  x_message.topic = ""; //clear string buffer
  x_message.topic.concat( topic );
  int i = 0;
  for ( i; i < length; i++)
  {
    x_message.payload[i] = ((char)payload[i]);
  }
  x_message.payload[i] = '\0';
  xQueueOverwrite( xQ_Message, (void *) &x_message );// send data to queue
} // void mqttCallback(char* topic, byte* payload, unsigned int length)
////
void setup()
{
  pinMode( GPIO_NUM_4, OUTPUT);
  digitalWrite( GPIO_NUM_4, LOW);
  //
  WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); //disable brownout detector
  //
  //eg = xEventGroupCreate(); // get an event group handle
  //
  x_message.topic.reserve( payloadSize );
  //
  xQ_Message  = xQueueCreate( 1, sizeof(stu_message) );
  //
  sema_mqttOK    =  xSemaphoreCreateBinary();
  xSemaphoreGive( sema_mqttOK );
  //
  xTaskCreatePinnedToCore( MQTTkeepalive, "MQTTkeepalive", 7000, NULL, 3, NULL, 1 );
  xTaskCreatePinnedToCore( fparseMQTT, "fparseMQTT", 9000, NULL, 4, NULL, 1 ); // assign all to core 1, WiFi in use.
  xTaskCreatePinnedToCore( fmqttWatchDog, "fmqttWatchDog", 3000, NULL, 2, NULL, 1 );
  if ( configInitCamera() )
  {
    log_i("        start camera task" );
    xTaskCreatePinnedToCore( capturePhoto_sendFTP, "capturePhoto_sendFTP", 60000, NULL, 6, NULL, 1 );
  } else {
    log_i( "       camera failed to initilize, rebooting in 5." );
    vTaskDelay( 2000 );
    ESP.restart();
  }
} // void setup()
////
void capturePhoto_sendFTP( void *pvParameters )
{
  TickType_t xLastWakeTime    = xTaskGetTickCount();
  const TickType_t xFrequency = 1000 * 5; //delay for mS
  //!!!!!Must wait for MQTT connection and proper setup of the sema_MQTT_KeepAlive!!!!
  while ( !MQTTclient.connected() )
  {
    vTaskDelay( 500 );
  }
  for (;;)
  {
    log_i( "tick");
    xSemaphoreTake( sema_MQTT_KeepAlive, portMAX_DELAY );
    if ( (wifiClient.connected()) && (WiFi.status() == WL_CONNECTED) )
    {
      camera_fb_t * fb = NULL; // pointer
      fb = esp_camera_fb_get();
      if (!fb)
      {
        log_i( "Camera capture failed" );
        esp_camera_fb_return(fb);
      } else
      {
        ftp.OpenConnection(); // try open FTP
        if ( ftp.isConnected() )
        {
          //try send file ftp
          ftp.ChangeWorkDir( ftp_path );
          ftp.DeleteFile( ftp_file_name );
          ftp.InitFile( ftp_file_type ); //"Type I"
          ftp.NewFile( ftp_file_name );
          ftp.WriteData( (unsigned char *)fb->buf, fb->len );
          ftp.CloseFile();
          ftp.CloseConnection();
        }
        esp_camera_fb_return(fb); //return the frame buffer back to the driver for reuse
      }
    }
    xSemaphoreGive( sema_MQTT_KeepAlive );
    xLastWakeTime = xTaskGetTickCount();
    vTaskDelayUntil( &xLastWakeTime, xFrequency );
  }
  vTaskDelete( NULL );
} //void capturePhoto_sendFTP( void *pvParameters )
////
void configureCameraSettings_grpE( int _lenC, int _Hmirror, int _Vflip, int _dcw, int _colorbar )
{
  sensor_t * s = esp_camera_sensor_get();
  s->set_lenc(s, _lenC);         // 0 = disable , 1 = enable
  s->set_hmirror(s, _Hmirror);   // 0 = disable , 1 = enable
  s->set_vflip(s, _Vflip);       // 0 = disable , 1 = enable
  s->set_dcw(s, _dcw);           // 0 = disable , 1 = enable
  ////s->set_colorbar(s, _colorbar); // 0 = disable , 1 = enable
}
////
void configureCameraSettings_grpD( int _gc, int _agc, int _celing, int _bpc, int _wpc, int _gma )
{
  sensor_t * s = esp_camera_sensor_get();
  s->set_gain_ctrl(s, _gc); // 0 = disable , 1 = enable
  s->set_agc_gain(s, _agc); // 0 to 30
  s->set_gainceiling(s, (gainceiling_t)_celing);  // 0 to 6
  s->set_bpc(s, _bpc);      // 0 = disable , 1 = enable
  s->set_wpc(s, _wpc);      // 0 = disable , 1 = enable
  s->set_raw_gma(s, _gma);  // 0 = disable , 1 = enable
}
////
void configureCameraSettings_grpC( int _exctl, int _aec2, int _ae, int _aec )
{
  sensor_t * s = esp_camera_sensor_get();
  s->set_exposure_ctrl(s, _exctl); // 0 = disable , 1 = enable
  s->set_aec2(s, _aec2);           // 0 = disable , 1 = enable
  s->set_ae_level(s, _ae);         // -2 to 2
  ////s->set_aec_value(s, _aec);       // 0 to 1200 CAUSES BLACK IMAGE????
}
////
void configureCameraSettings_grpB( int se, int wb, int awb, int wbmode )
{
  sensor_t * s = esp_camera_sensor_get();
  s->set_special_effect(s, se); // 0 to 6 (0 - No Effect, 1 - Negative, 2 - Grayscale, 3 - Red Tint, 4 - Green Tint, 5 - Blue Tint, 6 - Sepia)
  s->set_whitebal(s, wb);       // 0 = disable , 1 = enable
  s->set_awb_gain(s, awb);      // 0 = disable , 1 = enable
  s->set_wb_mode(s, wbmode);    // 0 to 4 - if awb_gain enabled (0 - Auto, 1 - Sunny, 2 - Cloudy, 3 - Office, 4 - Home)
}
////
void configureCameraSettings_grpA(int brightness, int contrast, int saturation )
{
  if ( ((brightness > -3) && (brightness < 3)) && ((contrast > -3) && (contrast < 3)) && ((saturation > -3) && (saturation < 3)) )
  {
    sensor_t * s = esp_camera_sensor_get();
    s->set_brightness( s, brightness ); // -2 to 2
    s->set_contrast( s, contrast );    // -2 to 2
    s->set_saturation( s, saturation );  // -2 to 2
  }
} //void configureCameraSettings()
////
void configureCameraSettings()
{
  sensor_t * s = esp_camera_sensor_get(); //see certs.h for more info
  s->set_brightness(s, -1);     // -2 to 2 **************************
  s->set_contrast(s, 0);       // -2 to 2
  s->set_saturation(s, 0);     // -2 to 2
  s->set_special_effect(s, 0); // 0 to 6 (0 - No Effect, 1 - Negative, 2 - Grayscale, 3 - Red Tint, 4 - Green Tint, 5 - Blue Tint, 6 - Sepia)
  s->set_whitebal(s, 1);       // 0 = disable , 1 = enable
  s->set_awb_gain(s, 1);       // 0 = disable , 1 = enable
  s->set_wb_mode(s, 0);        // 0 to 4 - if awb_gain enabled (0 - Auto, 1 - Sunny, 2 - Cloudy, 3 - Office, 4 - Home)
  s->set_exposure_ctrl(s, 1);  // 0 = disable , 1 = enable
  s->set_aec2(s, 0);           // 0 = disable , 1 = enable
  s->set_ae_level(s, 0);       // -2 to 2
  s->set_aec_value(s, 300);    // 0 to 1200
  s->set_gain_ctrl(s, 1);      // 0 = disable , 1 = enable
  s->set_agc_gain(s, 0);       // 0 to 30
  s->set_gainceiling(s, (gainceiling_t)0);  // 0 to 6
  s->set_bpc(s, 0);            // 0 = disable , 1 = enable
  s->set_wpc(s, 1);            // 0 = disable , 1 = enable
  s->set_raw_gma(s, 1);        // 0 = disable , 1 = enable
  s->set_lenc(s, 1);           // 0 = disable , 1 = enable
  s->set_hmirror(s, 0);        // 0 = disable , 1 = enable
  s->set_vflip(s, 0);          // 0 = disable , 1 = enable
  s->set_dcw(s, 1);            // 0 = disable , 1 = enable
  s->set_colorbar(s, 0);       // 0 = disable , 1 = enable
} //void configureCameraSettings()
////
bool configInitCamera()
{
  camera_config_t config = {}; // Stores the camera configuration parameters
  config.ledc_channel = LEDC_CHANNEL_0;
  config.ledc_timer   = LEDC_TIMER_0;
  config.pin_d0       = GPIO_NUM_5; //Y2
  config.pin_d1       = GPIO_NUM_18; //Y3
  config.pin_d2       = GPIO_NUM_19; //Y4
  config.pin_d3       = GPIO_NUM_21; //Y5
  config.pin_d4       = GPIO_NUM_36; //Y6
  config.pin_d5       = GPIO_NUM_39; //Y7
  config.pin_d6       = GPIO_NUM_34; //Y8
  config.pin_d7       = GPIO_NUM_35; // Y9
  config.pin_xclk     = GPIO_NUM_0; //XCLK
  config.pin_pclk     = GPIO_NUM_22; //PCLK
  config.pin_vsync    = GPIO_NUM_25; //VSSYNC
  config.pin_href     = GPIO_NUM_23; // HREF
  config.pin_sscb_sda = GPIO_NUM_26; //SIOD
  config.pin_sscb_scl = GPIO_NUM_27; //SIOC
  config.pin_pwdn     = GPIO_NUM_32; //PWDN
  config.pin_reset    = -1; //RESET
  config.xclk_freq_hz = 20000000;
  config.pixel_format = PIXFORMAT_JPEG; //assuming default is has PSRAM
  config.frame_size = FRAMESIZE_UXGA; // FRAMESIZE_ + QVGA|CIF|VGA|SVGA|XGA|SXGA|UXGA
  config.jpeg_quality = 10; //0-63 lower number means higher quality
  config.fb_count = 2;
  // Initialize the Camera
  esp_err_t err = esp_camera_init(&config);
  if (err != ESP_OK) {
    log_i("Camera init failed with error %d", err );
    return false;
  } else {
    configureCameraSettings();
    return true;
  }
} //void configInitCamera()
////
void connectToWiFi()
{
  int TryCount = 0;
  while ( WiFi.status() != WL_CONNECTED )
  {
    TryCount++;
    WiFi.disconnect();
    WiFi.begin( SSID, PASSWORD );
    vTaskDelay( 2500 );
    if ( TryCount == 10 )
    {
      ESP.restart();
    }
  }
  WiFi.onEvent( WiFiEvent );
}
////
void MQTTkeepalive( void *pvParameters )
{
  sema_MQTT_KeepAlive   = xSemaphoreCreateBinary();
  xSemaphoreGive( sema_MQTT_KeepAlive ); // found keep alive can mess with a publish, stop keep alive during publish
  MQTTclient.setKeepAlive( 90 ); // setting keep alive to 90 seconds makes for a very reliable connection, must be set before the 1st connection is made.
  TickType_t xLastWakeTime = xTaskGetTickCount();
  const TickType_t xFrequency = 250; //delay for ms
  for (;;)
  {
    //check for a is-connected and if the WiFi 'thinks' its connected, found checking on both is more realible than just a single check
    if ( (wifiClient.connected()) && (WiFi.status() == WL_CONNECTED) )
    {
      xSemaphoreTake( sema_MQTT_KeepAlive, portMAX_DELAY ); // !!!!!whiles MQTTlient.loop() is running no other mqtt operations should be in process!!!!!
      MQTTclient.loop();
      xSemaphoreGive( sema_MQTT_KeepAlive );
    }
    else {
      if ( !(wifiClient.connected()) || !(WiFi.status() == WL_CONNECTED) )
      {
        connectToWiFi();
      }
      connectToMQTT();
    }
    xLastWakeTime = xTaskGetTickCount();
    vTaskDelayUntil( &xLastWakeTime, xFrequency );
  }
  vTaskDelete ( NULL );
}
////
void connectToMQTT()
{
  byte mac[5]; // create client ID from mac address
  WiFi.macAddress(mac); // get mac address
  String clientID = String(mac[0]) + String(mac[4]) ; // use mac address to create clientID
  while ( !MQTTclient.connected() )
  {
    MQTTclient.connect( clientID.c_str(), mqtt_username, mqtt_password );
    vTaskDelay( 250 );
  }
  MQTTclient.setCallback( mqttCallback );
  MQTTclient.subscribe( topicOK );
  MQTTclient.subscribe( topicGrpA );
  MQTTclient.subscribe( topicGrpB );
  MQTTclient.subscribe( topicGrpC );
  MQTTclient.subscribe( topicGrpD );
  MQTTclient.subscribe( topicGrpE );
  MQTTclient.subscribe( topicFlash );
} //void connectToMQTT()
//////
void fmqttWatchDog( void * paramater )
{
  int UpdateImeTrigger = 86400; //seconds in a day
  int UpdateTimeInterval = 85000; // get another reading when = UpdateTimeTrigger
  int maxNonMQTTresponse = 3;
  TickType_t xLastWakeTime = xTaskGetTickCount();
  const TickType_t xFrequency = 60000; //delay for mS
  for (;;)
  {
    xLastWakeTime = xTaskGetTickCount();
    vTaskDelayUntil( &xLastWakeTime, xFrequency );
    xSemaphoreTake( sema_mqttOK, portMAX_DELAY ); // update mqttOK
    mqttOK++;
    xSemaphoreGive( sema_mqttOK );
    if ( mqttOK >= maxNonMQTTresponse )
    {
      log_i( "mqtt watchdog rest" );
      vTaskDelay( 200 );
      ESP.restart();
    }
    UpdateTimeInterval++; // trigger new time get
    if ( UpdateTimeInterval >= UpdateImeTrigger )
    {
      TimeSet = false; // sets doneTime to false to get an updated time after a days count of seconds
      UpdateTimeInterval = 0;
    }
  }
  vTaskDelete( NULL );
} //void fmqttWatchDog( void * paramater )
////
void fparseMQTT( void *pvParameters )
{
  struct stu_message px_message;
  for (;;)
  {
    if ( xQueueReceive(xQ_Message, &px_message, portMAX_DELAY) == pdTRUE )
    {
      xSemaphoreTake( sema_mqttOK, portMAX_DELAY );
      mqttOK = 0;
      xSemaphoreGive( sema_mqttOK );
      if ( !TimeSet)
      {
        if ( String(px_message.topic) == topicOK )
        {
          String temp = "";
          temp        = px_message.payload[0];
          temp        += px_message.payload[1];
          temp        += px_message.payload[2];
          temp        += px_message.payload[3];
          int year    =  temp.toInt();
          temp        = "";
          temp        = px_message.payload[5];
          temp        += px_message.payload[6];
          int month   =  temp.toInt();
          temp        = "";
          temp        = px_message.payload[8];
          temp        += px_message.payload[9];
          int day     =  temp.toInt();
          temp        = "";
          temp        = px_message.payload[11];
          temp        += px_message.payload[12];
          int hour    =  temp.toInt();
          temp        = "";
          temp        = px_message.payload[14];
          temp        += px_message.payload[15];
          int min     =  temp.toInt();
          rtc.setTime( 0, min, hour, day, month, year );
          log_i( "%s   rtc  %s ", px_message.payload, rtc.getTime() );
          TimeSet     = true;
        }
      }
      if ( String(px_message.topic) == topicGrpA )
      {
        //finding first letter
        String sTmp = String( px_message.payload );
        int commaIndex = sTmp.indexOf(',');
        if ( sTmp.substring(0, commaIndex) == "a" );
        {
          int commaIndex = sTmp.indexOf(',');
          int brt =  (sTmp.substring(0, commaIndex)).toInt();
          sTmp.remove( 0, (commaIndex + 1) );
          int ctrast = (sTmp.substring(0, commaIndex)).toInt();
          sTmp.remove( 0, (commaIndex + 1) ); // chop off begining of message
          int sat = (sTmp.substring(0, commaIndex)).toInt();
          configureCameraSettings_grpA( brt, ctrast, sat );
        }
      }
      if ( String(px_message.topic) == topicGrpB )
      {
        String sTmp = String( px_message.payload );
        int commaIndex = sTmp.indexOf(',');
        if ( sTmp.substring(0, commaIndex) == "b" );
        {
          int commaIndex = sTmp.indexOf(',');
          int se =  (sTmp.substring(0, commaIndex)).toInt();
          sTmp.remove( 0, (commaIndex + 1) );
          int wbb = (sTmp.substring(0, commaIndex)).toInt();
          sTmp.remove( 0, (commaIndex + 1) );
          int wba = (sTmp.substring(0, commaIndex)).toInt();
          sTmp.remove( 0, (commaIndex + 1) );
          int wbm = (sTmp.substring(0, commaIndex)).toInt();
          configureCameraSettings_grpB( se, wbb, wba, wbm );
        }
      }
      if ( String(px_message.topic) == topicGrpC )
      {
        String sTmp = String( px_message.payload );
        int commaIndex = sTmp.indexOf(',');
        if ( sTmp.substring(0, commaIndex) == "c" );
        {
          int commaIndex = sTmp.indexOf(',');
          int ex =  (sTmp.substring(0, commaIndex)).toInt();
          sTmp.remove( 0, (commaIndex + 1) );
          int aec2 = (sTmp.substring(0, commaIndex)).toInt();
          sTmp.remove( 0, (commaIndex + 1) );
          int ae = (sTmp.substring(0, commaIndex)).toInt();
          sTmp.remove( 0, (commaIndex + 1) );
          int aec = (sTmp.substring(0, commaIndex)).toInt();
          configureCameraSettings_grpC( ex, aec2, ae, aec );
        }
      }
      if ( String(px_message.topic) == topicGrpD )
      {
        String sTmp = String( px_message.payload );
        int commaIndex = sTmp.indexOf(',');
        if ( sTmp.substring(0, commaIndex) == "d" );
        {
          int commaIndex = sTmp.indexOf(',');
          int gc =  (sTmp.substring(0, commaIndex)).toInt();
          sTmp.remove( 0, (commaIndex + 1) );
          int agc = (sTmp.substring(0, commaIndex)).toInt();
          sTmp.remove( 0, (commaIndex + 1) );
          int celing = (sTmp.substring(0, commaIndex)).toInt();
          sTmp.remove( 0, (commaIndex + 1) );
          int bpc = (sTmp.substring(0, commaIndex)).toInt();
          sTmp.remove( 0, (commaIndex + 1) );
          int wpc = (sTmp.substring(0, commaIndex)).toInt();
          sTmp.remove( 0, (commaIndex + 1) );
          int gma = (sTmp.substring(0, commaIndex)).toInt();
          configureCameraSettings_grpD( gc, agc, celing, bpc, wpc, gma );
        }
      }
      if ( String(px_message.topic) == topicGrpE )
      {
        String sTmp = String( px_message.payload );
        int commaIndex = sTmp.indexOf(',');
        if ( sTmp.substring(0, commaIndex) == "e" );
        {
          int commaIndex = sTmp.indexOf(',');
          int lenC =  (sTmp.substring(0, commaIndex)).toInt();
          sTmp.remove( 0, (commaIndex + 1) );
          int Hmirror = (sTmp.substring(0, commaIndex)).toInt();
          sTmp.remove( 0, (commaIndex + 1) );
          int Vflip = (sTmp.substring(0, commaIndex)).toInt();
          sTmp.remove( 0, (commaIndex + 1) );
          int dcw = (sTmp.substring(0, commaIndex)).toInt();
          sTmp.remove( 0, (commaIndex + 1) );
          int colorbar = (sTmp.substring(0, commaIndex)).toInt();
          configureCameraSettings_grpE( lenC, Hmirror, Vflip, dcw, colorbar );
        }
      }
      if ( String(px_message.topic) == topicFlash )
      {
        FlashMode = bool(px_message.payload);
      }
    } //if ( xQueueReceive(xQ_Message, &px_message, portMAX_DELAY) == pdTRUE )
  } //for(;;)
  vTaskDelete( NULL );
} // void fparseMQTT( void *pvParameters )
////
void loop() {}
1 Like

Hi there and thanks for the kindly responses! :smiley:
I've already done a nice esp32cam alarm triggered by a PIR that through wifi sends a mqtt message and a mail with attached the captured photo.
I have done this mixing some Random Nerd Tutorial examples, and it works.

Now the next challenge is to bring this in a place without wifi coverage, and in the world the mentioned tutorial is the only one that wire together a esp32cam and a SIM800.

So if someone can share some knowledge about this, I would be happy