Run logic even when the Blynk server not available

Hi,
I’m building a solution with internal logic like “open/close valve by schedule”, “open/close window by temperature”. And I’d like to use Blynk as a GUI (display stats, configure parameters).
Unfortunately most of source codes I’ve seen look like “always connected to the Blynk server. No connection - no need to do anything”. That’s fine for sensors (like weather station) but not good for the actuating mechanism.

Looking to the forums I’ve found some similar questions with complains like “if the Blynk server is not available, logic starts working much slower, becomes virtually unstable” and so on. But these posts are quire old (2016, 2017).
Is this problem solved?
Is there any best practice how to make the program run its logic without degradation when the Blynk server is not connected?

I need your advises/recommendations for:

  • I configure schedule and temperature thresholds in the Blynk app
  • ESP8266 connects to the Blynk server, receives this configuration data, stores it in its local EEPROM.
  • if ESP8266 connected to the Blynk server it sends current temperature to it and can be reconfigured, pushed to open/close manually
  • it ESP8266 not connected it just process its own logic (open/close by schedule or temperature)
  • when the Blynk server becomes available ESP starts to communicate with it (sends temperature, receives configuration and commands)
  • if ESP reboots when the Blynk server is not available it uses configuration stored locally in its EEPROM and still proceed its the logic
  • it should survive either WiFI is not connected (router down) or the Blynk server is not available (router up, ISP down)

I also need your advice on the initial configuration.
Something like:

  1. no configuration in the EEPROM (no WiFi SSID defined, etc) - run a local web server where SSID, password, Blynk server, Blynk port, Blynk token can be defined
  2. Reset was pressed - clear configuration and goto 1.
    I’ve seen solutions like “run a local Web server for the configuration if not connected on start within 1 minute” but it is not suitable in my case.

Thanks for your ideas in advance.

Are you planning to do this all from one ESP8266 device, or will there be multiple devices operating multiple valves & windows and taking temperature readings etc?

Edited to add…
Is this gong to be used within your own network/home, or is it going to be used somewhere where you aren’t available to upload new code if the home owner decides to change their Wi-Fi SSID or password?
(I ask this because of your requirement for a web page to input credentials, not something that’s needed normally with a home setup).

Pete.

Actually there are 2 devices.

#1 manipulates valves by schedule. There is an option to manually open/close as well (only 1 valve)
It also calculates “ticks” from the flow meter, calculates and uploads water consumption (my statistics only, disconnections are not critical)

#2 manipulates windows by temperature (greenhouse). And a manual open/close if needed (a button on the device and a button in the Blynk app for remote). It also sends humidity and temperature to the Blynk App.

As the approach is the same I’ve combined my questions.

They are reachable more or less. I mean they should work unattended for a week. But on a weekend I come to them and upload a new software if need.

I ask this because of your requirement for a web page to input credentials, not something that’s needed normally with a home setup

Agree, it is not a must. But it is convenient because there are different SSIDs at home (development) and Greenhouse (production).
I’m afraid hardcoded values may cause:

  • Everything works fine at home
  • I change credentials and upload a “production” firmware
  • I come to the greenhouse and found it can’t connect (small mistake in the credentials…)

Yet another thought:

Some devices may be powered by batteries (say 18650). It’s a good idea to have WiFI mostly turned off and upload data/download commands by connecting to the Blynk server periodically (short connection twice an hour…)
It may even sleep until an interrupt wakes it up (signal from the flow meter). It calculates the consumption at uploads it once in 10 minutes, than goes to sleep until new watering starts…

Okay, a few comments then…

Use SPIFFS not EEPROM with the ESP8266 devices, and it’s good practice to keep the writers to a minimum to avoid ‘stressing’ the device memory. Only write a new value if it’s different from the old one as an example.

WiFiManager is what most people use to add credentials, including custom credentials such as Blynk Auth code. The library is very flexible caters for most situations. You mentioned a 1 minute delay not being suitable. This is configurable to avoid problems when the router takes a while to boot.
There is also Blynk’s dynamic provisioning, which I’ve not used.

As far as your original question is concerned, Blynk.begin will totally block all code execution until a connection to the Blynk server is established.
Blynk.config and Blynk.connect are a better solution. Blynk.config will still block code execution whilst it is trying to connect, but will then timeout and allow the rest of your code to run normally.
I’d take the following approach…
Attempt to connect to Wi-Fi. If connected then attempt to connect to Blynk. If connected then carry on as normal.
If you can’t connect to Wi-Fi then there’s no point in trying to connect to Blynk, it just wastes time.
Your logic after that depends on your preferences. If you think that the Wi-Fi isn’t likely to magically fix itself on its own then there’s not much point in trying to connect again every few seconds, so you could switch to a schedule that is much longer - every hour maybe.
If you’re connected to Wi-Fi but the Blynk server can’t be reached then either the internet is down, or the Blynk server is down. You wouldn’t expect either of these outages to last very long, so it’s probably worth re-checking once every 1-5 minutes.

