BLYNK_WRITE() return while virtual pin has no value

Hello all, I am developing an application requires this basic flow:

-wake from sleep
-blynk_write to download a value from a virtual pin (numeric display)
-once the virtual pin is synced (verified by a flag inside the blynk_write function), increment the downloaded value by one (set to zero if >50)
-virtual write the updated incremented value back to the virtual pin
-back to sleep

This is not working because upon first boot, the virtual pin does not yet contain any value (a blank box), and so the sync never happens, flag never gets set, and the esp loops forever waiting for sync. If I replace the numeric display with a slider, it gets a zero on first startup and all works fine. However, a slider takes up a lot more room and allows for the value to be ‘accidentally modified’ from the app.

Also, to add some wierdness to this… numeric displays work fine with some other esps I have that run on mains power and do not sleep (the same method of downloading/incrementing/uploading as above). The mains powered units also have some other sensors & libraries working (baro/temp/humid)… but I don’t see how that could affect the blynk_write operation. It may also be possible that it was just luck in timing of the bootup and adding/subtracting sliders/numeric displays in the app… maybe the other libs are actually nudging the timing of blynk_write in a way that gets it to work? That doesn’t seem likely. All I know is, unlike the sleeping units, my non-sleeping units seem to get the zero from blynk_write when the vpin is empty.

It would be great if blynk_write() could return something if no value is found on the cloud (a 0 would be nice for me)… or is there maybe some library function that I missed that can be used to determine if blynk_write cannot download a value (then I can simply code it use a zero in that case).

Thanks,
Kevin

Hello.

As quick workaround you can set value to Vpin via HTTP API. Regarding no value… We need to think here. @vshymanskyy what do you think?

Here is the code in question (sorry… I’m not much of a coder).

#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#include <WiFiUdp.h>
#include <ArduinoOTA.h>
#include <BlynkSimpleEsp8266.h>
extern "C" {
  #include "user_interface.h"
}

const char deviceName[] =   "name";               // This is added to all hostnames & messages
#define pirPin              13                    // Physical pin: PIR output
#define holdEnablePin       12                    // Physical pin: "stay on" output
#define batteryMonitor                            //comment out if plugged in
char auth0[] =              "yeap";// Blynk app auth token
#define firmwareVpin        V0                    // FW OTA Button
#define armButtonVpin       V1                    // Button to arm/disarm
#define triggersVpin        V3                    // Slider to indicate # of triggers
#define batteryVpin         V4                    // Battery Voltage display
#define statusVpin          V2                    // LED to indicate status
const char ssid[] =         "yeap";            // Wifi SSID
const char pass[]=          "yeap";// Wifi WPA2 password
//IPAddress staticIP          (yeap);        // Static local IP (setup your router accordingly)
//byte mac[] =                {yeap};// Wifi MAC
const char* otaPassword =   "yeap";            // OTA password (old pass: kwaker5)
const uint16_t otaPort =    yeap;                 // OTA port, defaults 8266
int pirTimeout =            30;                   // Seconds between last PIR read and deepsleep
#define triggersMax         50                    // Set trigger count to 0 when it is > this value
int otaTimeout =            300;                  // Seconds to wait for FW before OTA cancels and sleeps
int blynkTimeout =          5;                    // Seconds to wait before restart, when attempting to connect wifi+blynk
#ifdef batteryMonitor
  float vbattLow =          3.3;                  // battery voltage for low voltage mode
  float vbattCrit =         3.0;                  // battery voltage for "all deepsleep" mode
  float vbattRatio =        4.380;                // Input voltage at max 1.0V output (approx. 4.0-4.4 for
                                                  // 330k-100k with one L-ion cell, calibrate for accuracy)
//******************  END of USER CONFIG  ********************//
////////////////////////////////////////////////////////////////
  float vbatt = 0.0;                                     // Holder for battery vbatt
