diff --git a/libraries/WiFi/examples/WiFiMultiAdvanced/.skip.esp32h2 b/libraries/WiFi/examples/WiFiMultiAdvanced/.skip.esp32h2
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/libraries/WiFi/examples/WiFiMultiAdvanced/WiFiMultiAdvanced.ino b/libraries/WiFi/examples/WiFiMultiAdvanced/WiFiMultiAdvanced.ino
new file mode 100644
index 00000000000..55a3e4d49a6
--- /dev/null
+++ b/libraries/WiFi/examples/WiFiMultiAdvanced/WiFiMultiAdvanced.ino
@@ -0,0 +1,64 @@
+/*
+ *  This sketch tries to connect to the best AP available
+ *  and tests for captive portals on open networks
+ *
+ */
+
+#include <WiFi.h>
+#include <WiFiMulti.h>
+#include <HTTPClient.h>
+
+WiFiMulti wifiMulti;
+
+// callback used to check Internet connectivity
+bool testConnection(){
+  HTTPClient http;
+  http.begin("http://www.espressif.com");
+  int httpCode = http.GET();
+  // we expect to get a 301 because it will ask to use HTTPS instead of HTTP
+  if (httpCode == HTTP_CODE_MOVED_PERMANENTLY) return true;
+  return false;
+}
+
+void setup()
+{
+    Serial.begin(115200);
+    delay(10);
+
+    wifiMulti.addAP("ssid_from_AP_1", "your_password_for_AP_1");
+    wifiMulti.addAP("ssid_from_AP_2", "your_password_for_AP_2");
+    wifiMulti.addAP("ssid_from_AP_3", "your_password_for_AP_3");
+
+    // These options can help when you need ANY kind of wifi connection to get a config file, report errors, etc.
+    wifiMulti.setStrictMode(false);  // Default is true.  Library will disconnect and forget currently connected AP if it's not in the AP list.
+    wifiMulti.setAllowOpenAP(true);   // Default is false.  True adds open APs to the AP list.
+    wifiMulti.setConnectionTestCallbackFunc(testConnection);   // Attempts to connect to a remote webserver in case of captive portals.
+
+    Serial.println("Connecting Wifi...");
+    if(wifiMulti.run() == WL_CONNECTED) {
+        Serial.println("");
+        Serial.println("WiFi connected");
+        Serial.println("IP address: ");
+        Serial.println(WiFi.localIP());
+    }
+}
+
+void loop()
+{
+    static bool isConnected = false; 
+    uint8_t WiFiStatus = wifiMulti.run();
+    
+    if (WiFiStatus == WL_CONNECTED) {
+        if (!isConnected) {
+            Serial.println("");
+            Serial.println("WiFi connected");
+            Serial.println("IP address: ");
+            Serial.println(WiFi.localIP());
+        }
+        isConnected = true;
+    } else {
+        Serial.println("WiFi not connected!");
+        isConnected = false; 
+        delay(5000);
+    }
+}
diff --git a/libraries/WiFi/src/WiFiMulti.cpp b/libraries/WiFi/src/WiFiMulti.cpp
index 22ec1ea46f9..b0de3a44484 100644
--- a/libraries/WiFi/src/WiFiMulti.cpp
+++ b/libraries/WiFi/src/WiFiMulti.cpp
@@ -33,10 +33,9 @@ WiFiMulti::WiFiMulti()
     ipv6_support = false;
 }
 
