mirror of
				https://github.com/esp8266/Arduino.git
				synced 2025-11-03 14:33:37 +03:00 
			
		
		
		
	Experimental: add new WiFi (pseudo) modes: WIFI_SHUTDOWN & WIFI_RESUME (#6356)
* add new WiFimodes: WIFI_SHUTDOWN & WIFI_RESUME with example * restore WiFi.onWiFiModeChange()
This commit is contained in:
		@@ -21,36 +21,15 @@
 | 
			
		||||
 | 
			
		||||
#include <stddef.h>
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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)
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,7 @@ extern "C" {
 | 
			
		||||
 | 
			
		||||
// TODO: put declarations here, get rid of -Wno-implicit-function-declaration
 | 
			
		||||
 | 
			
		||||
#include <stddef.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <cont.h> // 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, uint32_t crc = 0xffffffff);
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										42
									
								
								cores/esp8266/crc32.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								cores/esp8266/crc32.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,42 @@
 | 
			
		||||
/*
 | 
			
		||||
 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"
 | 
			
		||||
 | 
			
		||||
// 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;
 | 
			
		||||
    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;
 | 
			
		||||
}
 | 
			
		||||
@@ -24,6 +24,7 @@
 | 
			
		||||
 | 
			
		||||
#include <list>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <coredecls.h>
 | 
			
		||||
#include "ESP8266WiFi.h"
 | 
			
		||||
#include "ESP8266WiFiGeneric.h"
 | 
			
		||||
 | 
			
		||||
@@ -38,12 +39,19 @@ extern "C" {
 | 
			
		||||
#include "lwip/opt.h"
 | 
			
		||||
#include "lwip/err.h"
 | 
			
		||||
#include "lwip/dns.h"
 | 
			
		||||
#include "lwip/dhcp.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"
 | 
			
		||||
#include "WiFiUdp.h"
 | 
			
		||||
#include "debug.h"
 | 
			
		||||
#include "include/WiFiState.h"
 | 
			
		||||
 | 
			
		||||
extern "C" void esp_schedule();
 | 
			
		||||
extern "C" void esp_yield();
 | 
			
		||||
@@ -200,15 +208,15 @@ WiFiEventHandler ESP8266WiFiGenericClass::onSoftAPModeProbeRequestReceived(std::
 | 
			
		||||
    return handler;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WiFiEventHandler ESP8266WiFiGenericClass::onWiFiModeChange(std::function<void(const WiFiEventModeChange&)> f)
 | 
			
		||||
// {
 | 
			
		||||
//     WiFiEventHandler handler = std::make_shared<WiFiEventHandlerOpaque>(WIFI_EVENT_MODE_CHANGE, [f](System_Event_t* e){
 | 
			
		||||
//         WiFiEventModeChange& dst = *reinterpret_cast<WiFiEventModeChange*>(&e->event_info);
 | 
			
		||||
//         f(dst);
 | 
			
		||||
//     });
 | 
			
		||||
//     sCbEventList.push_back(handler);
 | 
			
		||||
//     return handler;
 | 
			
		||||
// }
 | 
			
		||||
WiFiEventHandler ESP8266WiFiGenericClass::onWiFiModeChange(std::function<void(const WiFiEventModeChange&)> f)
 | 
			
		||||
{
 | 
			
		||||
    WiFiEventHandler handler = std::make_shared<WiFiEventHandlerOpaque>(WIFI_EVENT_MODE_CHANGE, [f](System_Event_t* e){
 | 
			
		||||
        WiFiEventModeChange& dst = *reinterpret_cast<WiFiEventModeChange*>(&e->event_info);
 | 
			
		||||
        f(dst);
 | 
			
		||||
    });
 | 
			
		||||
    sCbEventList.push_back(handler);
 | 
			
		||||
    return handler;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * callback for WiFi events
 | 
			
		||||
@@ -386,7 +394,29 @@ 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 (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
 | 
			
		||||
        wifi_fpm_do_wakeup();
 | 
			
		||||
        wifi_fpm_close();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if(_persistent){
 | 
			
		||||
        if(wifi_get_opmode() == (uint8) m && wifi_get_opmode_default() == (uint8) m){
 | 
			
		||||
            return true;
 | 
			
		||||
@@ -396,12 +426,6 @@ bool ESP8266WiFiGenericClass::mode(WiFiMode_t m) {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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 +455,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 +494,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 +524,10 @@ bool ESP8266WiFiGenericClass::forceSleepBegin(uint32 sleepUs) {
 | 
			
		||||
 * @return ok
 | 
			
		||||
 */
 | 
			
		||||
bool ESP8266WiFiGenericClass::forceSleepWake() {
 | 
			
		||||
    if (wifi_fpm_get_sleep_type() != NONE_SLEEP_T) {
 | 
			
		||||
        wifi_fpm_do_wakeup();
 | 
			
		||||
        wifi_fpm_close();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // restore last mode
 | 
			
		||||
    if(mode(_forceSleepLastMode)) {
 | 
			
		||||
@@ -600,7 +637,142 @@ 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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // WiFi is now in force-sleep mode
 | 
			
		||||
 | 
			
		||||
    if (state)
 | 
			
		||||
    {
 | 
			
		||||
        // 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)
 | 
			
		||||
        {
 | 
			
		||||
#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++);
 | 
			
		||||
        state->crc = shutdownCRC(state);
 | 
			
		||||
        DEBUG_WIFI("core: state is saved\n");
 | 
			
		||||
    }
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // success, invalidate saved state
 | 
			
		||||
    state->crc++;
 | 
			
		||||
 | 
			
		||||
    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);
 | 
			
		||||
 
 | 
			
		||||
@@ -42,6 +42,8 @@ typedef std::shared_ptr<WiFiEventHandlerOpaque> WiFiEventHandler;
 | 
			
		||||
 | 
			
		||||
typedef void (*WiFiEventCb)(WiFiEvent_t);
 | 
			
		||||
 | 
			
		||||
struct WiFiState;
 | 
			
		||||
 | 
			
		||||
class ESP8266WiFiGenericClass {
 | 
			
		||||
        // ----------------------------------------------------------------------------------------------
 | 
			
		||||
        // -------------------------------------- Generic WiFi function ---------------------------------
 | 
			
		||||
@@ -62,7 +64,7 @@ class ESP8266WiFiGenericClass {
 | 
			
		||||
        WiFiEventHandler onSoftAPModeStationConnected(std::function<void(const WiFiEventSoftAPModeStationConnected&)>);
 | 
			
		||||
        WiFiEventHandler onSoftAPModeStationDisconnected(std::function<void(const WiFiEventSoftAPModeStationDisconnected&)>);
 | 
			
		||||
        WiFiEventHandler onSoftAPModeProbeRequestReceived(std::function<void(const WiFiEventSoftAPModeProbeRequestReceived&)>);
 | 
			
		||||
        // WiFiEventHandler onWiFiModeChange(std::function<void(const WiFiEventModeChange&)>);
 | 
			
		||||
        WiFiEventHandler onWiFiModeChange(std::function<void(const WiFiEventModeChange&)>);
 | 
			
		||||
 | 
			
		||||
        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,6 +90,8 @@ class ESP8266WiFiGenericClass {
 | 
			
		||||
        bool forceSleepBegin(uint32 sleepUs = 0);
 | 
			
		||||
        bool forceSleepWake();
 | 
			
		||||
 | 
			
		||||
        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:
 | 
			
		||||
@@ -96,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;
 | 
			
		||||
 
 | 
			
		||||
@@ -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) {
 | 
			
		||||
 
 | 
			
		||||
@@ -34,7 +34,8 @@
 | 
			
		||||
 | 
			
		||||
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,
 | 
			
		||||
    /* these two pseudo modes are experimental: */ WIFI_SHUTDOWN = 4, WIFI_RESUME = 8
 | 
			
		||||
} WiFiMode_t;
 | 
			
		||||
 | 
			
		||||
typedef enum WiFiPhyMode
 | 
			
		||||
@@ -58,9 +59,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 
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										23
									
								
								libraries/ESP8266WiFi/src/include/WiFiState.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								libraries/ESP8266WiFi/src/include/WiFiState.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,23 @@
 | 
			
		||||
 | 
			
		||||
#ifndef WIFISTATE_H_
 | 
			
		||||
#define WIFISTATE_H_
 | 
			
		||||
 | 
			
		||||
#include <user_interface.h>
 | 
			
		||||
#include <ESP8266WiFiType.h>
 | 
			
		||||
 | 
			
		||||
struct WiFiState
 | 
			
		||||
{
 | 
			
		||||
    uint32_t crc;
 | 
			
		||||
    struct
 | 
			
		||||
    {
 | 
			
		||||
        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;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif // WIFISTATE_H_
 | 
			
		||||
							
								
								
									
										258
									
								
								libraries/esp8266/examples/WiFiShutdown/WiFiShutdown.ino
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										258
									
								
								libraries/esp8266/examples/WiFiShutdown/WiFiShutdown.ino
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,258 @@
 | 
			
		||||
 | 
			
		||||
// 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 <ESP8266WiFi.h>
 | 
			
		||||
#include <coredecls.h>         // crc32()
 | 
			
		||||
#include <include/WiFiState.h> // 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,
 | 
			
		||||
  e_wait_off
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
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));
 | 
			
		||||
              step = e_wait_off;
 | 
			
		||||
              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));
 | 
			
		||||
              step = e_wait_shutdown;
 | 
			
		||||
              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();
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    case e_wait_shutdown:
 | 
			
		||||
      if (millis() - startup > 5000) {
 | 
			
		||||
        step = e_start_resume;
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    case e_wait_off:
 | 
			
		||||
      if (millis() - startup > 5000) {
 | 
			
		||||
        step = e_start_normal;
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -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 \
 | 
			
		||||
 
 | 
			
		||||
@@ -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"
 | 
			
		||||
 
 | 
			
		||||
@@ -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"
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user