AutoReConnect to Multiple WiFi APs and Multiple Blynk Servers (local/cloud-based)

Just post some examples to AutoReConnect to Multiple WiFi APs and Multiple Blynk Servers (local/cloud-based) in GitHub with some contribution by @Madhukesh

AutoReconnect to demonstrate the usage of WiFi and Blynk AutoReConnect feature for ESP8266, ESP32

Why do we need this AutoReconnect?

Imagine you have a system with mission-semi-critical functions, measuring water level and control the sump pump or doing something much more important using extensive GUI, such as medical equipments, security and/or fire-smoke alarm, etc. You normally use a software timer to poll, or even place the function in loop(). But what if another function is blocking the loop() or setup().

So your function might not be executed, and the result would be disastrous.

You’d prefer to have your function called, no matter what happening with other functions (busy loop, bug, etc.).

The best choice is to use a Hardware Timer with Interrupt in cooperation with an Input Pin Interrupt to call your function.

The catch is your function is now part of an ISR (Interrupt Service Routine), and must be lean / mean, and follow certain rules. More to read on:

https://www.arduino.cc/reference/en/language/functions/external-interrupts/attachinterrupt/

What if your to-be-called function is a little bit fat and lazy, with some delay(), waiting loops, etc. making it’s incompatible with hardware interrupt ?

The second best choice is solutions which :

  1. Use non-blocking functions in loop()
  2. Permit non-blocking AutoReConnect feature to
  • auto(re)connect to the best WiFi AP available in the AP list (according to quality: highest RSSI/reliability APs first)
  • auto(re)connect to the best Blynk server available in the Blynk-Server list (according to priority: local Blynk servers first, then Cloud-based servers next)

Design principles of AutoReConnect

The design principles are as follows:

  1. Normal functions in the loop(), as well as those called by timers which are considered mission-semi-critical, and must not be interfered or blocked by any other bad tasks, intentionally or unintentionally. This principle can be applied to any of your projects.
  2. WiFi is considered just a communications function. Being connected or not must not interfere with the mission-semi-critical tasks.
  3. Enable multiple WiFi APs in a list, so that the program can search and use the best and still available AP in case the currently used AP is out-of-service.
  4. Blynk is considered just a Graphical-User-Interface (GUI). Being connected or not must not interfere with the mission-semi-critical tasks.
  5. Enable multiple Blynk Servers (local and cloud-based servers unlimited) in a list, so that the program can search and use the best and still available Blynk Server in case the currently used server is out-of-service.

Certainly, with Blynk GUI, we can achieve many more great features, such as remote check and control, configurable test case and value , etc. when possible and available.

This can be applied in many projects requiring reliable system control, where good, bad, or no connection has no effect on the operation of the system.

Examples

#ifndef ESP32
#error This code is intended to run on the ESP32 platform! Please check your Tools->Board setting.
#endif

/* Comment this out to disable prints and save space */
#define BLYNK_PRINT Serial

#include <WiFi.h>
#include <WiFiMulti.h>
#include <BlynkSimpleEsp32.h>

#define BLYNK_CONNECT_TIMEOUT_MS      5000L

#define BLYNK_SERVER_MAX_LEN      32
#define BLYNK_TOKEN_MAX_LEN       32

typedef struct
{
  char blynk_server[BLYNK_SERVER_MAX_LEN + 1];
  char blynk_token [BLYNK_TOKEN_MAX_LEN + 1];
}  Blynk_Credentials;

Blynk_Credentials Blynk_Creds[] =
{
  { "acct1.duckdns.org",    "blynk-token1"},
  { "acct2.duckdns.org",    "blynk-token2"},
  { "192.168.2.110",        "blynk-token3"},
  { "192.168.2.112",        "blynk-token4"},
  { "blynk-cloud.com",      "blynk-token5"},
  { "blynk-cloud.com",      "blynk-token6"}
};

#define NUMBER_BLYNK_SERVERS    ( sizeof(Blynk_Creds) / sizeof(Blynk_Credentials) )

#define BLYNK_SERVER_HARDWARE_PORT    8080

#define SSID_MAX_LEN      32
#define PASS_MAX_LEN      64

typedef struct
{
  char ssid[SSID_MAX_LEN + 1];
  char pass[PASS_MAX_LEN + 1];
}  WiFi_Credentials;

WiFi_Credentials WiFi_Creds[] =
{
  { "ssid1", "pass1"},
  { "ssid2", "pass2"},
  { "ssid3", "pass3"},
  { "ssid4", "pass4"},
};

#define NUMBER_SSIDS    ( sizeof(WiFi_Creds) / sizeof(WiFi_Credentials) )

WiFiMulti wifiMulti;

// For ESP32, this better be 2000 to enable connect the 1st time
#define WIFI_MULTI_CONNECT_WAITING_MS      2000L

uint8_t status;

uint8_t connectMultiWiFi(void)
{
  Serial.println("\nConnecting Wifi...");

  int i = 0;
  status = wifiMulti.run();
  delay(WIFI_MULTI_CONNECT_WAITING_MS);

  while ( ( i++ < 10 ) && ( status != WL_CONNECTED ) )
  {
    status = wifiMulti.run();

    if ( status == WL_CONNECTED )
      break;
    else
      delay(WIFI_MULTI_CONNECT_WAITING_MS);
  }

  if ( status == WL_CONNECTED )
  {
    Serial.println("WiFi connected to after " + String(i) + " times.");
    Serial.println("SSID = " + WiFi.SSID());
    Serial.println("RSSI = " + String(WiFi.RSSI()));
    Serial.println("Channel: " + String(WiFi.channel()));
    Serial.print("IP address: ");
    Serial.println(WiFi.localIP());
  }
  else
    Serial.println("WiFi not connected");

  return status;
}