The situation where people run into issues is when they have s check within the void loop to see if Blynk is connected, and if it’s not they try to reconnect. This will cause a 10 second block to code execution, followed by one loop of the void loop before trying again. That way you’re down to one void loop cycle every 10 seconds rather than hundreds or thousands of cycles per second.

Pete.

Thank you very much, Pete!
Now I see where to move.

And BTW

Do you mean WiFI Manager by tzapu? There are many different WiFi managers available in my PlatfromIO…
Every example contains a comment “and goes into a blocking loop awaiting configuration”. Hope it is just a standard comment but would like to confirm.

Yes

WiFiManager gives you the tools to handle this:

Configuration Portal Timeout

If you need to set a timeout so the ESP doesn’t hang waiting to be configured, for instance after a power failure, you can add

wifiManager.setConfigPortalTimeout(180);
which will wait 3 minutes (180 seconds). When the time passes, the autoConnect function will return, no matter the outcome. Check for connection and if it’s still not established do whatever is needed (on some modules I restart them to retry, on others I enter deep sleep)

What you wouldn’t want to do to keep looping back to the portal. You’d use a ‘carry on without Wi-Fi” mode instead.

Pete.

Thank you Pete.

Playing with Blynk.connect() I realize that I do not understand its logic.
It looks like if I use it as Blynk.connect(0) it continues immediately.
But if I use any number: Blynk.connect(1000), Blynk.connect(3000), Blynk.connect(10000) - it blocks further code execution.
What does “1000” mean? 1sec? 10 sec? I do not see any difference between 1000 and 10000 at all.

I’ve also tried to “block the Internet access” for this station on my WiFi router.
If I interrupt “normal working code execution” it starts reconnecting, It block my BlynkTimer for ~15 sec. After that everything works fie until new connection attempt in 1-5 min. That’s fine for me.
But if I restart the device when Internet access is blocked it never passes Blynk.connect. I mean

  • if Blynk was connected at the start and Internet disappeared later, this Blynk.connect causes 15 sec code execution freeze only.
  • if Blynk was not connected at all it freezes code execution forever.

I guess it might be related to DNS. If it was never connected before it hangs on my server name resolution. If it was connected earlier it uses cached DNS value.
What do you think?

Below is my console log. every dot is 1sec of me BlynkTimer.
I’ve used Blynk.connect(0) here but any other number completely freezes my code as well.
And I use a simple WiFI connection here (without WiFi Manager) to focus on Blynk connection specific only.

[1658]   Starting ASR-Guest
[7758]   WiFi connected with IP 192.168.42.13
[7758]   ~Configuring Blynk~
[7758]
    ___  __          __
   / _ )/ /_ _____  / /__
  / _  / / // / _ \/  '_/
 /____/_/\_, /_//_/_/\_\
        /___/ v0.6.1 on ESP8266

[7875]   ~Configuring timers~
.......... .......... .......... // loop works until a reconnect attempt 
..[40000]   Connecting Blynk // and freezes
[762122] NTP time: Sun Nov 24 07:50:39 2019  // here I enabled Internet
[762122] Connecting to XXX.ru:9443
[764074] Certificate OK
[764272]   Can't connect BLYNK
.[764277]   Connecting Blynk
[767278]   Can't connect BLYNK // That's expected for Blynk.connect(0) 
....... .......... ..........
.......... .[804277]   Connecting Blynk // new attempt
[804279] NTP time: Sun Nov 24 07:51:21 2019
[804279] Connecting to XXX.ru:9443
[806263] Certificate OK
[806462] Ready (ping: 2ms).
[806769]   Blynk connected
......... ..........

Sketch fragments:

void beakon() {
  static int num=1;
  Serial.print(".");
  blinkLED(1,1);
  if (num % 10 == 0) {
    Serial.print(" ");
  }
  if (++num > 30) {
    Serial.println();
    num=1;
  }
  terminal.println(millis());
  terminal.flush();
} // beakon

#define BLYNK_RECONNECT 40*1000
void blynkReconnect() {
  static unsigned int lastconnect = -1;
  if (!Blynk.connected() && (millis() - lastconnect > BLYNK_RECONNECT)) {
    lastconnect = millis();
    BLYNK_LOG1("  Connecting Blynk");
    Blynk.connect(0);
    if (Blynk.connected()) {
      BLYNK_LOG1("  Blynk connected");
      terminal.println("Blynk connected");
    } else 
      BLYNK_LOG1("  Can't connect BLYNK");
  }
} // blynkReconnect

