Skip to content
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

[BREAKING] wifi: remove pseudo-modes for shutdown, expose ::[resumeFrom]shutdown() #7956

Merged
merged 10 commits into from
May 15, 2021
59 changes: 35 additions & 24 deletions doc/esp8266wifi/generic-class.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -94,25 +91,6 @@ Switches to one of the regular WiFi modes, where ``m`` is one of:
- ``WIFI_AP``: switch to `Access Point (AP) <readme.rst#soft-access-point>`__ 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 <https://github.com/esp8266/Arduino/blob/master/libraries/ESP8266WiFi/examples/WiFiShutdown/WiFiShutdown.ino>`__ available in the examples folder of the ESP8266WiFi library.

getMode
~~~~~~~

Expand Down Expand Up @@ -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 <https://github.com/esp8266/Arduino/blob/master/libraries/ESP8266WiFi/examples/WiFiShutdown/WiFiShutdown.ino>`__ for an example of usage.

Other Function Calls
~~~~~~~~~~~~~~~~~~~~

Expand All @@ -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:
Expand Down
8 changes: 4 additions & 4 deletions libraries/ESP8266WiFi/examples/WiFiShutdown/WiFiShutdown.ino
Original file line number Diff line number Diff line change
@@ -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):
Expand Down Expand Up @@ -39,7 +39,7 @@ void setup() {
ESP.rtcUserMemoryRead(RTC_USER_DATA_SLOT_WIFI_STATE, reinterpret_cast<uint32_t *>(&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);
Expand All @@ -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<uint32_t *>(&state), sizeof(state));

// ---
Expand All @@ -77,4 +77,4 @@ void setup() {

void loop() {
// Nothing to do here.
}
}
2 changes: 2 additions & 0 deletions libraries/ESP8266WiFi/keywords.txt
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ enableSTA KEYWORD2
enableAP KEYWORD2
forceSleepBegin KEYWORD2
forceSleepWake KEYWORD2
shutdown KEYWORD2
resumeFromShutdown KEYWORD2

#ESP8266WiFi
printDiag KEYWORD2
Expand Down
114 changes: 61 additions & 53 deletions libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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){
Expand Down Expand Up @@ -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
Expand All @@ -766,57 +755,63 @@ bool ESP8266WiFiGenericClass::shutdown (uint32 sleepUs, WiFiState* state)
}

// WiFi is now in force-sleep mode
// finish filling state and process crc

state.state.persistent = persistent;
state.state.mode = before_off_mode;

if (state)
uint8_t i = 0;
for (auto& ntp: state.state.ntp)
{
// finish filling state and process crc
ntp = *sntp_getserver(i++);
}
i = 0;

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");
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())
Expand All @@ -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<const char*>(state.state.fwconfig.ssid);
ssid.concat(ptr, strnlen(ptr, sizeof(station_config::ssid)));
}

String pass;
{
const char* ptr = reinterpret_cast<const char*>(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)
{
Expand All @@ -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;
}
Expand Down
20 changes: 11 additions & 9 deletions libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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 --------------------------------
Expand Down
3 changes: 1 addition & 2 deletions libraries/ESP8266WiFi/src/ESP8266WiFiType.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading