Blynk.connected() is not reliable

I have a Pro subscription, and using an Adafruit HUZZAH32 Feather card. I’m also using Visual Code, PlatformIO with the Arduino framework.

I have converted two major projects from Blynk Legacy to Blynk IoT and have learned a lot. This two Blynk platforms are very different yet got much in common regarding the coding. I have spent hour after hours to make my code stable and bug-free, but I have discovered some things on my way. One of the most critical experience is that one can not trust Blynk.connected()! I have added some of my code in this message, and have done a lots of tests to confirm this issue. I’m using FreeRTOS quite extensively a have a own task just to handle Blynk.run(). The problem is that Blynk.connected() returns true but there is no WiFi. The reason I can claim this is because WiFi.RSSI() returns 0. My device is also running a small web-server which is unavailable when the WiFi fails. Even my WiFi-router is not abel to detect the device as online. But Blynk.connected() keep returning true. I’m also using ElegantOTA (I know that Blynk got an OTA, but haven’t tested it yet) and WebSerial. Could this cause the problem?

Nevertheless, the solution for me has been to run a ping() (ESP32Ping) check every minute to the local gateway (very often 192.168.10.1), and to call ESP.restart() if the ping fails.

I have to admit that Blynk.connected() do not fail very often, but it does! In my tests it has been around once every 3 days. I have now performed an installation at a factory plant in northern Norway to monitor a process on a machine. It is 500 km away from my office, and is therefore very hard to reach. I had to rest assure that the device was always online and that Blynk is connected before I did the installation. If this conditions fails, the device should perform an reboot and hopefully regain a stable state. In my tests before implementing at the factory, I luckily discovered this Blynk.connected() issue, and implemented a workaround utilizing ping.

Here are some pieces of code regarding my BlynkRun() implementation. The priority of the BlynkRun task is always the highest among the normal tasks, only a special watchdog task (not shown here) got a higher priority. In my test I print out the BlynkRunCtr, BlynkRunTaskCtr, Blynk.connected(), BlynkReconnectAttempts and WiFi.RSSI() every second just to check the status of the Blynk connection.

/*
platformio.ini:
;-------------------
[env:MyBlynkConnectedTest]

platform = espressif32
board = featheresp32
framework = arduino
upload_speed = 512000
monitor_speed = 115200

lib_deps = 
    ayushsharma82/WebSerial@^1.3.0
    ottowinter/ESPAsyncWebServer-esphome@^3.0.0
    ayushsharma82/AsyncElegantOTA@^2.2.7
    blynkkk/Blynk@^1.1.0
    marian-craciunescu/ESP32Ping@^1.7
;-------------------
*/
#include <Arduino.h>

#define BLYNK_TEMPLATE_ID "xxxxxxxxxxxx"
#define BLYNK_AUTH_TOKEN "yyyyyyyyyyyyyyyyyyyyyyyyy"

#include <BlynkSimpleEsp32.h>

const char *auth = BLYNK_AUTH_TOKEN;
const char *ssid = "YourNetworkName";
const char *pass = "YourPassword";

#define BLYNK_CONNECT_TIMEOUT 10000 // 10 seconds
#define MAX_BLYNK_RETRIES 3
#define _TMPSTRING_SIZE 128
int BlynkReconnectAttempts=0;
long BlynkRunCtr=0L;
long BlynkRunTaskCtr;
long BlynkRun_Task_overruns_ctr=0L;

/**** Piece of my test code ****/


void X_CreateBlynkRunTask(void);

void PartsOfMyInitCode(void)
{ // This is code typically placed in setup()
	Serial.begin(115200);
	//  xStartWatchdog(20); // Starts the watchdog with a 20 seconds timeout
  	Blynk.begin(auth,ssid,pass);
	//  MyWebSetup(); // Starts ElegantOTA and WebSerial
	//  xFeedWatchdog(10); // feeds the watchdog with 10 seconds timeout, also starts the Pinging
	X_CreateBlynkRunTask();
}


void PartsOfMyTestLoopCode(void)
{ // this code is part of a task that runs once a second just to print out som status information
	Serial.printf("BlynkRunCtr:%ld, BlynkRunTaskCtr:%ld, Blynk.connected():%d, BlynkReconnectAttempts:%ld, WiFi.RSSI():%d\n", 
	BlynkRunCtr, BlynkRunTaskCtr, (int)Blynk.connected(),BlynkReconnectAttempts, (int)WiFi.RSSI());
}