-WiFiMulti::~WiFiMulti()
+void WiFiMulti::APlistClean(void)
 {
-    for(uint32_t i = 0; i < APlist.size(); i++) {
-        WifiAPlist_t entry = APlist[i];
+    for(auto entry : APlist) {
         if(entry.ssid) {
             free(entry.ssid);
         }
@@ -47,17 +46,22 @@ WiFiMulti::~WiFiMulti()
     APlist.clear();
 }
 
+WiFiMulti::~WiFiMulti()
+{
+    APlistClean();
+}
+
 bool WiFiMulti::addAP(const char* ssid, const char *passphrase)
 {
     WifiAPlist_t newAP;
 
-    if(!ssid || *ssid == 0x00 || strlen(ssid) > 31) {
+    if(!ssid || *ssid == '\0' || strlen(ssid) > 31) {
         // fail SSID too long or missing!
         log_e("[WIFI][APlistAdd] no ssid or ssid too long");
         return false;
     }
 
-    if(passphrase && strlen(passphrase) > 64) {
+    if(passphrase && strlen(passphrase) > 63) {
         // fail passphrase too long!
         log_e("[WIFI][APlistAdd] passphrase too long");
         return false;
@@ -70,7 +74,7 @@ bool WiFiMulti::addAP(const char* ssid, const char *passphrase)
         return false;
     }
 
-    if(passphrase && *passphrase != 0x00) {
+    if(passphrase && *passphrase != '\0') {
         newAP.passphrase = strdup(passphrase);
         if(!newAP.passphrase) {
             log_e("[WIFI][APlistAdd] fail newAP.passphrase == 0");
@@ -80,7 +84,7 @@ bool WiFiMulti::addAP(const char* ssid, const char *passphrase)
     } else {
         newAP.passphrase = NULL;
     }
-
+    newAP.hasFailed = false;
     APlist.push_back(newAP);
     log_i("[WIFI][APlistAdd] add SSID: %s", newAP.ssid);
     return true;
@@ -91,9 +95,20 @@ uint8_t WiFiMulti::run(uint32_t connectTimeout)
     int8_t scanResult;
     uint8_t status = WiFi.status();
     if(status == WL_CONNECTED) {
-        for(uint32_t x = 0; x < APlist.size(); x++) {
-            if(WiFi.SSID()==APlist[x].ssid) {
+        if (!_bWFMInit && _connectionTestCBFunc != NULL){
+            if (_connectionTestCBFunc() == true) {
+                _bWFMInit = true;
+                return status;
+            }
+        } else {
+            if (!_bStrict) {
                 return status;
+            } else {
+                for(auto ap : APlist) {
+                    if(WiFi.SSID() == ap.ssid) {
+                        return status;
+                    }
+                }
             }
         }
         WiFi.disconnect(false,false);
@@ -102,22 +117,27 @@ uint8_t WiFiMulti::run(uint32_t connectTimeout)
     }
 
     scanResult = WiFi.scanNetworks();
-    if(scanResult == WIFI_SCAN_RUNNING) {
+    if (scanResult == WIFI_SCAN_RUNNING) {
         // scan is running
         return WL_NO_SSID_AVAIL;
-    } else if(scanResult >= 0) {
+    } else if (scanResult >= 0) {
         // scan done analyze
-        WifiAPlist_t bestNetwork { NULL, NULL };
+        int32_t bestIndex = 0;
+        WifiAPlist_t bestNetwork { NULL, NULL, false };
         int bestNetworkDb = INT_MIN;
+        int bestNetworkSec = WIFI_AUTH_MAX;
         uint8_t bestBSSID[6];
         int32_t bestChannel = 0;
 
         log_i("[WIFI] scan done");
 
-        if(scanResult == 0) {
+        if (scanResult == 0) {
             log_e("[WIFI] no networks found");
         } else {
             log_i("[WIFI] %d networks found", scanResult);
+
+            int8_t failCount = 0;
+            int8_t foundCount = 0;
             for(int8_t i = 0; i < scanResult; ++i) {
 
                 String ssid_scan;
@@ -127,22 +147,47 @@ uint8_t WiFiMulti::run(uint32_t connectTimeout)
                 int32_t chan_scan;
 
                 WiFi.getNetworkInfo(i, ssid_scan, sec_scan, rssi_scan, BSSID_scan, chan_scan);
+                // add any Open WiFi AP to the list, if allowed with setAllowOpenAP(true)
+                if (_bAllowOpenAP && sec_scan == WIFI_AUTH_OPEN){
+                    bool found = false;
+                    for(auto check : APlist) {
+                        if (ssid_scan == check.ssid){
+                            found = true;
+                            break;
+                        }
+                    }
+                    // If we didn't find it, add this Open WiFi AP to the list
+                    if (!found){
+                        log_i("[WIFI][APlistAdd] adding Open WiFi SSID: %s", ssid_scan.c_str());
+                        addAP(ssid_scan.c_str());
+                    }
+                }
 
                 bool known = false;
-                for(uint32_t x = APlist.size() ; x > 0; x--) {
-                    WifiAPlist_t entry = APlist[x-1];
+                for(uint32_t x = 0; x < APlist.size(); x++) {
+                    WifiAPlist_t entry = APlist[x];
 
                     if(ssid_scan == entry.ssid) { // SSID match
-                        known = true;
-                        if(rssi_scan > bestNetworkDb) { // best network
-                            if(sec_scan == WIFI_AUTH_OPEN || entry.passphrase) { // check for passphrase if not open wlan
-                                bestNetworkDb = rssi_scan;
-                                bestChannel = chan_scan;
-                                memcpy((void*) &bestNetwork, (void*) &entry, sizeof(bestNetwork));
-                                memcpy((void*) &bestBSSID, (void*) BSSID_scan, sizeof(bestBSSID));
+                        log_v("known ssid: %s, has failed: %s", entry.ssid, entry.hasFailed ? "yes" : "no");
+                        foundCount++;
+                        if (!entry.hasFailed){
+                            known = true;
+                            log_v("rssi_scan: %d, bestNetworkDb: %d", rssi_scan, bestNetworkDb);
+                            if(rssi_scan > bestNetworkDb) { // best network
+                                if(_bAllowOpenAP || (sec_scan == WIFI_AUTH_OPEN || entry.passphrase)) { // check for passphrase if not open wlan
+                                    log_v("best network is now: %s", ssid_scan);
+                                    bestIndex = x;
+                                    bestNetworkSec = sec_scan;
+                                    bestNetworkDb = rssi_scan;
+                                    bestChannel = chan_scan;
+                                    memcpy((void*) &bestNetwork, (void*) &entry, sizeof(bestNetwork));
+                                    memcpy((void*) &bestBSSID, (void*) BSSID_scan, sizeof(bestBSSID));
+                                }
                             }
+                            break;
+                        } else {
+                            failCount++;
                         }
-                        break;
                     }
                 }
 
@@ -152,8 +197,12 @@ uint8_t WiFiMulti::run(uint32_t connectTimeout)
                     log_d("       %d: [%d][%02X:%02X:%02X:%02X:%02X:%02X] %s (%d) %c", i, chan_scan, BSSID_scan[0], BSSID_scan[1], BSSID_scan[2], BSSID_scan[3], BSSID_scan[4], BSSID_scan[5], ssid_scan.c_str(), rssi_scan, (sec_scan == WIFI_AUTH_OPEN) ? ' ' : '*');
                 }
             }
+            log_v("foundCount = %d, failCount = %d", foundCount, failCount);
+            // if all the APs in the list have failed, reset the failure flags
+            if (foundCount == failCount) {
+                resetFails();   // keeps trying the APs in the list
+            }
         }
-
         // clean up ram
         WiFi.scanDelete();
 
@@ -163,12 +212,15 @@ uint8_t WiFiMulti::run(uint32_t connectTimeout)
             if (ipv6_support == true) {
                 WiFi.enableIPv6();
             }
-            WiFi.begin(bestNetwork.ssid, bestNetwork.passphrase, bestChannel, bestBSSID);
+            WiFi.disconnect();
+            delay(10);
+            WiFi.begin(bestNetwork.ssid, (_bAllowOpenAP && bestNetworkSec == WIFI_AUTH_OPEN) ? NULL : bestNetwork.passphrase, bestChannel, bestBSSID);
             status = WiFi.status();
+            _bWFMInit = true;
 
             auto startTime = millis();
             // wait for connection, fail, or timeout
-            while(status != WL_CONNECTED && status != WL_NO_SSID_AVAIL && status != WL_CONNECT_FAILED && (millis() - startTime) <= connectTimeout) {
+            while(status != WL_CONNECTED && (millis() - startTime) <= connectTimeout) {  // && status != WL_NO_SSID_AVAIL && status != WL_CONNECT_FAILED
                 delay(10);
                 status = WiFi.status();
             }
@@ -180,15 +232,32 @@ uint8_t WiFiMulti::run(uint32_t connectTimeout)
                 log_d("[WIFI] IP: %s", WiFi.localIP().toString().c_str());
                 log_d("[WIFI] MAC: %s", WiFi.BSSIDstr().c_str());
                 log_d("[WIFI] Channel: %d", WiFi.channel());
+
+                if (_connectionTestCBFunc != NULL) {
+                    // We connected to an AP but if it's a captive portal we're not going anywhere.  Test it.
+                    if (_connectionTestCBFunc()) {
+                        resetFails();
+                    } else {
+                        markAsFailed(bestIndex);
+                        WiFi.disconnect();
+                        delay(10);
+                        status = WiFi.status();
+                    }
+                } else {
+                    resetFails();
+                }
                 break;
             case WL_NO_SSID_AVAIL:
                 log_e("[WIFI] Connecting Failed AP not found.");
+                markAsFailed(bestIndex);
                 break;
             case WL_CONNECT_FAILED:
                 log_e("[WIFI] Connecting Failed.");
+                markAsFailed(bestIndex);
                 break;
             default:
                 log_e("[WIFI] Connecting Failed (%d).", status);
+                markAsFailed(bestIndex);
                 break;
             }
         } else {
@@ -210,3 +279,27 @@ uint8_t WiFiMulti::run(uint32_t connectTimeout)
 void WiFiMulti::enableIPv6(bool state) {
     ipv6_support = state;
 }
+
+void WiFiMulti::markAsFailed(int32_t i) {
+    APlist[i].hasFailed = true;
+    log_d("[WIFI] Marked SSID %s as failed", APlist[i].ssid);
+}
+
+void WiFiMulti::resetFails(){
+    for(uint32_t i = 0; i < APlist.size(); i++) {
+        APlist[i].hasFailed = false;
+    }
+    log_d("[WIFI] Resetting failure flags");
+}
+
+void WiFiMulti::setStrictMode(bool bStrict) {
+    _bStrict = bStrict;
+}
+
+void WiFiMulti::setAllowOpenAP(bool bAllowOpenAP) {
+    _bAllowOpenAP = bAllowOpenAP;
+}
+
+void WiFiMulti::setConnectionTestCallbackFunc(ConnectionTestCB_t cbFunc) {
+    _connectionTestCBFunc = cbFunc;
+}
diff --git a/libraries/WiFi/src/WiFiMulti.h b/libraries/WiFi/src/WiFiMulti.h
index d8551fab9dd..75a40c87679 100644
--- a/libraries/WiFi/src/WiFiMulti.h
+++ b/libraries/WiFi/src/WiFiMulti.h
@@ -32,8 +32,11 @@
 typedef struct {
     char * ssid;
     char * passphrase;
+    bool hasFailed;
 } WifiAPlist_t;
 
+typedef std::function<bool(void)> ConnectionTestCB_t;
+
 class WiFiMulti
 {
 public:
@@ -41,13 +44,36 @@ class WiFiMulti
     ~WiFiMulti();
 
     bool addAP(const char* ssid, const char *passphrase = NULL);
-
-    void enableIPv6(bool state);
     uint8_t run(uint32_t connectTimeout=5000);
+    void enableIPv6(bool state);
+
+    // Force (default: true) to only keep connected or to connect to an AP from the provided WiFiMulti list.
+    // When bStrict is false, it will keep the last/current connected AP even if not in the WiFiMulti List.
+    void setStrictMode(bool bStrict = true);
+    
+    // allows (true) to connect to ANY open AP, even if not in the user list
+    // default false (do not connect to an open AP that has not been explicitaly added by the user to list)
+    void setAllowOpenAP(bool bAllowOpenAP = false);
+
+    // clears the current list of Multi APs and frees the memory
+    void APlistClean(void);
+
+    // allow the user to define a callback function that will validate the connection to the Internet.
+    // if the callback returns true, the connection is considered valid and the AP will added to the validated AP list.
+    // set the callback to NULL to disable the feature and validate any SSID that is in the list.
+    void setConnectionTestCallbackFunc(ConnectionTestCB_t cbFunc);
 
 private:
     std::vector<WifiAPlist_t> APlist;
     bool ipv6_support;
+
+    bool _bStrict = true;
+    bool _bAllowOpenAP = false;
+    ConnectionTestCB_t _connectionTestCBFunc = NULL;
+    bool _bWFMInit = false;
+
+    void markAsFailed(int32_t i);
+    void resetFails();
 };
 
 #endif /* WIFICLIENTMULTI_H_ */