Hello Everyone,
I was wondering if anyone has played much with Image Gallery widget with Blynk2.0.
I’ve got a previously functioning project on Blynk1.0 up and running with Blynk2.0, including Edgent being used for provisioning. My project uses the image gallery to display pictures taken with the ESP32-CAM that the app is connected to.
Currently, I have the Datastream for the widget setup as follows:
Virtual Pin 0
Integer datatype
Units none
Min 0 Max 12 values (I have 12 images I cycle through, 3 of which are regularly replaced).
Default value 3
Here is my code:
// Fill-in information for your Blynk connection here
#define BLYNK_TEMPLATE_ID "xxxx"
#define BLYNK_DEVICE_NAME "xxxx"
#define BLYNK_HEARTBEAT 30
#define BLYNK_FIRMWARE_VERSION "0.1.0"
#define BLYNK_PRINT Serial
//#define BLYNK_DEBUG
#define APP_DEBUG
// Pin Defines
#define PHOTOCLICK V5
#define LED 4
#define DOORUP V7
#define DOORDOWN V6
// Uncomment your board, or configure a custom board in Settings.h
//define USE_WROVER_BOARD
// Libraries
#include "BlynkEdgent.h"
#include "esp_camera.h"
#include "Arduino.h"
#include <WiFi.h>
#include <WiFiClient.h>
#include <ESP32_FTPClient.h>
#include "soc/soc.h" // Disable brownout problems
#include "soc/rtc_cntl_reg.h" // Disable brownout problems
// Blynkv LED setup
WidgetLED led1(V1);
WidgetLED led2(V2);
WidgetLED led3(V3);
WidgetLED led4(V4);
// 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
//FTP server information and setup
char ftp_server[] = "files.000webhost.com";
char ftp_user[] = "xxxxx";
char ftp_pass[] ="xxxxx";
ESP32_FTPClient ftp (ftp_server,ftp_user,ftp_pass, 5000, 2); // you can pass a FTP timeout and debbug mode on the last 2 arguments
// Variables
bool takePhoto = false;
bool loading = false;
String displayImage = "https://coopcommandimages.000webhostapp.com/uploads/1.png";
String coopImageURL = "https://coopcommandimages.000webhostapp.com/uploads/1.png";
String coopImage = "coopPic.jpg";
int i = 1;
int ic = 1;
int imageFlip = 1;
char coopRx; // Info received from CoopCommand
bool newDataRx = false; //has CoopCam received new data from CoopCommand
void setup() {
// Startup Serial, Blynk, Blynk Timers and initial imamge. Disable brownout detector
Serial.begin(115200);
delay(50);
BlynkEdgent.begin();
pinMode(LED, OUTPUT);
timer.setInterval(1000,coopCom);
timer.setInterval(250,loadingImage);
Blynk.setProperty(V0, "url", 1, coopImageURL);
Blynk.virtualWrite(V0, 1);
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); //disable brownout detector
// Configure the Camera
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;
// Image settings depending on PSRAM availability
if(psramFound()){
config.frame_size = FRAMESIZE_SVGA; // FRAMESIZE_ + QVGA|CIF|VGA|SVGA|XGA|SXGA|UXGA
config.jpeg_quality = 20;
config.fb_count = 2;
}
else {
config.frame_size = FRAMESIZE_VGA;
config.jpeg_quality = 20;
config.fb_count = 1;
}
// Initialize Camera
esp_err_t err = esp_camera_init(&config);
if (err != ESP_OK) {
return;
}
}
// Changes image in Blynk app depending on camera state
void loadingImage( void ) {
if (loading) {
displayImage= "https://coopcommandimages.000webhostapp.com/uploads/";
displayImage+= i;
displayImage+= ".png";
Blynk.setProperty(V0, "url", 1, displayImage);
if (i <= 6) {
i ++;
}
else if (i > 6) {
i = 1;
}
}
else if (!loading) {
if (ic == 1) {
coopImageURL = "https://coopcommandimages.000webhostapp.com/uploads/coopPic2.jpg";
}
else if (ic == 2) {
coopImageURL = "https://coopcommandimages.000webhostapp.com/uploads/coopPic3.jpg";
}
else if (ic == 3) {
coopImageURL = "https://coopcommandimages.000webhostapp.com/uploads/coopPic4.jpg";
}
Blynk.setProperty(V0, "url", 1, coopImageURL);
Blynk.virtualWrite(V0, 1);
i = 1;
}
}
// Sends a new image to the FTP server
void sendPhoto ( void ) {
if (takePhoto) {
camera_fb_t * fb = NULL;
// Take Picture with Camera
fb = esp_camera_fb_get();
digitalWrite(LED, LOW);
delay (50);
if(!fb) {
// Serial.println("Camera capture failed");
ESP.restart();
return;
}
if (imageFlip == 1) {
ftp.OpenConnection();
// Create the new file and send the image
ftp.ChangeWorkDir("/public_html/uploads/");
ftp.InitFile("Type I");
ftp.NewFile("coopPic2.jpg");
ftp.WriteData( fb->buf, fb->len );
ftp.CloseFile();
ftp.CloseConnection();
takePhoto = false;
imageFlip = 2;
ic = 1;
esp_camera_fb_return(fb);
Serial.print('N');
}
else if (imageFlip == 2) {
ftp.OpenConnection();
// Create the new file and send the image
ftp.ChangeWorkDir("/public_html/uploads/");
ftp.InitFile("Type I");
ftp.NewFile("coopPic3.jpg");
ftp.WriteData( fb->buf, fb->len );
ftp.CloseFile();
ftp.CloseConnection();
takePhoto = false;
imageFlip = 3;
ic = 2;
esp_camera_fb_return(fb);
Serial.print('N');
}
else if (imageFlip == 3) {
ftp.OpenConnection();
// Create the new file and send the image
ftp.ChangeWorkDir("/public_html/uploads/");
ftp.InitFile("Type I");
ftp.NewFile("coopPic4.jpg");
ftp.WriteData( fb->buf, fb->len );
ftp.CloseFile();
ftp.CloseConnection();
takePhoto = false;
imageFlip = 1;
ic = 3;
esp_camera_fb_return(fb);
Serial.print('N');
}
}
}
// Serial communication with CoopCommand board
void coopCom ( void ) {
if (Serial.available() > 0) {
coopRx = Serial.read();
newDataRx = true;
}
if (newDataRx == true) {
if (coopRx == 'O') { //If CoopCommand says the door is up
led1.on();
led2.off();
led3.off();
led4.off();
newDataRx = false;
}
if (coopRx == 'S') { //If CoopCommand says the door is down
led1.off();
led2.on();
led3.off();
led4.off();
newDataRx = false;
}
if (coopRx == 'U') { //If CoopCommand says the door is opening
led1.off();
led2.off();
led3.on();
led4.off();
newDataRx = false;
}
if (coopRx == 'D') { //If CoopCommand says the door is closing
led1.off();
led2.off();
led3.off();
led4.on();
newDataRx = false;
}
}
}
// Put the door up
BLYNK_WRITE(DOORUP) {
Serial.print('U');
}
// Put the door down
BLYNK_WRITE(DOORDOWN) {
Serial.print('D');
}
// Take a photo and display it in the app
BLYNK_WRITE(V5) {
digitalWrite(LED, HIGH);
Serial.print('L');
takePhoto = true;
loading = true;
loadingImage();
timer.setTimeout(250, sendPhoto);
timer.setTimeout(3000, []()
{
loading = false;
loadingImage();
});
}
// Sync app and device when connected
BLYNK_CONNECTED() {
Blynk.syncAll();
}
void loop() {
BlynkEdgent.run();
}
In Blynk 1.0, it was simply set as a V0 virtual pin, with the list of available image URLs in the app. When I request a picture is taken, it cycles through images 1-9 which are a series of “loading” images that lets the user know a picture is being taken and uploaded. When the ESP32-CAM uploads the image, it cycles between 1 of 3 different locations and then points the app at the new image, while using Blynk.setProperty on that image to force a cache flush on the image to ensure the user was viewing a new image and cycling through 3 images seemed to be enough that it would definitely wipe the cache.
With Blynk2.0, The app will not cycle the images, nor update them. If I go into “stopped” mode and look at the image list I see the new images are in the thumbnail view but don’t go to the live app view and the app never cycles between the images, nor shows any of the “loading” images.
I’m guessing I’ve set the datastream up wrong, or that Blynk2.0 uses the widget differently and I’ll need to change my code but there isn’t a lot of info available yet for this widget so I thought I would reach out here.
Thanks!