/**** My BlynkRun implementation ****/
void BlynkRun(void)
{
  if (Blynk.connected())
  {
   Blynk.run();
   BlynkRunCtr++;
  }
  else
  {
     char _szDateTime[MY_STR_TIME_LENGHT];
    strLocalDateTime(_szDateTime);   
    Serial.printf("BlynkRun: %s Blynk not connected!\nTrying to reconnect...\n", _szDateTime);
    BlynkReconnectAttempts++;
    while (Blynk.connect(BLYNK_CONNECT_TIMEOUT) != SUCCESS)
    {  
      if (++BlynkReconnectCtr > MAX_BLYNK_RETRIES) {
        Serial.println("BlynkRun: Blynk not responding! Rebooting now!");
        ESP.restart();
      }
      else
      {
        Serial.println("BlynkRun: New attempt to reconnect to Blynk");
      }
    }  
    strLocalDateTime(_szDateTime);
    Serial.printf("BlynkRun: %s Blynk has reconnected successfully\n",_szDateTime);
  }
}

/****  BlynkRun task implementation ****/
void X_BlynkRun_Task( void * pvParameters ){
    TickType_t xLastWakeTime;
    const TickType_t xFrequency = 100/portTICK_PERIOD_MS; // every 100 ms
    BaseType_t xWasDelayed;
    // Initialise the xLastWakeTime variable with the current time.
    xLastWakeTime = xTaskGetTickCount();
    BlynkRunTaskCtr=0;
    while(1){
        BlynkRun(); // Blynk.run
        
        if (BlynkRunTaskCtr++ % 50 == 0)
        {
            // watchdog code not included here  xFeedWatchdog(10); // feeds the watchdog every 5 seconds
        }

        // Wait for the next cycle.
        xWasDelayed =  xTaskDelayUntil( &xLastWakeTime, xFrequency );
        if (xWasDelayed == pdFALSE)
        {
            BlynkRun_Task_overruns_ctr++;
            //Serial.println("X_BlynkRun_Task() xTaskDelayUntil caused no delayed");
        }

     }
}

/**** Starting the BlynkRun Task ****/
TaskHandle_t t_X_BlynkRun_Task;

void X_CreateBlynkRunTask(void)
{

 Serial.println("Creating BlynkRun task");

 xTaskCreate(
                    X_BlynkRun_Task,   // Function to implement the task 
                    "BlynkRun", // Name of the task 
                    4096,      // Stack size in words 
                    NULL,       // Task input parameter 
                    1,          // | portPRIVILEGE_BIT. Priority of the task. Higher number , higher priority. Max is 24 
                    &t_X_BlynkRun_Task);
}

When you look at the Device Info tab in the web console, what value is shown for the Heartbeat Interval value?

Blynk.connected() will continue to return true until the server fails to respond to a ping (internally within the Blynk library). The ping interval is dictated by the Heartbeat Interval value.

It may be that you have a long Heartbeat Interval set, and that there are times when a Blynk ping has just happened and the connection is dropped, but you aren’t giving enough time for the next ping before you’re intervening.

Pete.

@PeteKnight some devices has HB interval set to 5 seconds whilst others are set to 45 seconds. I didn’t chose those values intentionally. Why are they different and what is the optimal value? Another question that raised in my mind is if it is a bad design to call Blynk.connected() before Blynk.run(). I should maybe always call Blynk.run() first and then check with Blynk.connected()?

It depends what you are trying to achieve.

No, the purpose of using Blynk.connected before Blynk.run is to avoid Blynk.run being called when the device tries to re-connect.
Otherwise Blynk.run will attempt to make a connection to the Blynk server and this will stop all code execution during this period (which defaults to around 18 seconds) but this can be overridden (within reason) by specifying a shorter default using Blynk.connect(timeout in ms) in void setup.
You’re already setting this to 10 seconds, so if you don’t want constant 10 second delays in that task thread when Blynk isn’t connected then stick with the current approach.

Pete.

Thank you for the help @PeteKnight