Blynk 2.0 Image Gallery Widget Datastream Setup

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!

you have to put url so datastream type will be
string

for ref

Thanks for the reply, however your video seems to reference “Blynk1.0”, which my application functions for.

I’ve tried switching the datastream to a string with no apparent change in function. Some interesting behaviour is that sometimes after requesting a picture to be taken the image gallery image will start to flicker. The image never changes or updates but will flicker until I request another photo, at which point it will go back to the same image, with no flickering.

Not my video. just browsed youtube.

kindly share your ad widget setup

The widget setup is as follows:

Name: CoopCam
Datastream PHOTO (V0)
Images:
-A list of 12 URL’s that the device points the app towards. 9 of the images are static “loading” images and 3 are photos taken by the ESP32-CAM that it cycles through while force refreshing the cache with the Blynk.Setproperty command in my code.

Datastream setup is in my main post.

What is your project about?

Its part of a bigger project, and ultimately is just adding remote control/viewing ability to a previously functioning project.

Basically, I’ve automated my chicken coop, and the design has reached a point that many are showing interest so I’ve been interested in turning it potentially into a commercial product.
It is open source, and will remain that way however so people can build their own if they desire.

Essentially it is an ATMEGA328P based main board with sensors for Water and Coop temperature as well as a Photoresistor circuit to track daylight. It has an onboard motor driver for controlling a coop door and opens/closes it based on a user settable light value. It also has relays for controlling the lights in the coop, a water heater and a circulation fan, all with user settable values.

The ESP32-CAM exists for remote monitoring of the coop. For now (functioning in Blynk1.0) it just keeps track of where the coop door is (open, closed, opening, closing) and displays appropriate LED’s in the APP as well as lets the user “override” the door open or closed. It also allows the user to take a picture inside their coop so they can see if all their chickens are inside. That way, if they think a chicken got locked out they can override the door open and the chicken can come inside.

Eventually, I’d like to have the ESP32 also receiving data from all available data sources in the coop and displaying it in the app but door and camera is my primary concern.

Here is the project in its entirety:

1 Like

will go through

1 Like

What does this 1 mean

I’m guessing you found that in the

void loadingImage( void ) {

}

function?

If so, when

loading

is true (which is set when the camera is asked to take a picture) it is supposed to rotate through a set of “loading” images, which are meant to look like a loading animation for the user so they know a picture is being uploaded. Once the ESP32 is done uploading the image via FTP, it sets the flag as false and points back at one of 3 photo locations that it rotates through.

The reason for rotating through 3 images is that I find the Blynk server holds an image in its cache, and will not flush it if the url appears the same, even if requested. So I rotate through 3 url locations so that the server will give the user the image that was just taken, as opposed to whatever is stuck in the cache.

I know this is likely a sloppy solution but I haven’t figured out a better one that works yet, the Blynk server cache seems very stubborn to get to flush its image.

So update from yesterday:

String is NOT an available data type for the Image Gallery datastream. If you set your datastream to a string it is not available as a datastream in the app for Image Gallery.

Integer seems to be the only option for Image Gallery but the documentation appears to be incomplete in the Blynk docs so I’m wondering if there have been some changes here. I did find one of my problems, which is that in Blynk 1.0, the image gallery started at image [1] while in Blynk 2.0 the gallery starts at [0].
Altering my code with the correct location pointers has my loading image rotation working again but the server still refuses to refresh the cache and the image is constantly flickering in the app.

okk…

May be problem at blynk end. try messaging BLYYNKANDROIDDEV

I wonder if I am best to make a new topic with a new tag or just tag them in this?

Probably best to raise it as an issue on GitHub.
Follow the link on the Blynk web console.

Pete.

It looks like a couple other people are having similar issues to me in the GitHub, I’ve joined that conversation.

Thanks again!

Edit: I can update this forum with any details I learn if you want to leave this open, if not I can make a new post once I have additional details.

Yes, of course!

Pete.

1 Like

Alright, a couple more updates:

The new Blynk cloud seems to be much more sensitive to Blynk.Setproperty commands in the Image Widget in comparison to the “old” Blynk. The flickering in my case is caused by this line being called every run through:

Blynk.setProperty(V0, "url", 1, coopImageURL);

I’ve added a boolean flag that is set when the app requests a photo and then is set back to false after it updates the image url in the

Blynk.setProperty(V0, "url", 1, coopImageURL);

There is no more flickering in my app so that part is solved.

The Blynk server is still refusing the flush the cache, but one issue down.

The indexing in Image Gallery, Menu and some other widgets now starts from the min value of the associated datastream.
Will add this to the docs.

Thank you very much, I think this, and revisiting my code has helped me solve the problem. It seems to be working reliably now.

I think I was getting lucky with my code in Blynk1.0 as according to docs it probably shouldn’t have worked as well as it did. I was re-arranging the images in the app using the setProperty function before but it would sometimes encounter weird glitches where it would hang on an image.

I’ve re-written my image swapping function with better pointing and allowing the image order to remain static in the app. I’ll be doing more testing but I think I’ve got it.

Thanks everyone for the help!

Updated loadingImage function:

// Changes image in Blynk app depending on camera state
void loadingImage( void ) {
if (loading) { 
  Blynk.virtualWrite(V0, (i+2));
  if (i <= 9) {
    i ++;
  }
  else if (i > 9) {
    i = 1;
  }
}
else if (!loading) {
  if (ic == 1) {
    coopImageURL = "https://coopcommandimages.000webhostapp.com/uploads/coopPic5.jpg";
  }
  else if (ic == 2) {
    coopImageURL = "https://coopcommandimages.000webhostapp.com/uploads/coopPic6.jpg";
  }
  else if (ic == 3) {
    coopImageURL = "https://coopcommandimages.000webhostapp.com/uploads/coopPic7.jpg";
  }
  if (newImage) {
  Blynk.setProperty(V0, "url", (ic-1), coopImageURL);
  Blynk.virtualWrite(V0, (ic-1));
  newImage = false;       
}
i = 1; 
}
}

Edit: Sorry, that was a slightly outdated code block, it is correct now.