bool connectMultiBlynk(void)
{
  for (int i = 0; i < NUMBER_BLYNK_SERVERS; i++)
  {
    Blynk.config(Blynk_Creds[i].blynk_token, Blynk_Creds[i].blynk_server, BLYNK_SERVER_HARDWARE_PORT);

    if ( Blynk.connect(BLYNK_CONNECT_TIMEOUT_MS) )
    {
      Serial.println("Blynk connected to #" + String(i));
      Serial.println("Blynk Server = " + String(Blynk_Creds[i].blynk_server));
      Serial.println("Blynk Token  = " + String(Blynk_Creds[i].blynk_token));
      return true;
    }
  }

  Serial.println("Blynk not connected");
  return false;

}

void heartBeatPrint(void)
{
  static int num = 1;

  if (Blynk.connected())
  {
    Serial.print("B");
  }
  else
  {
    Serial.print("F");
  }
  
  if (num == 80) 
  {
    Serial.println();
    num = 1;
  }
  else if (num++ % 10 == 0) 
  {
    Serial.print(" ");
  }
} 

void check_status()
{
  static unsigned long checkstatus_timeout = 0;

#define STATUS_CHECK_INTERVAL     15000L

  // Send status report every STATUS_REPORT_INTERVAL (60) seconds: we don't need to send updates frequently if there is no status change.
  if ((millis() > checkstatus_timeout) || (checkstatus_timeout == 0))
  {
    // report status to Blynk
    heartBeatPrint();

    checkstatus_timeout = millis() + STATUS_CHECK_INTERVAL;
  }
}

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

  Serial.println("\nStart MultiBlynkWiFi_ESP32");

  for (int i = 0; i < NUMBER_SSIDS; i++)
  {
    wifiMulti.addAP(WiFi_Creds[i].ssid, WiFi_Creds[i].pass);
  }

  if ( connectMultiWiFi() )
  {
    connectMultiBlynk();
  }
}

void loop()
{
  if ( ( status = WiFi.status() ) != WL_CONNECTED )
  {
    if ( connectMultiWiFi() )
    {
      connectMultiBlynk();
    }
  }
  else if ( !Blynk.connected() )
  {
    connectMultiBlynk();
  }

  if ( Blynk.connected() )
     Blynk.run();

  check_status();
}

Contributions and thanks

  1. Thanks to Madhukesh for repetitively giving a lots of unusual requests and giving reasons to start this library.
4 Likes

Dear @khoih
you still give great piece of code as well as practical ideas to all of us and sincerely thank you for this.

One fast question: According to the above description:
{ connect to the best WiFi AP available in the AP list (according to quality: highest RSSI/reliability APs first) }
But looking at the given example, I cannot see how you take into consideration the RSSI value for the specified APs so to select the strongest? Sorry if I didn’t see an obvious implementation.

Thanks and Best Regards,
Mike Kranidis

1 Like

Dear @mikekgr
That’s the magical power of ESP8266 and EP32 WiFiMulti library, and I just use.

Actually, the library measures the RSSI of each AP in the list, then makes the comparison and picks the AP with the best RSSI.

See the following code snippet from …/arduino-1.8.10/hardware/esp8266com/esp8266/libraries/ESP8266WiFi/src/ESP8266WiFiMulti.cpp


for(auto entry : APlist) 
{
  if (ssid_scan == entry.ssid) { // SSID match
    known = true;
    if (rssi_scan > bestNetworkDb)
    {
      // best network
      if (sec_scan == ENC_TYPE_NONE || entry.passphrase)
      { 
         // check for passphrase if not open wlan
         bestNetworkDb = rssi_scan;
         bestChannel = chan_scan;
         bestNetwork = entry;
         memcpy((void*) &bestBSSID, (void*) BSSID_scan, sizeof(bestBSSID));
       }
     }
     break;
  }
}

You can try the code example and see the AP with best RSSI will be selected to connect to.

Regards,
KH

2 Likes

Dear @khoih,

You said WIFIMulti is non blocking, but when you define :

#define BLYNK_CONNECT_TIMEOUT_MS 5000L

if blynk disconnected, the code is blocked for 15 seconds, every 15 seconds.

#define STATUS_CHECK_INTERVAL 15000L

I think there is no way to avoid that. :thinking:

1 Like

Dear @Blynk_Coeur,

I think there is no way to avoid that. :thinking:

That’s correct. Whenever we have connection problem, we have to let the code have some time to try reconnecting.

Not blocking, I meant here, is that the code is not hanging there forever, forbidding other tasks to do something necessary, but not super-critical. If the task is super-critical, we must have been putting it inside some ISR.

That’s why we must consider a trade-off, permitting us to have a balancing calculation between

  1. Max. Time to permit Blynk/WiFi to reconnect vs Max time other task can wait by modifying worst case waiting time BLYNK_CONNECT_TIMEOUT_MS
  2. Chance of success to reconnect

The Multi-WiFi and MultiBlynk provides a good relief whenever a WiFi AP or Blynk server in the list is lost by connecting to new AP/Server right away, so that we don’t have to stay in the waiting loop, even for 3 * BLYNK_CONNECT_TIMEOUT_MS.

1 Like

To avoid hanging, better use ESP32.
Or use vituino :joy:
Or wait for blynk V2 :thinking:

1 Like

To avoid hanging, better use ESP32.

I’m not sure about this if you are still using Blynk V1 and don’t write multi-tasking code to use ESP32 multi-core. But if you have no usable AP/Server, what you can do :slight_smile:

Or wait for blynk V2 :thinking:

I hope so and think you might have some insider’s knowledge :wink:

1 Like

Yes, you have to use multitasking to avoid hanging.