#endif
const char hostNameX[] =           "ESP_";               // WiFi and OTA hostname, default "ESP-[deviceName]"
const char notifyLowBattX[] =      ": Battery Low";      // Low battery notification text
const char notifyCritBattX[] =     ": Battery Critical!";// Critical battery notification text
const char notifyOTAreadyX[] =     ": OTA Ready";        // OTA ready notification text
const char notifyOTAtimeoutX[] =   ": OTA Timeout!";     // OTA Timeout notification text
const char notifyPIRX[] =          ": Security breach!"; // Armed PIR notification text
                                                         // Declare and set the null arrays 
char hostName[sizeof(hostNameX) + sizeof(deviceName) + 1] = {0};
char notifyLowBatt[sizeof(notifyLowBattX) + sizeof(deviceName) + 1] = {0};
char notifyCritBatt[sizeof(notifyCritBattX) + sizeof(deviceName) + 1] = {0};
char notifyOTAready[sizeof(notifyOTAreadyX) + sizeof(deviceName) + 1] = {0};
char notifyOTAtimeout[sizeof(notifyOTAtimeoutX) + sizeof(deviceName) + 1] = {0};
char notifyPIR[sizeof(notifyPIRX) + sizeof(deviceName) + 1] = {0};

unsigned long lastPirHigh = 0;                           // Holder for PIR timeout timer
unsigned long otaStartTime = 0;                          // Holder for OTA timeout timer
unsigned long startConnectTime = 0;  // Holder for time when connection attemps are started
int armButton = 0;                                       // Holder for arm button state
int nTriggers = 0;                                       // Holder for # of triggers (also on app)
bool areAllSynced = 0;                                   // Blynk.syncAll() only after wake up
bool isArmButtonSet = 0;                                 // Blynk.run() until variables are downloaded
bool isTriggersSliderSet = 0;                            // ...
bool fwButton = 0;                                       // Holder for firmware button position
bool isFWbuttonSet = 0;                                  // Holder for sync finished/unfinished status
bool OTAnotificationSent = 0;                            // Holder for OTA ready notification
int state = 0;                                           // Holder for state machine variable
//////////////////////////////////////////////////////////////////////////
void setup()  
{  
  #ifdef debug
    Serial.begin(115200);
    Serial.println();
    Serial.println("Debug...");
    Serial.println("Holding enable pin high");
  #endif
  
  pinMode(holdEnablePin, OUTPUT);
  digitalWrite(holdEnablePin,HIGH);        //Make sure we stay on until we're ready to sleep
  pinMode(pirPin, INPUT);
  
  lastPirHigh = millis();                  //We've already seen PIR high, mark it now

  sprintf(hostName, "%s%s", hostNameX, deviceName); // Combine our strings
  sprintf(notifyLowBatt, "%s%s", deviceName, notifyLowBattX);
  sprintf(notifyCritBatt, "%s%s", deviceName, notifyCritBattX);
  sprintf(notifyOTAready, "%s%s", deviceName, notifyOTAreadyX);
  sprintf(notifyOTAtimeout, "%s%s", deviceName, notifyOTAtimeoutX);
  sprintf(notifyPIR, "%s%s", deviceName, notifyPIRX);
  
  WiFi.mode(WIFI_STA);                     // Configure wifi
  wifi_station_set_hostname(hostName);
  wifi_station_connect();
  Blynk.begin(auth0,ssid,pass);             // Default Blynk server
  //Blynk.begin(auth1, SSID, pass, IPAddress(XXX,XXX,XXX,XXX)); //private server, static IP saves battery
  checkConnection();                       // Connect to Wifi and Blynk
  
  //Initialize OTA server
  #ifdef debug
    Serial.print("Initializing OTA... ");
  #endif
  ArduinoOTA.setPort(otaPort);
  ArduinoOTA.setHostname(hostName);
  ArduinoOTA.setPassword((const char *)otaPassword);
  ArduinoOTA.begin();
  #ifdef debug
    Serial.println("Done!");
  #endif
}
//////////////////////////////////////////////////////////////////////////
// This function will run every time Blynk connection is established
BLYNK_CONNECTED() {
  if (!areAllSynced) {          //On first connect, sync all the Vpins
    Blynk.syncAll();
    #ifdef debug
      Serial.println("Synced All!");
    #endif
    BLYNK_LOG("Synced all");
    areAllSynced = 1;
  }
}