void setup() {
  Serial.begin(9600);
  pinMode(LED_BLUE, OUTPUT);
  blinkLED(100, 3);
  delay(1000);

  Serial.printf("\n\n -= Free Heap %i =-\n\n", ESP.getFreeHeap());
  BLYNK_LOG2("  Starting ", ssid);
  WiFi.begin(ssid,pass);
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print(">");
    delay(1000);
  }
  Serial.println();
  if (WiFi.status() == WL_CONNECTED) {
    BLYNK_LOG2("  WiFi connected with IP ", WiFi.localIP());
  }
  BLYNK_LOG1("  ~Configuring Blynk~");
  Blynk.config(auth, blynk_server, blynk_port);
  BLYNK_LOG1("  ~Configuring timers~");
  timer.setInterval(1000L, beakon); // .
} // setup

void loop() {
  if (Blynk.connected()) // checks to see if connected to server
    Blynk.run(); // runs only if connected to server
  else
    blynkReconnect();
  timer.run(); // runs all the time
}

Pete.

I’ve seen this thread yesterday.
Unfortunately I still can’t understand why both Bynk.connect(1000) and Bynk.connect(3000) blocks code execution for 15 sec.
Moreover, I believe “unsigned long timeout = BLYNK_TIMEOUT_MS*3” does not relate to the case with the passed timeout. I mean connect(3000) should be 3 sec, not 9. But I have 15.

Because the Blynk library Blynk.connect(timeout) is badly explained, understood and even written, so many people have problem with the call. This will be very lengthy and need your patience and lot of tests to verify.

I’ll explain first through your example and code hereinafter:

A) Whenever Blynk.connect() is called with an argument, the Blynk.connect(int32_t timeout = BLYNK_TIMEOUT_MS*3), written in libraries/src/Blynk/BlynkProtocol.h will be called. If timeout = 0, such as in you call, if Blynk is not yet connected, the following statement will never be true.

// Never be true when timeout = 0
while ((state != CONNECTED) && (BlynkMillis() - started < timeout))
{
  run();
}

Therefore, run() will never be called. So you stay in not CONNECTED forever. So avoid calling Blynk.connect(0). All harm and no gain. From now on, it’s better to use only Blynk.connect().

The solution is :

  1. To leave it to use default value, for ESP8266 is BLYNK_TIMEOUT_MS = 3000UL => total timeout is 9s
  2. Put another call to Blynk.connect(), no argument, in setup() before exit, so that you have a chance to connect to Blynk before trying blynkReconnect() in loop().

Your code also has some logic problem in blynkReconnect().

if (!Blynk.connected() && (millis() - lastconnect > BLYNK_RECONNECT)) 
{
    ...
    Blynk.connect(/*0*/);
    ...
}

The Blynk.connect() will never be executed before 40s of BLYNK_RECONNECT as you wait until (millis() - lastconnect > BLYNK_RECONNECT) to permit calling Blynk.connect() no matter what happens.

I guess the logic must be

if (!Blynk.connected() && (millis() - lastconnect < BLYNK_RECONNECT)) 
{
    ...
    Blynk.connect();
    ...
}

So the better code will be for now:

#define BLYNK_RECONNECT 40*1000
void blynkReconnect() 
{
  static unsigned int lastconnect = -1;
  if (!Blynk.connected() && (millis() - lastconnect < BLYNK_RECONNECT))   //(millis() - lastconnect > BLYNK_RECONNECT)) 
  {
    lastconnect = millis();
    BLYNK_LOG1("  Connecting Blynk");
    Blynk.connect(/*0*/);
    if (Blynk.connected()) 
    {
      BLYNK_LOG1("  Blynk connected");
    } 
    else
    {
      BLYNK_LOG1("  Can't connect BLYNK");
    }
  }
} // blynkReconnect

void setup() {
  Serial.begin(115200);

  Serial.printf("\n\n -= Free Heap %i =-\n\n", ESP.getFreeHeap());

  #if USE_BLYNK_WM
    Blynk.begin();
  #else
    BLYNK_LOG2("  Starting ", ssid);
  
    WiFi.begin(ssid,pass);
    while (WiFi.status() != WL_CONNECTED) {
      Serial.print(">");
      delay(1000);
    }
    Serial.println();
    if (WiFi.status() == WL_CONNECTED) {
      BLYNK_LOG2("  WiFi connected with IP ", WiFi.localIP());
    }
    BLYNK_LOG1("  ~Configuring Blynk~");
    Blynk.config(auth, blynk_server, BLYNK_HARDWARE_PORT);

    Blynk.connect();
  #endif
  
  BLYNK_LOG1("  ~Configuring timers~");
  timer.setInterval(1000L, beakon); // .
} // setup

