-
Notifications
You must be signed in to change notification settings - Fork 13.3k
Feature request: wifimulti persistent support #2698
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
Comments
Has this been considered or dealt with elsewhere? |
Who is working on this enhancement? |
Nobody atm, current focus is on stabilizing 2.5.0. |
I would love this feature ! |
Is anybody willing to work on this ? |
should the available wifi's be stored in FLASH? |
@tablatronix? I thought this was just for forwarding the persistent flag from multi to wifi, but reading your original description says otherwise. |
Yes it is to make the wifimulti persistent, I have not tested the SDK functions above to test if this even works though. |
FYI: Yesterday my WiFi Multi redesign is merged into master. @devyte requested to look at this old request from @tablatronix and I made a proof of concept. Please find my results below: The ESP8266 contains an array to store up to 5 WiFi configurations to flash. You're right: After this change, new configurations can be saved to flash with The ESP82866 does not automatically connect to the next registered network on connection failure. It must be called manually with However: Saving WiFi credentials to flash contains a serious security issue. Consider the following use case:
// Fixed 5 configs
#define NUM_CONFIGS 5
void printConfigs()
{
struct station_config config[NUM_CONFIGS];
// Clear config
memset(config, 0, sizeof(config));
// Print configs
Serial.printf(PSTR("Get AP info: %s\n"), wifi_station_get_ap_info(config) ? "Ok" : "Failed");
for (uint8_t i = 0; i < NUM_CONFIGS; i++) {
Serial.printf(PSTR("Config %d:\n"), i);
Serial.printf(PSTR(" ssid: %s\n"), config[i].ssid);
Serial.printf(PSTR(" password: %s\n"), config[i].password);
Serial.printf(PSTR(" bssid_set: %d\n"), config[i].bssid_set);
Serial.printf(PSTR(" bssid: %02x:%02x:%02x:%02x:%02x\n"),
config[i].bssid[0], config[i].bssid[1], config[i].bssid[2],
config[i].bssid[3], config[i].bssid[4], config[i].bssid[5]);
Serial.printf(PSTR(" threshold.rssi: %d\n"), config[i].threshold.rssi);
Serial.printf(PSTR(" threshold.authmode: %d\n"), (uint8_t)config[i].threshold.authmode);
}
} Now all SSID's, passwords and MAC addresses are printed on the serial console in plain text which allows the hacker to come back and login to your WiFi networks... I know, security is a different topic, but I'm a little bit disappointed about this weak ESP8266 implementation. I also noticed that this applicable in the current master version. For this reason, I think saving the WiFi credentials in plain text to flash is not the way forward... It would be more save to store SSID/password in encrypted flash with What is your opinion? Official API documentation: Test sketch to experiment: #include <ESP8266WiFi.h>
#include <PolledTimeout.h>
#define NUM_CONFIGS 5
#define WIFI1_SSID ""
#define WIFI1_PASS ""
const uint8_t WIFI1_BSSID[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
#define WIFI2_SSID ""
#define WIFI2_PASS ""
const uint8_t WIFI2_BSSID[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
#define WIFI3_SSID ""
#define WIFI3_PASS ""
const uint8_t WIFI3_BSSID[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
#define WIFI4_SSID ""
#define WIFI4_PASS ""
const uint8_t WIFI4_BSSID[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
uint8_t configIndex;
void eraseConfig()
{
struct station_config config;
memset(&config, 0, sizeof(station_config));
// Get current config from flash
wifi_station_get_config_default(&config);
if (strlen((const char *)config.ssid) > 0) {
Serial.println(F("System restore..."));
system_restore();
Serial.println(F("System restart..."));
system_restart();
delay(1000);
Serial.println(F("Never reached!"));
}
}
void setAutoConnect(bool set)
{
if (wifi_station_get_auto_connect() != set) {
Serial.printf(PSTR("Set auto connect %d: %s"),
set, wifi_station_set_auto_connect(set) ? "OK" : "Failed");
}
Serial.printf(PSTR("Auto connect: %d\n"), wifi_station_get_auto_connect());
}
void saveConfig(const char *ssid, const char *password, const uint8_t *bssid=NULL)
{
struct station_config stationConf;
// The number of APs that can be recorded (MAX: 5)
if (wifi_station_ap_number_set(5)) {
Serial.println(F("wifi_station_ap_number_set(): OK"));
} else {
Serial.println(F("wifi_station_ap_number_set(): Failed"));
}
Serial.printf(PSTR("Saving: %s, %s\n"), ssid, password);
memset(&stationConf, 0, sizeof(stationConf));
os_memcpy(&stationConf.ssid, ssid, 32);
os_memcpy(&stationConf.password, password, 64);
if (bssid != NULL) {
os_memcpy(&stationConf.bssid, WIFI1_BSSID, 6);
stationConf.bssid_set = 1; // 1: Check MAC address AP
} else {
stationConf.bssid_set = 0; // 0: No check MAC address AP
}
wifi_station_set_config(&stationConf);
// Needed as it is not saved? Hmm, delay does not make sense...
delay(1000);
}
void printConfigs()
{
struct station_config config[NUM_CONFIGS];
// Clear config
memset(config, 0, sizeof(config));
// Print fixed 5 configs
Serial.printf(PSTR("Get AP info: %s\n"), wifi_station_get_ap_info(config) ? "Ok" : "Failed");
for (uint8_t i = 0; i < NUM_CONFIGS; i++) {
Serial.printf(PSTR("Config %d:\n"), i);
Serial.printf(PSTR(" ssid: %s\n"), config[i].ssid);
Serial.printf(PSTR(" password: %s\n"), config[i].password);
Serial.printf(PSTR(" bssid_set: %d\n"), config[i].bssid_set);
Serial.printf(PSTR(" bssid: %02x:%02x:%02x:%02x:%02x\n"),
config[i].bssid[0], config[i].bssid[1], config[i].bssid[2],
config[i].bssid[3], config[i].bssid[4], config[i].bssid[5]);
Serial.printf(PSTR(" threshold.rssi: %d\n"), config[i].threshold.rssi);
Serial.printf(PSTR(" threshold.authmode: %d\n"), (uint8_t)config[i].threshold.authmode);
}
// Get current API ID
configIndex = wifi_station_get_current_ap_id();
Serial.printf(PSTR("Current AP ID: %d\n"), configIndex);
}
void connectNextAP()
{
configIndex++;
configIndex %= NUM_CONFIGS;
Serial.printf(PSTR("\nConnecting to: %d\n"), configIndex);
wifi_station_ap_change(configIndex);
using esp8266::polledTimeout::oneShotMs;
oneShotMs connectTimeout(6000);
do {
delay(10);
} while (!connectTimeout && (WiFi.status() != WL_CONNECTED));
delay(1000);
}
void setup()
{
delay(500);
Serial.begin(115200);
// Erase config from flash
//eraseConfig();
// Set in station mode before config
WiFi.mode(WIFI_STA);
// Set auto connect
setAutoConnect(true);
// Save config
#if 0
saveConfig(WIFI1_SSID, WIFI1_PASS, NULL);
saveConfig(WIFI2_SSID, WIFI2_PASS, NULL);
saveConfig(WIFI3_SSID, WIFI3_PASS, NULL);
#endif
// Connect to config index 0..4
#if 0
// Force changing to other AP as this is not done automatically
if (wifi_station_ap_change(0)) {
Serial.println(F("wifi_station_ap_change(): OK"));
} else {
Serial.println(F("wifi_station_ap_change(): Failed"));
}
#endif
printConfigs();
}
void loop()
{
if (wifi_station_get_connect_status() == 5) {
Serial.printf(PSTR("Connected %s\n"), WiFi.SSID());
} else {
Serial.println(F("Disconnected"));
}
delay(1000);
if (WiFi.status() != WL_CONNECTED) {
connectNextAP();
}
} |
This is unrelated, and it entirely doable right now simply by just dumping flash, but a good point to bring up in an esp sdk issue, there is no reason it cannot be hashed, but its hard to hide them if the user has the device.. |
@tablatronix and @devyte, I dived deep into the SDK:
This is correct.
Correct: The ESP8266 can use one of the registered WiFi AP configurations.
Partially correct:
Note: AP configurations can be erased with
The SDK does not provide an interface to directly address the index of an AP config. It writes a new config incrementally to a new index: 0, 1, 2, 3, 4, wrapping to 0, etc. Limitation: There is no SDK API to retrieve the number of AP's, set by Saving multi WiFi configs (max 5) to RAM works as expected ( Problem: I encountered a problem to read the saved WiFi configs (max 5) from flash after calling
The consequence is that the config index of the SSID/password is unknown after a A workaround is to reset the CPU with Test case for persistent to save to flash:
Due too this limitation, I'm not sure how to proceed. My test code (5 AP's persistent to save to flash): #include <IPAddress.h>
#include <PolledTimeout.h>
extern "C" {
#include "user_interface.h"
}
// Configure 5 WiFi AP's
#define WIFI_SSID1 ""
#define WIFI_PASS1 ""
#define WIFI_SSID2 ""
#define WIFI_PASS2 ""
#define WIFI_SSID3 ""
#define WIFI_PASS3 ""
#define WIFI_SSID4 ""
#define WIFI_PASS4 ""
#define WIFI_SSID5 ""
#define WIFI_PASS5 ""
#define NUM_APS 5 // MAX 5
#if NUM_APS > 5
#error "Max AP's is fixed to 5 in the SDK"
#endif
// MAKE SURE TO POWER-CYCLE AFTER FLASHING THIS SKETCH
// false: Save AP configs to RAM works!
// true: Save AP configs to flash does NOT work
#define WIFI_PERSISTENT true
typedef enum WiFiMode
{
WIFI_STA = 1,
WIFI_AP = 2,
WIFI_AP_STA = 3
} WiFiMode_t;
void restore()
{
struct station_config config;
// Factory reset:
// - wifi_station_set_auto_connect
// - wifi_set_phy_mode
// - wifi_softap_set_config related
// - wifi_station_set_configrelated
// - wifi_set_opmode
// Clear station config
memset(&config, 0, sizeof(station_config));
if (!wifi_station_get_config(& config)) {
Serial.println(F("Failed to retrieve station config"));
return;
}
if (strlen((const char *)config.ssid)) {
// Get current config from flash
if (wifi_station_get_config_default(&config)) {
Serial.println(F("Erasing WiFi credentials..."));
Serial.println(F("System restore..."));
system_restore();
Serial.println(F("System restart..."));
system_restart();
// The ESP8266 will not restart immediately.
// Do not call other functions after calling this API.
while (1) {
;
}
} else {
Serial.println(F("Load WiFi config failed"));
}
} else {
Serial.println(F("No WiFi configs found"));
}
}
void setAPNumber(uint8_t ap_number)
{
if (!wifi_station_ap_number_set(ap_number)) {
Serial.println(F("AP set failed"));
} else {
Serial.printf(PSTR("AP number set: %d\n"), ap_number);
}
}
void setMode(uint8_t mode)
{
if (wifi_fpm_get_sleep_type() == MODEM_SLEEP_T) {
Serial.println(F("Wakeup"));
wifi_fpm_do_wakeup();
wifi_fpm_close();
}
if (mode <= 3) {
if (WIFI_PERSISTENT) {
if (!wifi_set_opmode(mode)) {
Serial.println(F("Mode set flash failed"));
return;
}
} else {
if (!wifi_set_opmode_current(mode)) {
Serial.println(F("Mode set failed"));
return;
}
}
} else {
Serial.println(PSTR("Mode must be 1..3")); // Mode 0?
return;
}
Serial.printf(PSTR("Mode set: %d\n"), wifi_get_opmode());
}
void setCfg(const char *ssid, const char *password, uint8_t rssi=0)
{
struct station_config stationConf;
Serial.printf(PSTR("WiFi add config %s\n"), ssid);
memset(&stationConf, 0, sizeof(stationConf));
os_memcpy(&stationConf.ssid, ssid, 32);
os_memcpy(&stationConf.password, password, 64);
stationConf.threshold.rssi = rssi;
if (WIFI_PERSISTENT) {
// Set Wi-Fi Station configuration; save to flash.
if (!wifi_station_set_config(&stationConf)) {
Serial.println(F("Set config failed"));
return;
}
} else {
// Set Wi-Fi Station configuration; setting in flash is not updated.
if (!wifi_station_set_config_current(&stationConf)) {
Serial.println(F("Set config failed"));
return;
}
}
}
IPAddress localIP()
{
struct ip_info ip;
wifi_get_ip_info(STATION_IF, &ip);
return IPAddress(ip.ip.addr);
}
const char *SSID()
{
struct station_config conf;
// SSID can be up to 32chars, => plus null term
static char tmp[33];
if (wifi_station_get_config(&conf)) {
memcpy(tmp, conf.ssid, sizeof(conf.ssid));
// Nullterm in case of 32 char ssid
tmp[32] = '\0';
} else {
Serial.println(F("SSID get config failed"));
tmp[0] = '\0';
}
return tmp;
}
uint8_t *BSSID()
{
static struct station_config conf;
wifi_station_get_config(&conf);
return reinterpret_cast<uint8_t*>(conf.bssid);
}
int8_t RSSI()
{
return wifi_station_get_rssi();
}
uint8_t channel()
{
return wifi_get_channel();
}
void wifiPrintStatus()
{
IPAddress ip;
uint8_t *mac;
station_status_t status;
status = wifi_station_get_connect_status();
switch (status) {
case STATION_IDLE:
Serial.println(F("WiFi idle."));
break;
case STATION_CONNECTING:
Serial.println(F("WiFi connecting."));
break;
case STATION_WRONG_PASSWORD:
Serial.println(F("WiFi wrong password."));
break;
case STATION_NO_AP_FOUND:
Serial.println(F("WiFi no AP found."));
break;
case STATION_CONNECT_FAIL:
Serial.println(F("WiFi station connect fail."));
break;
case STATION_GOT_IP:
ip = localIP();
mac = BSSID();
Serial.println(F("Connected:"));
Serial.printf(PSTR(" SSID: %s\n"), SSID());
Serial.printf(PSTR(" IP: %d.%d.%d.%d\n"), ip[0], ip[1], ip[2], ip[3]);
Serial.printf(PSTR(" MAC: %02X:%02X:%02X:%02X:%02X:%02X\n"),
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
Serial.printf(PSTR(" CH: %d\n"), channel());
Serial.printf(PSTR(" RSSI: %d\n"), RSSI());
break;
}
}
void wifiWaitConnected()
{
using esp8266::polledTimeout::oneShotMs;
oneShotMs connectTimeout(10000);
do {
delay(10);
if (wifi_station_get_connect_status() == STATION_GOT_IP) {
wifiPrintStatus();
return;
}
} while (!connectTimeout);
Serial.println(F("Timeout"));
}
void wifiConnect()
{
Serial.println(F("WiFi connect..."));
// Connect Wi-Fi Station to AP.
if (!wifi_station_connect()) {
Serial.println(F("Station connect failed"));
return;
}
wifiWaitConnected();
}
void cmdWiFiAPChangeIndex(uint8_t ap_index)
{
if (ap_index < 5) {
Serial.println(F("Disconnecting"));
if (!wifi_station_disconnect()) {
Serial.println(F("Disconnect failed"));
}
delay(200);
if (!wifi_station_ap_change(ap_index)) {
Serial.println(F("AP change failed"));
} else {
Serial.printf(PSTR("Connecting AP index %d...\n"), ap_index);
wifiWaitConnected();
wifiAPPrint();
}
} else {
Serial.println(PSTR("AP index must be 1..5"));
}
}
void wifiAPPrint()
{
// Fixed number of WiFi configurations
const uint8_t numConfigs = 5;
struct station_config config[numConfigs];
// Clear config
memset(config, 0, sizeof(config));
// Print configs
if (wifi_station_get_ap_info(config)) {
for (uint8_t i = 0; i < numConfigs; i++) {
Serial.printf(PSTR("Config %d:\n"), i);
Serial.printf(PSTR(" ssid: %s\n"), config[i].ssid);
Serial.printf(PSTR(" password: %s\n"), config[i].password);
Serial.printf(PSTR(" bssid_set: %d\n"), config[i].bssid_set);
Serial.printf(PSTR(" bssid: %02x:%02x:%02x:%02x:%02x\n"),
config[i].bssid[0], config[i].bssid[1], config[i].bssid[2],
config[i].bssid[3], config[i].bssid[4], config[i].bssid[5]);
Serial.printf(PSTR(" threshold.rssi: %d\n"), config[i].threshold.rssi);
Serial.printf(PSTR(" threshold.authmode: %d\n"), (uint8_t)config[i].threshold.authmode);
}
Serial.printf(PSTR("AP index: %d\n"), wifi_station_get_current_ap_id());
} else {
Serial.println(F("Load AP info failed"));
}
// Print WiFi status
wifiPrintStatus();
}
void setup()
{
delay(500);
Serial.begin(115200);
Serial.println(F("\nESP8266 Auto Connect proof of concept"));
// Erase AP config (reset CPU once)
// Erase all SSID's/passwords, Mode=2, Auto connect = 1
restore();
// Set station mode
setMode(1);
//delay(1000); // Delay is not needed
// Set AP number
setAPNumber(NUM_APS);
//delay(250); // Delay is not needed
// Add WiFi AP logins (max 5)
setCfg(WIFI_SSID1, WIFI_PASS1, 0);
//delay(250); // Delay is not needed
setCfg(WIFI_SSID2, WIFI_PASS2, 0);
//delay(250); // Delay is not needed
setCfg(WIFI_SSID3, WIFI_PASS3, 0);
//delay(250); // Delay is not needed
setCfg(WIFI_SSID4, WIFI_PASS4, 0);
//delay(250); // Delay is not needed
setCfg(WIFI_SSID5, WIFI_PASS5, 0);
//delay(250); // Delay is not needed
//delay(1000); // Delay is not needed
// Print config
wifiAPPrint();
// Hmmm, AP configs not read from flash!
// Only the last saved config is read at index 0.
// All AP configs are correct after a CPU reset.
while (1) {
delay(100);
}
// WiFi connect
wifiConnect();
// Print config
wifiAPPrint();
}
void loop()
{
for (uint8_t i = 0; i < NUM_APS; i++) {
delay(30000);
cmdWiFiAPChangeIndex(i);
}
} Output shows only the last AP config on index 0 which is wrong:
|
Wow you've done a great job with this, very thorough |
Thanks! Now finding a solution. :-) |
Hi . excellent Thanks |
To save the access point, I wrote the following code. But when a new access point is saved, the previous access point is lost |
@Abedi98 The easiest way is to use the WiFiMulti example in master: |
I do not want to do this as hard code in setup. like this :
|
@d-a-v, yes you can close the feature requests. |
Add sdk persistent support for wifimulti.
Some info from @kentaylor
kentaylor commented on May 10
ref: tzapu/WiFiManager#166
The text was updated successfully, but these errors were encountered: