Skip to content

Wifi multi network support for esp32/esp8266/PicoW #601

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 22 commits into from
Jul 23, 2024
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/Wippersnapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2479,7 +2479,7 @@ void Wippersnapper::haltError(String error, ws_led_status_t ledStatusColor) {
#else
// Calls to delay() and yield() feed the ESP8266's
// hardware and software watchdog timers, delayMicroseconds does not.
delayMicroseconds(1000);
delayMicroseconds(1000000);
#endif
}
}
Expand Down
1 change: 1 addition & 0 deletions src/Wippersnapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,7 @@ class Wippersnapper {
Adafruit_MQTT *_mqtt; /*!< Reference to Adafruit_MQTT, _mqtt. */

secretsConfig _config; /*!< Wippersnapper secrets.json as a struct. */
networkConfig _multiNetworks[3]; /*!< Wippersnapper networks as structs. */

// TODO: Does this need to be within this class?
int32_t totalDigitalPins; /*!< Total number of digital-input capable pins */
Expand Down
41 changes: 37 additions & 4 deletions src/network_interfaces/Wippersnapper_ESP32.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "Adafruit_MQTT_Client.h"
#include "Arduino.h"
#include "WiFi.h"
#include "WiFiMulti.h"
#include <WiFiClientSecure.h>
extern Wippersnapper WS;

Expand Down Expand Up @@ -109,7 +110,14 @@ class Wippersnapper_ESP32 : public Wippersnapper {

// Was the network within secrets.json found?
for (int i = 0; i < n; ++i) {
if (strcmp(_ssid, WiFi.SSID(i).c_str()) == 0)
if (strlen(WS._multiNetworks[0].ssid) > 0) {
// multi network mode
for (int j = 0; j < 5; j++) {
if (strcmp(WS._multiNetworks[j].ssid, WiFi.SSID(i).c_str()) == 0)
return true;
}
} // else single network mode
else if (strcmp(_ssid, WiFi.SSID(i).c_str()) == 0)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider changing this to else to avoid doing an extra call to strcmp and c_str

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The normal network ssid doesn't get added to the altNetworks, so this check needs to still happen.
It has just occurred to me that I failed to test one eventuality that the primary ssid could connect when none of the altNetworks were found (which would fail). I always had some alt networks in range.

I should move this to be a separate check, dropping the else, probably above the multi-network/altNetworks check as that will be the less common path.

return true;
}

Expand Down Expand Up @@ -191,6 +199,7 @@ class Wippersnapper_ESP32 : public Wippersnapper {
const char *_ssid; ///< WiFi SSID
const char *_pass; ///< WiFi password
WiFiClientSecure *_mqtt_client; ///< Pointer to a WiFi client object (TLS/SSL)
WiFiMulti _wifiMulti; ///< WiFiMulti object for multi-network mode

const char *_aio_root_ca_staging =
"-----BEGIN CERTIFICATE-----\n"
Expand Down Expand Up @@ -262,11 +271,35 @@ class Wippersnapper_ESP32 : public Wippersnapper {
if (strlen(_ssid) == 0) {
_status = WS_SSID_INVALID;
} else {
WiFi.setAutoReconnect(false);
_disconnect();
delay(100);
WiFi.begin(_ssid, _pass);
_status = WS_NET_DISCONNECTED;
delay(5000);
if (strlen(WS._multiNetworks[0].ssid) > 0) {
// multi network mode
_wifiMulti.APlistClean();
_wifiMulti.setAllowOpenAP(false);
// add default network
_wifiMulti.addAP(_ssid, _pass);
// add array of alternative networks
for (int i = 0; i < 3; i++) {
if (strlen(WS._multiNetworks[i].ssid) > 0) {
_wifiMulti.addAP(WS._multiNetworks[i].ssid,
WS._multiNetworks[i].pass);
}
}
if (_wifiMulti.run(20000) == WL_CONNECTED) {
_status = WS_NET_CONNECTED;
} else {
_status = WS_NET_DISCONNECTED;
}
} else {
// single network mode
WiFi.begin(_ssid, _pass);
_status = WS_NET_DISCONNECTED;
WS.feedWDT();
delay(5000);
}
WS.feedWDT();
}
}

Expand Down
83 changes: 68 additions & 15 deletions src/network_interfaces/Wippersnapper_ESP8266.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "Adafruit_MQTT.h"
#include "Adafruit_MQTT_Client.h"
#include "ESP8266WiFi.h"
#include "ESP8266WiFiMulti.h"
#include "Wippersnapper.h"

/* NOTE - Projects that require "Secure MQTT" (TLS/SSL) also require a new
Expand Down Expand Up @@ -64,6 +65,9 @@ class Wippersnapper_ESP8266 : public Wippersnapper {
_ssid = 0;
_pass = 0;
_wifi_client = new WiFiClient;
WiFi.persistent(false);
WiFi.mode(WIFI_STA);
// _wifiMulti
}

/**************************************************************************/
Expand Down Expand Up @@ -132,7 +136,14 @@ class Wippersnapper_ESP8266 : public Wippersnapper {

// Was the network within secrets.json found?
for (int i = 0; i < n; ++i) {
if (strcmp(_ssid, WiFi.SSID(i).c_str()) == 0)
if (strlen(WS._multiNetworks[0].ssid) > 0) {
// multi network mode
for (int j = 0; j < 3; j++) {
if (strcmp(WS._multiNetworks[j].ssid, WiFi.SSID(i).c_str()) == 0)
return true;
}
} // else single network mode
else if (strcmp(_ssid, WiFi.SSID(i).c_str()) == 0)
return true;
}

Expand Down Expand Up @@ -211,6 +222,7 @@ class Wippersnapper_ESP8266 : public Wippersnapper {
const char *_ssid = NULL;
const char *_pass = NULL;
WiFiClient *_wifi_client;
ESP8266WiFiMulti _wifiMulti;

/**************************************************************************/
/*!
Expand All @@ -222,21 +234,62 @@ class Wippersnapper_ESP8266 : public Wippersnapper {
if (WiFi.status() == WL_CONNECTED)
return;

// Attempt connection
_disconnect();
delay(100);
// ESP8266 MUST be in STA mode to avoid device acting as client/server
WiFi.mode(WIFI_STA);
WiFi.begin(_ssid, _pass);
_status = WS_NET_DISCONNECTED;
delay(100);
if (strlen(_ssid) == 0) {
_status = WS_SSID_INVALID;
} else {
WiFi.setAutoReconnect(false);
// Attempt connection
_disconnect();
delay(100);
// ESP8266 MUST be in STA mode to avoid device acting as client/server
WiFi.mode(WIFI_STA);
WiFi.begin(_ssid, _pass);
_status = WS_NET_DISCONNECTED;
delay(100);

if (strlen(WS._multiNetworks[0].ssid) > 0) {
// multi network mode
for (int i = 0; i < 5; i++) {
if (strlen(WS._multiNetworks[i].ssid) > 0 &&
(_wifiMulti.existsAP(WS._multiNetworks[i].ssid) == false)) {
// doesn't exist, add it
_wifiMulti.addAP(WS._multiNetworks[i].ssid,
WS._multiNetworks[i].pass);
}
}
// add default network
if (_wifiMulti.existsAP(_ssid) == false) {
_wifiMulti.addAP(_ssid, _pass);
}
long startRetry = millis();
WS_DEBUG_PRINTLN("CONNECTING");
while (_wifiMulti.run(5000) != WL_CONNECTED &&
millis() - startRetry < 10000) {
// ESP8266 WDT requires yield() during a busy-loop so it doesn't bite
yield();
}
if (_wifiMulti.run(5000) == WL_CONNECTED) {
_status = WS_NET_CONNECTED;
} else {
_status = WS_NET_DISCONNECTED;
}
} else {
// single network mode

// wait for a connection to be established
long startRetry = millis();
WS_DEBUG_PRINTLN("CONNECTING");
while (WiFi.status() != WL_CONNECTED && millis() - startRetry < 10000) {
// ESP8266 WDT requires yield() during a busy-loop so it doesn't bite
yield();
// wait for a connection to be established
long startRetry = millis();
WS_DEBUG_PRINTLN("CONNECTING");
while (WiFi.status() != WL_CONNECTED && millis() - startRetry < 10000) {
// ESP8266 WDT requires yield() during a busy-loop so it doesn't bite
yield();
}
if (WiFi.status() == WL_CONNECTED) {
_status = WS_NET_CONNECTED;
} else {
_status = WS_NET_DISCONNECTED;
}
}
WS.feedWDT();
}
}

Expand Down
52 changes: 40 additions & 12 deletions src/network_interfaces/ws_networking_pico.h
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,14 @@ class ws_networking_pico : public Wippersnapper {

// Was the network within secrets.json found?
for (int i = 0; i < n; ++i) {
if (strcmp(_ssid, WiFi.SSID(i)) == 0)
if (strlen(WS._multiNetworks[0].ssid) > 0) {
// multi network mode
for (int j = 0; j < 3; j++) {
if (strcmp(WS._multiNetworks[j].ssid, WiFi.SSID(i)) == 0)
return true;
}
} // else single network mode
else if (strcmp(_ssid, WiFi.SSID(i)) == 0)
return true;
}

Expand Down Expand Up @@ -191,6 +198,7 @@ class ws_networking_pico : public Wippersnapper {
const char *_ssid; ///< WiFi SSID
const char *_pass; ///< WiFi password
WiFiClientSecure *_mqtt_client; ///< Pointer to a secure MQTT client object
WiFiMulti _wifiMulti; ///< WiFiMulti object for multi-network mode

const char *_aio_root_ca_staging =
"-----BEGIN CERTIFICATE-----\n"
Expand Down Expand Up @@ -259,27 +267,47 @@ class ws_networking_pico : public Wippersnapper {
if (WiFi.status() == WL_CONNECTED)
return;

WiFi.mode(WIFI_STA);
WS.feedWDT();
WiFi.setTimeout(20000);
WS.feedWDT();

if (strlen(_ssid) == 0) {
_status = WS_SSID_INVALID;
} else {
_disconnect();
delay(5000);
WS.feedWDT();
WiFi.mode(WIFI_STA);
WS.feedWDT();
WiFi.setTimeout(20000);
WS.feedWDT();
WiFi.begin(_ssid, _pass);
// Wait setTimeout duration for a connection and check if connected every
// 5 seconds
for (int i = 0; i < 4; i++) {
WS.feedWDT();
delay(5000);
if (strlen(WS._multiNetworks[0].ssid) > 0) {
// multi network mode
_wifiMulti.clearAPList();
// add default network
_wifiMulti.addAP(_ssid, _pass);
// add array of alternative networks
for (int i = 0; i < 5; i++) {
_wifiMulti.addAP(WS._multiNetworks[i].ssid,
WS._multiNetworks[i].pass);
}
WS.feedWDT();
if (WiFi.status() == WL_CONNECTED) {
if (_wifiMulti.run(10000) == WL_CONNECTED) {
WS.feedWDT();
_status = WS_NET_CONNECTED;
return;
}
WS.feedWDT();
} else {
WiFi.begin(_ssid, _pass);
// Wait setTimeout duration for a connection and check if connected
// every 5 seconds
for (int i = 0; i < 4; i++) {
WS.feedWDT();
delay(5000);
WS.feedWDT();
if (WiFi.status() == WL_CONNECTED) {
_status = WS_NET_CONNECTED;
return;
}
}
}
_status = WS_NET_DISCONNECTED;
}
Expand Down
3 changes: 3 additions & 0 deletions src/provisioning/ConfigJson.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
#include "Config.h"
#include <ArduinoJson.h>

// Converters for network configuration
void convertToJson(const networkConfig &src, JsonVariant dst);
void convertFromJson(JsonVariantConst src, networkConfig &dst);
// Converters for secrets configuration
void convertToJson(const secretsConfig &src, JsonVariant dst);
void convertFromJson(JsonVariantConst src, secretsConfig &dst);
Expand Down
42 changes: 42 additions & 0 deletions src/provisioning/littlefs/WipperSnapper_LittleFS.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,48 @@ void WipperSnapper_LittleFS::parseSecrets() {
fsHalt(String("ERROR: deserializeJson() failed with code ") +
error.c_str());
}
if (doc.containsKey("network_type_wifi")) {
// set default network config
convertFromJson(doc["network_type_wifi"], WS._config.network);

if (!doc["network_type_wifi"].containsKey("alternative_networks")) {
// do nothing extra, we already have the only network
WS_DEBUG_PRINTLN("Found single wifi network in secrets.json");

} else if (doc["network_type_wifi"]["alternative_networks"]
.is<JsonArray>()) {

WS_DEBUG_PRINTLN("Found multiple wifi networks in secrets.json");
// Parse network credentials from array in secrets
JsonArray networks = doc["network_type_wifi"]["alternative_networks"];
int8_t networkCount = networks.size();
WS_DEBUG_PRINT("Network count: ");
WS_DEBUG_PRINTLN(networkCount);
if (networkCount == 0) {
fsHalt(
"ERROR: No networks found in network_type_wifi in secrets.json!");
}
// check if over 5, warn user and take first five
for (int i = 0; i < networkCount; i++) {
if (i >= 3) {
WS_DEBUG_PRINT("WARNING: More than 3 networks in secrets.json, "
"only the first 3 will be used. Not using ");
WS_DEBUG_PRINTLN(networks[i]["network_ssid"].as<const char *>());
break;
}
convertFromJson(networks[i], WS._multiNetworks[i]);
WS_DEBUG_PRINT("Added SSID: ");
WS_DEBUG_PRINTLN(WS._multiNetworks[i].ssid);
WS_DEBUG_PRINT("PASS: ");
WS_DEBUG_PRINTLN(WS._multiNetworks[i].pass);
}
} else {
fsHalt("ERROR: Unrecognised value type for "
"network_type_wifi.alternative_networks in secrets.json!");
}
} else {
fsHalt("ERROR: Could not find network_type_wifi in secrets.json!");
}

// Extract a config struct from the JSON document
WS._config = doc.as<secretsConfig>();
Expand Down
Loading
Loading