Hi. I'm looking for a low cost way to automatically store and view images online that are taken from my esp32 cam. The camera would be on a different WiFi network from where I want to view the image. Ideally, I'd like to view from my phone. Is this possible with the Arduino IoT cloud?
Thanks!
It's likely possible. However, split the total project into smaller parts. Then search for partial solutions for those parts. Finally integrate the parts into one system.
1 Like
I have several cams FTP their images to a Raspberry Pi (RPi). The RPi takes care of image processing and placing the images on our website.
Here is my 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; ////YUV422,GRAYSCALE,RGB565,JPEG
config.frame_size = FRAMESIZE_UXGA; // FRAMESIZE_ + QVGA|CIF|VGA|SVGA|XGA|SXGA|UXGA
/*
FRAMESIZE_UXGA (1600 x 1200)
FRAMESIZE_QVGA (320 x 240)
FRAMESIZE_CIF (352 x 288)
FRAMESIZE_VGA (640 x 480)
FRAMESIZE_SVGA (800 x 600)
FRAMESIZE_XGA (1024 x 768)
FRAMESIZE_SXGA (1280 x 1024)
*/
config.jpeg_quality = 10; //0-63 lower number means higher quality
/*
* The image quality (jpeg_quality) can be a number between 0 and 63.
* A lower number means a higher quality.
* However, very low numbers for image quality,
* specially at higher resolution can make the ESP32-CAM to crash or it may not be able to take the photos properly.
*
* So, if you notice that the images taken with the ESP32-CAM are cut in half, or with strange colors,
* that’s probably a sign that you need to lower the quality (select a higher number).
*/
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() {}
Oh, the code will also allow me to remotly change camera settings.
2 Likes
Thanks Idahowalker. FTP to RPi seems to be the way to go.
This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.