But we still have more issue to tackle in B.

B) You apparently use SSL to connect to the server. In order to check if the certificate is OK and not expired, it must get the NTP time from trusted source (Blynk is using "pool.ntp.org", "time.nist.gov"). Without the Internet, with the way the Blynk code is intentionally written now, you’re blocked forever until you get a valid timestamp.

This is the Blynk 0.6.1 code in src/BlynkSimpleESP8266_SSL.h

while (now < 100000) 
{
  delay(500);
  now = time(nullptr);
}

No way to get out to your loop() if you don’t get time from NTP server, unless you modify the code as I did in Blynk_WM library or you can modify as follows to stay maximum 15s there:

int i = 0;
while ( (i++ < 30) && (now < 100000) ) 
{
  delay(500);
  now = time(nullptr);
}

After modifying like that, you’ll have the chance to go to the loop() to execute important tasks waiting there. You will rely on the powerful Blynk.run() to reconnect to WiFi / Internet / Blynk later.

C) In your setup() code

while (WiFi.status() != WL_CONNECTED) 
{
  Serial.print(">");
  delay(1000);
}

you’ll be stuck there forever if there is no WiFi connection. There is no chance to enter loop() at all. I suggest to to change to

int i = 0;
while ( (i++ < 20) && (WiFi.status() != WL_CONNECTED) )
{
  Serial.print(">");
  delay(1000);
}

to stay there maximum 20s. The Blynk.run() will then have a chance to auto-reconnect WiFi and Blynk later if you let it run.
Current you block it from running by:

void loop() {
  if (Blynk.connected()) // checks to see if connected to server
    Blynk.run(); // runs only if connected to server
  else
    blynkReconnect();
  timer.run(); // runs all the time
}

So the best way is letting the famous clean loop() wins.

void loop() 
{
  Blynk.run(); // always runs to permit reconnecting to server
  timer.run(); // runs all the time
}

The blynkReconnect() is not necessary anymore and will be deleted.
Finally, the code will be:

#define BLYNK_PRINT Serial
#define BLYNK_DEBUG true

#include <ESP8266WiFi.h>

//#define USE_BLYNK_WM   true
#define USE_BLYNK_WM   false

#define USE_SSL     true

#if USE_BLYNK_WM
  #if USE_SSL
    #include <BlynkSimpleEsp8266_SSL_WM.h>        //https://github.com/khoih-prog/Blynk_WM
  #else
    #include <BlynkSimpleEsp8266_WM.h>            //https://github.com/khoih-prog/Blynk_WM
  #endif
#else
  #if USE_SSL
    #include <BlynkSimpleEsp8266_SSL.h>        
  #else
    #include <BlynkSimpleEsp8266.h>       
  #endif
#endif

#if !USE_BLYNK_WM
  #define USE_LOCAL_SERVER    true
  
  // If local server
  #if USE_LOCAL_SERVER
    char blynk_server[]   = "xxx.duckdns.org";  // or Blynk cloud
  #else
    char blynk_server[]   = "";
  #endif

char auth[]     = "***";
char ssid[]     = "***";
char pass[]     = "***";

#define BLYNK_HARDWARE_PORT     9443
#endif

BlynkTimer timer;

void beakon()
{
  static int num=1;
  
  Serial.print("B");
  
  if (num % 10 == 0) 
  {
    Serial.print(" ");
  }
  if (++num > 30) 
  {
    Serial.println();
    num=1;
  }
  
  //terminal.println(millis());
  //terminal.flush();
} // beakon

void setup() 
{
  Serial.begin(115200);

  Serial.printf("\n\n -= Free Heap %i =-\n\n", ESP.getFreeHeap());

  #if USE_BLYNK_WM
    Blynk.begin();
  #else
    BLYNK_LOG2("  Starting ", ssid);
  
    WiFi.begin(ssid,pass);
    //while (WiFi.status() != WL_CONNECTED) {
    // New
    int i = 0;
    while ( (i++ < 20) && (WiFi.status() != WL_CONNECTED) ) {
      Serial.print(">");
      delay(1000);
    }
    Serial.println();
    if (WiFi.status() == WL_CONNECTED) {
      BLYNK_LOG2("  WiFi connected with IP ", WiFi.localIP());
    }
    BLYNK_LOG1("  ~Configuring Blynk~");
    Blynk.config(auth, blynk_server, BLYNK_HARDWARE_PORT);
    
    // New
    Blynk.connect();
  #endif
  
  BLYNK_LOG1("  ~Configuring timers~");
  timer.setInterval(1000L, beakon); // .
} // setup

void loop() 
{
  Blynk.run(); // always runs to permit reconnecting to server
  timer.run(); // runs all the time
}