From 72dc2d774f44ecbf2bec455d9be5656c4b61b836 Mon Sep 17 00:00:00 2001 From: David Gauchard Date: Mon, 15 Jul 2019 15:57:49 +0200 Subject: [PATCH 01/12] add new WiFimodes: WIFI_SHUTDOWN & WIFI_RESUME with example restore WiFi.onWiFiModeChange() add crc32() in core --- cores/esp8266/coredecls.h | 2 + cores/esp8266/crc32.cpp | 23 ++ .../ESP8266WiFi/src/ESP8266WiFiGeneric.cpp | 217 ++++++++++++--- .../ESP8266WiFi/src/ESP8266WiFiGeneric.h | 13 +- libraries/ESP8266WiFi/src/ESP8266WiFiSTA.cpp | 10 +- libraries/ESP8266WiFi/src/ESP8266WiFiType.h | 5 +- libraries/ESP8266WiFi/src/include/WiFiState.h | 23 ++ .../examples/WiFiShutdown/WiFiShutdown.ino | 248 ++++++++++++++++++ 8 files changed, 505 insertions(+), 36 deletions(-) create mode 100644 cores/esp8266/crc32.cpp create mode 100644 libraries/ESP8266WiFi/src/include/WiFiState.h create mode 100644 libraries/esp8266/examples/WiFiShutdown/WiFiShutdown.ino diff --git a/cores/esp8266/coredecls.h b/cores/esp8266/coredecls.h index 3ac87eee9f..e04df9e473 100644 --- a/cores/esp8266/coredecls.h +++ b/cores/esp8266/coredecls.h @@ -8,6 +8,7 @@ extern "C" { // TODO: put declarations here, get rid of -Wno-implicit-function-declaration +#include #include #include // g_pcont declaration @@ -20,6 +21,7 @@ void settimeofday_cb (void (*cb)(void)); void disable_extra4k_at_link_time (void) __attribute__((noinline)); uint32_t sqrt32 (uint32_t n); +uint32_t crc32 (const void* data, size_t length); #ifdef __cplusplus } diff --git a/cores/esp8266/crc32.cpp b/cores/esp8266/crc32.cpp new file mode 100644 index 0000000000..34b0d3f33b --- /dev/null +++ b/cores/esp8266/crc32.cpp @@ -0,0 +1,23 @@ + +#include "coredecls.h" + +// taken from esp8266/examples/RTCUserMemory/RTCUserMemory.ino +uint32_t crc32 (const void* data, size_t length) +{ + const uint8_t* ldata = (const uint8_t*)data; + uint32_t crc = 0xffffffff; + while (length--) + { + uint8_t c = *ldata++; + for (uint32_t i = 0x80; i > 0; i >>= 1) + { + bool bit = crc & 0x80000000; + if (c & i) + bit = !bit; + crc <<= 1; + if (bit) + crc ^= 0x04c11db7; + } + } + return crc; +} diff --git a/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp b/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp index 31daf370a0..5acff80b18 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp +++ b/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp @@ -24,6 +24,7 @@ #include #include +#include #include "ESP8266WiFi.h" #include "ESP8266WiFiGeneric.h" @@ -38,12 +39,15 @@ extern "C" { #include "lwip/opt.h" #include "lwip/err.h" #include "lwip/dns.h" +#include "lwip/dhcp.h" +#include "lwip/apps/sntp.h" #include "lwip/init.h" // LWIP_VERSION_ } #include "WiFiClient.h" #include "WiFiUdp.h" #include "debug.h" +#include "include/WiFiState.h" extern "C" void esp_schedule(); extern "C" void esp_yield(); @@ -200,15 +204,15 @@ WiFiEventHandler ESP8266WiFiGenericClass::onSoftAPModeProbeRequestReceived(std:: return handler; } -// WiFiEventHandler ESP8266WiFiGenericClass::onWiFiModeChange(std::function f) -// { -// WiFiEventHandler handler = std::make_shared(WIFI_EVENT_MODE_CHANGE, [f](System_Event_t* e){ -// WiFiEventModeChange& dst = *reinterpret_cast(&e->event_info); -// f(dst); -// }); -// sCbEventList.push_back(handler); -// return handler; -// } +WiFiEventHandler ESP8266WiFiGenericClass::onWiFiModeChange(std::function f) +{ + WiFiEventHandler handler = std::make_shared(WIFI_EVENT_MODE_CHANGE, [f](System_Event_t* e){ + WiFiEventModeChange& dst = *reinterpret_cast(&e->event_info); + f(dst); + }); + sCbEventList.push_back(handler); + return handler; +} /** * callback for WiFi events @@ -227,7 +231,7 @@ void ESP8266WiFiGenericClass::_eventCallback(void* arg) for(auto it = std::begin(sCbEventList); it != std::end(sCbEventList); ) { WiFiEventHandler &handler = *it; if (handler->canExpire() && handler.unique()) { - it = sCbEventList.erase(it); + it = sCbEventList.erase(it); } else { (*handler)(event); @@ -386,22 +390,36 @@ bool ESP8266WiFiGenericClass::getPersistent(){ * set new mode * @param m WiFiMode_t */ -bool ESP8266WiFiGenericClass::mode(WiFiMode_t m) { +bool ESP8266WiFiGenericClass::mode(WiFiMode_t m, WiFiState* state) { + if (m == WIFI_SHUTDOWN) { + return shutdown(0, state); + } + else if (m == WIFI_RESUME) { + return resumeFromShutdown(state); + } + else if (m & ~(WIFI_STA | WIFI_AP)) + // any other bits than legacy disallowed + return false; + + // m is now WIFI_STA, WIFI_AP or WIFI_AP_STA + + if (wifi_fpm_get_sleep_type() != NONE_SLEEP_T) { + // wifi may have been put asleep by ESP8266WiFiGenericClass::preinitWiFiOff + wifi_fpm_do_wakeup(); + wifi_fpm_close(); + } + if(_persistent){ if(wifi_get_opmode() == (uint8) m && wifi_get_opmode_default() == (uint8) m){ + saveState(state, m, _persistent); return true; } } else if(wifi_get_opmode() == (uint8) m){ + saveState(state, m, _persistent); return true; } bool ret = false; - - if (m != WIFI_STA && m != WIFI_AP_STA) - // calls lwIP's dhcp_stop(), - // safe to call even if not started - wifi_station_dhcpc_stop(); - ETS_UART_INTR_DISABLE(); if(_persistent) { ret = wifi_set_opmode(m); @@ -431,15 +449,13 @@ bool ESP8266WiFiGenericClass::enableSTA(bool enable) { WiFiMode_t currentMode = getMode(); bool isEnabled = ((currentMode & WIFI_STA) != 0); - if(isEnabled != enable) { - if(enable) { - return mode((WiFiMode_t)(currentMode | WIFI_STA)); - } else { - return mode((WiFiMode_t)(currentMode & (~WIFI_STA))); - } - } else { + if (isEnabled == enable) return true; - } + + if (enable) + return mode((WiFiMode_t)(currentMode | WIFI_STA)); + + return mode((WiFiMode_t)(currentMode & (~WIFI_STA))); } /** @@ -472,16 +488,29 @@ bool ESP8266WiFiGenericClass::enableAP(bool enable){ bool ESP8266WiFiGenericClass::forceSleepBegin(uint32 sleepUs) { _forceSleepLastMode = getMode(); if(!mode(WIFI_OFF)) { + DEBUG_WIFI("core: error with mode(WIFI_OFF)\n"); return false; } - if(sleepUs == 0) { + if(sleepUs == 0 || sleepUs > 0xFFFFFFF) { sleepUs = 0xFFFFFFF; } wifi_fpm_set_sleep_type(MODEM_SLEEP_T); + delay(0); wifi_fpm_open(); - return (wifi_fpm_do_sleep(sleepUs) == 0); + delay(0); + auto ret = wifi_fpm_do_sleep(sleepUs); + if (ret != 0) + { + DEBUG_WIFI("core: error %d with wifi_fpm_do_sleep: (-1=sleep status error, -2=force sleep not enabled)\n", ret); + return false; + } + // fpm_is_open() is always 1 here, with or without delay + // wifi_fpm_set_wakeup_cb(cb): callback is never called + // no power reduction without this delay + delay(10); + return true; } /** @@ -489,8 +518,10 @@ bool ESP8266WiFiGenericClass::forceSleepBegin(uint32 sleepUs) { * @return ok */ bool ESP8266WiFiGenericClass::forceSleepWake() { - wifi_fpm_do_wakeup(); - wifi_fpm_close(); + if (wifi_fpm_get_sleep_type() != NONE_SLEEP_T) { + wifi_fpm_do_wakeup(); + wifi_fpm_close(); + } // restore last mode if(mode(_forceSleepLastMode)) { @@ -600,7 +631,133 @@ void wifi_dns_found_callback(const char *name, CONST ip_addr_t *ipaddr, void *ca esp_schedule(); // resume the hostByName function } -//meant to be called from user-defined preinit() +uint32_t ESP8266WiFiGenericClass::shutdownCRC (const WiFiState* state) +{ + return state? crc32(&state->state, sizeof(state->state)): 0; +} + +bool ESP8266WiFiGenericClass::shutdownValidCRC (const WiFiState* state) +{ + return state && (crc32(&state->state, sizeof(state->state)) == state->crc); +} + +bool ESP8266WiFiGenericClass::shutdown (uint32 sleepUs, WiFiState* state) +{ + bool persistent = _persistent; + WiFiMode_t before_off_mode = getMode(); + + if ((before_off_mode & WIFI_STA) && state) + { + bool ret = wifi_get_ip_info(STATION_IF, &state->state.ip); + if (!ret) + { + DEBUG_WIFI("core: error with wifi_get_ip_info(STATION_IF)\n"); + return false; + } + memset(state->state.fwconfig.bssid, 0xff, 6); + ret = wifi_station_get_config(&state->state.fwconfig); + if (!ret) + { + DEBUG_WIFI("core: error with wifi_station_get_config\n"); + return false; + } + state->state.channel = wifi_get_channel(); + } + + // disable persistence in FW so in case of power failure + // it doesn't wake up in off mode. + // persistence state will be restored on WiFi resume. + WiFi.persistent(false); + if (!WiFi.forceSleepBegin(sleepUs)) + { + // WIFI_OFF mode set by forceSleepBegin() + DEBUG_WIFI("core: error with forceSleepBegin()\n"); + WiFi.mode(before_off_mode); + WiFi.persistent(persistent); + return false; + } + saveState(state, before_off_mode, persistent); + return true; +} + +void ESP8266WiFiGenericClass::saveState (WiFiState* state, WiFiMode_t mode, bool persistent) +{ + if (state) + { + state->state.persistent = persistent; + state->state.mode = mode; + uint8_t i = 0; + for (auto& ntp: state->state.ntp) + ntp = *sntp_getserver(i++); + i = 0; + for (auto& dns: state->state.dns) + dns = WiFi.dnsIP(i++); + state->crc = shutdownCRC(state); + DEBUG_WIFI("core: state is saved\n"); + } +} + +bool ESP8266WiFiGenericClass::resumeFromShutdown (WiFiState* state) +{ + if (wifi_fpm_get_sleep_type() != NONE_SLEEP_T) { + wifi_fpm_do_wakeup(); + wifi_fpm_close(); + } + + if (!state || shutdownCRC(state) != state->crc) + { + DEBUG_WIFI("core: resume: no state or bad crc\n"); + return false; + } + + persistent(state->state.persistent); + + if (!mode(state->state.mode)) + { + DEBUG_WIFI("core: resume: can't set wifi mode to %d\n", state->state.mode); + return false; + } + + if (state->state.mode & WIFI_STA) + { + IPAddress local(state->state.ip.ip); + if (local) + { + DEBUG_WIFI("core: resume: static address '%s'\n", local.toString().c_str()); + WiFi.config(state->state.ip.ip, state->state.ip.gw, state->state.ip.netmask, state->state.dns[0], state->state.dns[1]); + uint8_t i = 0; + for (const auto& ntp: state->state.ntp) + { + IPAddress ip(ntp); + if (ip.isSet()) + { + DEBUG_WIFI("core: resume: start SNTP, server='%s'\n", ip.toString().c_str()); + sntp_setserver(i++, &ntp); + } + } + } + // state->state.fwconfig.bssid is not real bssid (it's what user may have provided when bssid_set==1) + if (WiFi.begin((const char*)state->state.fwconfig.ssid, + (const char*)state->state.fwconfig.password, + state->state.channel, + nullptr/*(const uint8_t*)state->state.fwconfig.bssid*/, // <- try with gw's mac address? + true) == WL_CONNECT_FAILED) + { + DEBUG_WIFI("core: resume: WiFi.begin failed\n"); + return false; + } + } + + if (state->state.mode & WIFI_AP) + { + DEBUG_WIFI("core: resume AP mode TODO\n"); + return false; + } + + return true; +} + +//meant to be called from user-defined ::preinit() void ESP8266WiFiGenericClass::preinitWiFiOff () { // https://github.com/esp8266/Arduino/issues/2111#issuecomment-224251391 // WiFi.persistent(false); diff --git a/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.h b/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.h index 60fdad5785..0beb7ca11f 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.h +++ b/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.h @@ -42,6 +42,8 @@ typedef std::shared_ptr WiFiEventHandler; typedef void (*WiFiEventCb)(WiFiEvent_t); +struct WiFiState; + class ESP8266WiFiGenericClass { // ---------------------------------------------------------------------------------------------- // -------------------------------------- Generic WiFi function --------------------------------- @@ -62,7 +64,7 @@ class ESP8266WiFiGenericClass { WiFiEventHandler onSoftAPModeStationConnected(std::function); WiFiEventHandler onSoftAPModeStationDisconnected(std::function); WiFiEventHandler onSoftAPModeProbeRequestReceived(std::function); - // WiFiEventHandler onWiFiModeChange(std::function); + WiFiEventHandler onWiFiModeChange(std::function); int32_t channel(void); @@ -79,7 +81,7 @@ class ESP8266WiFiGenericClass { void persistent(bool persistent); - bool mode(WiFiMode_t); + bool mode(WiFiMode_t, WiFiState* state = nullptr); WiFiMode_t getMode(); bool enableSTA(bool enable); @@ -88,9 +90,16 @@ class ESP8266WiFiGenericClass { bool forceSleepBegin(uint32 sleepUs = 0); bool forceSleepWake(); + void saveState (WiFiState* state, WiFiMode_t mode = WIFI_STA, bool persistent = false); + bool shutdown (uint32 sleepUs = 0, WiFiState* stateSave = nullptr); + bool resumeFromShutdown (WiFiState* savedState = nullptr); + + static uint32_t shutdownCRC (const WiFiState* state); + static bool shutdownValidCRC (const WiFiState* state); static void preinitWiFiOff (); //meant to be called in user-defined preinit() protected: + static bool _shutdown; static bool _persistent; static WiFiMode_t _forceSleepLastMode; diff --git a/libraries/ESP8266WiFi/src/ESP8266WiFiSTA.cpp b/libraries/ESP8266WiFi/src/ESP8266WiFiSTA.cpp index aaca892cdc..8f52942b74 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFiSTA.cpp +++ b/libraries/ESP8266WiFi/src/ESP8266WiFiSTA.cpp @@ -369,18 +369,24 @@ bool ESP8266WiFiSTAClass::reconnect() { * @return one value of wl_status_t enum */ bool ESP8266WiFiSTAClass::disconnect(bool wifioff) { - bool ret; + bool ret = false; struct station_config conf; *conf.ssid = 0; *conf.password = 0; + // API Reference: wifi_station_disconnect() need to be called after system initializes and the ESP8266 Station mode is enabled. + if (WiFi.getMode() & WIFI_STA) + ret = wifi_station_disconnect(); + else + ret = true; + ETS_UART_INTR_DISABLE(); if(WiFi._persistent) { wifi_station_set_config(&conf); } else { wifi_station_set_config_current(&conf); } - ret = wifi_station_disconnect(); + ETS_UART_INTR_ENABLE(); if(wifioff) { diff --git a/libraries/ESP8266WiFi/src/ESP8266WiFiType.h b/libraries/ESP8266WiFi/src/ESP8266WiFiType.h index c1fb2985be..b8fcc1c478 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFiType.h +++ b/libraries/ESP8266WiFi/src/ESP8266WiFiType.h @@ -34,7 +34,7 @@ typedef enum WiFiMode { - WIFI_OFF = 0, WIFI_STA = 1, WIFI_AP = 2, WIFI_AP_STA = 3 + WIFI_OFF = 0, WIFI_STA = 1, WIFI_AP = 2, WIFI_AP_STA = 3, WIFI_SHUTDOWN = 4, WIFI_RESUME = 8 } WiFiMode_t; typedef enum WiFiPhyMode @@ -58,9 +58,10 @@ typedef enum WiFiEvent WIFI_EVENT_SOFTAPMODE_STACONNECTED, WIFI_EVENT_SOFTAPMODE_STADISCONNECTED, WIFI_EVENT_SOFTAPMODE_PROBEREQRECVED, + WIFI_EVENT_MODE_CHANGE, + WIFI_EVENT_SOFTAPMODE_DISTRIBUTE_STA_IP, WIFI_EVENT_MAX, WIFI_EVENT_ANY = WIFI_EVENT_MAX, - WIFI_EVENT_MODE_CHANGE } WiFiEvent_t; enum WiFiDisconnectReason diff --git a/libraries/ESP8266WiFi/src/include/WiFiState.h b/libraries/ESP8266WiFi/src/include/WiFiState.h new file mode 100644 index 0000000000..9312ee199d --- /dev/null +++ b/libraries/ESP8266WiFi/src/include/WiFiState.h @@ -0,0 +1,23 @@ + +#ifndef WIFISTATE_H_ +#define WIFISTATE_H_ + +#include +#include + +struct WiFiState +{ + uint32_t crc; + struct + { + bool persistent; + WiFiMode_t mode; + uint8_t channel; + station_config fwconfig; + ip_info ip; + ip_addr_t dns[2]; + ip_addr_t ntp[2]; + } state; +}; + +#endif // WIFISTATE_H_ diff --git a/libraries/esp8266/examples/WiFiShutdown/WiFiShutdown.ino b/libraries/esp8266/examples/WiFiShutdown/WiFiShutdown.ino new file mode 100644 index 0000000000..44d0fdaedd --- /dev/null +++ b/libraries/esp8266/examples/WiFiShutdown/WiFiShutdown.ino @@ -0,0 +1,248 @@ + +// demonstrate the use of WiFi.mode(SHUTDOWN/RESUME) +// released to public domain + +// current on wemos d1 mini (including: ldo, usbserial chip): +// ~85mA during normal operations +// ~30mA during wifi shutdown +// ~5mA during deepsleep + +#ifndef STASSID +#define STASSID "mynetwork" +#define STAPSK "mynetworkpasswd" +#endif + +#define WAIT_NTP 0 // define this to 1 for NTP check too + +#include +#include // crc32() +#include // WiFiState structure details + +enum state_e { + e_initial, + e_start_resume, + e_start_normal, + e_off_restart, + e_wait_connected, + e_wait_ntp, + e_shutdown, + e_wait_shutdown +}; + +static state_e step = e_initial; // step +static int wifi_timeout = 0; // wifi timeout counter +static bool time_is_set = false; // WAIT_NTP=1: wait for network - dhcp packet must have ntp server + +// non volatile data +struct nv_s { + WiFiState wss; // core's wifi save state + + uint32_t crc; + struct { + int rstcounter[7]; + } data; +}; +static nv_s* nv = (nv_s*)RTC_USER_MEM; // user non volatile area + +#define SEP "###### " +#define EV "!!!!!! " +#define NFO "------ " + +void resetUserCrc() { + nv->crc = crc32(&nv->data, sizeof(nv->data)); +} + +void printNv() { + Serial.printf(NFO "nfo1/2 wifi-nv-state: valid=%d, " + "persistent=%d, " + "mode=%d, " + "channel=%d, " + "ip=%s, " + "dns=%s, " + "ntp=%s\n", + WiFi.shutdownValidCRC(&nv->wss), + nv->wss.state.persistent, + nv->wss.state.mode, + nv->wss.state.channel, + IPAddress(&nv->wss.state.ip.ip).toString().c_str(), + IPAddress(&nv->wss.state.dns[0]).toString().c_str(), + IPAddress(&nv->wss.state.ntp[0]).toString().c_str()); + + Serial.printf(NFO "nfo2/2 rst reason counters: default:%d wdt:%d exception:%d softwdt:%d reset:%d deepsleep:%d extsys:%d\n", + nv->data.rstcounter[0], + nv->data.rstcounter[1], + nv->data.rstcounter[2], + nv->data.rstcounter[3], + nv->data.rstcounter[4], + nv->data.rstcounter[5], + nv->data.rstcounter[6]); +} + +void timeset_cb() { + time_is_set = true; + + static bool first = true; + if (first) { + first = false; + } +} + +decltype(millis()) startup; + + +WiFiEventHandler evOff = WiFi.onWiFiModeChange([](const WiFiEventModeChange& event) { + Serial.printf(EV "mode changed event: ev:%d->%d getMode=%d\n", event.oldMode, event.newMode, wifi_get_opmode()); +}); + +void preinit() { + ESP8266WiFiClass::preinitWiFiOff(); +} + +void setup() { + WiFi.persistent(false); + startup = millis(); + Serial.begin(115200); + settimeofday_cb(timeset_cb); + + // prepare non volatile user structure + if (crc32(&nv->data, sizeof(nv->data)) != nv->crc) { + memset(&nv->data, 0, sizeof(nv->data)); + Serial.printf(SEP "reset NV user data\n"); + } + // update reset reason + nv->data.rstcounter[system_get_rst_info()->reason]++; + // recalculate crc + resetUserCrc(); + // nfo + printNv(); + + Serial.println("setup()"); +} + +#define TEST(x...) ({ auto v = x; Serial.printf(SEP "'%s': result = %d\n", #x, v); v; }) + +void loop() { + + static int prev = 255; + if (step != prev) { + prev = step; + Serial.printf(NFO "step %d - wifi getMode=%d=%d heap=%d freeheap=%d\n", + prev, + WiFi.getMode(), + wifi_get_opmode(), + ESP.getFreeHeap(), + ESP.getFreeHeap()); + printNv(); + } + + switch (step) { + case e_initial: { + if (WiFi.shutdownValidCRC(&nv->wss)) { + step = e_start_resume; + } else { + step = e_start_normal; + } + break; + } + + + case e_start_resume: + Serial.println(SEP "CRC valid => WIFI_RESUME"); + startup = millis(); + + if (!TEST(WiFi.mode(WIFI_RESUME, &nv->wss))) { + Serial.printf(SEP "issue resuming WiFi\n"); + step = e_off_restart; + } else { + Serial.printf(SEP "waiting for connected\\n"); + step = e_wait_connected; + } + break; + + + case e_start_normal: + Serial.printf(SEP "CRC NOT valid, begin/WIFI_STA (current mode = %d)\n", wifi_get_opmode()); + startup = millis(); + if (!TEST(WiFi.mode(WIFI_STA)) || !TEST(WiFi.begin(STASSID, STAPSK))) { + Serial.printf(SEP "issue setting up STA\n"); + step = e_off_restart; + } else { + Serial.printf(SEP "waiting for connected\n"); + step = e_wait_connected; + } + break; + + + case e_wait_connected: + if (WiFi.status() == WL_CONNECTED) { + Serial.printf(SEP "connected! ---- startup time: %ld ms ----\n\n\n", millis() - startup); + wifi_timeout = 0; + if (WAIT_NTP) { + step = e_wait_ntp; + Serial.printf(SEP "wait for NTP\n"); + } else { + step = e_shutdown; + } + } else if ((millis() - startup > 10000)) { + Serial.printf(SEP "connected TIMEOUT! status=%d\n", WiFi.status()); + wifi_timeout++; + step = e_off_restart; + } + break; + + + case e_off_restart: + Serial.printf(SEP "OFF -> wait 2s\n"); + (void)TEST(WiFi.mode(WIFI_OFF)); + delay(2000); // test - mad wifi loop until :oom if delay not there - to verify + step = e_initial; + break; + + + case e_wait_ntp: + // check when NTP has set time + if (time_is_set) { + Serial.printf(SEP "NTP is set\n"); + time_is_set = false; + step = e_shutdown; + } + break; + + + case e_shutdown: { + static int deepsleep = 0; + switch (++deepsleep) { + case 1: { + Serial.println(SEP "WIFI_OFF for 5s"); + TEST(WiFi.mode(WIFI_OFF, &nv->wss)); + break; + } + case 2: // several loop on shutdown + case 3: // to check if it affects + case 4: { // reconnection duration + Serial.println(SEP "WIFI_SHUTDOWN for 5s"); + TEST(WiFi.mode(WIFI_SHUTDOWN, &nv->wss)); + break; + } + default: { + Serial.println(SEP "DEEPSLEEP for 5s (bind GPIO16 <=> RST)"); + TEST(WiFi.mode(WIFI_SHUTDOWN, &nv->wss)); + Serial.flush(); + ESP.deepSleep(5000000); + // will reboot, GPIO16 must be connected to reset + } + } + + startup = millis(); + step = e_wait_shutdown; + break; + } + + + case e_wait_shutdown: + if (millis() - startup > 5000) { + step = e_start_resume; + } + break; + } +} From 16ac25a1c1518ae8e8dfe75248597ed76e7800a5 Mon Sep 17 00:00:00 2001 From: David Gauchard Date: Mon, 29 Jul 2019 18:08:43 +0200 Subject: [PATCH 02/12] remove useless variable --- libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.h | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.h b/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.h index 0beb7ca11f..146ad37ae3 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.h +++ b/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.h @@ -99,7 +99,6 @@ class ESP8266WiFiGenericClass { static void preinitWiFiOff (); //meant to be called in user-defined preinit() protected: - static bool _shutdown; static bool _persistent; static WiFiMode_t _forceSleepLastMode; From 44b84a2d994895d0a8c838f78cff2f06c70430dd Mon Sep 17 00:00:00 2001 From: david gauchard Date: Mon, 29 Jul 2019 22:35:23 +0200 Subject: [PATCH 03/12] per CI review (lwip1, emulation) --- libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp | 14 ++++++++++++-- tests/host/Makefile | 1 + tests/host/common/MocklwIP.cpp | 9 +++++++++ tests/host/common/user_interface.cpp | 6 ++++-- 4 files changed, 26 insertions(+), 4 deletions(-) diff --git a/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp b/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp index 5acff80b18..c079e96442 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp +++ b/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp @@ -40,8 +40,12 @@ extern "C" { #include "lwip/err.h" #include "lwip/dns.h" #include "lwip/dhcp.h" -#include "lwip/apps/sntp.h" #include "lwip/init.h" // LWIP_VERSION_ +#if LWIP_VERSION_MAJOR == 1 +#include "lwip/sntp.h" +#else +#include "lwip/apps/sntp.h" +#endif } #include "WiFiClient.h" @@ -688,7 +692,13 @@ void ESP8266WiFiGenericClass::saveState (WiFiState* state, WiFiMode_t mode, bool state->state.mode = mode; uint8_t i = 0; for (auto& ntp: state->state.ntp) + { +#if LWIP_VERSION_MAJOR == 1 + ntp = sntp_getserver(i++); +#else ntp = *sntp_getserver(i++); +#endif + } i = 0; for (auto& dns: state->state.dns) dns = WiFi.dnsIP(i++); @@ -726,7 +736,7 @@ bool ESP8266WiFiGenericClass::resumeFromShutdown (WiFiState* state) DEBUG_WIFI("core: resume: static address '%s'\n", local.toString().c_str()); WiFi.config(state->state.ip.ip, state->state.ip.gw, state->state.ip.netmask, state->state.dns[0], state->state.dns[1]); uint8_t i = 0; - for (const auto& ntp: state->state.ntp) + for (CONST auto& ntp: state->state.ntp) { IPAddress ip(ntp); if (ip.isSet()) diff --git a/tests/host/Makefile b/tests/host/Makefile index 3768c11b5e..aaf8a88d91 100644 --- a/tests/host/Makefile +++ b/tests/host/Makefile @@ -69,6 +69,7 @@ CORE_CPP_FILES := $(addprefix $(CORE_PATH)/,\ libb64/cdecode.cpp \ Schedule.cpp \ HardwareSerial.cpp \ + crc32.cpp \ ) \ $(addprefix $(LIBRARIES_PATH)/ESP8266SdFat/src/, \ FatLib/FatFile.cpp \ diff --git a/tests/host/common/MocklwIP.cpp b/tests/host/common/MocklwIP.cpp index a4b9bbcad4..52e13473b9 100644 --- a/tests/host/common/MocklwIP.cpp +++ b/tests/host/common/MocklwIP.cpp @@ -12,4 +12,13 @@ err_t dhcp_renew(struct netif *netif) return ERR_OK; } +void sntp_setserver(u8_t, const ip_addr_t) +{ +} + +const ip_addr_t* sntp_getserver(u8_t) +{ + return IP_ADDR_ANY; +} + } // extern "C" diff --git a/tests/host/common/user_interface.cpp b/tests/host/common/user_interface.cpp index ceba51de73..2a999e3c31 100644 --- a/tests/host/common/user_interface.cpp +++ b/tests/host/common/user_interface.cpp @@ -461,7 +461,9 @@ bool smartconfig_stop (void) return true; } - - +sleep_type_t wifi_fpm_get_sleep_type(void) +{ + return NONE_SLEEP_T; +} } // extern "C" From db5e6af8d99941f6da4aefb5b68c8dde79bd57fb Mon Sep 17 00:00:00 2001 From: david gauchard Date: Mon, 29 Jul 2019 22:36:20 +0200 Subject: [PATCH 04/12] fix style --- libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp b/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp index c079e96442..b68aa37476 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp +++ b/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp @@ -235,7 +235,7 @@ void ESP8266WiFiGenericClass::_eventCallback(void* arg) for(auto it = std::begin(sCbEventList); it != std::end(sCbEventList); ) { WiFiEventHandler &handler = *it; if (handler->canExpire() && handler.unique()) { - it = sCbEventList.erase(it); + it = sCbEventList.erase(it); } else { (*handler)(event); From 31cddd322503c6abbd71c33535e395ac54bc2b28 Mon Sep 17 00:00:00 2001 From: david gauchard Date: Tue, 30 Jul 2019 01:55:36 +0200 Subject: [PATCH 05/12] reorder structure members --- libraries/ESP8266WiFi/src/include/WiFiState.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/ESP8266WiFi/src/include/WiFiState.h b/libraries/ESP8266WiFi/src/include/WiFiState.h index 9312ee199d..caceb6f0bc 100644 --- a/libraries/ESP8266WiFi/src/include/WiFiState.h +++ b/libraries/ESP8266WiFi/src/include/WiFiState.h @@ -10,13 +10,13 @@ struct WiFiState uint32_t crc; struct { - bool persistent; - WiFiMode_t mode; - uint8_t channel; station_config fwconfig; ip_info ip; ip_addr_t dns[2]; ip_addr_t ntp[2]; + WiFiMode_t mode; + uint8_t channel; + bool persistent; } state; }; From 7f2e3e6ad40f388354319de50ebff838d7bcb10c Mon Sep 17 00:00:00 2001 From: david gauchard Date: Tue, 30 Jul 2019 01:58:39 +0200 Subject: [PATCH 06/12] WiFi.mode state parameter: only useful with SHUTDOWN or RESUME --- libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp b/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp index b68aa37476..4810f1ae4d 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp +++ b/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp @@ -406,6 +406,10 @@ bool ESP8266WiFiGenericClass::mode(WiFiMode_t m, WiFiState* state) { return false; // m is now WIFI_STA, WIFI_AP or WIFI_AP_STA + if (state) + { + DEBUG_WIFI("core: state is useless without SHUTDOWN or RESUME\n"); + } if (wifi_fpm_get_sleep_type() != NONE_SLEEP_T) { // wifi may have been put asleep by ESP8266WiFiGenericClass::preinitWiFiOff @@ -415,11 +419,9 @@ bool ESP8266WiFiGenericClass::mode(WiFiMode_t m, WiFiState* state) { if(_persistent){ if(wifi_get_opmode() == (uint8) m && wifi_get_opmode_default() == (uint8) m){ - saveState(state, m, _persistent); return true; } } else if(wifi_get_opmode() == (uint8) m){ - saveState(state, m, _persistent); return true; } From ca163a8234f2d89e94f9084c78a256ada257769c Mon Sep 17 00:00:00 2001 From: david gauchard Date: Tue, 30 Jul 2019 02:11:59 +0200 Subject: [PATCH 07/12] state saving was initially part of the shutdown function moving it where it was --- libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp | 12 ++++++------ libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.h | 1 - 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp b/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp index 4810f1ae4d..cc5a5c4b7e 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp +++ b/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp @@ -682,16 +682,15 @@ bool ESP8266WiFiGenericClass::shutdown (uint32 sleepUs, WiFiState* state) WiFi.persistent(persistent); return false; } - saveState(state, before_off_mode, persistent); - return true; -} -void ESP8266WiFiGenericClass::saveState (WiFiState* state, WiFiMode_t mode, bool persistent) -{ + // WiFi is now in force-sleep mode + if (state) { + // finish filling state and process crc + state->state.persistent = persistent; - state->state.mode = mode; + state->state.mode = before_off_mode; uint8_t i = 0; for (auto& ntp: state->state.ntp) { @@ -707,6 +706,7 @@ void ESP8266WiFiGenericClass::saveState (WiFiState* state, WiFiMode_t mode, bool state->crc = shutdownCRC(state); DEBUG_WIFI("core: state is saved\n"); } + return true; } bool ESP8266WiFiGenericClass::resumeFromShutdown (WiFiState* state) diff --git a/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.h b/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.h index 146ad37ae3..1be7c017c6 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.h +++ b/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.h @@ -90,7 +90,6 @@ class ESP8266WiFiGenericClass { bool forceSleepBegin(uint32 sleepUs = 0); bool forceSleepWake(); - void saveState (WiFiState* state, WiFiMode_t mode = WIFI_STA, bool persistent = false); bool shutdown (uint32 sleepUs = 0, WiFiState* stateSave = nullptr); bool resumeFromShutdown (WiFiState* savedState = nullptr); From 68f97967c34acbf6aa3670565d221ba77585a696 Mon Sep 17 00:00:00 2001 From: david gauchard Date: Tue, 30 Jul 2019 02:26:00 +0200 Subject: [PATCH 08/12] fix example to be consistent --- .../examples/WiFiShutdown/WiFiShutdown.ino | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/libraries/esp8266/examples/WiFiShutdown/WiFiShutdown.ino b/libraries/esp8266/examples/WiFiShutdown/WiFiShutdown.ino index 44d0fdaedd..1c0624fd60 100644 --- a/libraries/esp8266/examples/WiFiShutdown/WiFiShutdown.ino +++ b/libraries/esp8266/examples/WiFiShutdown/WiFiShutdown.ino @@ -26,7 +26,8 @@ enum state_e { e_wait_connected, e_wait_ntp, e_shutdown, - e_wait_shutdown + e_wait_shutdown, + e_wait_off }; static state_e step = e_initial; // step @@ -136,6 +137,7 @@ void loop() { } switch (step) { + case e_initial: { if (WiFi.shutdownValidCRC(&nv->wss)) { step = e_start_resume; @@ -214,7 +216,8 @@ void loop() { switch (++deepsleep) { case 1: { Serial.println(SEP "WIFI_OFF for 5s"); - TEST(WiFi.mode(WIFI_OFF, &nv->wss)); + TEST(WiFi.mode(WIFI_OFF)); + step = e_wait_off; break; } case 2: // several loop on shutdown @@ -222,6 +225,7 @@ void loop() { case 4: { // reconnection duration Serial.println(SEP "WIFI_SHUTDOWN for 5s"); TEST(WiFi.mode(WIFI_SHUTDOWN, &nv->wss)); + step = e_wait_shutdown; break; } default: { @@ -234,7 +238,6 @@ void loop() { } startup = millis(); - step = e_wait_shutdown; break; } @@ -244,5 +247,12 @@ void loop() { step = e_start_resume; } break; + + + case e_wait_off: + if (millis() - startup > 5000) { + step = e_start_normal; + } + break; } } From 1726798299a57553caaf08ac86174c1a00a0edff Mon Sep 17 00:00:00 2001 From: David Gauchard Date: Tue, 30 Jul 2019 11:24:57 +0200 Subject: [PATCH 09/12] poison saved sate structure on success, per suggestion on maintainers gitter --- libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp b/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp index cc5a5c4b7e..ed49ca9d6a 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp +++ b/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp @@ -766,6 +766,9 @@ bool ESP8266WiFiGenericClass::resumeFromShutdown (WiFiState* state) return false; } + // success, invalidate saved state + state->crc++; + return true; } From 0d8f787a7b5c9e6a3cae726a39ffc35eb416ca06 Mon Sep 17 00:00:00 2001 From: David Gauchard Date: Fri, 30 Aug 2019 14:58:10 +0200 Subject: [PATCH 10/12] comment (mention experimental) --- libraries/ESP8266WiFi/src/ESP8266WiFiType.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/ESP8266WiFi/src/ESP8266WiFiType.h b/libraries/ESP8266WiFi/src/ESP8266WiFiType.h index b8fcc1c478..86803edce4 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFiType.h +++ b/libraries/ESP8266WiFi/src/ESP8266WiFiType.h @@ -34,7 +34,8 @@ typedef enum WiFiMode { - WIFI_OFF = 0, WIFI_STA = 1, WIFI_AP = 2, WIFI_AP_STA = 3, WIFI_SHUTDOWN = 4, WIFI_RESUME = 8 + WIFI_OFF = 0, WIFI_STA = 1, WIFI_AP = 2, WIFI_AP_STA = 3, + /* these two pseudo modes are experimental: */ WIFI_SHUTDOWN = 4, WIFI_RESUME = 8 } WiFiMode_t; typedef enum WiFiPhyMode From 6fb0a349792390b0b600cb5a7c8f99da29f49a1b Mon Sep 17 00:00:00 2001 From: David Gauchard Date: Mon, 2 Sep 2019 18:23:48 +0200 Subject: [PATCH 11/12] comments --- libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.h | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.h b/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.h index 1be7c017c6..1f5ec5c995 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.h +++ b/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.h @@ -90,9 +90,6 @@ class ESP8266WiFiGenericClass { bool forceSleepBegin(uint32 sleepUs = 0); bool forceSleepWake(); - bool shutdown (uint32 sleepUs = 0, WiFiState* stateSave = nullptr); - bool resumeFromShutdown (WiFiState* savedState = nullptr); - static uint32_t shutdownCRC (const WiFiState* state); static bool shutdownValidCRC (const WiFiState* state); static void preinitWiFiOff (); //meant to be called in user-defined preinit() @@ -103,17 +100,22 @@ class ESP8266WiFiGenericClass { static void _eventCallback(void *event); + // called by WiFi.mode(SHUTDOWN/RESTORE, state) + // - sleepUs is WiFi.forceSleepBegin() parameter, 0 = forever + // - saveState is the user's state to hold configuration on restore + bool shutdown (uint32 sleepUs = 0, WiFiState* stateSave = nullptr); + bool resumeFromShutdown (WiFiState* savedState = nullptr); + // ---------------------------------------------------------------------------------------------- // ------------------------------------ Generic Network function -------------------------------- // ---------------------------------------------------------------------------------------------- public: - int hostByName(const char* aHostname, IPAddress& aResult); int hostByName(const char* aHostname, IPAddress& aResult, uint32_t timeout_ms); bool getPersistent(); - protected: + protected: friend class ESP8266WiFiSTAClass; friend class ESP8266WiFiScanClass; friend class ESP8266WiFiAPClass; From 6e1c44e5455be68a35d760a6371badc723504189 Mon Sep 17 00:00:00 2001 From: david gauchard Date: Thu, 5 Sep 2019 02:14:23 +0200 Subject: [PATCH 12/12] unify crc32 calls --- cores/esp8266/core_esp8266_eboot_command.cpp | 27 +++----------------- cores/esp8266/coredecls.h | 2 +- cores/esp8266/crc32.cpp | 25 +++++++++++++++--- 3 files changed, 26 insertions(+), 28 deletions(-) diff --git a/cores/esp8266/core_esp8266_eboot_command.cpp b/cores/esp8266/core_esp8266_eboot_command.cpp index 44d65a8c3e..2ddc65933e 100644 --- a/cores/esp8266/core_esp8266_eboot_command.cpp +++ b/cores/esp8266/core_esp8266_eboot_command.cpp @@ -21,36 +21,15 @@ #include #include +#include "coredecls.h" #include "eboot_command.h" -extern "C" { -static uint32_t crc_update(uint32_t crc, const uint8_t *data, size_t length) -{ - uint32_t i; - bool bit; - uint8_t c; - - while (length--) { - c = *data++; - for (i = 0x80; i > 0; i >>= 1) { - bit = crc & 0x80000000; - if (c & i) { - bit = !bit; - } - crc <<= 1; - if (bit) { - crc ^= 0x04c11db7; - } - } - } - return crc; -} +extern "C" { static uint32_t eboot_command_calculate_crc32(const struct eboot_command* cmd) { - return crc_update(0xffffffff, (const uint8_t*) cmd, - offsetof(struct eboot_command, crc32)); + return crc32((const uint8_t*) cmd, offsetof(struct eboot_command, crc32)); } int eboot_command_read(struct eboot_command* cmd) diff --git a/cores/esp8266/coredecls.h b/cores/esp8266/coredecls.h index e04df9e473..4a70609bff 100644 --- a/cores/esp8266/coredecls.h +++ b/cores/esp8266/coredecls.h @@ -21,7 +21,7 @@ void settimeofday_cb (void (*cb)(void)); void disable_extra4k_at_link_time (void) __attribute__((noinline)); uint32_t sqrt32 (uint32_t n); -uint32_t crc32 (const void* data, size_t length); +uint32_t crc32 (const void* data, size_t length, uint32_t crc = 0xffffffff); #ifdef __cplusplus } diff --git a/cores/esp8266/crc32.cpp b/cores/esp8266/crc32.cpp index 34b0d3f33b..cc2a2ee64e 100644 --- a/cores/esp8266/crc32.cpp +++ b/cores/esp8266/crc32.cpp @@ -1,11 +1,30 @@ +/* + crc32.cpp + + Copyright (c) 2015 Ivan Grokhotkov. 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 "coredecls.h" -// taken from esp8266/examples/RTCUserMemory/RTCUserMemory.ino -uint32_t crc32 (const void* data, size_t length) +// moved from core_esp8266_eboot_command.cpp +uint32_t crc32 (const void* data, size_t length, uint32_t crc /*= 0xffffffff*/) { const uint8_t* ldata = (const uint8_t*)data; - uint32_t crc = 0xffffffff; while (length--) { uint8_t c = *ldata++;