/************************************************************************************************************************************** WiFiMulti_Generic_Impl.h For any WiFi shields, such as ESP32, ESP8266, Portenta_H7, WiFiNINA W101, W102, W13x, or custom, such as ESP8266/ESP32-AT, etc WiFiMulti_Generic is a library to adapt the ESP32/ESP8266 WiFiMulti feature to other WiFi modules Based on and modified from WiFiMulti of ESP32 core: https://github.com/espressif/arduino-esp32 Based on and modified from WiFiMulti of ESP8266 core: https://github.com/esp8266/Arduino Built by Khoi Hoang https://github.com/khoih-prog/WiFiMulti_Generic License under GPL-3.0 This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published bythe Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see Original author: @file WiFiMulti.cpp @date 16.05.2015 @author Markus Sattler Copyright (c) 2015 Markus Sattler. All rights reserved. This file is part of the esp32 core for Arduino environment. Version: 1.2.2 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.0.0 K Hoang 15/02/2020 Initial coding for ESP32, ESP8266, WiFiNINA and ESP_AT modules 1.1.0 K Hoang 16/02/2020 Add support to WiFi101 and many more boards (PortentaH7, megaAVR, Sparkfun SAMD, etc.) 1.1.1 K Hoang 26/04/2020 Fix bug 1.2.0 K Hoang 12/08/2022 Add support to RASPBERRY_PI_PICO_W using CYW4343 WiFi 1.2.1 K Hoang 16/08/2022 Workaround for RP2040W WiFi.status() bug 1.2.2 K Hoang 16/08/2022 Better workaround for RP2040W WiFi.status() bug using ping() to local gateway ***************************************************************************************************************************************/ #pragma once #ifndef _WIFIMULTI_GENERIC_IMPL_H_ #define _WIFIMULTI_GENERIC_IMPL_H_ #include #include WiFiMulti_Generic::WiFiMulti_Generic() { } WiFiMulti_Generic::~WiFiMulti_Generic() { 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); } } APlist.clear(); } bool WiFiMulti_Generic::addAP(const char* ssid, const char *passphrase) { WifiAPlist_t newAP; if (!ssid || *ssid == 0x00 || strlen(ssid) > 31) { // fail SSID too long or missing! WFM_LOGERROR("[addAP] No ssid or ssid too long"); return false; } if (passphrase && strlen(passphrase) > 64) { // fail passphrase too long! WFM_LOGERROR("[addAP] Passphrase too long"); return false; } newAP.ssid = strdup(ssid); if (!newAP.ssid) { WFM_LOGERROR("[addAP] Fail : newAP.ssid == 0"); return false; } if (passphrase && *passphrase != 0x00) { newAP.passphrase = strdup(passphrase); if (!newAP.passphrase) { WFM_LOGERROR("[addAP] Fail : newAP.passphrase == 0"); free(newAP.ssid); return false; } } else { newAP.passphrase = NULL; } APlist.push_back(newAP); WFM_LOGINFO1("[addAP] add SSID:", newAP.ssid); return true; } uint8_t WiFiMulti_Generic::run(const 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) { return status; } } WiFi.disconnect(false, false); delay(10); status = WiFi.status(); } scanResult = WiFi.scanNetworks(); if (scanResult == WIFI_SCAN_RUNNING) { // scan is running return WL_NO_SSID_AVAIL; } else if (scanResult >= 0) { // scan done analyze WifiAPlist_t bestNetwork { NULL, NULL }; int bestNetworkDb = INT_MIN; uint8_t bestBSSID[6]; int32_t bestChannel = 0; WFM_LOGINFO("[run] Scan done"); if (scanResult == 0) { WFM_LOGERROR("[run] No networks found"); } else { WFM_LOGINFO1("[run] Number of Networks found:", scanResult); for (int8_t i = 0; i < scanResult; ++i) { String ssid_scan; int32_t rssi_scan; uint8_t sec_scan; uint8_t* BSSID_scan; int32_t chan_scan; WiFi.getNetworkInfo(i, ssid_scan, sec_scan, rssi_scan, BSSID_scan, chan_scan); bool known = false; for (uint32_t x = APlist.size() ; x > 0; x--) { WifiAPlist_t entry = APlist[x - 1]; 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)); } } break; } } WFM_PRINT_LINE; if (known) { WFM_LOGDEBUG3(" Known => #", i, ", Channel:", chan_scan); WFM_HEXLOGDEBUG5_SEPARATOR(":", BSSID_scan[0], BSSID_scan[1], BSSID_scan[2], BSSID_scan[3], BSSID_scan[4], BSSID_scan[5]); WFM_LOGDEBUG5("SSID:", ssid_scan.c_str(), ", RSSI:", rssi_scan, ", Secured:", (sec_scan == WIFI_AUTH_OPEN) ? 'n' : 'y'); } else { WFM_LOGDEBUG3("Unknown => #", i, ", Channel:", chan_scan); WFM_HEXLOGDEBUG5_SEPARATOR(":", BSSID_scan[0], BSSID_scan[1], BSSID_scan[2], BSSID_scan[3], BSSID_scan[4], BSSID_scan[5]); WFM_LOGDEBUG5("SSID:", ssid_scan.c_str(), ", RSSI:", rssi_scan, ", Secured:", (sec_scan == WIFI_AUTH_OPEN) ? 'n' : 'y'); } } } WFM_PRINT_LINE; // clean up ram WiFi.scanDelete(); if (bestNetwork.ssid) { WFM_LOGINFO0("[run] Connecting BSSID: "); WFM_HEXLOGINFO5_SEPARATOR(":", bestBSSID[0], bestBSSID[1], bestBSSID[2], bestBSSID[3], bestBSSID[4], bestBSSID[5]); WFM_LOGINFO5("SSID: ", bestNetwork.ssid, ", Channel: ", bestChannel, ", Best dB: ", bestNetworkDb); WiFi.begin(bestNetwork.ssid, bestNetwork.passphrase, bestChannel, bestBSSID); status = WiFi.status(); 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); status = WiFi.status(); } switch (status) { case WL_CONNECTED: WFM_LOGINFO("[run] Connecting done."); WFM_LOGDEBUG3("[run] SSID: ", WiFi.SSID().c_str(), ", IP: ", WiFi.localIP().toString().c_str()); WFM_LOGDEBUG3("[run] MAC: ", WiFi.BSSIDstr().c_str(), ", Channel: ", WiFi.channel()); break; case WL_NO_SSID_AVAIL: WFM_LOGERROR("[run] Connecting Failed, AP not found."); break; case WL_CONNECT_FAILED: WFM_LOGERROR("[run] Connecting Failed."); break; default: WFM_LOGERROR1("[run] Connecting Failed, status =", status); break; } } else { WFM_LOGERROR("[run] no matching wifi found!"); } } else { // start scan WFM_LOGDEBUG("[run] delete old wifi config..."); WiFi.disconnect(); WFM_LOGDEBUG("[run] start scan"); // scan wifi async mode WiFi.scanNetworks(true); } return status; } #endif // _WIFIMULTI_GENERIC_IMPL_H_