diff --git a/libraries/WiFi/examples/WiFIMultiAdvanced/WiFiMultiAdvanced.ino b/libraries/WiFi/examples/WiFIMultiAdvanced/WiFiMultiAdvanced.ino
new file mode 100644
index 00000000000..c3ca79eb107
--- /dev/null
+++ b/libraries/WiFi/examples/WiFIMultiAdvanced/WiFiMultiAdvanced.ino
@@ -0,0 +1,47 @@
+/*
+ *  This sketch tries to connect to the best AP available, and tests for captive portals on open networks
+ *
+ */
+
+#include <WiFi.h>
+#include <WiFiMulti.h>
+
+WiFiMulti wifiMulti;
+
+bool bConnected = 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.setTestConnection(true);   // Default is false.  Attempts to connect to a remote webserver in case of captive portals.  Most useful with AllowOpenAP = true.
+
+    // Optional - defaults to http://www.amazon.com with testphrase 301 Moved.
+    // You can also set this to a simple test page on your own server to ensure you can reach it
+    // wifiMulti.setTestURL("http://www.brainjar.com/java/host/test.html");  // Must include http://
+    // wifiMulti.setTestPhrase("200 OK");  // Use a non-302 status code to ensure we bypass captive portals.  Can be any text in the webpage.
+    
+    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()
+{
+    if( wifiMulti.run() != WL_CONNECTED) {
+        Serial.println("WiFi not connected!");
+        delay(5000);
+    }
+}
\ No newline at end of file
diff --git a/libraries/WiFi/src/WiFiMulti.cpp b/libraries/WiFi/src/WiFiMulti.cpp
index 730850333f1..9db3d92e338 100644
--- a/libraries/WiFi/src/WiFiMulti.cpp
+++ b/libraries/WiFi/src/WiFiMulti.cpp
@@ -90,10 +90,19 @@ 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 (_bTestConnection && !_bWFMInit){
+            if (testConnection()) {
+                _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);
         delay(10);
@@ -104,10 +113,12 @@ uint8_t WiFiMulti::run(uint32_t connectTimeout)
     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, NULL };
         int bestNetworkDb = INT_MIN;
+        int bestNetworkSec;
         uint8_t bestBSSID[6];
         int32_t bestChannel = 0;
 
@@ -117,6 +128,9 @@ uint8_t WiFiMulti::run(uint32_t connectTimeout)
             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,21 +141,45 @@ uint8_t WiFiMulti::run(uint32_t connectTimeout)
 
                 WiFi.getNetworkInfo(i, ssid_scan, sec_scan, rssi_scan, BSSID_scan, chan_scan);
 
+                if (_bAllowOpenAP && sec_scan == WIFI_AUTH_OPEN){
+                    bool found = false;
+                    for(uint32_t o = 0; o < APlist.size(); o++) {
+                        WifiAPlist_t check = APlist[o];
+                        if (ssid_scan == check.ssid){
+                            found = true;
+                        }
+                    }
+                    if (!found){
+                        log_i("[WIFI] added %s to APList", ssid_scan);
+                        APlistAdd(ssid_scan.c_str());
+                    }
+                }
+
                 bool known = false;
                 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));
+                    
+                    if(ssid_scan == entry.ssid) { // It's on the list
+                    log_i("known ssid: %s, failed: %i", entry.ssid, entry.fail);
+                        foundCount++;
+                        if (!entry.fail){
+                            known = true;
+                            log_i("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_i("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;
                     }
                 }
 
@@ -151,21 +189,31 @@ 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_i("foundCount = %d, failCount = %d", foundCount, failCount);
+            
+            if (foundCount == failCount){
+                failCount = 0;
+                resetFails();
+            }
+            foundCount = 0;
         }