BLYNK_WRITE(firmwareVpin) {      // Sync firmware button from app, and set the flag
  fwButton = param.asInt();
  #ifdef debug
    Serial.print(firmwareVpin);
    Serial.print(" button reads: ");
    Serial.println(fwButton);
  #endif
  isFWbuttonSet = 1;
}
BLYNK_WRITE(armButtonVpin) {     //read arm/disarm value from app Vpin
  armButton = param.asInt();
  #ifdef debug
    Serial.print(armButtonVpin);
    Serial.print(" button reads: ");
    Serial.println(armButton);
  #endif
  isArmButtonSet = 1;
}
BLYNK_WRITE(triggersVpin) {      //Read # of triggers from app Vpin
  nTriggers = param.asInt();     //!!!!!!Won't sync with value display unless it contains value :(
  #ifdef debug
    Serial.print(triggersVpin);
    Serial.print(" nTriggers reads: ");
    Serial.println(nTriggers);
  #endif
  isTriggersSliderSet = 1;
}
//////////////////////////////////////////////////////////////////////////
void loop() {  
  Blynk.run();                   //since we're reading virtual pins, we need to loop run()

  // FW button on: send a notification, set flag, start timer
  if(isFWbuttonSet && fwButton && !OTAnotificationSent){
    #ifdef debug
      Serial.println("Sending OTA notification");
    #endif
    Blynk.notify(notifyOTAready); 
    OTAnotificationSent = 1;
    otaStartTime = millis();
  }
  // FW button on: handle OTA calls
  if(isFWbuttonSet && fwButton) {
    ArduinoOTA.handle();
    if(millis() - otaStartTime > 1000*otaTimeout)  {// OTA timeout... reset so we send another notification
      digitalWrite(holdEnablePin,LOW);
      //ESP.deepSleep(0);
      delay(500);
    }
  }
  // FW button off: do PIR routine
  if(isFWbuttonSet && isArmButtonSet && isTriggersSliderSet && !fwButton) {
    #ifdef debug
      Serial.println("No OTA... proceding with normal routine");
    #endif
    OTAnotificationSent = 0;
    doStuff();
  }
}
//////////////////////////////////////////////////////////////////////////
void doStuff()  {         //the main routine that runs after the Vpins are downloaded
    switch(state)  {
    /*  state:
     *  0 = Wake, send message(s) and turn app LED on
     *  1 = PIR off <=30sec, wait
     *  2 = PIR off >30sec, turn off app LED, deepsleep
     */
    case 0:  //Send alarm "on" stuff
      nTriggers++;
      if(nTriggers > triggersMax) nTriggers = 0; //Reset triggers if it's too big
      #ifdef debug
        Serial.println("Sending to BLYNK_LOG");
      #endif
      BLYNK_LOG("PIR Activated");
      #ifdef debug
        Serial.println("Updating Virtual Pins");
      #endif
      Blynk.virtualWrite(statusVpin,1023);
      Blynk.virtualWrite(triggersVpin,nTriggers);

      if(armButton){                //only send email/notify if armed
        #ifdef debug
          //Serial.println("Sending alarm email");
          Serial.println("Sending alarm notification");
        #endif
        //Blynk.email("porche@surewest.net", "From Blynk", stringMail);
        Blynk.notify(notifyPIR);
      }
      #ifdef debug
        Serial.println("Disconnecting Blynk");
      #endif
      Blynk.disconnect();           // Shut down wifi to save battery and get a clean adc read
      wifi_station_disconnect();
      WiFi.mode(WIFI_OFF);
      WiFi.forceSleepBegin();
      delay(1);
      #ifdef debug
        Serial.println("Disconnected");
      #endif
      state = 1; 
    break;
    
    case 1:  //Wait for PIR to timeout
      while(millis()-lastPirHigh < pirTimeout*1000){   //loop in here until PIR timeout (avoiding Blynk.run() loop again)
        if(digitalRead(pirPin)) {                      //reset timer if PIR triggers while waiting
          #ifdef debug
            Serial.print(".");
          #endif
          lastPirHigh = millis();
        }
        delay(1);                                      //must delay otherwise WDT will trigger and turn us off (side effect of turning radio off?)
      }
      #ifdef debug                                     //PIR timeout... move on
        Serial.println();
        Serial.println("PIR timed out");
      #endif
      state = 2;
    break;
    
    case 2:  //Read battery, send voltage & alarm "off" stuff, then disable
      #ifdef batteryMonitor            
        #ifdef debug
          Serial.println("Reading battery voltage...");
        #endif
        delay(100);
        vbatt = analogRead(A0);
        vbatt = vbatt * (vbattRatio / 1024.0);         // 4.2 is the nominal voltage of the 18560 battery
      #endif
      
      #ifdef debug
        Serial.println("Reconnecting to Blynk...");
      #endif
      WiFi.forceSleepWake();                           // Turn wifi on
      WiFi.mode(WIFI_STA);
      wifi_station_set_hostname(hostName);
      wifi_station_connect();
      Blynk.begin(auth0,ssid,pass);                     // Default Blynk server
      checkConnection();
      
      #ifdef batteryMonitor                            // Update battery status/notify
        Blynk.virtualWrite(batteryVpin,vbatt);
        if(vbatt <= vbattLow) {
          Blynk.notify(notifyLowBatt);
        }
        if(vbatt <= vbattCrit) {
          Blynk.notify(notifyCritBatt);
        }
      #endif
      
      #ifdef debug
        Serial.println("Sending Vpin OFF");
      #endif
      Blynk.virtualWrite(statusVpin,0);        // Turn off LED
      #ifdef debug
        Serial.println("Sending BLYNK_LOG");
      #endif
      BLYNK_LOG("Going to Sleep");
      #ifdef debug
        Serial.println("Entering Deep Sleep...");
      #endif
      yield();
      digitalWrite(holdEnablePin,LOW);
      ESP.deepSleep(0);
    break;

    default:  //This should never happen
      #ifdef debug
        Serial.println("INVALID STATE!!!");
      #endif
      digitalWrite(holdEnablePin,LOW);
      ESP.deepSleep(0);
    break;
  }
}
// Check connection to Blynk Server ////////////////////////////////
void checkConnection() {
  if(!Blynk.connected()) {           // We aren't connected
    startConnectTime = millis();     // Reset connection timer
    #ifdef debug
      Serial.print("Checking connection to: ");
      Serial.println(ssid);
    #endif
    while (!Blynk.connect()) {       // Loop until connected 
      if(millis() - startConnectTime > 1000*blynkTimeout) {// Reboot if not connected before timeout
        #ifdef debug
          Serial.println("Failed to connect... shutting down.");
        #endif
        digitalWrite(holdEnablePin,LOW);
        ESP.restart();
        delay(500);
      }
    }
    #ifdef debug
      Serial.print("Connected with IP address: ");
      Serial.println(WiFi.localIP());
      Serial.println("Setting auth1 token...");
    #endif
  }
}

