1
0
mirror of https://github.com/esp8266/Arduino.git synced 2025-04-19 23:22:16 +03:00
esp8266/libraries/ESP8266WiFi/src/ESP8266WiFiMulti.cpp
david gauchard 39080e317e
delay / esp_delay: transparently manage recurrent scheduled functions (#8802)
Recurrent scheduled functions will always be running in background.

esp_delay()'s interval (intvl_ms) is internally kept to its highest value allowing to honor recurrent scheduled functions requirements.

It transparently allows to keep with the arduino and nonos-sdk trivial programming way and still use background services or drivers running regularly.
2023-01-14 22:25:57 +01:00

529 lines
14 KiB
C++

/**
*
* @file ESP8266WiFiMulti.cpp
* @date 30.09.2020
* @author Markus Sattler, Erriez
*
* Copyright (c) 2015-2020 Markus Sattler. All rights reserved.
* This file is part of the esp8266 core for Arduino environment.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#include "PolledTimeout.h"
#include "ESP8266WiFiMulti.h"
#include <coredecls.h>
#include <limits.h>
#include <string.h>
/**
* @brief Print WiFi status
* @details
* Macro DEBUG_ESP_WIFI and DEBUG_ESP_PORT must be configured
* @param status
* WiFi status
*/
static void printWiFiStatus(wl_status_t status)
{
#ifdef DEBUG_ESP_WIFI
IPAddress ip;
uint8_t *mac;
switch (status) {
case WL_CONNECTED:
ip = WiFi.localIP();
mac = WiFi.BSSID();
DEBUG_WIFI_MULTI("[WIFIM] Connected:\n");
DEBUG_WIFI_MULTI("[WIFIM] SSID: %s\n", WiFi.SSID().c_str());
DEBUG_WIFI_MULTI("[WIFIM] IP: %d.%d.%d.%d\n", ip[0], ip[1], ip[2], ip[3]);
DEBUG_WIFI_MULTI("[WIFIM] MAC: %02X:%02X:%02X:%02X:%02X:%02X\n",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
DEBUG_WIFI_MULTI("[WIFIM] CH: %d\n", WiFi.channel());
DEBUG_WIFI_MULTI("[WIFIM] RSSI: %d\n", WiFi.RSSI());
break;
case WL_NO_SSID_AVAIL:
DEBUG_WIFI_MULTI("[WIFIM] Connecting failed AP not found.\n");
break;
case WL_CONNECT_FAILED:
DEBUG_WIFI_MULTI("[WIFIM] Connecting failed.\n");
break;
case WL_WRONG_PASSWORD:
DEBUG_WIFI_MULTI("[WIFIM] Wrong password.\n");
break;
default:
DEBUG_WIFI_MULTI("[WIFIM] Connecting failed (%d).\n", status);
break;
}
#else
// Suppress warning unused variable
(void)(status);
#endif
}
/**
* @brief Wait for WiFi connect status change, protected with timeout
* @param connectTimeoutMs
* WiFi connection timeout in ms
* @return
* WiFi connection status
*/
static wl_status_t waitWiFiConnect(uint32_t connectTimeoutMs)
{
wl_status_t status = WL_CONNECT_FAILED;
// Wait for WiFi to connect
// stop waiting upon status checked every 100ms or when timeout is reached
esp_delay(connectTimeoutMs,
[&status]() {
status = WiFi.status();
return status != WL_CONNECTED && status != WL_CONNECT_FAILED;
}, 100);
// Check status
if (status == WL_CONNECTED) {
// Connected, print WiFi status
printWiFiStatus(status);
// Return WiFi status
return status;
} else if (status == WL_CONNECT_FAILED) {
DEBUG_WIFI_MULTI("[WIFIM] Connect failed\n");
} else {
DEBUG_WIFI_MULTI("[WIFIM] Connect timeout\n");
}
// Return WiFi connect failed
return WL_CONNECT_FAILED;
}
/**
* @brief Constructor
*/
ESP8266WiFiMulti::ESP8266WiFiMulti() : _firstRun(true)
{
}
/**
* @brief Destructor
*/
ESP8266WiFiMulti::~ESP8266WiFiMulti()
{
// Cleanup memory
APlistClean();
}
/**
* @brief Add Access Point
* @param ssid
* WiFi SSID char array, max 32 characters + NULL character
* @param passphrase
* WiFi password char array, max 63 characters + NULL character
* @retval true
* Success
* @retval false
* Failure
*/
bool ESP8266WiFiMulti::addAP(const char *ssid, const char *passphrase)
{
return APlistAdd(ssid, passphrase);
}
/**
* @brief Remove all Access Points from list
*/
void ESP8266WiFiMulti::cleanAPlist(void)
{
APlistClean();
}
/**
* @brief Check if Access Point exists in list
* @param ssid
* WiFi SSID
* @param passphrase
* WiFi Password
* @retval true
* Success
* @retval false
* Failure
*/
bool ESP8266WiFiMulti::existsAP(const char *ssid, const char *passphrase)
{
return APlistExists(ssid, passphrase);
}
/**
* @brief Keep WiFi connected to Access Point with strongest WiFi signal (RSSI)
* @param connectTimeoutMs
* Timeout in ms per WiFi connection (excluding fixed 5 seconds scan timeout)
* @return
* WiFi status
*/
wl_status_t ESP8266WiFiMulti::run(uint32_t connectTimeoutMs)
{
int8_t scanResult;
wl_status_t status;
// Fast connect to previous WiFi on startup
if (_firstRun) {
_firstRun = false;
// Check if previous WiFi connection saved
if (strlen(WiFi.SSID().c_str())) {
DEBUG_WIFI_MULTI("[WIFIM] Connecting saved WiFi\n");
// Connect to previous saved WiFi
WiFi.begin();
// Wait for status change
status = waitWiFiConnect(connectTimeoutMs);
}
}
// Check connection state
status = WiFi.status();
if (status == WL_CONNECTED) {
// Already connected
return status;
}
// Start WiFi scan
scanResult = startScan();
if (scanResult < 0) {
// No WiFi scan results
return WL_NO_SSID_AVAIL;
}
// Try to connect to multiple WiFi's with strongest signal (RSSI)
return connectWiFiMulti(connectTimeoutMs);
}
/**
* @brief Start WiFi scan
* @retval >0
* Number of detected WiFi SSID's
* @retval 0
* No WiFi connections found
* @retval -2
* WiFi scan failed
*/
int8_t ESP8266WiFiMulti::startScan()
{
int8_t scanResult;
DEBUG_WIFI_MULTI("[WIFIM] Start scan\n");
// Clean previous scan
WiFi.scanDelete();
// Remove previous WiFi SSID/password
WiFi.disconnect();
// Start wifi scan in async mode
WiFi.scanNetworks(true);
// Wait for WiFi scan change or timeout
// stop waiting upon status checked every 100ms or when timeout is reached
esp_delay(WIFI_SCAN_TIMEOUT_MS,
[&scanResult]() {
scanResult = WiFi.scanComplete();
return scanResult < 0;
}, 100);
// Check for scan timeout which may occur when scan does not report completion
if (scanResult < 0) {
DEBUG_WIFI_MULTI("[WIFIM] Scan timeout\n");
return WIFI_SCAN_FAILED;
}
// Print WiFi scan result
printWiFiScan();
// Return (positive) number of detected WiFi networks
return scanResult;
}
/**
* @brief Connect to multiple WiFi's
* @param connectTimeoutMs
* WiFi connect timeout in ms
* @return
* WiFi connection status
*/
wl_status_t ESP8266WiFiMulti::connectWiFiMulti(uint32_t connectTimeoutMs)
{
int8_t scanResult;
String ssid;
int32_t rssi;
uint8_t encType;
uint8_t *bssid;
int32_t channel;
bool hidden;
// Get scan results
scanResult = WiFi.scanComplete();
// Find known WiFi networks
uint8_t known[_APlist.size()];
uint8_t numNetworks = 0;
for (int8_t i = 0; i < scanResult; i++) {
// Get network information
WiFi.getNetworkInfo(i, ssid, encType, rssi, bssid, channel, hidden);
// Check if the WiFi network contains an entry in AP list
for (auto entry : _APlist) {
// Check SSID
if (ssid == entry.ssid) {
// Known network
known[numNetworks++] = i;
}
}
}
// Sort WiFi networks by RSSI
for (int i = 0; i < numNetworks; i++) {
for (int j = i + 1; j < numNetworks; j++) {
if (WiFi.RSSI(known[j]) > WiFi.RSSI(known[i])) {
int8_t tmp;
// Swap indices
tmp = known[i];
known[i] = known[j];
known[j] = tmp;
}
}
}
// Print sorted indices
DEBUG_WIFI_MULTI("[WIFIM] Sorted indices: ");
for (int8_t i = 0; i < numNetworks; i++) {
DEBUG_WIFI_MULTI("%d ", known[i]);
}
DEBUG_WIFI_MULTI("\n");
// Create indices for AP connection failures
uint8_t connectSkipIndex[_APlist.size()];
memset(connectSkipIndex, 0, sizeof(connectSkipIndex));
// Connect to known WiFi AP's sorted by RSSI
for (int8_t i = 0; i < numNetworks; i++) {
// Get network information
WiFi.getNetworkInfo(known[i], ssid, encType, rssi, bssid, channel, hidden);
for (uint8_t j = 0; j < _APlist.size(); j++) {
auto &entry = _APlist[j];
// Check SSID
if (ssid == entry.ssid) {
DEBUG_WIFI_MULTI("[WIFIM] Connecting %s\n", ssid.c_str());
// Connect to WiFi
WiFi.begin(ssid, entry.passphrase, channel, bssid);
// Wait for status change
if (waitWiFiConnect(connectTimeoutMs) == WL_CONNECTED) {
return WL_CONNECTED;
}
// Failed to connect, skip for hidden SSID connects
connectSkipIndex[j] = true;
}
}
}
// Try to connect to hidden AP's which are not reported by WiFi scan
for (uint8_t i = 0; i < _APlist.size(); i++) {
auto &entry = _APlist[i];
if (!connectSkipIndex[i]) {
DEBUG_WIFI_MULTI("[WIFIM] Try hidden connect %s\n", entry.ssid);
// Connect to WiFi
WiFi.begin(entry.ssid, entry.passphrase);
// Wait for status change
if (waitWiFiConnect(connectTimeoutMs) == WL_CONNECTED) {
return WL_CONNECTED;
}
}
}
DEBUG_WIFI_MULTI("[WIFIM] Could not connect\n");
// Could not connect to any WiFi network
return WL_CONNECT_FAILED;
}
// ##################################################################################
/**
* @brief Add WiFi connection to internal AP list
* @param ssid
* WiFi SSID
* @param passphrase
* WiFi Password
* @retval true
* Success
* @retval false
* Failure
*/
bool ESP8266WiFiMulti::APlistAdd(const char *ssid, const char *passphrase)
{
WifiAPEntry newAP;
if (!ssid || (*ssid == 0x00) || (strlen(ssid) > 32)) {
// Fail SSID too long or missing!
DEBUG_WIFI_MULTI("[WIFIM][APlistAdd] No ssid or ssid too long\n");
return false;
}
// For passphrase, max is 63 ascii + null. For psk, 64hex + null.
if (passphrase && (strlen(passphrase) > 64)) {
// fail passphrase too long!
DEBUG_WIFI_MULTI("[WIFIM][APlistAdd] Passphrase too long\n");
return false;
}
if (APlistExists(ssid, passphrase)) {
DEBUG_WIFI_MULTI("[WIFIM][APlistAdd] SSID: %s already exists\n", ssid);
return true;
}
newAP.ssid = strdup(ssid);
if (!newAP.ssid) {
DEBUG_WIFI_MULTI("[WIFIM][APlistAdd] Fail newAP.ssid == 0\n");
return false;
}
if (passphrase) {
newAP.passphrase = strdup(passphrase);
} else {
newAP.passphrase = strdup("");
}
if (!newAP.passphrase) {
DEBUG_WIFI_MULTI("[WIFIM][APlistAdd] Fail newAP.passphrase == 0\n");
free(newAP.ssid);
return false;
}
_APlist.push_back(newAP);
DEBUG_WIFI_MULTI("[WIFIM][APlistAdd] Add SSID: %s\n", newAP.ssid);
return true;
}
/**
* @brief Check if AP exists in list
* @param ssid
* WiFi SSID
* @param passphrase
* WiFi Password
* @return
*/
bool ESP8266WiFiMulti::APlistExists(const char *ssid, const char *passphrase)
{
if (!ssid || (*ssid == 0x00) || (strlen(ssid) > 32)) {
// Fail SSID too long or missing
DEBUG_WIFI_MULTI("[WIFIM][APlistExists] No ssid or ssid too long\n");
return false;
}
for (auto entry : _APlist) {
if (!strcmp(entry.ssid, ssid)) {
if (!passphrase) {
if (!strcmp(entry.passphrase, "")) {
return true;
}
} else {
if (!strcmp(entry.passphrase, passphrase)) {
return true;
}
}
}
}
return false;
}
/**
* @brief Remove all AP's from list
*/
void ESP8266WiFiMulti::APlistClean(void)
{
// Remove all entries from APlist
for (auto entry : _APlist) {
if (entry.ssid) {
free(entry.ssid);
}
if (entry.passphrase) {
free(entry.passphrase);
}
}
_APlist.clear();
}
/**
* @brief Print WiFi scan results
* @details
* Macro DEBUG_ESP_WIFI and DEBUG_ESP_PORT must be configured
*/
void ESP8266WiFiMulti::printWiFiScan()
{
#ifdef DEBUG_ESP_WIFI
String ssid;
int32_t rssi;
uint8_t encryptionType;
uint8_t* bssid;
int32_t channel;
bool hidden;
int8_t scanResult;
scanResult = WiFi.scanComplete();
DEBUG_WIFI_MULTI("[WIFIM] %d networks found:\n", scanResult);
// Print unsorted scan results
for (int8_t i = 0; i < scanResult; i++) {
bool known = false;
WiFi.getNetworkInfo(i, ssid, encryptionType, rssi, bssid, channel, hidden);
for(auto entry : _APlist) {
if(ssid == entry.ssid) {
// SSID match
known = true;
}
}
if (known) {
DEBUG_WIFI_MULTI(" --->");
} else {
DEBUG_WIFI_MULTI(" ");
}
DEBUG_WIFI_MULTI(" %d: [CH %02d] [%02X:%02X:%02X:%02X:%02X:%02X] %ddBm %c %s\n",
i,
channel,
bssid[0], bssid[1], bssid[2],
bssid[3], bssid[4], bssid[5],
rssi,
(encryptionType == ENC_TYPE_NONE) ? ' ' : '*',
ssid.c_str());
esp_yield();
}
#endif
}