-
+        
         // clean up ram
         WiFi.scanDelete();
 
         if(bestNetwork.ssid) {
-            log_i("[WIFI] Connecting BSSID: %02X:%02X:%02X:%02X:%02X:%02X SSID: %s Channal: %d (%d)", bestBSSID[0], bestBSSID[1], bestBSSID[2], bestBSSID[3], bestBSSID[4], bestBSSID[5], bestNetwork.ssid, bestChannel, bestNetworkDb);
-
-            WiFi.begin(bestNetwork.ssid, bestNetwork.passphrase, bestChannel, bestBSSID);
+            log_i("[WIFI] Connecting BSSID: %02X:%02X:%02X:%02X:%02X:%02X SSID: %s Channel: %d (%d)", bestBSSID[0], bestBSSID[1], bestBSSID[2], bestBSSID[3], bestBSSID[4], bestBSSID[5], bestNetwork.ssid, bestChannel, bestNetworkDb);
+            
+            WiFi.disconnect();
+            delay(100);
+            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) {
-                delay(10);
+            while(status != WL_CONNECTED && status != WL_NO_SSID_AVAIL && (millis() - startTime) <= connectTimeout) {  // && status != WL_CONNECT_FAILED
+                delay(500);
                 status = WiFi.status();
             }
 
@@ -176,20 +224,39 @@ 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 (_bTestConnection){
+                    // We connected to an AP but if it's a captive portal we're not going anywhere.  Test it.
+                    if (testConnection()){
+                        resetFails();
+                    } else {
+                        markAsFailed(bestIndex);
+                        WiFi.disconnect();
+                        delay(100);
+                        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 {
             log_e("[WIFI] no matching wifi found!");
         }
+        delay(2000);  //letting wifi stabilize...
     } else {
         // start scan
         log_d("[WIFI] delete old wifi config...");
@@ -199,6 +266,143 @@ uint8_t WiFiMulti::run(uint32_t connectTimeout)
         // scan wifi async mode
         WiFi.scanNetworks(true);
     }
-
     return status;
 }
+
+void WiFiMulti::markAsFailed(int32_t i) {
+    APlist[i].fail = true;
+    log_i("marked %s as failed",APlist[i].ssid);
+}
+
+void WiFiMulti::resetFails(){
+    log_i("resetting failure flags");
+    for(uint32_t i = 0; i < APlist.size(); i++) {
+        APlist[i].fail = false;
+    }
+}
+
+void WiFiMulti::setStrictMode(bool bStrict) {
+    _bStrict = bStrict;
+}
+
+void WiFiMulti::setAllowOpenAP(bool bAllowOpenAP) {
+    _bAllowOpenAP = bAllowOpenAP;
+}
+
+void WiFiMulti::setTestConnection(bool bTestConnection){
+    _bTestConnection = bTestConnection;
+}
+void WiFiMulti::setTestPhrase(const char* testPhrase){
+    _testPhrase = testPhrase;
+}
+void WiFiMulti::setTestURL(String testURL){
+    _testURL = testURL;
+}
+
+bool WiFiMulti::testConnection(){
+    //parse url
+    int8_t split = _testURL.indexOf('/',7);
+    String host = _testURL.substring(7, split);
+    String url = (split < 0) ? "/":_testURL.substring(split,_testURL.length());
+    log_i("Testing connection to %s.  Test phrase is \"%s\"",_testURL.c_str(), _testPhrase.c_str());
+    // Use WiFiClient class to create TCP connections
+    WiFiClient client;
+    const int httpPort = 80;
+    if (!client.connect(host.c_str(), httpPort)) {
+        log_e("Connection failed");
+        return false;
+    } else {
+        log_i("Connected to test host");
+    }
+
+    // This will send the request to the server
+    client.print(String("GET ") + url + " HTTP/1.1\r\n" +
+                 "Host: " + host + "\r\n" +
+                 "Connection: close\r\n\r\n");
+    unsigned long timeout = millis();
+    while (client.available() == 0) {
+        if (millis() - timeout > 5000) {
+            log_e(">>>Client timeout!");
+            client.stop();
+            return false;
+        }
+    }
+
+    bool bSuccess = false;
+    timeout = millis();
+    while(client.available()) {
+        if (millis() - timeout < 5000) {
+            bSuccess = client.find(_testPhrase.c_str());
+            if (bSuccess){
+                log_i("Success. Found test phrase");
+            } else {
+                log_e("Failed.  Can't find test phrase");
+            }
+            return bSuccess;
+        } else {
+            log_e("Test phrase check timed out!");
+            break;
+        }
+    }
+}
+
+// ##################################################################################
+
+bool WiFiMulti::APlistAdd(const char* ssid, const char *passphrase)
+{
+
+    WifiAPlist_t newAP;
+
+    if(!ssid || *ssid == 0x00 || strlen(ssid) > 31) {
+        // fail SSID to long or missing!
+        log_e("[WIFI][APlistAdd] no ssid or ssid too long");
+        return false;
+    }
+
+    if(passphrase && strlen(passphrase) > 63) {
+        // fail passphrase to long!
+        log_e("[WIFI][APlistAdd] passphrase too long");
+        return false;
+    }
+
+    newAP.ssid = strdup(ssid);
+
+    if(!newAP.ssid) {
+        log_e("[WIFI][APlistAdd] fail newAP.ssid == 0");
+        return false;
+    }
+
+    if(passphrase && *passphrase != 0x00) {
+        newAP.passphrase = strdup(passphrase);
+        if(!newAP.passphrase) {
+            log_e("[WIFI][APlistAdd] fail newAP.passphrase == 0");
+            free(newAP.ssid);
+            return false;
+        }
+    } else {
+        newAP.passphrase = NULL;
+    }
+        
+    newAP.fail = false;
+
+    APlist.push_back(newAP);
+    log_i("[WIFI][APlistAdd] added SSID: %s", newAP.ssid);
+    return true;
+}
+
+void WiFiMulti::APlistClean(void)
+{
+    for(uint32_t i = 0; i < APlist.size(); i++) {
+        WifiAPlist_t entry = APlist[i];
+        if(entry.ssid) {
+            free(entry.ssid);
+        }
+        if(entry.passphrase) {
+            free(entry.passphrase);
+        }
+        //why doesn't this work???
+        //free(entry.fail);
+    }
+    APlist.clear();
+}
+
diff --git a/libraries/WiFi/src/WiFiMulti.h b/libraries/WiFi/src/WiFiMulti.h
index 38ddb5d9f95..1a77f20c7fd 100644
--- a/libraries/WiFi/src/WiFiMulti.h
+++ b/libraries/WiFi/src/WiFiMulti.h
@@ -32,6 +32,7 @@
 typedef struct {
     char * ssid;
     char * passphrase;
+    bool fail;
 } WifiAPlist_t;
 
 class WiFiMulti
@@ -42,10 +43,31 @@ class WiFiMulti
 
     bool addAP(const char* ssid, const char *passphrase = NULL);
 
+    void setStrictMode(bool bStrict);
+    void setAllowOpenAP(bool bAllowOpenAP);
+    void setTestConnection(bool bTestConnection);
+    void setTestPhrase(const char* testPhrase);
+    void setTestURL(String url);
+
     uint8_t run(uint32_t connectTimeout=5000);
 
+    void APlistClean(void);
+
 private:
     std::vector<WifiAPlist_t> APlist;
+
+    bool _bStrict = true;
+    bool _bAllowOpenAP = false;
+    bool _bTestConnection = false;
+    String _testPhrase = "301 Moved";
+    String _testURL = "http://www.amazon.com";
+    bool _bWFMInit = false;
+
+    void markAsFailed(int32_t i);
+    void resetFails();
+
+    bool testConnection();
+    bool APlistAdd(const char* ssid, const char *passphrase = NULL);
 };
 
 #endif /* WIFICLIENTMULTI_H_ */