If you change this line:

  // FW button off: do PIR routine
  if(isFWbuttonSet && isArmButtonSet && isTriggersSliderSet && !fwButton) {

to this…

  // FW button off: do PIR routine
  if(isFWbuttonSet && isArmButtonSet && !fwButton) {

The code will not be stuck in the loop (the OTA notify above was useful in identifying where the bug occurs). Again, if I replace the source for nTriggers with a slider instead of a value box, it works fine. I try to imagine a case where you might need blynk_write to hang when there is no value to download. I personally think it should return a 0 in this case… or something we can go off of (“nan”?).

{edit: Sorry I type too slow… I read up on the HTTP api and it gave me another idea that won’t be so bad to implement. I will just add a time out function that will virtual write the trigger count to zero if blynk_write takes longer than 5sec(?) to return. This should work fine and won’t be of much significance to battery life, since it will only occur the first bootup after adding the numeric display to the app… once there is a value it will always return something.

Thanks again Dmitry… you guys are wicked… in a good way!

Dmitry, your mention of updating via http api… does that have to do with my other post regarding bridge with sleeping devices? Does the http api update bridged virtual pins when the ‘slave hardware’ is disconnected? (unlike virtualWrite)

No. You cna just manually set value to vPIN via Http api.

Do not understand your question.