diff --git a/doc/esp8266wifi/generic-class.rst b/doc/esp8266wifi/generic-class.rst index 44b8e6e73..36a0180c2 100644 --- a/doc/esp8266wifi/generic-class.rst +++ b/doc/esp8266wifi/generic-class.rst @@ -80,9 +80,6 @@ Once ``WiFi.persistent(false)`` is called, ``WiFi.begin``, ``WiFi.disconnect``, mode ~~~~ -Regular WiFi modes -__________________ - .. code:: cpp bool mode(WiFiMode_t m) @@ -94,25 +91,6 @@ Switches to one of the regular WiFi modes, where ``m`` is one of: - ``WIFI_AP``: switch to `Access Point (AP) `__ mode. - ``WIFI_AP_STA``: enable both Station (STA) and Access Point (AP) mode. -Pseudo-modes -____________ - -.. code:: cpp - - bool mode(WiFiMode_t m, WiFiState* state) - -Used with the following pseudo-modes, where ``m`` is one of: - -- ``WIFI_SHUTDOWN``: Fills in the provided ``WiFiState`` structure, switches to ``WIFI_OFF`` mode and puts WiFi into forced sleep, preserving energy. -- ``WIFI_RESUME``: Turns WiFi on and tries to re-establish the WiFi connection stored in the ``WiFiState`` structure. - -These modes are used in low-power scenarios, e.g. where ESP.deepSleep is used between actions to preserve battery power. - -It is the user's responsibility to preserve the WiFiState between ``WIFI_SHUTDOWN`` and ``WIFI_RESUME``, e.g. by storing it -in RTC user data and/or flash memory. - -There is an example sketch `WiFiShutdown.ino `__ available in the examples folder of the ESP8266WiFi library. - getMode ~~~~~~~ @@ -199,6 +177,41 @@ getPhyMode Gets the WiFi radio phy mode that is currently set. +forceSleepBegin +~~~~~~~~~~~~~~~ + +.. code:: cpp + + bool forceSleepBegin (uint32 sleepUs=0) + +Saves the currently set WiFi mode and starts forced modem sleep for the specified time (us) + +forceSleepWake +~~~~~~~~~~~~~~ + +.. code:: cpp + + bool forceSleepWake () + +Called after `forceSleepBegin()`. Restores the previous WiFi mode and attempts reconnection when STA was active. + +shutdown and resumeFromShutdown +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: cpp + + bool shutdown (WiFiState& state) + bool shutdown (WiFiState& state, uint32 sleepUs) + bool resumeFromShutdown (WiFiState& state) + bool shutdownValidCRC (const WiFiState& state) + +Stores the STA interface IP configuration in the specified ``state`` struct and calls ``forceSleepBegin(sleepUs)``. +Restores STA interface configuration from the ``state`` and calls ``forceSleepWake()``. + +These methods are intended to be used in low-power scenarios, e.g. where ESP.deepSleep is used between actions to preserve battery power. It is the user's responsibility to preserve the WiFiState between ``shutdown()`` and ``resumeFromShutdown()`` by storing it in the RTC user data and/or flash memory. + +See `WiFiShutdown.ino `__ for an example of usage. + Other Function Calls ~~~~~~~~~~~~~~~~~~~~ @@ -208,8 +221,6 @@ Other Function Calls WiFiSleepType_t getSleepMode () bool enableSTA (bool enable) bool enableAP (bool enable) - bool forceSleepBegin (uint32 sleepUs=0) - bool forceSleepWake () int hostByName (const char *aHostname, IPAddress &aResult) appeared with SDK pre-V3: diff --git a/libraries/ESP8266WiFi/examples/WiFiShutdown/WiFiShutdown.ino b/libraries/ESP8266WiFi/examples/WiFiShutdown/WiFiShutdown.ino index 7fac16b45..b03357c14 100644 --- a/libraries/ESP8266WiFi/examples/WiFiShutdown/WiFiShutdown.ino +++ b/libraries/ESP8266WiFi/examples/WiFiShutdown/WiFiShutdown.ino @@ -1,5 +1,5 @@ -// Demonstrate the use of WiFi.mode(WIFI_SHUTDOWN)/WiFi.mode(WIFI_RESUME) +// Demonstrate the use of WiFi.shutdown() and WiFi.resumeFromShutdown() // Released to public domain // Current on WEMOS D1 mini (including: LDO, usbserial chip): @@ -39,7 +39,7 @@ void setup() { ESP.rtcUserMemoryRead(RTC_USER_DATA_SLOT_WIFI_STATE, reinterpret_cast(&state), sizeof(state)); unsigned long start = millis(); - if (!WiFi.mode(WIFI_RESUME, &state) + if (!WiFi.resumeFromShutdown(state) || (WiFi.waitForConnectResult(10000) != WL_CONNECTED)) { Serial.println("Cannot resume WiFi connection, connecting via begin..."); WiFi.persistent(false); @@ -63,7 +63,7 @@ void setup() { // Here you can do whatever you need to do that needs a WiFi connection. // --- - WiFi.mode(WIFI_SHUTDOWN, &state); + WiFi.shutdown(state); ESP.rtcUserMemoryWrite(RTC_USER_DATA_SLOT_WIFI_STATE, reinterpret_cast(&state), sizeof(state)); // --- @@ -77,4 +77,4 @@ void setup() { void loop() { // Nothing to do here. -} \ No newline at end of file +} diff --git a/libraries/ESP8266WiFi/keywords.txt b/libraries/ESP8266WiFi/keywords.txt index ccdef7680..e5c11a1d9 100644 --- a/libraries/ESP8266WiFi/keywords.txt +++ b/libraries/ESP8266WiFi/keywords.txt @@ -56,6 +56,8 @@ enableSTA KEYWORD2 enableAP KEYWORD2 forceSleepBegin KEYWORD2 forceSleepWake KEYWORD2 +shutdown KEYWORD2 +resumeFromShutdown KEYWORD2 #ESP8266WiFi printDiag KEYWORD2 diff --git a/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp b/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp index 7518f3c59..19da592c7 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp +++ b/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp @@ -401,21 +401,10 @@ bool ESP8266WiFiGenericClass::getPersistent(){ * set new mode * @param m WiFiMode_t */ -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)) +bool ESP8266WiFiGenericClass::mode(WiFiMode_t m) { + 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 (state) - { - DEBUG_WIFI("core: state is useless without SHUTDOWN or RESUME\n"); } if(_persistent){ @@ -719,37 +708,37 @@ void wifi_dns_found_callback(const char *name, const ip_addr_t *ipaddr, void *ca esp_schedule(); // break delay in hostByName } -uint32_t ESP8266WiFiGenericClass::shutdownCRC (const WiFiState* state) +uint32_t ESP8266WiFiGenericClass::shutdownCRC (const WiFiState& state) { - return state? crc32(&state->state, sizeof(state->state)): 0; + return crc32(&state.state, sizeof(state.state)); } -bool ESP8266WiFiGenericClass::shutdownValidCRC (const WiFiState* state) +bool ESP8266WiFiGenericClass::shutdownValidCRC (const WiFiState& state) { - return state && (crc32(&state->state, sizeof(state->state)) == state->crc); + return crc32(&state.state, sizeof(state.state)) == state.crc; } -bool ESP8266WiFiGenericClass::shutdown (uint32 sleepUs, WiFiState* state) +bool ESP8266WiFiGenericClass::shutdown (WiFiState& state, uint32 sleepUs) { bool persistent = _persistent; WiFiMode_t before_off_mode = getMode(); - if ((before_off_mode & WIFI_STA) && state) + if (before_off_mode & WIFI_STA) { - bool ret = wifi_get_ip_info(STATION_IF, &state->state.ip); + 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); + 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(); + state.state.channel = wifi_get_channel(); } // disable persistence in FW so in case of power failure @@ -766,57 +755,63 @@ bool ESP8266WiFiGenericClass::shutdown (uint32 sleepUs, WiFiState* state) } // WiFi is now in force-sleep mode + // finish filling state and process crc - if (state) + state.state.persistent = persistent; + state.state.mode = before_off_mode; + + uint8_t i = 0; + for (auto& ntp: state.state.ntp) { - // finish filling state and process crc - - state->state.persistent = persistent; - state->state.mode = before_off_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"); + 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"); + return true; } -bool ESP8266WiFiGenericClass::resumeFromShutdown (WiFiState* state) +bool ESP8266WiFiGenericClass::shutdown (WiFiState& state) { + return shutdown(state, 0); +} + +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) + if (shutdownCRC(state) != state.crc) { - DEBUG_WIFI("core: resume: no state or bad crc\n"); + DEBUG_WIFI("core: resume: bad crc\n"); return false; } - persistent(state->state.persistent); + persistent(state.state.persistent); - if (!mode(state->state.mode)) + if (!mode(state.state.mode)) { - DEBUG_WIFI("core: resume: can't set wifi mode to %d\n", 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) + if (state.state.mode & WIFI_STA) { - IPAddress local(state->state.ip.ip); + 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]); + 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()) @@ -826,10 +821,23 @@ bool ESP8266WiFiGenericClass::resumeFromShutdown (WiFiState* state) } } } - auto beginResult = WiFi.begin((const char*)state->state.fwconfig.ssid, - (const char*)state->state.fwconfig.password, - state->state.channel, - state->state.fwconfig.bssid, + + String ssid; + { + const char* ptr = reinterpret_cast(state.state.fwconfig.ssid); + ssid.concat(ptr, strnlen(ptr, sizeof(station_config::ssid))); + } + + String pass; + { + const char* ptr = reinterpret_cast(state.state.fwconfig.password); + pass.concat(ptr, strnlen(ptr, sizeof(station_config::password))); + } + + auto beginResult = WiFi.begin(ssid.c_str(), + pass.c_str(), + state.state.channel, + state.state.fwconfig.bssid, true); if (beginResult == WL_CONNECT_FAILED) { @@ -843,14 +851,14 @@ bool ESP8266WiFiGenericClass::resumeFromShutdown (WiFiState* state) } } - if (state->state.mode & WIFI_AP) + if (state.state.mode & WIFI_AP) { DEBUG_WIFI("core: resume AP mode TODO\n"); return false; } // success, invalidate saved state - state->crc++; + state.crc++; return true; } diff --git a/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.h b/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.h index 0e6a11e39..3d3ceb326 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.h +++ b/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.h @@ -122,7 +122,7 @@ class ESP8266WiFiGenericClass { static void persistent(bool persistent); - bool mode(WiFiMode_t, WiFiState* state = nullptr); + bool mode(WiFiMode_t); WiFiMode_t getMode(); bool enableSTA(bool enable); @@ -131,21 +131,23 @@ class ESP8266WiFiGenericClass { bool forceSleepBegin(uint32 sleepUs = 0); bool forceSleepWake(); - static uint32_t shutdownCRC (const WiFiState* state); - static bool shutdownValidCRC (const WiFiState* state); + // wrappers around mode() and forceSleepBegin/Wake + // - sleepUs is WiFi.forceSleepBegin() parameter, 0 means forever + // - saveState is the user's state to hold configuration on restore + bool shutdown(WiFiState& stateSave); + bool shutdown(WiFiState& stateSave, uint32 sleepUs); + bool resumeFromShutdown(WiFiState& savedState); + + static bool shutdownValidCRC (const WiFiState& state); static void preinitWiFiOff () __attribute__((deprecated("WiFi is off by default at boot, use enableWiFiAtBoot() for legacy behavior"))); protected: static bool _persistent; static WiFiMode_t _forceSleepLastMode; - static void _eventCallback(void *event); + static uint32_t shutdownCRC (const WiFiState& state); - // 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); + static void _eventCallback(void *event); // ---------------------------------------------------------------------------------------------- // ------------------------------------ Generic Network function -------------------------------- diff --git a/libraries/ESP8266WiFi/src/ESP8266WiFiType.h b/libraries/ESP8266WiFi/src/ESP8266WiFiType.h index 7e5e1dc7e..9538b132c 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFiType.h +++ b/libraries/ESP8266WiFi/src/ESP8266WiFiType.h @@ -34,8 +34,7 @@ typedef enum WiFiMode { - 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 + WIFI_OFF = 0, WIFI_STA = 1, WIFI_AP = 2, WIFI_AP_STA = 3 } WiFiMode_t; typedef enum WiFiPhyMode diff --git a/libraries/esp8266/examples/LowPowerDemo/LowPowerDemo.ino b/libraries/esp8266/examples/LowPowerDemo/LowPowerDemo.ino index 7556c5882..2c22416e0 100644 --- a/libraries/esp8266/examples/LowPowerDemo/LowPowerDemo.ino +++ b/libraries/esp8266/examples/LowPowerDemo/LowPowerDemo.ino @@ -195,7 +195,7 @@ void runTest2() { void runTest3() { Serial.println(F("\n3rd test - Forced Modem Sleep")); - WiFi.mode(WIFI_SHUTDOWN, &nv->wss); // shut the modem down and save the WiFi state for faster reconnection + WiFi.shutdown(nv->wss); // shut the modem down and save the WiFi state for faster reconnection // WiFi.forceSleepBegin(delay_in_uS); // alternate method of Forced Modem Sleep for an optional timed shutdown, // with WiFi.forceSleepBegin(0xFFFFFFF); the modem sleeps until you wake it, with values <= 0xFFFFFFE it's timed // delay(10); // it doesn't always go to sleep unless you delay(10); yield() wasn't reliable @@ -290,7 +290,7 @@ void runTest7() { delay(50); // debounce time for the switch, pushbutton released waitPushbutton(false, blinkDelay); // set true if you want to see Automatic Modem Sleep digitalWrite(LED, LOW); // turn the LED on, at least briefly - //WiFi.mode(WIFI_SHUTDOWN, &nv->wss); // Forced Modem Sleep for a more Instant Deep Sleep, + //WiFi.shutdown(nv->wss); // Forced Modem Sleep for a more Instant Deep Sleep, // and no extended RFCAL as it goes into Deep Sleep Serial.println(F("going into Deep Sleep now...")); printMillis(); // show time difference across sleep @@ -308,7 +308,7 @@ void runTest8() { readVoltage(); // read internal VCC Serial.println(F("press the switch to continue")); waitPushbutton(false, blinkDelay); // set true if you want to see Automatic Modem Sleep - //WiFi.mode(WIFI_SHUTDOWN, &nv->wss); // Forced Modem Sleep for a more Instant Deep Sleep, + //WiFi.shutdown(nv->wss); // Forced Modem Sleep for a more Instant Deep Sleep, // and no extended RFCAL as it goes into Deep Sleep Serial.println(F("going into Deep Sleep now...")); Serial.flush(); // needs a delay(10) or Serial.flush() else it doesn't print the whole message @@ -322,7 +322,7 @@ void runTest9() { readVoltage(); // read internal VCC Serial.println(F("press the switch to continue")); waitPushbutton(false, blinkDelay); // set true if you want to see Automatic Modem Sleep - WiFi.mode(WIFI_SHUTDOWN, &nv->wss); // Forced Modem Sleep for a more Instant Deep Sleep + WiFi.shutdown(nv->wss); // Forced Modem Sleep for a more Instant Deep Sleep Serial.println(F("going into Deep Sleep now...")); Serial.flush(); // needs a delay(10) or Serial.flush() else it doesn't print the whole message testPoint_HIGH; // testPoint set HIGH to track Deep Sleep period, cleared at startup() @@ -335,7 +335,7 @@ void runTest10() { readVoltage(); // read internal VCC Serial.println(F("press the switch to continue")); waitPushbutton(false, blinkDelay); // set true if you want to see Automatic Modem Sleep - //WiFi.mode(WIFI_SHUTDOWN); // Forced Modem Sleep for a more Instant Deep Sleep + //WiFi.forceSleepBegin(); // Forced Modem Sleep for a more Instant Deep Sleep Serial.println(F("going into Deep Sleep now...")); Serial.flush(); // needs a delay(10) or Serial.flush() else it doesn't print the whole message testPoint_HIGH; // testPoint set HIGH to track Deep Sleep period, cleared at startup() @@ -397,15 +397,15 @@ void updateRTCcrc() { // updates the reset count CRC void initWiFi() { digitalWrite(LED, LOW); // give a visual indication that we're alive but busy with WiFi uint32_t wifiBegin = millis(); // how long does it take to connect - if ((crc32((uint8_t*) &nv->rtcData.rstCount + 1, sizeof(nv->wss)) && !WiFi.shutdownValidCRC(&nv->wss))) { + if ((crc32((uint8_t*) &nv->rtcData.rstCount + 1, sizeof(nv->wss)) && !WiFi.shutdownValidCRC(nv->wss))) { // if good copy of wss, overwrite invalid (primary) copy memcpy((uint32_t*) &nv->wss, (uint32_t*) &nv->rtcData.rstCount + 1, sizeof(nv->wss)); } - if (WiFi.shutdownValidCRC(&nv->wss)) { // if we have a valid WiFi saved state + if (WiFi.shutdownValidCRC(nv->wss)) { // if we have a valid WiFi saved state memcpy((uint32_t*) &nv->rtcData.rstCount + 1, (uint32_t*) &nv->wss, sizeof(nv->wss)); // save a copy of it Serial.println(F("resuming WiFi")); } - if (!(WiFi.mode(WIFI_RESUME, &nv->wss))) { // couldn't resume, or no valid saved WiFi state yet + if (!(WiFi.resumeFromShutdown(nv->wss))) { // couldn't resume, or no valid saved WiFi state yet /* Explicitly set the ESP8266 as a WiFi-client (STAtion mode), otherwise by default it would try to act as both a client and an access-point and could cause network issues with other WiFi devices on your network. */