From 810fed9279bfc6ff2132439ae09256d3caeac570 Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Tue, 13 Apr 2021 11:10:50 +0200 Subject: [PATCH 01/39] WiFi is off by default anyway. --- libraries/esp8266/examples/I2SInput/I2SInput.ino | 2 -- 1 file changed, 2 deletions(-) diff --git a/libraries/esp8266/examples/I2SInput/I2SInput.ino b/libraries/esp8266/examples/I2SInput/I2SInput.ino index 6e2e11b07b..8729c0e6a6 100644 --- a/libraries/esp8266/examples/I2SInput/I2SInput.ino +++ b/libraries/esp8266/examples/I2SInput/I2SInput.ino @@ -27,12 +27,10 @@ course be sure to wire up VCC(3.3V) and GND. */ -#include #include void setup() { Serial.begin(115200); - WiFi.forceSleepBegin(); delay(500); i2s_rxtx_begin(true, false); // Enable I2S RX From a1ceef33d5f63ef52b657cd61ae5ef26e4638167 Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Thu, 15 Apr 2021 19:05:36 +0200 Subject: [PATCH 02/39] Set deep sleep resume and powerup RF modes to OFF. --- cores/esp8266/Esp.h | 3 -- cores/esp8266/core_esp8266_phy.cpp | 37 +++++++++++++++++-- .../src/enable_wifi_at_boot_time.cpp | 12 ++++++ 3 files changed, 45 insertions(+), 7 deletions(-) diff --git a/cores/esp8266/Esp.h b/cores/esp8266/Esp.h index 9f97175c29..94831a4b51 100644 --- a/cores/esp8266/Esp.h +++ b/cores/esp8266/Esp.h @@ -57,9 +57,6 @@ enum RFMode { RF_DISABLED = 4 // disable RF after deep-sleep wake up, just like modem sleep, there will be the smallest current. }; -#define RF_MODE(mode) int __get_rf_mode() { return mode; } -#define RF_PRE_INIT() void __run_user_rf_pre_init() - // compatibility definitions #define WakeMode RFMode #define WAKE_RF_DEFAULT RF_DEFAULT diff --git a/cores/esp8266/core_esp8266_phy.cpp b/cores/esp8266/core_esp8266_phy.cpp index 2a324a85f9..e853edbd79 100644 --- a/cores/esp8266/core_esp8266_phy.cpp +++ b/cores/esp8266/core_esp8266_phy.cpp @@ -295,6 +295,7 @@ static const uint8_t ICACHE_FLASH_ATTR phy_init_data[128] = // so we use mangled names here. #define __get_adc_mode _Z14__get_adc_modev #define __get_rf_mode _Z13__get_rf_modev +#define __get_rf_powerup_mode _Z21__get_rf_powerup_modev #define __run_user_rf_pre_init _Z22__run_user_rf_pre_initv static bool spoof_init_data = false; @@ -325,19 +326,43 @@ extern int IRAM_ATTR __wrap_spi_flash_read(uint32_t addr, uint32_t* dst, size_t return 0; } -extern int __get_rf_mode(void) __attribute__((weak)); +extern int __get_rf_disable_mode(void) __attribute__((noinline, weak)); +extern int __get_rf_disable_mode(void) +{ + // Starting from arduino core v3: wifi is disabled at boot time + // mode == 4: Disable RF after deep-sleep wake up, just like modem sleep; this has the least current + // consumption; the device is not able to transmit or receive data after wake up. + return 4; +} + +extern int __get_rf_powerup_disable_mode(void) __attribute__((noinline, weak)); +extern int __get_rf_powerup_disable_mode(void) +{ + // Starting from arduino core v3: wifi is disabled at boot time + // mode == 2: RF initialization only calibrates VDD33 which will take about 2 ms; + // this has the least current consumption. + return 2; +} + +extern int __get_rf_mode(void) __attribute__((noinline, weak)); extern int __get_rf_mode(void) { - return -1; // mode not set + return __get_rf_disable_mode(); } -extern int __get_adc_mode(void) __attribute__((weak)); +extern int __get_rf_powerup_mode(void) __attribute__((noinline, weak)); +extern int __get_rf_powerup_mode(void) +{ + return __get_rf_powerup_disable_mode(); +} + +extern int __get_adc_mode(void) __attribute__((noinline, weak)); extern int __get_adc_mode(void) { return 33; // default ADC mode } -extern void __run_user_rf_pre_init(void) __attribute__((weak)); +extern void __run_user_rf_pre_init(void) __attribute__((noinline, weak)); extern void __run_user_rf_pre_init(void) { return; // default do noting @@ -365,6 +390,10 @@ void user_rf_pre_init() if (rf_mode >= 0) { system_phy_set_rfoption(rf_mode); } + rf_mode = __get_rf_powerup_mode(); + if (rf_mode >= 0) { + system_phy_set_powerup_option(rf_mode); + } __run_user_rf_pre_init(); } diff --git a/libraries/ESP8266WiFi/src/enable_wifi_at_boot_time.cpp b/libraries/ESP8266WiFi/src/enable_wifi_at_boot_time.cpp index 5eb62eec15..997f48b50f 100644 --- a/libraries/ESP8266WiFi/src/enable_wifi_at_boot_time.cpp +++ b/libraries/ESP8266WiFi/src/enable_wifi_at_boot_time.cpp @@ -26,3 +26,15 @@ extern "C" void __disableWiFiAtBootTime() // (note: c++ ctors not called yet at this point) ESP8266WiFiClass::persistent(true); } + +extern "C" int __get_rf_disable_mode(void) +{ + // overrides the default __get_rf_disable_mode + return -1; // mode not set +} + +extern "C" int __get_rf_powerup_disable_mode(void) +{ + // overrides the default __get_rf__powerup_disable_mode + return -1; // mode not set +} From 9538ef900320e2a972fa721e9307965bf96c964f Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Thu, 15 Apr 2021 19:26:28 +0200 Subject: [PATCH 03/39] Implement forced light sleep in Esp class --- cores/esp8266/Esp.cpp | 47 +++++++++++++++++++++++++++++++++++++++++++ cores/esp8266/Esp.h | 5 +++++ 2 files changed, 52 insertions(+) diff --git a/cores/esp8266/Esp.cpp b/cores/esp8266/Esp.cpp index 20da390008..416065d35f 100644 --- a/cores/esp8266/Esp.cpp +++ b/cores/esp8266/Esp.cpp @@ -138,6 +138,53 @@ uint64_t EspClass::deepSleepMax() } +extern os_timer_t* timer_list; +namespace { + sleep_type_t saved_sleep_type; + os_timer_t* saved_timer_list; + fpm_wakeup_cb saved_wakeupCb; +} + +bool EspClass::forcedLightSleepBegin(uint32_t duration_us, fpm_wakeup_cb wakeupCb) +{ + // Setting duration to 0xFFFFFFF, it disconnects the RTC timer + if (!duration_us || duration_us > 0xFFFFFFF) { + duration_us = 0xFFFFFFF; + } + wifi_fpm_close(); + saved_sleep_type = wifi_fpm_get_sleep_type(); + wifi_fpm_set_sleep_type(LIGHT_SLEEP_T); + wifi_fpm_open(); + saved_wakeupCb = wakeupCb; + wifi_fpm_set_wakeup_cb([]() { + if (saved_wakeupCb) saved_wakeupCb(); + esp_schedule(); + }); + { + esp8266::InterruptLock lock; + saved_timer_list = timer_list; + timer_list = nullptr; + } + return wifi_fpm_do_sleep(duration_us) == 0; +} + +void EspClass::forcedLightSleepEnd(bool cancel) +{ + if (!cancel) { +#ifdef HAVE_ESP_SUSPEND + esp_suspend(); +#else + esp_yield(); // it goes to sleep from SYS context and waits for an interrupt +#endif + } + { + esp8266::InterruptLock lock; + timer_list = saved_timer_list; + } + wifi_fpm_close(); + wifi_fpm_set_sleep_type(saved_sleep_type); +} + /* Layout of RTC Memory is as follows: Ref: Espressif doc 2C-ESP8266_Non_OS_SDK_API_Reference, section 3.3.23 (system_rtc_mem_write) diff --git a/cores/esp8266/Esp.h b/cores/esp8266/Esp.h index 94831a4b51..26654c12bc 100644 --- a/cores/esp8266/Esp.h +++ b/cores/esp8266/Esp.h @@ -95,6 +95,11 @@ class EspClass { static void deepSleepInstant(uint64_t time_us, RFMode mode = RF_DEFAULT); static uint64_t deepSleepMax(); + static bool forcedLightSleepBegin(uint32_t duration_us = 0, void (*wakeupCb)() = nullptr); + /// The prior sleep type is restored, but only as automatic. + /// If any forced sleep mode was effective before forcedLightSleepBegin, it must be explicitly re-entered. + static void forcedLightSleepEnd(bool cancel = false); + static bool rtcUserMemoryRead(uint32_t offset, uint32_t *data, size_t size); static bool rtcUserMemoryWrite(uint32_t offset, uint32_t *data, size_t size); From 1ecdfc1c78ccc5086998bd4438b72262b96fbefe Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Thu, 15 Apr 2021 19:29:05 +0200 Subject: [PATCH 04/39] Add low power forced light sleep demo. --- .../ForcedLightSleep/ForcedLightSleep.ino | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 libraries/esp8266/examples/ForcedLightSleep/ForcedLightSleep.ino diff --git a/libraries/esp8266/examples/ForcedLightSleep/ForcedLightSleep.ino b/libraries/esp8266/examples/ForcedLightSleep/ForcedLightSleep.ino new file mode 100644 index 0000000000..2cffa40d33 --- /dev/null +++ b/libraries/esp8266/examples/ForcedLightSleep/ForcedLightSleep.ino @@ -0,0 +1,56 @@ +#include +#include + +#define WAKE_UP_PIN 0 // D3/GPIO0, can also force a serial flash upload with RESET +// you can use any GPIO for WAKE_UP_PIN except for D0/GPIO16 as it doesn't support interrupts + +void IRAM_ATTR wakeupPinIsr() { + schedule_function([]() { + Serial.println("GPIO went from HI to LO"); + }); +} + +void IRAM_ATTR wakeupPinIsrWE() { + schedule_function([]() { + Serial.println("GPIO wakeup IRQ"); + }); + wakeupPinIsr(); + attachInterrupt(WAKE_UP_PIN, wakeupPinIsr, FALLING); +} + +//void wakeupCallback() { +//} + +void setup() { + Serial.begin(74880); + while (!Serial); + delay(100); + pinMode(LED_BUILTIN, OUTPUT); // activity and status indicator + digitalWrite(LED_BUILTIN, LOW); // turn on the LED + pinMode(WAKE_UP_PIN, INPUT_PULLUP); // polled to advance tests, interrupt for Forced Light Sleep + attachInterrupt(WAKE_UP_PIN, wakeupPinIsr, FALLING); +} + +using oneShotYieldMs = esp8266::polledTimeout::timeoutTemplate; +oneShotYieldMs gotoSleep(2000); + +void loop() { + if (gotoSleep && ESP.forcedLightSleepBegin(10000000/*, wakeupCallback*/)) { + // No new timers, no delay(), between forcedLightSleepBegin() and forcedLightSleepEnd(). + // Only ONLOW_WE or ONHIGH_WE interrupts work, no edge, that's an SDK or CPU limitation. + // If the GPIO is in wakeup state while attaching the interrupt, it cannot trigger a wakeup, + // but any sleep duration will be honored. + bool wakeupPinIsHigh = digitalRead(WAKE_UP_PIN); + // the GPIO might still bounce to LOW between both digital reads, disabling wakeup + if (wakeupPinIsHigh) { + attachInterrupt(WAKE_UP_PIN, wakeupPinIsrWE, ONLOW_WE); + } + wakeupPinIsHigh &= digitalRead(WAKE_UP_PIN); + digitalWrite(LED_BUILTIN, HIGH); // turn the LED off so they know the CPU isn't running + ESP.forcedLightSleepEnd(!wakeupPinIsHigh); + digitalWrite(LED_BUILTIN, LOW); // turn on the LED + if (wakeupPinIsHigh) { + gotoSleep.reset(); + } + } +} From ef2bc9534216040c005de58ecd6304e3ad0fba0e Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Thu, 15 Apr 2021 19:49:03 +0200 Subject: [PATCH 05/39] Implement auto light sleep and auto modem sleep in Esp class --- cores/esp8266/Esp.cpp | 374 ++++++++++++++++++++++-------------------- cores/esp8266/Esp.h | 10 ++ 2 files changed, 205 insertions(+), 179 deletions(-) diff --git a/cores/esp8266/Esp.cpp b/cores/esp8266/Esp.cpp index 416065d35f..99e0eeed4c 100644 --- a/cores/esp8266/Esp.cpp +++ b/cores/esp8266/Esp.cpp @@ -35,14 +35,14 @@ extern "C" { #include "user_interface.h" -extern struct rst_info resetInfo; + extern struct rst_info resetInfo; } //#define DEBUG_SERIAL Serial #ifndef PUYA_SUPPORT - #define PUYA_SUPPORT 1 +#define PUYA_SUPPORT 1 #endif /** @@ -93,14 +93,14 @@ EspClass ESP; void EspClass::wdtEnable(uint32_t timeout_ms) { - (void) timeout_ms; + (void)timeout_ms; /// This API can only be called if software watchdog is stopped system_soft_wdt_restart(); } void EspClass::wdtEnable(WDTO_t timeout_ms) { - wdtEnable((uint32_t) timeout_ms); + wdtEnable((uint32_t)timeout_ms); } void EspClass::wdtDisable(void) @@ -133,8 +133,8 @@ void EspClass::deepSleepInstant(uint64_t time_us, WakeMode mode) //Note: system_rtc_clock_cali_proc() returns a uint32_t, even though system_deep_sleep() takes a uint64_t. uint64_t EspClass::deepSleepMax() { - //cali*(2^31-1)/(2^12) - return (uint64_t)system_rtc_clock_cali_proc()*(0x80000000-1)/(0x1000); + //cali*(2^31-1)/(2^12) + return (uint64_t)system_rtc_clock_cali_proc() * (0x80000000 - 1) / (0x1000); } @@ -185,6 +185,22 @@ void EspClass::forcedLightSleepEnd(bool cancel) wifi_fpm_set_sleep_type(saved_sleep_type); } +void EspClass::autoModemSleep(bool on) { + if (on) { + wifi_fpm_close(); + saved_sleep_type = wifi_fpm_get_sleep_type(); + } + wifi_fpm_set_sleep_type(on ? MODEM_SLEEP_T : saved_sleep_type); +} + +void EspClass::autoLightSleep(bool on) { + if (on) { + wifi_fpm_close(); + saved_sleep_type = wifi_fpm_get_sleep_type(); + } + wifi_fpm_set_sleep_type(on ? LIGHT_SLEEP_T : saved_sleep_type); +} + /* Layout of RTC Memory is as follows: Ref: Espressif doc 2C-ESP8266_Non_OS_SDK_API_Reference, section 3.3.23 (system_rtc_mem_write) @@ -193,9 +209,9 @@ Ref: Espressif doc 2C-ESP8266_Non_OS_SDK_API_Reference, section 3.3.23 (system_r SDK function signature: bool system_rtc_mem_read ( - uint32 des_addr, - void * src_addr, - uint32 save_size + uint32 des_addr, + void * src_addr, + uint32 save_size ) The system data section can't be used by the user, so: @@ -219,7 +235,7 @@ retrieved by eboot on boot. That means that user data present there will be lost - Arduino/bootloaders/eboot/eboot_command.h RTC_MEM definition */ -bool EspClass::rtcUserMemoryRead(uint32_t offset, uint32_t *data, size_t size) +bool EspClass::rtcUserMemoryRead(uint32_t offset, uint32_t* data, size_t size) { if (offset * 4 + size > 512 || size == 0) { return false; @@ -228,7 +244,7 @@ bool EspClass::rtcUserMemoryRead(uint32_t offset, uint32_t *data, size_t size) } } -bool EspClass::rtcUserMemoryWrite(uint32_t offset, uint32_t *data, size_t size) +bool EspClass::rtcUserMemoryWrite(uint32_t offset, uint32_t* data, size_t size) { if (offset * 4 + size > 512 || size == 0) { return false; @@ -250,11 +266,11 @@ void EspClass::restart(void) [[noreturn]] void EspClass::rebootIntoUartDownloadMode() { - wdtDisable(); - /* disable hardware watchdog */ - CLEAR_PERI_REG_MASK(PERIPHS_HW_WDT, 0x1); + wdtDisable(); + /* disable hardware watchdog */ + CLEAR_PERI_REG_MASK(PERIPHS_HW_WDT, 0x1); - esp8266RebootIntoUartDownloadMode(); + esp8266RebootIntoUartDownloadMode(); } uint16_t EspClass::getVcc(void) @@ -304,7 +320,7 @@ String EspClass::getCoreVersion() return String(buf); } -const char * EspClass::getSdkVersion(void) +const char* EspClass::getSdkVersion(void) { return system_get_sdk_version(); } @@ -344,9 +360,9 @@ uint32_t EspClass::getFlashChipSize(void) return getFlashChipRealSize(); #else uint32_t data; - uint8_t * bytes = (uint8_t *) &data; + uint8_t* bytes = (uint8_t*)&data; // read first 4 byte (magic byte + flash config) - if(spi_flash_read(0x0000, &data, 4) == SPI_FLASH_RESULT_OK) { + if (spi_flash_read(0x0000, &data, 4) == SPI_FLASH_RESULT_OK) { return magicFlashChipSize((bytes[3] & 0xf0) >> 4); } return 0; @@ -356,9 +372,9 @@ uint32_t EspClass::getFlashChipSize(void) uint32_t EspClass::getFlashChipSpeed(void) { uint32_t data; - uint8_t * bytes = (uint8_t *) &data; + uint8_t* bytes = (uint8_t*)&data; // read first 4 byte (magic byte + flash config) - if(spi_flash_read(0x0000, &data, 4) == SPI_FLASH_RESULT_OK) { + if (spi_flash_read(0x0000, &data, 4) == SPI_FLASH_RESULT_OK) { return magicFlashChipSpeed(bytes[3] & 0x0F); } return 0; @@ -368,9 +384,9 @@ FlashMode_t EspClass::getFlashChipMode(void) { FlashMode_t mode = FM_UNKNOWN; uint32_t data; - uint8_t * bytes = (uint8_t *) &data; + uint8_t* bytes = (uint8_t*)&data; // read first 4 byte (magic byte + flash config) - if(spi_flash_read(0x0000, &data, 4) == SPI_FLASH_RESULT_OK) { + if (spi_flash_read(0x0000, &data, 4) == SPI_FLASH_RESULT_OK) { mode = magicFlashChipMode(bytes[2]); } return mode; @@ -378,45 +394,45 @@ FlashMode_t EspClass::getFlashChipMode(void) #if !FLASH_MAP_SUPPORT uint32_t EspClass::magicFlashChipSize(uint8_t byte) { - switch(byte & 0x0F) { - case 0x0: // 4 Mbit (512KB) - return (512_kB); - case 0x1: // 2 MBit (256KB) - return (256_kB); - case 0x2: // 8 MBit (1MB) - return (1_MB); - case 0x3: // 16 MBit (2MB) - return (2_MB); - case 0x4: // 32 MBit (4MB) - return (4_MB); - case 0x8: // 64 MBit (8MB) - return (8_MB); - case 0x9: // 128 MBit (16MB) - return (16_MB); - default: // fail? - return 0; + switch (byte & 0x0F) { + case 0x0: // 4 Mbit (512KB) + return (512_kB); + case 0x1: // 2 MBit (256KB) + return (256_kB); + case 0x2: // 8 MBit (1MB) + return (1_MB); + case 0x3: // 16 MBit (2MB) + return (2_MB); + case 0x4: // 32 MBit (4MB) + return (4_MB); + case 0x8: // 64 MBit (8MB) + return (8_MB); + case 0x9: // 128 MBit (16MB) + return (16_MB); + default: // fail? + return 0; } } #endif uint32_t EspClass::magicFlashChipSpeed(uint8_t byte) { - switch(byte & 0x0F) { - case 0x0: // 40 MHz - return (40_MHz); - case 0x1: // 26 MHz - return (26_MHz); - case 0x2: // 20 MHz - return (20_MHz); - case 0xf: // 80 MHz - return (80_MHz); - default: // fail? - return 0; + switch (byte & 0x0F) { + case 0x0: // 40 MHz + return (40_MHz); + case 0x1: // 26 MHz + return (26_MHz); + case 0x2: // 20 MHz + return (20_MHz); + case 0xf: // 80 MHz + return (80_MHz); + default: // fail? + return 0; } } FlashMode_t EspClass::magicFlashChipMode(uint8_t byte) { - FlashMode_t mode = (FlashMode_t) byte; - if(mode > FM_DOUT) { + FlashMode_t mode = (FlashMode_t)byte; + if (mode > FM_DOUT) { mode = FM_UNKNOWN; } return mode; @@ -437,54 +453,54 @@ uint32_t EspClass::getFlashChipSizeByChipId(void) { * 40 - ? may be Speed ? //todo: find docu to this * C8 - manufacturer ID */ - switch(chipId) { + switch (chipId) { // GigaDevice - case 0x1740C8: // GD25Q64B - return (8_MB); - case 0x1640C8: // GD25Q32B - return (4_MB); - case 0x1540C8: // GD25Q16B - return (2_MB); - case 0x1440C8: // GD25Q80 - return (1_MB); - case 0x1340C8: // GD25Q40 - return (512_kB); - case 0x1240C8: // GD25Q20 - return (256_kB); - case 0x1140C8: // GD25Q10 - return (128_kB); - case 0x1040C8: // GD25Q12 - return (64_kB); + case 0x1740C8: // GD25Q64B + return (8_MB); + case 0x1640C8: // GD25Q32B + return (4_MB); + case 0x1540C8: // GD25Q16B + return (2_MB); + case 0x1440C8: // GD25Q80 + return (1_MB); + case 0x1340C8: // GD25Q40 + return (512_kB); + case 0x1240C8: // GD25Q20 + return (256_kB); + case 0x1140C8: // GD25Q10 + return (128_kB); + case 0x1040C8: // GD25Q12 + return (64_kB); // Winbond - case 0x1840EF: // W25Q128 - return (16_MB); - case 0x1640EF: // W25Q32 - return (4_MB); - case 0x1540EF: // W25Q16 - return (2_MB); - case 0x1440EF: // W25Q80 - return (1_MB); - case 0x1340EF: // W25Q40 - return (512_kB); + case 0x1840EF: // W25Q128 + return (16_MB); + case 0x1640EF: // W25Q32 + return (4_MB); + case 0x1540EF: // W25Q16 + return (2_MB); + case 0x1440EF: // W25Q80 + return (1_MB); + case 0x1340EF: // W25Q40 + return (512_kB); // BergMicro - case 0x1640E0: // BG25Q32 - return (4_MB); - case 0x1540E0: // BG25Q16 - return (2_MB); - case 0x1440E0: // BG25Q80 - return (1_MB); - case 0x1340E0: // BG25Q40 - return (512_kB); + case 0x1640E0: // BG25Q32 + return (4_MB); + case 0x1540E0: // BG25Q16 + return (2_MB); + case 0x1440E0: // BG25Q80 + return (1_MB); + case 0x1340E0: // BG25Q40 + return (512_kB); // XMC - Wuhan Xinxin Semiconductor Manufacturing Corp - case 0x164020: // XM25QH32B - return (4_MB); + case 0x164020: // XM25QH32B + return (4_MB); - default: - return 0; + default: + return 0; } } @@ -494,12 +510,12 @@ uint32_t EspClass::getFlashChipSizeByChipId(void) { * @return ok or not */ bool EspClass::checkFlashConfig(bool needsEquals) { - if(needsEquals) { - if(getFlashChipRealSize() == getFlashChipSize()) { + if (needsEquals) { + if (getFlashChipRealSize() == getFlashChipSize()) { return true; } } else { - if(getFlashChipRealSize() >= getFlashChipSize()) { + if (getFlashChipRealSize() >= getFlashChipSize()) { return true; } } @@ -530,22 +546,22 @@ bool EspClass::checkFlashCRC() { String EspClass::getResetReason(void) { const __FlashStringHelper* buff; - switch(resetInfo.reason) { + switch (resetInfo.reason) { // normal startup by power on - case REASON_DEFAULT_RST: buff = F("Power On"); break; + case REASON_DEFAULT_RST: buff = F("Power On"); break; // hardware watch dog reset - case REASON_WDT_RST: buff = F("Hardware Watchdog"); break; + case REASON_WDT_RST: buff = F("Hardware Watchdog"); break; // exception reset, GPIO status won’t change - case REASON_EXCEPTION_RST: buff = F("Exception"); break; + case REASON_EXCEPTION_RST: buff = F("Exception"); break; // software watch dog reset, GPIO status won’t change - case REASON_SOFT_WDT_RST: buff = F("Software Watchdog"); break; + case REASON_SOFT_WDT_RST: buff = F("Software Watchdog"); break; // software restart ,system_restart , GPIO status won’t change - case REASON_SOFT_RESTART: buff = F("Software/System restart"); break; + case REASON_SOFT_RESTART: buff = F("Software/System restart"); break; // wake up from deep-sleep - case REASON_DEEP_SLEEP_AWAKE: buff = F("Deep-Sleep Wake"); break; + case REASON_DEEP_SLEEP_AWAKE: buff = F("Deep-Sleep Wake"); break; // // external system reset - case REASON_EXT_SYS_RST: buff = F("External System"); break; - default: buff = F("Unknown"); break; + case REASON_EXT_SYS_RST: buff = F("External System"); break; + default: buff = F("Unknown"); break; } return String(buff); } @@ -561,7 +577,7 @@ String EspClass::getResetInfo(void) { return getResetReason(); } -struct rst_info * EspClass::getResetInfoPtr(void) { +struct rst_info* EspClass::getResetInfoPtr(void) { return &resetInfo; } @@ -578,61 +594,61 @@ bool EspClass::eraseConfig(void) { return true; } -uint8_t *EspClass::random(uint8_t *resultArray, const size_t outputSizeBytes) +uint8_t *EspClass::random(uint8_t* resultArray, const size_t outputSizeBytes) { - /** - * The ESP32 Technical Reference Manual v4.1 chapter 24 has the following to say about random number generation (no information found for ESP8266): - * - * "When used correctly, every 32-bit value the system reads from the RNG_DATA_REG register of the random number generator is a true random number. - * These true random numbers are generated based on the noise in the Wi-Fi/BT RF system. - * When Wi-Fi and BT are disabled, the random number generator will give out pseudo-random numbers. - * - * When Wi-Fi or BT is enabled, the random number generator is fed two bits of entropy every APB clock cycle (normally 80 MHz). - * Thus, for the maximum amount of entropy, it is advisable to read the random register at a maximum rate of 5 MHz. - * A data sample of 2 GB, read from the random number generator with Wi-Fi enabled and the random register read at 5 MHz, - * has been tested using the Dieharder Random Number Testsuite (version 3.31.1). - * The sample passed all tests." - * - * Since ESP32 is the sequal to ESP8266 it is unlikely that the ESP8266 is able to generate random numbers more quickly than 5 MHz when run at a 80 MHz frequency. - * A maximum random number frequency of 0.5 MHz is used here to leave some margin for possibly inferior components in the ESP8266. - * It should be noted that the ESP8266 has no Bluetooth functionality, so turning the WiFi off is likely to cause RANDOM_REG32 to use pseudo-random numbers. - * - * It is possible that yield() must be called on the ESP8266 to properly feed the hardware random number generator new bits, since there is only one processor core available. - * However, no feeding requirements are mentioned in the ESP32 documentation, and using yield() could possibly cause extended delays during number generation. - * Thus only delayMicroseconds() is used below. - */ - - constexpr uint8_t cooldownMicros = 2; - static uint32_t lastCalledMicros = micros() - cooldownMicros; - - uint32_t randomNumber = 0; - - for(size_t byteIndex = 0; byteIndex < outputSizeBytes; ++byteIndex) - { - if(byteIndex % 4 == 0) + /** + * The ESP32 Technical Reference Manual v4.1 chapter 24 has the following to say about random number generation (no information found for ESP8266): + * + * "When used correctly, every 32-bit value the system reads from the RNG_DATA_REG register of the random number generator is a true random number. + * These true random numbers are generated based on the noise in the Wi-Fi/BT RF system. + * When Wi-Fi and BT are disabled, the random number generator will give out pseudo-random numbers. + * + * When Wi-Fi or BT is enabled, the random number generator is fed two bits of entropy every APB clock cycle (normally 80 MHz). + * Thus, for the maximum amount of entropy, it is advisable to read the random register at a maximum rate of 5 MHz. + * A data sample of 2 GB, read from the random number generator with Wi-Fi enabled and the random register read at 5 MHz, + * has been tested using the Dieharder Random Number Testsuite (version 3.31.1). + * The sample passed all tests." + * + * Since ESP32 is the sequal to ESP8266 it is unlikely that the ESP8266 is able to generate random numbers more quickly than 5 MHz when run at a 80 MHz frequency. + * A maximum random number frequency of 0.5 MHz is used here to leave some margin for possibly inferior components in the ESP8266. + * It should be noted that the ESP8266 has no Bluetooth functionality, so turning the WiFi off is likely to cause RANDOM_REG32 to use pseudo-random numbers. + * + * It is possible that yield() must be called on the ESP8266 to properly feed the hardware random number generator new bits, since there is only one processor core available. + * However, no feeding requirements are mentioned in the ESP32 documentation, and using yield() could possibly cause extended delays during number generation. + * Thus only delayMicroseconds() is used below. + */ + + constexpr uint8_t cooldownMicros = 2; + static uint32_t lastCalledMicros = micros() - cooldownMicros; + + uint32_t randomNumber = 0; + + for (size_t byteIndex = 0; byteIndex < outputSizeBytes; ++byteIndex) { - // Old random number has been used up (random number could be exactly 0, so we can't check for that) + if (byteIndex % 4 == 0) + { + // Old random number has been used up (random number could be exactly 0, so we can't check for that) - uint32_t timeSinceLastCall = micros() - lastCalledMicros; - if(timeSinceLastCall < cooldownMicros) - delayMicroseconds(cooldownMicros - timeSinceLastCall); + uint32_t timeSinceLastCall = micros() - lastCalledMicros; + if (timeSinceLastCall < cooldownMicros) + delayMicroseconds(cooldownMicros - timeSinceLastCall); - randomNumber = RANDOM_REG32; - lastCalledMicros = micros(); - } + randomNumber = RANDOM_REG32; + lastCalledMicros = micros(); + } - resultArray[byteIndex] = randomNumber; - randomNumber >>= 8; - } + resultArray[byteIndex] = randomNumber; + randomNumber >>= 8; + } - return resultArray; + return resultArray; } uint32_t EspClass::random() { - union { uint32_t b32; uint8_t b8[4]; } result; - random(result.b8, 4); - return result.b32; + union { uint32_t b32; uint8_t b8[4]; } result; + random(result.b8, 4); + return result.b32; } uint32_t EspClass::getSketchSize() { @@ -642,7 +658,7 @@ uint32_t EspClass::getSketchSize() { image_header_t image_header; uint32_t pos = APP_START_OFFSET; - if (spi_flash_read(pos, (uint32_t*) &image_header, sizeof(image_header)) != SPI_FLASH_RESULT_OK) { + if (spi_flash_read(pos, (uint32_t*)&image_header, sizeof(image_header)) != SPI_FLASH_RESULT_OK) { return 0; } pos += sizeof(image_header); @@ -653,8 +669,8 @@ uint32_t EspClass::getSketchSize() { section_index < image_header.num_segments; ++section_index) { - section_header_t section_header = {0, 0}; - if (spi_flash_read(pos, (uint32_t*) §ion_header, sizeof(section_header)) != SPI_FLASH_RESULT_OK) { + section_header_t section_header = { 0, 0 }; + if (spi_flash_read(pos, (uint32_t*)§ion_header, sizeof(section_header)) != SPI_FLASH_RESULT_OK) { return 0; } pos += sizeof(section_header); @@ -681,37 +697,37 @@ uint32_t EspClass::getFreeSketchSpace() { } bool EspClass::updateSketch(Stream& in, uint32_t size, bool restartOnFail, bool restartOnSuccess) { - if(!Update.begin(size)){ + if (!Update.begin(size)) { #ifdef DEBUG_SERIAL - DEBUG_SERIAL.print("Update "); - Update.printError(DEBUG_SERIAL); + DEBUG_SERIAL.print("Update "); + Update.printError(DEBUG_SERIAL); #endif - if(restartOnFail) ESP.restart(); - return false; - } + if (restartOnFail) ESP.restart(); + return false; +} - if(Update.writeStream(in) != size){ + if (Update.writeStream(in) != size) { #ifdef DEBUG_SERIAL - DEBUG_SERIAL.print("Update "); - Update.printError(DEBUG_SERIAL); + DEBUG_SERIAL.print("Update "); + Update.printError(DEBUG_SERIAL); #endif - if(restartOnFail) ESP.restart(); - return false; - } + if (restartOnFail) ESP.restart(); + return false; + } - if(!Update.end()){ + if (!Update.end()) { #ifdef DEBUG_SERIAL - DEBUG_SERIAL.print("Update "); - Update.printError(DEBUG_SERIAL); + DEBUG_SERIAL.print("Update "); + Update.printError(DEBUG_SERIAL); #endif - if(restartOnFail) ESP.restart(); - return false; - } + if (restartOnFail) ESP.restart(); + return false; + } #ifdef DEBUG_SERIAL DEBUG_SERIAL.println("Update SUCCESS"); #endif - if(restartOnSuccess) ESP.restart(); + if (restartOnSuccess) ESP.restart(); return true; } @@ -762,18 +778,18 @@ static SpiFlashOpResult spi_flash_write_page_break(uint32_t offset, uint32_t *da #if PUYA_SUPPORT // Special wrapper for spi_flash_write *only for PUYA flash chips* // Already handles paging, could be used as a `spi_flash_write_page_break` replacement -static SpiFlashOpResult spi_flash_write_puya(uint32_t offset, uint32_t *data, size_t size) { +static SpiFlashOpResult spi_flash_write_puya(uint32_t offset, uint32_t* data, size_t size) { if (data == nullptr) { - return SPI_FLASH_RESULT_ERR; + return SPI_FLASH_RESULT_ERR; } if (size % 4 != 0) { - return SPI_FLASH_RESULT_ERR; + return SPI_FLASH_RESULT_ERR; } // PUYA flash chips need to read existing data, update in memory and write modified data again. - static uint32_t *flash_write_puya_buf = nullptr; + static uint32_t* flash_write_puya_buf = nullptr; if (flash_write_puya_buf == nullptr) { - flash_write_puya_buf = (uint32_t*) malloc(FLASH_PAGE_SIZE); + flash_write_puya_buf = (uint32_t*)malloc(FLASH_PAGE_SIZE); // No need to ever free this, since the flash chip will never change at runtime. if (flash_write_puya_buf == nullptr) { // Memory could not be allocated. @@ -834,7 +850,7 @@ static bool isAlignedPointer(const uint8_t *ptr) { } -size_t EspClass::flashWriteUnalignedMemory(uint32_t address, const uint8_t *data, size_t size) { +size_t EspClass::flashWriteUnalignedMemory(uint32_t address, const uint8_t* data, size_t size) { auto flash_write = [](uint32_t address, uint8_t *data, size_t size) { return spi_flash_write(address, reinterpret_cast(data), size) == SPI_FLASH_RESULT_OK; }; @@ -904,7 +920,7 @@ size_t EspClass::flashWriteUnalignedMemory(uint32_t address, const uint8_t *data return written; } -bool EspClass::flashWrite(uint32_t address, const uint32_t *data, size_t size) { +bool EspClass::flashWrite(uint32_t address, const uint32_t* data, size_t size) { SpiFlashOpResult result; #if PUYA_SUPPORT if (getFlashChipVendorId() == SPI_FLASH_VENDOR_PUYA) { @@ -918,7 +934,7 @@ bool EspClass::flashWrite(uint32_t address, const uint32_t *data, size_t size) { return result == SPI_FLASH_RESULT_OK; } -bool EspClass::flashWrite(uint32_t address, const uint8_t *data, size_t size) { +bool EspClass::flashWrite(uint32_t address, const uint8_t* data, size_t size) { if (data && size) { if (!isAlignedAddress(address) || !isAlignedPointer(data) diff --git a/cores/esp8266/Esp.h b/cores/esp8266/Esp.h index 26654c12bc..d6b0ea5bcd 100644 --- a/cores/esp8266/Esp.h +++ b/cores/esp8266/Esp.h @@ -100,6 +100,16 @@ class EspClass { /// If any forced sleep mode was effective before forcedLightSleepBegin, it must be explicitly re-entered. static void forcedLightSleepEnd(bool cancel = false); + /// If parameter on == false, the prior sleep type is restored, but only as automatic. + /// If any forced sleep mode was effective before autoModemSleep, + /// it would have to be restored explicitly. + void autoModemSleep(bool on = true); + + /// If parameter on == false, the prior sleep type is restored, but only as automatic. + /// If any forced sleep mode was effective before autoLightSleep, + /// it would have to be restored explicitly. + void autoLightSleep(bool on = true); + static bool rtcUserMemoryRead(uint32_t offset, uint32_t *data, size_t size); static bool rtcUserMemoryWrite(uint32_t offset, uint32_t *data, size_t size); From 9891613524cefd5f0d61e99b5d14cb35db1bb4bf Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Thu, 15 Apr 2021 19:51:32 +0200 Subject: [PATCH 06/39] Add auto sleep mode demo. --- .../examples/AutoSleepDemo/AutoSleepDemo.ino | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 libraries/esp8266/examples/AutoSleepDemo/AutoSleepDemo.ino diff --git a/libraries/esp8266/examples/AutoSleepDemo/AutoSleepDemo.ino b/libraries/esp8266/examples/AutoSleepDemo/AutoSleepDemo.ino new file mode 100644 index 0000000000..a91a5147bf --- /dev/null +++ b/libraries/esp8266/examples/AutoSleepDemo/AutoSleepDemo.ino @@ -0,0 +1,26 @@ +#include +#include + +void setup() { + Serial.begin(74880); + while (!Serial); + delay(100); + Serial.println("AutoLightSleep"); + Serial.flush(); +} + +void loop() { + delay(10000); // this enters the idle task + + Serial.println("entering auto sleep section"); + Serial.flush(); + ESP.autoLightSleep(); + // ESP.autoModemSleep(); + + delay(10000); // this enters the idle task with auto light sleep + + ESP.autoLightSleep(false); + // ESP.autoModemSleep(false); + Serial.println("left auto sleep section"); + Serial.flush(); +} From ea946264029aff56abd1e11f37526d7d0c652328 Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Fri, 16 Apr 2021 16:10:55 +0200 Subject: [PATCH 07/39] Refactoring MODEM forced sleep code from ESP8266WiFi to core ESP class. --- cores/esp8266/Esp.cpp | 407 ++++++++++-------- cores/esp8266/Esp.h | 7 +- .../ESP8266WiFi/src/ESP8266WiFiGeneric.cpp | 40 +- 3 files changed, 239 insertions(+), 215 deletions(-) diff --git a/cores/esp8266/Esp.cpp b/cores/esp8266/Esp.cpp index 99e0eeed4c..6a20731980 100644 --- a/cores/esp8266/Esp.cpp +++ b/cores/esp8266/Esp.cpp @@ -35,14 +35,14 @@ extern "C" { #include "user_interface.h" - extern struct rst_info resetInfo; +extern struct rst_info resetInfo; } //#define DEBUG_SERIAL Serial #ifndef PUYA_SUPPORT -#define PUYA_SUPPORT 1 + #define PUYA_SUPPORT 1 #endif /** @@ -93,14 +93,14 @@ EspClass ESP; void EspClass::wdtEnable(uint32_t timeout_ms) { - (void)timeout_ms; + (void) timeout_ms; /// This API can only be called if software watchdog is stopped system_soft_wdt_restart(); } void EspClass::wdtEnable(WDTO_t timeout_ms) { - wdtEnable((uint32_t)timeout_ms); + wdtEnable((uint32_t) timeout_ms); } void EspClass::wdtDisable(void) @@ -133,16 +133,57 @@ void EspClass::deepSleepInstant(uint64_t time_us, WakeMode mode) //Note: system_rtc_clock_cali_proc() returns a uint32_t, even though system_deep_sleep() takes a uint64_t. uint64_t EspClass::deepSleepMax() { - //cali*(2^31-1)/(2^12) - return (uint64_t)system_rtc_clock_cali_proc() * (0x80000000 - 1) / (0x1000); + //cali*(2^31-1)/(2^12) + return (uint64_t)system_rtc_clock_cali_proc()*(0x80000000-1)/(0x1000); } extern os_timer_t* timer_list; namespace { - sleep_type_t saved_sleep_type; - os_timer_t* saved_timer_list; - fpm_wakeup_cb saved_wakeupCb; + sleep_type_t saved_sleep_type = NONE_SLEEP_T; + os_timer_t* saved_timer_list = nullptr; + fpm_wakeup_cb saved_wakeupCb = nullptr; +} + +bool EspClass::forcedModemSleep(bool on, uint32_t duration_us, fpm_wakeup_cb wakeupCb) +{ + if (!on) + { + const sleep_type_t sleepType = wifi_fpm_get_sleep_type(); + if (sleepType != NONE_SLEEP_T) { + if (sleepType == MODEM_SLEEP_T) wifi_fpm_do_wakeup(); + wifi_fpm_close(); + } + wifi_fpm_set_sleep_type(saved_sleep_type); + saved_sleep_type = NONE_SLEEP_T; + return true; + } + + // Setting duration to 0xFFFFFFF, it disconnects the RTC timer + if (!duration_us || duration_us > 0xFFFFFFF) { + duration_us = 0xFFFFFFF; + } + wifi_fpm_close(); + saved_sleep_type = wifi_fpm_get_sleep_type(); + wifi_fpm_set_sleep_type(MODEM_SLEEP_T); + wifi_fpm_open(); + saved_wakeupCb = nullptr; + if (wakeupCb) wifi_fpm_set_wakeup_cb(wakeupCb); + auto ret_do_sleep = wifi_fpm_do_sleep(duration_us); + if (ret_do_sleep != 0) + { +#ifdef DEBUG_SERIAL + DEBUG_SERIAL.printf("core: error %d with wifi_fpm_do_sleep: (-1=sleep status error, -2=force sleep not enabled)\n", ret_do_sleep); +#endif + return false; + } + // SDK turns on forced modem sleep in idle task +#ifdef HAVE_ESP_SUSPEND + esp_delay(10); +#else + delay(10); +#endif + return true; } bool EspClass::forcedLightSleepBegin(uint32_t duration_us, fpm_wakeup_cb wakeupCb) @@ -157,7 +198,10 @@ bool EspClass::forcedLightSleepBegin(uint32_t duration_us, fpm_wakeup_cb wakeupC wifi_fpm_open(); saved_wakeupCb = wakeupCb; wifi_fpm_set_wakeup_cb([]() { - if (saved_wakeupCb) saved_wakeupCb(); + if (saved_wakeupCb) { + saved_wakeupCb(); + saved_wakeupCb = nullptr; + } esp_schedule(); }); { @@ -183,6 +227,7 @@ void EspClass::forcedLightSleepEnd(bool cancel) } wifi_fpm_close(); wifi_fpm_set_sleep_type(saved_sleep_type); + saved_sleep_type = NONE_SLEEP_T; } void EspClass::autoModemSleep(bool on) { @@ -191,6 +236,7 @@ void EspClass::autoModemSleep(bool on) { saved_sleep_type = wifi_fpm_get_sleep_type(); } wifi_fpm_set_sleep_type(on ? MODEM_SLEEP_T : saved_sleep_type); + if (!on) saved_sleep_type = NONE_SLEEP_T; } void EspClass::autoLightSleep(bool on) { @@ -199,6 +245,7 @@ void EspClass::autoLightSleep(bool on) { saved_sleep_type = wifi_fpm_get_sleep_type(); } wifi_fpm_set_sleep_type(on ? LIGHT_SLEEP_T : saved_sleep_type); + if (!on) saved_sleep_type = NONE_SLEEP_T; } /* @@ -209,9 +256,9 @@ Ref: Espressif doc 2C-ESP8266_Non_OS_SDK_API_Reference, section 3.3.23 (system_r SDK function signature: bool system_rtc_mem_read ( - uint32 des_addr, - void * src_addr, - uint32 save_size + uint32 des_addr, + void * src_addr, + uint32 save_size ) The system data section can't be used by the user, so: @@ -235,7 +282,7 @@ retrieved by eboot on boot. That means that user data present there will be lost - Arduino/bootloaders/eboot/eboot_command.h RTC_MEM definition */ -bool EspClass::rtcUserMemoryRead(uint32_t offset, uint32_t* data, size_t size) +bool EspClass::rtcUserMemoryRead(uint32_t offset, uint32_t *data, size_t size) { if (offset * 4 + size > 512 || size == 0) { return false; @@ -244,7 +291,7 @@ bool EspClass::rtcUserMemoryRead(uint32_t offset, uint32_t* data, size_t size) } } -bool EspClass::rtcUserMemoryWrite(uint32_t offset, uint32_t* data, size_t size) +bool EspClass::rtcUserMemoryWrite(uint32_t offset, uint32_t *data, size_t size) { if (offset * 4 + size > 512 || size == 0) { return false; @@ -266,11 +313,11 @@ void EspClass::restart(void) [[noreturn]] void EspClass::rebootIntoUartDownloadMode() { - wdtDisable(); - /* disable hardware watchdog */ - CLEAR_PERI_REG_MASK(PERIPHS_HW_WDT, 0x1); + wdtDisable(); + /* disable hardware watchdog */ + CLEAR_PERI_REG_MASK(PERIPHS_HW_WDT, 0x1); - esp8266RebootIntoUartDownloadMode(); + esp8266RebootIntoUartDownloadMode(); } uint16_t EspClass::getVcc(void) @@ -320,7 +367,7 @@ String EspClass::getCoreVersion() return String(buf); } -const char* EspClass::getSdkVersion(void) +const char * EspClass::getSdkVersion(void) { return system_get_sdk_version(); } @@ -360,9 +407,9 @@ uint32_t EspClass::getFlashChipSize(void) return getFlashChipRealSize(); #else uint32_t data; - uint8_t* bytes = (uint8_t*)&data; + uint8_t * bytes = (uint8_t *) &data; // read first 4 byte (magic byte + flash config) - if (spi_flash_read(0x0000, &data, 4) == SPI_FLASH_RESULT_OK) { + if(spi_flash_read(0x0000, &data, 4) == SPI_FLASH_RESULT_OK) { return magicFlashChipSize((bytes[3] & 0xf0) >> 4); } return 0; @@ -372,9 +419,9 @@ uint32_t EspClass::getFlashChipSize(void) uint32_t EspClass::getFlashChipSpeed(void) { uint32_t data; - uint8_t* bytes = (uint8_t*)&data; + uint8_t * bytes = (uint8_t *) &data; // read first 4 byte (magic byte + flash config) - if (spi_flash_read(0x0000, &data, 4) == SPI_FLASH_RESULT_OK) { + if(spi_flash_read(0x0000, &data, 4) == SPI_FLASH_RESULT_OK) { return magicFlashChipSpeed(bytes[3] & 0x0F); } return 0; @@ -384,9 +431,9 @@ FlashMode_t EspClass::getFlashChipMode(void) { FlashMode_t mode = FM_UNKNOWN; uint32_t data; - uint8_t* bytes = (uint8_t*)&data; + uint8_t * bytes = (uint8_t *) &data; // read first 4 byte (magic byte + flash config) - if (spi_flash_read(0x0000, &data, 4) == SPI_FLASH_RESULT_OK) { + if(spi_flash_read(0x0000, &data, 4) == SPI_FLASH_RESULT_OK) { mode = magicFlashChipMode(bytes[2]); } return mode; @@ -394,45 +441,45 @@ FlashMode_t EspClass::getFlashChipMode(void) #if !FLASH_MAP_SUPPORT uint32_t EspClass::magicFlashChipSize(uint8_t byte) { - switch (byte & 0x0F) { - case 0x0: // 4 Mbit (512KB) - return (512_kB); - case 0x1: // 2 MBit (256KB) - return (256_kB); - case 0x2: // 8 MBit (1MB) - return (1_MB); - case 0x3: // 16 MBit (2MB) - return (2_MB); - case 0x4: // 32 MBit (4MB) - return (4_MB); - case 0x8: // 64 MBit (8MB) - return (8_MB); - case 0x9: // 128 MBit (16MB) - return (16_MB); - default: // fail? - return 0; + switch(byte & 0x0F) { + case 0x0: // 4 Mbit (512KB) + return (512_kB); + case 0x1: // 2 MBit (256KB) + return (256_kB); + case 0x2: // 8 MBit (1MB) + return (1_MB); + case 0x3: // 16 MBit (2MB) + return (2_MB); + case 0x4: // 32 MBit (4MB) + return (4_MB); + case 0x8: // 64 MBit (8MB) + return (8_MB); + case 0x9: // 128 MBit (16MB) + return (16_MB); + default: // fail? + return 0; } } #endif uint32_t EspClass::magicFlashChipSpeed(uint8_t byte) { - switch (byte & 0x0F) { - case 0x0: // 40 MHz - return (40_MHz); - case 0x1: // 26 MHz - return (26_MHz); - case 0x2: // 20 MHz - return (20_MHz); - case 0xf: // 80 MHz - return (80_MHz); - default: // fail? - return 0; + switch(byte & 0x0F) { + case 0x0: // 40 MHz + return (40_MHz); + case 0x1: // 26 MHz + return (26_MHz); + case 0x2: // 20 MHz + return (20_MHz); + case 0xf: // 80 MHz + return (80_MHz); + default: // fail? + return 0; } } FlashMode_t EspClass::magicFlashChipMode(uint8_t byte) { - FlashMode_t mode = (FlashMode_t)byte; - if (mode > FM_DOUT) { + FlashMode_t mode = (FlashMode_t) byte; + if(mode > FM_DOUT) { mode = FM_UNKNOWN; } return mode; @@ -453,54 +500,54 @@ uint32_t EspClass::getFlashChipSizeByChipId(void) { * 40 - ? may be Speed ? //todo: find docu to this * C8 - manufacturer ID */ - switch (chipId) { + switch(chipId) { // GigaDevice - case 0x1740C8: // GD25Q64B - return (8_MB); - case 0x1640C8: // GD25Q32B - return (4_MB); - case 0x1540C8: // GD25Q16B - return (2_MB); - case 0x1440C8: // GD25Q80 - return (1_MB); - case 0x1340C8: // GD25Q40 - return (512_kB); - case 0x1240C8: // GD25Q20 - return (256_kB); - case 0x1140C8: // GD25Q10 - return (128_kB); - case 0x1040C8: // GD25Q12 - return (64_kB); + case 0x1740C8: // GD25Q64B + return (8_MB); + case 0x1640C8: // GD25Q32B + return (4_MB); + case 0x1540C8: // GD25Q16B + return (2_MB); + case 0x1440C8: // GD25Q80 + return (1_MB); + case 0x1340C8: // GD25Q40 + return (512_kB); + case 0x1240C8: // GD25Q20 + return (256_kB); + case 0x1140C8: // GD25Q10 + return (128_kB); + case 0x1040C8: // GD25Q12 + return (64_kB); // Winbond - case 0x1840EF: // W25Q128 - return (16_MB); - case 0x1640EF: // W25Q32 - return (4_MB); - case 0x1540EF: // W25Q16 - return (2_MB); - case 0x1440EF: // W25Q80 - return (1_MB); - case 0x1340EF: // W25Q40 - return (512_kB); + case 0x1840EF: // W25Q128 + return (16_MB); + case 0x1640EF: // W25Q32 + return (4_MB); + case 0x1540EF: // W25Q16 + return (2_MB); + case 0x1440EF: // W25Q80 + return (1_MB); + case 0x1340EF: // W25Q40 + return (512_kB); // BergMicro - case 0x1640E0: // BG25Q32 - return (4_MB); - case 0x1540E0: // BG25Q16 - return (2_MB); - case 0x1440E0: // BG25Q80 - return (1_MB); - case 0x1340E0: // BG25Q40 - return (512_kB); + case 0x1640E0: // BG25Q32 + return (4_MB); + case 0x1540E0: // BG25Q16 + return (2_MB); + case 0x1440E0: // BG25Q80 + return (1_MB); + case 0x1340E0: // BG25Q40 + return (512_kB); // XMC - Wuhan Xinxin Semiconductor Manufacturing Corp - case 0x164020: // XM25QH32B - return (4_MB); + case 0x164020: // XM25QH32B + return (4_MB); - default: - return 0; + default: + return 0; } } @@ -510,12 +557,12 @@ uint32_t EspClass::getFlashChipSizeByChipId(void) { * @return ok or not */ bool EspClass::checkFlashConfig(bool needsEquals) { - if (needsEquals) { - if (getFlashChipRealSize() == getFlashChipSize()) { + if(needsEquals) { + if(getFlashChipRealSize() == getFlashChipSize()) { return true; } } else { - if (getFlashChipRealSize() >= getFlashChipSize()) { + if(getFlashChipRealSize() >= getFlashChipSize()) { return true; } } @@ -546,22 +593,22 @@ bool EspClass::checkFlashCRC() { String EspClass::getResetReason(void) { const __FlashStringHelper* buff; - switch (resetInfo.reason) { + switch(resetInfo.reason) { // normal startup by power on - case REASON_DEFAULT_RST: buff = F("Power On"); break; + case REASON_DEFAULT_RST: buff = F("Power On"); break; // hardware watch dog reset - case REASON_WDT_RST: buff = F("Hardware Watchdog"); break; + case REASON_WDT_RST: buff = F("Hardware Watchdog"); break; // exception reset, GPIO status won’t change - case REASON_EXCEPTION_RST: buff = F("Exception"); break; + case REASON_EXCEPTION_RST: buff = F("Exception"); break; // software watch dog reset, GPIO status won’t change - case REASON_SOFT_WDT_RST: buff = F("Software Watchdog"); break; + case REASON_SOFT_WDT_RST: buff = F("Software Watchdog"); break; // software restart ,system_restart , GPIO status won’t change - case REASON_SOFT_RESTART: buff = F("Software/System restart"); break; + case REASON_SOFT_RESTART: buff = F("Software/System restart"); break; // wake up from deep-sleep - case REASON_DEEP_SLEEP_AWAKE: buff = F("Deep-Sleep Wake"); break; + case REASON_DEEP_SLEEP_AWAKE: buff = F("Deep-Sleep Wake"); break; // // external system reset - case REASON_EXT_SYS_RST: buff = F("External System"); break; - default: buff = F("Unknown"); break; + case REASON_EXT_SYS_RST: buff = F("External System"); break; + default: buff = F("Unknown"); break; } return String(buff); } @@ -577,7 +624,7 @@ String EspClass::getResetInfo(void) { return getResetReason(); } -struct rst_info* EspClass::getResetInfoPtr(void) { +struct rst_info * EspClass::getResetInfoPtr(void) { return &resetInfo; } @@ -594,61 +641,61 @@ bool EspClass::eraseConfig(void) { return true; } -uint8_t *EspClass::random(uint8_t* resultArray, const size_t outputSizeBytes) +uint8_t* EspClass::random(uint8_t* resultArray, const size_t outputSizeBytes) { - /** - * The ESP32 Technical Reference Manual v4.1 chapter 24 has the following to say about random number generation (no information found for ESP8266): - * - * "When used correctly, every 32-bit value the system reads from the RNG_DATA_REG register of the random number generator is a true random number. - * These true random numbers are generated based on the noise in the Wi-Fi/BT RF system. - * When Wi-Fi and BT are disabled, the random number generator will give out pseudo-random numbers. - * - * When Wi-Fi or BT is enabled, the random number generator is fed two bits of entropy every APB clock cycle (normally 80 MHz). - * Thus, for the maximum amount of entropy, it is advisable to read the random register at a maximum rate of 5 MHz. - * A data sample of 2 GB, read from the random number generator with Wi-Fi enabled and the random register read at 5 MHz, - * has been tested using the Dieharder Random Number Testsuite (version 3.31.1). - * The sample passed all tests." - * - * Since ESP32 is the sequal to ESP8266 it is unlikely that the ESP8266 is able to generate random numbers more quickly than 5 MHz when run at a 80 MHz frequency. - * A maximum random number frequency of 0.5 MHz is used here to leave some margin for possibly inferior components in the ESP8266. - * It should be noted that the ESP8266 has no Bluetooth functionality, so turning the WiFi off is likely to cause RANDOM_REG32 to use pseudo-random numbers. - * - * It is possible that yield() must be called on the ESP8266 to properly feed the hardware random number generator new bits, since there is only one processor core available. - * However, no feeding requirements are mentioned in the ESP32 documentation, and using yield() could possibly cause extended delays during number generation. - * Thus only delayMicroseconds() is used below. - */ - - constexpr uint8_t cooldownMicros = 2; - static uint32_t lastCalledMicros = micros() - cooldownMicros; - - uint32_t randomNumber = 0; - - for (size_t byteIndex = 0; byteIndex < outputSizeBytes; ++byteIndex) + /** + * The ESP32 Technical Reference Manual v4.1 chapter 24 has the following to say about random number generation (no information found for ESP8266): + * + * "When used correctly, every 32-bit value the system reads from the RNG_DATA_REG register of the random number generator is a true random number. + * These true random numbers are generated based on the noise in the Wi-Fi/BT RF system. + * When Wi-Fi and BT are disabled, the random number generator will give out pseudo-random numbers. + * + * When Wi-Fi or BT is enabled, the random number generator is fed two bits of entropy every APB clock cycle (normally 80 MHz). + * Thus, for the maximum amount of entropy, it is advisable to read the random register at a maximum rate of 5 MHz. + * A data sample of 2 GB, read from the random number generator with Wi-Fi enabled and the random register read at 5 MHz, + * has been tested using the Dieharder Random Number Testsuite (version 3.31.1). + * The sample passed all tests." + * + * Since ESP32 is the sequal to ESP8266 it is unlikely that the ESP8266 is able to generate random numbers more quickly than 5 MHz when run at a 80 MHz frequency. + * A maximum random number frequency of 0.5 MHz is used here to leave some margin for possibly inferior components in the ESP8266. + * It should be noted that the ESP8266 has no Bluetooth functionality, so turning the WiFi off is likely to cause RANDOM_REG32 to use pseudo-random numbers. + * + * It is possible that yield() must be called on the ESP8266 to properly feed the hardware random number generator new bits, since there is only one processor core available. + * However, no feeding requirements are mentioned in the ESP32 documentation, and using yield() could possibly cause extended delays during number generation. + * Thus only delayMicroseconds() is used below. + */ + + constexpr uint8_t cooldownMicros = 2; + static uint32_t lastCalledMicros = micros() - cooldownMicros; + + uint32_t randomNumber = 0; + + for(size_t byteIndex = 0; byteIndex < outputSizeBytes; ++byteIndex) + { + if(byteIndex % 4 == 0) { - if (byteIndex % 4 == 0) - { - // Old random number has been used up (random number could be exactly 0, so we can't check for that) + // Old random number has been used up (random number could be exactly 0, so we can't check for that) - uint32_t timeSinceLastCall = micros() - lastCalledMicros; - if (timeSinceLastCall < cooldownMicros) - delayMicroseconds(cooldownMicros - timeSinceLastCall); + uint32_t timeSinceLastCall = micros() - lastCalledMicros; + if(timeSinceLastCall < cooldownMicros) + delayMicroseconds(cooldownMicros - timeSinceLastCall); - randomNumber = RANDOM_REG32; - lastCalledMicros = micros(); - } - - resultArray[byteIndex] = randomNumber; - randomNumber >>= 8; + randomNumber = RANDOM_REG32; + lastCalledMicros = micros(); } - return resultArray; + resultArray[byteIndex] = randomNumber; + randomNumber >>= 8; + } + + return resultArray; } uint32_t EspClass::random() { - union { uint32_t b32; uint8_t b8[4]; } result; - random(result.b8, 4); - return result.b32; + union { uint32_t b32; uint8_t b8[4]; } result; + random(result.b8, 4); + return result.b32; } uint32_t EspClass::getSketchSize() { @@ -658,7 +705,7 @@ uint32_t EspClass::getSketchSize() { image_header_t image_header; uint32_t pos = APP_START_OFFSET; - if (spi_flash_read(pos, (uint32_t*)&image_header, sizeof(image_header)) != SPI_FLASH_RESULT_OK) { + if (spi_flash_read(pos, (uint32_t*) &image_header, sizeof(image_header)) != SPI_FLASH_RESULT_OK) { return 0; } pos += sizeof(image_header); @@ -669,8 +716,8 @@ uint32_t EspClass::getSketchSize() { section_index < image_header.num_segments; ++section_index) { - section_header_t section_header = { 0, 0 }; - if (spi_flash_read(pos, (uint32_t*)§ion_header, sizeof(section_header)) != SPI_FLASH_RESULT_OK) { + section_header_t section_header = {0, 0}; + if (spi_flash_read(pos, (uint32_t*) §ion_header, sizeof(section_header)) != SPI_FLASH_RESULT_OK) { return 0; } pos += sizeof(section_header); @@ -697,37 +744,37 @@ uint32_t EspClass::getFreeSketchSpace() { } bool EspClass::updateSketch(Stream& in, uint32_t size, bool restartOnFail, bool restartOnSuccess) { - if (!Update.begin(size)) { + if(!Update.begin(size)){ #ifdef DEBUG_SERIAL - DEBUG_SERIAL.print("Update "); - Update.printError(DEBUG_SERIAL); + DEBUG_SERIAL.print("Update "); + Update.printError(DEBUG_SERIAL); #endif - if (restartOnFail) ESP.restart(); - return false; -} + if(restartOnFail) ESP.restart(); + return false; + } - if (Update.writeStream(in) != size) { + if(Update.writeStream(in) != size){ #ifdef DEBUG_SERIAL - DEBUG_SERIAL.print("Update "); - Update.printError(DEBUG_SERIAL); + DEBUG_SERIAL.print("Update "); + Update.printError(DEBUG_SERIAL); #endif - if (restartOnFail) ESP.restart(); - return false; - } + if(restartOnFail) ESP.restart(); + return false; + } - if (!Update.end()) { + if(!Update.end()){ #ifdef DEBUG_SERIAL - DEBUG_SERIAL.print("Update "); - Update.printError(DEBUG_SERIAL); + DEBUG_SERIAL.print("Update "); + Update.printError(DEBUG_SERIAL); #endif - if (restartOnFail) ESP.restart(); - return false; - } + if(restartOnFail) ESP.restart(); + return false; + } #ifdef DEBUG_SERIAL DEBUG_SERIAL.println("Update SUCCESS"); #endif - if (restartOnSuccess) ESP.restart(); + if(restartOnSuccess) ESP.restart(); return true; } @@ -778,18 +825,18 @@ static SpiFlashOpResult spi_flash_write_page_break(uint32_t offset, uint32_t *da #if PUYA_SUPPORT // Special wrapper for spi_flash_write *only for PUYA flash chips* // Already handles paging, could be used as a `spi_flash_write_page_break` replacement -static SpiFlashOpResult spi_flash_write_puya(uint32_t offset, uint32_t* data, size_t size) { +static SpiFlashOpResult spi_flash_write_puya(uint32_t offset, uint32_t *data, size_t size) { if (data == nullptr) { - return SPI_FLASH_RESULT_ERR; + return SPI_FLASH_RESULT_ERR; } if (size % 4 != 0) { - return SPI_FLASH_RESULT_ERR; + return SPI_FLASH_RESULT_ERR; } // PUYA flash chips need to read existing data, update in memory and write modified data again. - static uint32_t* flash_write_puya_buf = nullptr; + static uint32_t *flash_write_puya_buf = nullptr; if (flash_write_puya_buf == nullptr) { - flash_write_puya_buf = (uint32_t*)malloc(FLASH_PAGE_SIZE); + flash_write_puya_buf = (uint32_t*) malloc(FLASH_PAGE_SIZE); // No need to ever free this, since the flash chip will never change at runtime. if (flash_write_puya_buf == nullptr) { // Memory could not be allocated. diff --git a/cores/esp8266/Esp.h b/cores/esp8266/Esp.h index d6b0ea5bcd..6a194c50e3 100644 --- a/cores/esp8266/Esp.h +++ b/cores/esp8266/Esp.h @@ -95,7 +95,10 @@ class EspClass { static void deepSleepInstant(uint64_t time_us, RFMode mode = RF_DEFAULT); static uint64_t deepSleepMax(); + static bool forcedModemSleep(bool on = true, uint32_t duration_us = 0, void (*wakeupCb)() = nullptr); + static bool forcedLightSleepBegin(uint32_t duration_us = 0, void (*wakeupCb)() = nullptr); + /// The prior sleep type is restored, but only as automatic. /// If any forced sleep mode was effective before forcedLightSleepBegin, it must be explicitly re-entered. static void forcedLightSleepEnd(bool cancel = false); @@ -103,12 +106,12 @@ class EspClass { /// If parameter on == false, the prior sleep type is restored, but only as automatic. /// If any forced sleep mode was effective before autoModemSleep, /// it would have to be restored explicitly. - void autoModemSleep(bool on = true); + static void autoModemSleep(bool on = true); /// If parameter on == false, the prior sleep type is restored, but only as automatic. /// If any forced sleep mode was effective before autoLightSleep, /// it would have to be restored explicitly. - void autoLightSleep(bool on = true); + static void autoLightSleep(bool on = true); static bool rtcUserMemoryRead(uint32_t offset, uint32_t *data, size_t size); static bool rtcUserMemoryWrite(uint32_t offset, uint32_t *data, size_t size); diff --git a/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp b/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp index f64e6d3ac5..2cd9b5aea6 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp +++ b/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp @@ -427,11 +427,9 @@ bool ESP8266WiFiGenericClass::mode(WiFiMode_t m) { char backup_hostname [33] { 0 }; // hostname is 32 chars long (RFC) - if (m != WIFI_OFF && wifi_fpm_get_sleep_type() != NONE_SLEEP_T) { + if (m != WIFI_OFF && !ESP.forcedModemSleep(false)) { memcpy(backup_hostname, wifi_station_hostname, sizeof(backup_hostname)); - // wifi starts asleep by default - wifi_fpm_do_wakeup(); - wifi_fpm_close(); + return false; } bool ret = false; @@ -526,26 +524,7 @@ bool ESP8266WiFiGenericClass::forceSleepBegin(uint32 sleepUs) { DEBUG_WIFI("core: error with mode(WIFI_OFF)\n"); return false; } - - if(sleepUs == 0 || sleepUs > 0xFFFFFFF) { - sleepUs = 0xFFFFFFF; - } - - wifi_fpm_set_sleep_type(MODEM_SLEEP_T); - esp_yield(); - wifi_fpm_open(); - esp_yield(); - 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; + return ESP.forcedModemSleep(true, sleepUs); } /** @@ -553,13 +532,8 @@ 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)) { + if(ESP.forcedModemSleep(false) && mode(_forceSleepLastMode)) { if((_forceSleepLastMode & WIFI_STA) != 0){ wifi_station_connect(); } @@ -794,9 +768,9 @@ bool ESP8266WiFiGenericClass::shutdown (WiFiState& state) { bool ESP8266WiFiGenericClass::resumeFromShutdown (WiFiState& state) { - if (wifi_fpm_get_sleep_type() != NONE_SLEEP_T) { - wifi_fpm_do_wakeup(); - wifi_fpm_close(); + if (!ESP.forcedModemSleep(false)) + { + return false; } if (shutdownCRC(state) != state.crc) From 7137d0f4900fab4112882d6ab571f02a1c6b4bb6 Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Fri, 16 Apr 2021 16:55:44 +0200 Subject: [PATCH 08/39] Add new Esp class member functions to host test MockEsp.cpp --- tests/host/common/MockEsp.cpp | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/tests/host/common/MockEsp.cpp b/tests/host/common/MockEsp.cpp index 18e7be6c83..891897c3f8 100644 --- a/tests/host/common/MockEsp.cpp +++ b/tests/host/common/MockEsp.cpp @@ -93,6 +93,34 @@ void eboot_command_write(struct eboot_command* cmd) EspClass ESP; +bool EspClass::forcedModemSleep(bool on, uint32_t duration_us, void (*wakeupCb)()) +{ + (void)on; + (void)duration_us; + (void)wakeupCb; + return true; +} + +bool EspClass::forcedLightSleepBegin(uint32_t duration_us, void (*wakeupCb)()) +{ + (void)duration_us; + (void)wakeupCb; + return true; +} + +void EspClass::forcedLightSleepEnd(bool cancel) +{ + (void)cancel; +} + +void EspClass::autoModemSleep(bool on) { + (void)on; +} + +void EspClass::autoLightSleep(bool on) { + (void)on; +} + void EspClass::restart() { mockverbose("Esp.restart(): exiting\n"); From 7fea53920fc71c201816f910ba8e1c77fee8a7e4 Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Fri, 16 Apr 2021 18:21:53 +0200 Subject: [PATCH 09/39] API refactoring. ESP class is singleton without data members, define all member functions static. --- cores/esp8266/Esp.cpp | 51 +++++++++---------- cores/esp8266/Esp.h | 23 +++++---- .../ESP8266WiFi/src/ESP8266WiFiGeneric.cpp | 14 +++-- .../examples/AutoSleepDemo/AutoSleepDemo.ino | 3 +- tests/host/common/MockEsp.cpp | 17 ++++--- 5 files changed, 54 insertions(+), 54 deletions(-) diff --git a/cores/esp8266/Esp.cpp b/cores/esp8266/Esp.cpp index 6a20731980..3730429d1d 100644 --- a/cores/esp8266/Esp.cpp +++ b/cores/esp8266/Esp.cpp @@ -145,20 +145,8 @@ namespace { fpm_wakeup_cb saved_wakeupCb = nullptr; } -bool EspClass::forcedModemSleep(bool on, uint32_t duration_us, fpm_wakeup_cb wakeupCb) +bool EspClass::forcedModemSleep(uint32_t duration_us, fpm_wakeup_cb wakeupCb) { - if (!on) - { - const sleep_type_t sleepType = wifi_fpm_get_sleep_type(); - if (sleepType != NONE_SLEEP_T) { - if (sleepType == MODEM_SLEEP_T) wifi_fpm_do_wakeup(); - wifi_fpm_close(); - } - wifi_fpm_set_sleep_type(saved_sleep_type); - saved_sleep_type = NONE_SLEEP_T; - return true; - } - // Setting duration to 0xFFFFFFF, it disconnects the RTC timer if (!duration_us || duration_us > 0xFFFFFFF) { duration_us = 0xFFFFFFF; @@ -186,6 +174,16 @@ bool EspClass::forcedModemSleep(bool on, uint32_t duration_us, fpm_wakeup_cb wak return true; } +void EspClass::forcedModemSleepOff() +{ + const sleep_type_t sleepType = wifi_fpm_get_sleep_type(); + if (sleepType != NONE_SLEEP_T) { + if (sleepType == MODEM_SLEEP_T) wifi_fpm_do_wakeup(); + wifi_fpm_close(); + } + autoSleepOff(); +} + bool EspClass::forcedLightSleepBegin(uint32_t duration_us, fpm_wakeup_cb wakeupCb) { // Setting duration to 0xFFFFFFF, it disconnects the RTC timer @@ -230,22 +228,21 @@ void EspClass::forcedLightSleepEnd(bool cancel) saved_sleep_type = NONE_SLEEP_T; } -void EspClass::autoModemSleep(bool on) { - if (on) { - wifi_fpm_close(); - saved_sleep_type = wifi_fpm_get_sleep_type(); - } - wifi_fpm_set_sleep_type(on ? MODEM_SLEEP_T : saved_sleep_type); - if (!on) saved_sleep_type = NONE_SLEEP_T; +void EspClass::autoModemSleep() { + wifi_fpm_close(); + saved_sleep_type = wifi_fpm_get_sleep_type(); + wifi_fpm_set_sleep_type(MODEM_SLEEP_T); } -void EspClass::autoLightSleep(bool on) { - if (on) { - wifi_fpm_close(); - saved_sleep_type = wifi_fpm_get_sleep_type(); - } - wifi_fpm_set_sleep_type(on ? LIGHT_SLEEP_T : saved_sleep_type); - if (!on) saved_sleep_type = NONE_SLEEP_T; +void EspClass::autoLightSleep() { + wifi_fpm_close(); + saved_sleep_type = wifi_fpm_get_sleep_type(); + wifi_fpm_set_sleep_type(LIGHT_SLEEP_T); +} + +void EspClass::autoSleepOff() { + wifi_fpm_set_sleep_type(saved_sleep_type); + saved_sleep_type = NONE_SLEEP_T; } /* diff --git a/cores/esp8266/Esp.h b/cores/esp8266/Esp.h index 6a194c50e3..5b45b17a6d 100644 --- a/cores/esp8266/Esp.h +++ b/cores/esp8266/Esp.h @@ -95,23 +95,24 @@ class EspClass { static void deepSleepInstant(uint64_t time_us, RFMode mode = RF_DEFAULT); static uint64_t deepSleepMax(); - static bool forcedModemSleep(bool on = true, uint32_t duration_us = 0, void (*wakeupCb)() = nullptr); + static bool forcedModemSleep(uint32_t duration_us = 0, void (*wakeupCb)() = nullptr); + /// The prior sleep type is restored, but only as automatic. + /// If any forced sleep mode was effective before forcedModemSleep, + /// it would have to be restored explicitly. + static void forcedModemSleepOff(); static bool forcedLightSleepBegin(uint32_t duration_us = 0, void (*wakeupCb)() = nullptr); - /// The prior sleep type is restored, but only as automatic. - /// If any forced sleep mode was effective before forcedLightSleepBegin, it must be explicitly re-entered. - static void forcedLightSleepEnd(bool cancel = false); - - /// If parameter on == false, the prior sleep type is restored, but only as automatic. - /// If any forced sleep mode was effective before autoModemSleep, + /// If any forced sleep mode was effective before forcedLightSleepBegin, /// it would have to be restored explicitly. - static void autoModemSleep(bool on = true); + static void forcedLightSleepEnd(bool cancel = false); - /// If parameter on == false, the prior sleep type is restored, but only as automatic. - /// If any forced sleep mode was effective before autoLightSleep, + static void autoModemSleep(); + static void autoLightSleep(); + /// The prior sleep type is restored, but only as automatic. + /// If any forced sleep mode was effective before auto{Modem,Light}Sleep, /// it would have to be restored explicitly. - static void autoLightSleep(bool on = true); + static void autoSleepOff(); static bool rtcUserMemoryRead(uint32_t offset, uint32_t *data, size_t size); static bool rtcUserMemoryWrite(uint32_t offset, uint32_t *data, size_t size); diff --git a/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp b/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp index 2cd9b5aea6..35245f17a2 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp +++ b/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp @@ -427,9 +427,9 @@ bool ESP8266WiFiGenericClass::mode(WiFiMode_t m) { char backup_hostname [33] { 0 }; // hostname is 32 chars long (RFC) - if (m != WIFI_OFF && !ESP.forcedModemSleep(false)) { + if (m != WIFI_OFF) { memcpy(backup_hostname, wifi_station_hostname, sizeof(backup_hostname)); - return false; + ESP.forcedModemSleepOff(); } bool ret = false; @@ -524,7 +524,7 @@ bool ESP8266WiFiGenericClass::forceSleepBegin(uint32 sleepUs) { DEBUG_WIFI("core: error with mode(WIFI_OFF)\n"); return false; } - return ESP.forcedModemSleep(true, sleepUs); + return ESP.forcedModemSleep(sleepUs); } /** @@ -533,7 +533,8 @@ bool ESP8266WiFiGenericClass::forceSleepBegin(uint32 sleepUs) { */ bool ESP8266WiFiGenericClass::forceSleepWake() { // restore last mode - if(ESP.forcedModemSleep(false) && mode(_forceSleepLastMode)) { + ESP.forcedModemSleepOff(); + if(mode(_forceSleepLastMode)) { if((_forceSleepLastMode & WIFI_STA) != 0){ wifi_station_connect(); } @@ -768,10 +769,7 @@ bool ESP8266WiFiGenericClass::shutdown (WiFiState& state) { bool ESP8266WiFiGenericClass::resumeFromShutdown (WiFiState& state) { - if (!ESP.forcedModemSleep(false)) - { - return false; - } + ESP.forcedModemSleepOff(); if (shutdownCRC(state) != state.crc) { diff --git a/libraries/esp8266/examples/AutoSleepDemo/AutoSleepDemo.ino b/libraries/esp8266/examples/AutoSleepDemo/AutoSleepDemo.ino index a91a5147bf..3eb2ade58e 100644 --- a/libraries/esp8266/examples/AutoSleepDemo/AutoSleepDemo.ino +++ b/libraries/esp8266/examples/AutoSleepDemo/AutoSleepDemo.ino @@ -19,8 +19,7 @@ void loop() { delay(10000); // this enters the idle task with auto light sleep - ESP.autoLightSleep(false); - // ESP.autoModemSleep(false); + ESP.autoSleepOff(); Serial.println("left auto sleep section"); Serial.flush(); } diff --git a/tests/host/common/MockEsp.cpp b/tests/host/common/MockEsp.cpp index 891897c3f8..39583c3245 100644 --- a/tests/host/common/MockEsp.cpp +++ b/tests/host/common/MockEsp.cpp @@ -93,14 +93,17 @@ void eboot_command_write(struct eboot_command* cmd) EspClass ESP; -bool EspClass::forcedModemSleep(bool on, uint32_t duration_us, void (*wakeupCb)()) +bool EspClass::forcedModemSleep(uint32_t duration_us, void (*wakeupCb)()) { - (void)on; (void)duration_us; (void)wakeupCb; return true; } +void EspClass::forcedModemSleepOff() +{ +} + bool EspClass::forcedLightSleepBegin(uint32_t duration_us, void (*wakeupCb)()) { (void)duration_us; @@ -113,14 +116,16 @@ void EspClass::forcedLightSleepEnd(bool cancel) (void)cancel; } -void EspClass::autoModemSleep(bool on) { - (void)on; +void EspClass::autoModemSleep() { } -void EspClass::autoLightSleep(bool on) { - (void)on; +void EspClass::autoLightSleep() { } +void EspClass::autoSleepOff() { +} + + void EspClass::restart() { mockverbose("Esp.restart(): exiting\n"); From 446aea7c41214314373b3fb0a6c901f66db6f449 Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Sat, 17 Apr 2021 21:20:40 +0200 Subject: [PATCH 10/39] Missed that there are different functions for auto and force sleep type setting. --- cores/esp8266/Esp.cpp | 15 +++++++++------ cores/esp8266/core_esp8266_main.cpp | 2 +- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/cores/esp8266/Esp.cpp b/cores/esp8266/Esp.cpp index 3730429d1d..d25658940f 100644 --- a/cores/esp8266/Esp.cpp +++ b/cores/esp8266/Esp.cpp @@ -153,6 +153,7 @@ bool EspClass::forcedModemSleep(uint32_t duration_us, fpm_wakeup_cb wakeupCb) } wifi_fpm_close(); saved_sleep_type = wifi_fpm_get_sleep_type(); + wifi_set_opmode(NULL_MODE); wifi_fpm_set_sleep_type(MODEM_SLEEP_T); wifi_fpm_open(); saved_wakeupCb = nullptr; @@ -181,7 +182,8 @@ void EspClass::forcedModemSleepOff() if (sleepType == MODEM_SLEEP_T) wifi_fpm_do_wakeup(); wifi_fpm_close(); } - autoSleepOff(); + wifi_fpm_set_sleep_type(saved_sleep_type); + saved_sleep_type = NONE_SLEEP_T; } bool EspClass::forcedLightSleepBegin(uint32_t duration_us, fpm_wakeup_cb wakeupCb) @@ -192,6 +194,7 @@ bool EspClass::forcedLightSleepBegin(uint32_t duration_us, fpm_wakeup_cb wakeupC } wifi_fpm_close(); saved_sleep_type = wifi_fpm_get_sleep_type(); + wifi_set_opmode(NULL_MODE); wifi_fpm_set_sleep_type(LIGHT_SLEEP_T); wifi_fpm_open(); saved_wakeupCb = wakeupCb; @@ -230,18 +233,18 @@ void EspClass::forcedLightSleepEnd(bool cancel) void EspClass::autoModemSleep() { wifi_fpm_close(); - saved_sleep_type = wifi_fpm_get_sleep_type(); - wifi_fpm_set_sleep_type(MODEM_SLEEP_T); + saved_sleep_type = wifi_get_sleep_type(); + wifi_set_sleep_type(MODEM_SLEEP_T); } void EspClass::autoLightSleep() { wifi_fpm_close(); - saved_sleep_type = wifi_fpm_get_sleep_type(); - wifi_fpm_set_sleep_type(LIGHT_SLEEP_T); + saved_sleep_type = wifi_get_sleep_type(); + wifi_set_sleep_type(LIGHT_SLEEP_T); } void EspClass::autoSleepOff() { - wifi_fpm_set_sleep_type(saved_sleep_type); + wifi_set_sleep_type(saved_sleep_type); saved_sleep_type = NONE_SLEEP_T; } diff --git a/cores/esp8266/core_esp8266_main.cpp b/cores/esp8266/core_esp8266_main.cpp index ccacc95e01..119e292557 100644 --- a/cores/esp8266/core_esp8266_main.cpp +++ b/cores/esp8266/core_esp8266_main.cpp @@ -403,7 +403,7 @@ extern "C" void __disableWiFiAtBootTime (void) { // Starting from arduino core v3: wifi is disabled at boot time // WiFi.begin() or WiFi.softAP() will wake WiFi up - wifi_set_opmode_current(0/*WIFI_OFF*/); + wifi_set_opmode_current(NULL_MODE); wifi_fpm_set_sleep_type(MODEM_SLEEP_T); wifi_fpm_open(); wifi_fpm_do_sleep(0xFFFFFFF); From 6a07829cd93e8a4a8ec07ea0917696269c8372ba Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Sat, 17 Apr 2021 22:10:36 +0200 Subject: [PATCH 11/39] DRY --- cores/esp8266/core_esp8266_main.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/cores/esp8266/core_esp8266_main.cpp b/cores/esp8266/core_esp8266_main.cpp index 119e292557..78ca7b2c3a 100644 --- a/cores/esp8266/core_esp8266_main.cpp +++ b/cores/esp8266/core_esp8266_main.cpp @@ -403,10 +403,7 @@ extern "C" void __disableWiFiAtBootTime (void) { // Starting from arduino core v3: wifi is disabled at boot time // WiFi.begin() or WiFi.softAP() will wake WiFi up - wifi_set_opmode_current(NULL_MODE); - wifi_fpm_set_sleep_type(MODEM_SLEEP_T); - wifi_fpm_open(); - wifi_fpm_do_sleep(0xFFFFFFF); + ESP.forcedModemSleep(); } #if FLASH_MAP_SUPPORT From 49c2ac0507ac57f34524bed5f20141633452708f Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Sat, 17 Apr 2021 23:28:31 +0200 Subject: [PATCH 12/39] enable callback in forced light sleep example --- .../examples/ForcedLightSleep/ForcedLightSleep.ino | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/libraries/esp8266/examples/ForcedLightSleep/ForcedLightSleep.ino b/libraries/esp8266/examples/ForcedLightSleep/ForcedLightSleep.ino index 2cffa40d33..ffa5c44945 100644 --- a/libraries/esp8266/examples/ForcedLightSleep/ForcedLightSleep.ino +++ b/libraries/esp8266/examples/ForcedLightSleep/ForcedLightSleep.ino @@ -18,8 +18,11 @@ void IRAM_ATTR wakeupPinIsrWE() { attachInterrupt(WAKE_UP_PIN, wakeupPinIsr, FALLING); } -//void wakeupCallback() { -//} +void wakeupCallback() { + schedule_function([]() { + Serial.println("wakeup callback was performed"); + }); +} void setup() { Serial.begin(74880); @@ -35,7 +38,7 @@ using oneShotYieldMs = esp8266::polledTimeout::timeoutTemplate Date: Sun, 18 Apr 2021 01:48:48 +0200 Subject: [PATCH 13/39] instead of crude manual mangling and no prototypes, put the weak symbols in coredecls.h --- cores/esp8266/core_esp8266_phy.cpp | 12 +----------- cores/esp8266/coredecls.h | 4 ++++ 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/cores/esp8266/core_esp8266_phy.cpp b/cores/esp8266/core_esp8266_phy.cpp index e853edbd79..c655805a84 100644 --- a/cores/esp8266/core_esp8266_phy.cpp +++ b/cores/esp8266/core_esp8266_phy.cpp @@ -27,6 +27,7 @@ #include "ets_sys.h" #include "spi_flash.h" #include "user_interface.h" +#include "coredecls.h" extern "C" { @@ -288,21 +289,10 @@ static const uint8_t ICACHE_FLASH_ATTR phy_init_data[128] = /*[114] =*/ 1 }; - -// These functions will be overridden from C++ code. -// Unfortunately, we can't use extern "C" because Arduino preprocessor -// doesn't generate forward declarations for extern "C" functions correctly, -// so we use mangled names here. -#define __get_adc_mode _Z14__get_adc_modev -#define __get_rf_mode _Z13__get_rf_modev -#define __get_rf_powerup_mode _Z21__get_rf_powerup_modev -#define __run_user_rf_pre_init _Z22__run_user_rf_pre_initv - static bool spoof_init_data = false; extern int __real_spi_flash_read(uint32_t addr, uint32_t* dst, size_t size); extern int IRAM_ATTR __wrap_spi_flash_read(uint32_t addr, uint32_t* dst, size_t size); -extern int __get_adc_mode(); /* Verified that the wide filtering of all 128 byte flash reads during diff --git a/cores/esp8266/coredecls.h b/cores/esp8266/coredecls.h index 73af6d8cf1..967edd1bc3 100644 --- a/cores/esp8266/coredecls.h +++ b/cores/esp8266/coredecls.h @@ -25,6 +25,10 @@ void disable_extra4k_at_link_time (void) __attribute__((noinline)); void enable_wifi_enterprise_patch(void) __attribute__((noinline)); void __disableWiFiAtBootTime (void) __attribute__((noinline)); void __real_system_restart_local() __attribute__((noreturn)); +int __get_adc_mode(); +int __get_rf_mode(); +int __get_rf_powerup_mode(); +void __run_user_rf_pre_init(); uint32_t sqrt32(uint32_t n); From 79a7ee8bb0fecc8e43a6b4a11824e8d4aceaa21b Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Sun, 18 Apr 2021 12:27:04 +0200 Subject: [PATCH 14/39] Example for a forced modem sleep with timeout and callback. --- .../ForcedModemSleep/ForcedModemSleep.ino | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 libraries/esp8266/examples/ForcedModemSleep/ForcedModemSleep.ino diff --git a/libraries/esp8266/examples/ForcedModemSleep/ForcedModemSleep.ino b/libraries/esp8266/examples/ForcedModemSleep/ForcedModemSleep.ino new file mode 100644 index 0000000000..118ab583a3 --- /dev/null +++ b/libraries/esp8266/examples/ForcedModemSleep/ForcedModemSleep.ino @@ -0,0 +1,28 @@ +#include +#include + +using oneShotYieldMs = esp8266::polledTimeout::timeoutTemplate; +oneShotYieldMs gotoSleep(2000); + +void wakeupCallback() { + digitalWrite(LED_BUILTIN, LOW); // turn on the LED + schedule_function([]() { + Serial.println("wakeup callback was performed"); + gotoSleep.reset(2000); + }); +} + +void setup() { + Serial.begin(74880); + while (!Serial); + delay(100); + pinMode(LED_BUILTIN, OUTPUT); // activity and status indicator + digitalWrite(LED_BUILTIN, LOW); // turn on the LED +} + +void loop() { + if (gotoSleep && ESP.forcedModemSleep(10 * 1000 * 1000, wakeupCallback)) { + digitalWrite(LED_BUILTIN, HIGH); // turn the LED off so they know the modem isn't running + gotoSleep.resetToNeverExpires(); + } +} From 788731ed78a712e42cc5f4f9ff5c6acddbc328b3 Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Sun, 18 Apr 2021 12:43:54 +0200 Subject: [PATCH 15/39] Save 9ms for force MODEM sleep activation. --- cores/esp8266/Esp.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/cores/esp8266/Esp.cpp b/cores/esp8266/Esp.cpp index d25658940f..688b5fba30 100644 --- a/cores/esp8266/Esp.cpp +++ b/cores/esp8266/Esp.cpp @@ -168,9 +168,9 @@ bool EspClass::forcedModemSleep(uint32_t duration_us, fpm_wakeup_cb wakeupCb) } // SDK turns on forced modem sleep in idle task #ifdef HAVE_ESP_SUSPEND - esp_delay(10); + esp_delay(1); #else - delay(10); + delay(1); #endif return true; } @@ -216,10 +216,11 @@ bool EspClass::forcedLightSleepBegin(uint32_t duration_us, fpm_wakeup_cb wakeupC void EspClass::forcedLightSleepEnd(bool cancel) { if (!cancel) { + // SDK turns on forced light sleep in idle task #ifdef HAVE_ESP_SUSPEND esp_suspend(); #else - esp_yield(); // it goes to sleep from SYS context and waits for an interrupt + esp_yield(); #endif } { From 433cd17214cdd4346a78429722b6414ba10d07d3 Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Mon, 19 Apr 2021 14:47:51 +0200 Subject: [PATCH 16/39] Add timer_list debug dump to forced light sleep --- cores/esp8266/Esp.cpp | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/cores/esp8266/Esp.cpp b/cores/esp8266/Esp.cpp index 688b5fba30..02fbd09ea6 100644 --- a/cores/esp8266/Esp.cpp +++ b/cores/esp8266/Esp.cpp @@ -186,6 +186,42 @@ void EspClass::forcedModemSleepOff() saved_sleep_type = NONE_SLEEP_T; } +#ifdef DEBUG_SERIAL +namespace { + void walk_timer_list() { + os_timer_t* timer_root; + { + esp8266::InterruptLock lock; + auto src = timer_list; + auto dest = src ? new os_timer_t : nullptr; + timer_root = dest; + while (dest) { + *dest = *src; + src = src->timer_next; + dest->timer_next = src ? new os_timer_t(*timer_list) : nullptr; + dest = dest->timer_next; + } + } + DEBUG_SERIAL.printf("=============\n"); + for (os_timer_t* timer_node = timer_root; nullptr != timer_node; timer_node = timer_node->timer_next) { + DEBUG_SERIAL.printf("timer_address = %p\n", timer_node); + DEBUG_SERIAL.printf("timer_expire = %u\n", timer_node->timer_expire); + DEBUG_SERIAL.printf("timer_period = %u\n", timer_node->timer_period); + DEBUG_SERIAL.printf("timer_func = %p\n", timer_node->timer_func); + DEBUG_SERIAL.printf("timer_next = %p\n", timer_node->timer_next); + if (timer_node->timer_next) DEBUG_SERIAL.printf("=============\n"); + } + DEBUG_SERIAL.printf("=============\n"); + DEBUG_SERIAL.flush(); + while (timer_root) { + auto next = timer_root->timer_next; + delete timer_root; + timer_root = next; + } + } +} +#endif + bool EspClass::forcedLightSleepBegin(uint32_t duration_us, fpm_wakeup_cb wakeupCb) { // Setting duration to 0xFFFFFFF, it disconnects the RTC timer @@ -205,6 +241,9 @@ bool EspClass::forcedLightSleepBegin(uint32_t duration_us, fpm_wakeup_cb wakeupC } esp_schedule(); }); +#ifdef DEBUG_SERIAL + walk_timer_list(); +#endif { esp8266::InterruptLock lock; saved_timer_list = timer_list; @@ -223,6 +262,9 @@ void EspClass::forcedLightSleepEnd(bool cancel) esp_yield(); #endif } +#ifdef DEBUG_SERIAL + walk_timer_list(); +#endif { esp8266::InterruptLock lock; timer_list = saved_timer_list; From 24efea2f664d8c479eb246875ece3223ab0e92d7 Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Thu, 22 Apr 2021 16:36:22 +0200 Subject: [PATCH 17/39] Auto light sleep is all about WiFi and requires a connection to an AP --- .../examples/AutoSleepDemo/AutoSleepDemo.ino | 109 +++++++++++++++--- 1 file changed, 94 insertions(+), 15 deletions(-) diff --git a/libraries/esp8266/examples/AutoSleepDemo/AutoSleepDemo.ino b/libraries/esp8266/examples/AutoSleepDemo/AutoSleepDemo.ino index 3eb2ade58e..c04031e71c 100644 --- a/libraries/esp8266/examples/AutoSleepDemo/AutoSleepDemo.ino +++ b/libraries/esp8266/examples/AutoSleepDemo/AutoSleepDemo.ino @@ -1,25 +1,104 @@ -#include +#include +#include #include +#include + +#ifndef D5 +#define D5 (14) +#endif + +// enter your WiFi configuration below +const char* AP_SSID = "SSID"; // your router's SSID here +const char* AP_PASS = "PSK"; // your router's password here + +uint32_t timeout = 30E3; // 30 second timeout on the WiFi connection +esp8266::polledTimeout::oneShotMs wifiTimeout(timeout); // 30 second timeout on WiFi connection + +ESP8266WebServer server(80); + +void handleRoot() { + server.send(200, "text/plain", "hello from esp8266!\r\n"); +} + +void handleNotFound() { + String message = "File Not Found\n\n"; + message += "URI: "; + message += server.uri(); + message += "\nMethod: "; + message += (server.method() == HTTP_GET) ? "GET" : "POST"; + message += "\nArguments: "; + message += server.args(); + message += "\n"; + for (uint8_t i = 0; i < server.args(); i++) { + message += " " + server.argName(i) + ": " + server.arg(i) + "\n"; + } + server.send(404, "text/plain", message); +} void setup() { - Serial.begin(74880); + Serial.begin(74800); while (!Serial); delay(100); - Serial.println("AutoLightSleep"); - Serial.flush(); -} + Serial.println(); + WiFi.mode(WIFI_STA); + WiFi.begin(AP_SSID, AP_PASS); + Serial.print(F("connecting to WiFi ")); + Serial.println(WiFi.SSID()); -void loop() { - delay(10000); // this enters the idle task + wifiTimeout.reset(timeout); + while (((!WiFi.localIP()) || (WiFi.status() != WL_CONNECTED)) && (!wifiTimeout)) { + yield(); + } + if ((WiFi.status() != WL_CONNECTED) || !WiFi.localIP()) { + Serial.println(F("WiFi timed out and didn't connect")); + } else { + Serial.println(F("IP address: ")); + Serial.println(WiFi.localIP()); + } + WiFi.setAutoReconnect(true); + + //if (MDNS.begin("esp8266")) { + // Serial.println("MDNS responder started"); + //} + + server.on("/", handleRoot); + + server.on("/on", []() { + tone(D5, 440); + server.send(200, "text/plain", "tone on"); + }); + + server.on("/off", []() { + noTone(D5); + server.send(200, "text/plain", "tone off"); + }); - Serial.println("entering auto sleep section"); - Serial.flush(); - ESP.autoLightSleep(); - // ESP.autoModemSleep(); + server.on("/sleep", []() { + ESP.autoLightSleep(); + wifi_fpm_set_wakeup_cb([]() { + schedule_function([]() { + Serial.println("auto light sleep wakeup CB"); + }); + }); + server.send(200, "text/plain", "auto light sleep on"); + }); - delay(10000); // this enters the idle task with auto light sleep + server.on("/nosleep", []() { + ESP.autoSleepOff(); + server.send(200, "text/plain", "sleep off"); + }); - ESP.autoSleepOff(); - Serial.println("left auto sleep section"); - Serial.flush(); + server.onNotFound(handleNotFound); + + server.begin(); + + Serial.println("HTTP server started"); + pinMode(LED_BUILTIN, OUTPUT); +} + +void loop() { + digitalWrite(LED_BUILTIN, LOW); + server.handleClient(); + digitalWrite(LED_BUILTIN, HIGH); + delay(1); } From 83e70eeae066695123c491b0171aa9bdf3378c99 Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Sat, 24 Apr 2021 23:31:20 +0200 Subject: [PATCH 18/39] Some comments from memory of what testing revealed. Independent verification is welcome. --- .../examples/ForcedLightSleep/ForcedLightSleep.ino | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/libraries/esp8266/examples/ForcedLightSleep/ForcedLightSleep.ino b/libraries/esp8266/examples/ForcedLightSleep/ForcedLightSleep.ino index ffa5c44945..1dbda46d85 100644 --- a/libraries/esp8266/examples/ForcedLightSleep/ForcedLightSleep.ino +++ b/libraries/esp8266/examples/ForcedLightSleep/ForcedLightSleep.ino @@ -5,16 +5,20 @@ // you can use any GPIO for WAKE_UP_PIN except for D0/GPIO16 as it doesn't support interrupts void IRAM_ATTR wakeupPinIsr() { + // For edge-triggered IRQ. schedule_function([]() { Serial.println("GPIO went from HI to LO"); }); } void IRAM_ATTR wakeupPinIsrWE() { + // Wakeup IRQs are level-triggered only. schedule_function([]() { Serial.println("GPIO wakeup IRQ"); }); wakeupPinIsr(); + // return to falling edge IRQ, otherwise level-triggered IRQ + // keeps triggering this ISR back-to-back, consuming nearly all CPU time. attachInterrupt(WAKE_UP_PIN, wakeupPinIsr, FALLING); } @@ -41,8 +45,8 @@ void loop() { if (gotoSleep && ESP.forcedLightSleepBegin(10 * 1000 * 1000, wakeupCallback)) { // No new timers, no delay(), between forcedLightSleepBegin() and forcedLightSleepEnd(). // Only ONLOW_WE or ONHIGH_WE interrupts work, no edge, that's an SDK or CPU limitation. - // If the GPIO is in wakeup state while attaching the interrupt, it cannot trigger a wakeup, - // but any sleep duration will be honored. + // If the GPIO is in the state that will cause a wakeup on attaching the interrupt, + // it cannot trigger a wakeup later, but any sleep duration will be honored. bool wakeupPinIsHigh = digitalRead(WAKE_UP_PIN); // the GPIO might still bounce to LOW between both digital reads, disabling wakeup if (wakeupPinIsHigh) { From c0326f0dd2d25cb526cc101ec5f4307000fda701 Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Sun, 25 Apr 2021 12:45:46 +0200 Subject: [PATCH 19/39] Copyright notices and clarifying introductory comments in example code. --- .../examples/AutoSleepDemo/AutoSleepDemo.ino | 26 +++++++++++++++++++ .../ForcedLightSleep/ForcedLightSleep.ino | 21 +++++++++++++++ .../ForcedModemSleep/ForcedModemSleep.ino | 21 +++++++++++++++ 3 files changed, 68 insertions(+) diff --git a/libraries/esp8266/examples/AutoSleepDemo/AutoSleepDemo.ino b/libraries/esp8266/examples/AutoSleepDemo/AutoSleepDemo.ino index c04031e71c..a977d33355 100644 --- a/libraries/esp8266/examples/AutoSleepDemo/AutoSleepDemo.ino +++ b/libraries/esp8266/examples/AutoSleepDemo/AutoSleepDemo.ino @@ -1,3 +1,29 @@ +/* + ESP8266 auto sleep mode with webserver example + + Copyright (c) 2021 Dirk O. Kaar. 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 + + + This example starts in the default sleep mode, which would be auto modem sleep. + Use a web browser to open http://.../sleep to let it switch into auto light sleep, + open http://.../nosleep to revert to the previous auto modem sleep. +*/ + #include #include #include diff --git a/libraries/esp8266/examples/ForcedLightSleep/ForcedLightSleep.ino b/libraries/esp8266/examples/ForcedLightSleep/ForcedLightSleep.ino index 1dbda46d85..64da272758 100644 --- a/libraries/esp8266/examples/ForcedLightSleep/ForcedLightSleep.ino +++ b/libraries/esp8266/examples/ForcedLightSleep/ForcedLightSleep.ino @@ -1,3 +1,24 @@ +/* + ESP8266 forced light sleep mode example + + Copyright (c) 2021 Dirk O. Kaar. 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 #include diff --git a/libraries/esp8266/examples/ForcedModemSleep/ForcedModemSleep.ino b/libraries/esp8266/examples/ForcedModemSleep/ForcedModemSleep.ino index 118ab583a3..cd2f62200d 100644 --- a/libraries/esp8266/examples/ForcedModemSleep/ForcedModemSleep.ino +++ b/libraries/esp8266/examples/ForcedModemSleep/ForcedModemSleep.ino @@ -1,3 +1,24 @@ +/* + ESP8266 forced modem sleep mode example + + Copyright (c) 2021 Dirk O. Kaar. 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 #include From 7c4879f22cf24dbcd8738cacd37c89fd77078f1a Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Sun, 25 Apr 2021 13:58:30 +0200 Subject: [PATCH 20/39] Increase delay time to let example actually show measurable times in light sleep mode. --- libraries/esp8266/examples/AutoSleepDemo/AutoSleepDemo.ino | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libraries/esp8266/examples/AutoSleepDemo/AutoSleepDemo.ino b/libraries/esp8266/examples/AutoSleepDemo/AutoSleepDemo.ino index a977d33355..c6625b0fe0 100644 --- a/libraries/esp8266/examples/AutoSleepDemo/AutoSleepDemo.ino +++ b/libraries/esp8266/examples/AutoSleepDemo/AutoSleepDemo.ino @@ -126,5 +126,7 @@ void loop() { digitalWrite(LED_BUILTIN, LOW); server.handleClient(); digitalWrite(LED_BUILTIN, HIGH); - delay(1); + // in order to see relevant durations when the CPU is auto light sleeping + // in this example, regardless of the added latency for web requests: + delay(300); } From 01eb94236bdf9ca197096e8a3151ed237f1e7782 Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Sun, 25 Apr 2021 14:14:19 +0200 Subject: [PATCH 21/39] Remove unintended copy&paste artifact. --- libraries/esp8266/examples/AutoSleepDemo/AutoSleepDemo.ino | 5 ----- 1 file changed, 5 deletions(-) diff --git a/libraries/esp8266/examples/AutoSleepDemo/AutoSleepDemo.ino b/libraries/esp8266/examples/AutoSleepDemo/AutoSleepDemo.ino index c6625b0fe0..59b072892c 100644 --- a/libraries/esp8266/examples/AutoSleepDemo/AutoSleepDemo.ino +++ b/libraries/esp8266/examples/AutoSleepDemo/AutoSleepDemo.ino @@ -101,11 +101,6 @@ void setup() { server.on("/sleep", []() { ESP.autoLightSleep(); - wifi_fpm_set_wakeup_cb([]() { - schedule_function([]() { - Serial.println("auto light sleep wakeup CB"); - }); - }); server.send(200, "text/plain", "auto light sleep on"); }); From 382f6fdea304855e79b45720e8710d0aabbf10c0 Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Sun, 25 Apr 2021 14:33:05 +0200 Subject: [PATCH 22/39] Reset ISR to edge triggered mode when back from sleep via timeout instead of IRQ. --- .../esp8266/examples/ForcedLightSleep/ForcedLightSleep.ino | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libraries/esp8266/examples/ForcedLightSleep/ForcedLightSleep.ino b/libraries/esp8266/examples/ForcedLightSleep/ForcedLightSleep.ino index 64da272758..bb112fe513 100644 --- a/libraries/esp8266/examples/ForcedLightSleep/ForcedLightSleep.ino +++ b/libraries/esp8266/examples/ForcedLightSleep/ForcedLightSleep.ino @@ -47,6 +47,9 @@ void wakeupCallback() { schedule_function([]() { Serial.println("wakeup callback was performed"); }); + // return to falling edge IRQ, otherwise level-triggered IRQ with wakeup + // would get called unexpectedly while awake. + attachInterrupt(WAKE_UP_PIN, wakeupPinIsr, FALLING); } void setup() { From 213a2e0bea14742308e9046909906cd6b44f2530 Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Sun, 25 Apr 2021 16:06:37 +0200 Subject: [PATCH 23/39] AttachInterrupt is not available in ISR (IRAM_ATTR), fix crash on stuck-pressed button. --- .../examples/ForcedLightSleep/ForcedLightSleep.ino | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/libraries/esp8266/examples/ForcedLightSleep/ForcedLightSleep.ino b/libraries/esp8266/examples/ForcedLightSleep/ForcedLightSleep.ino index bb112fe513..145e18de9e 100644 --- a/libraries/esp8266/examples/ForcedLightSleep/ForcedLightSleep.ino +++ b/libraries/esp8266/examples/ForcedLightSleep/ForcedLightSleep.ino @@ -33,14 +33,13 @@ void IRAM_ATTR wakeupPinIsr() { } void IRAM_ATTR wakeupPinIsrWE() { - // Wakeup IRQs are level-triggered only. + // Wakeup IRQs are available as level-triggered only. + detachInterrupt(WAKE_UP_PIN); schedule_function([]() { Serial.println("GPIO wakeup IRQ"); }); wakeupPinIsr(); - // return to falling edge IRQ, otherwise level-triggered IRQ - // keeps triggering this ISR back-to-back, consuming nearly all CPU time. - attachInterrupt(WAKE_UP_PIN, wakeupPinIsr, FALLING); + // reattach falling edge IRQ in loop } void wakeupCallback() { @@ -83,5 +82,7 @@ void loop() { if (wakeupPinIsHigh) { gotoSleep.reset(); } + // restore falling edge IRQ + attachInterrupt(WAKE_UP_PIN, wakeupPinIsr, FALLING); } } From 56c40f3cb02e9a4143dac702a80a554a87a5e036 Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Sun, 25 Apr 2021 16:17:41 +0200 Subject: [PATCH 24/39] SDK needs idle task to perform mode switching. Reset the callback pointer when it is no longer needed. --- cores/esp8266/Esp.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/cores/esp8266/Esp.cpp b/cores/esp8266/Esp.cpp index 02fbd09ea6..0dc40398aa 100644 --- a/cores/esp8266/Esp.cpp +++ b/cores/esp8266/Esp.cpp @@ -168,9 +168,9 @@ bool EspClass::forcedModemSleep(uint32_t duration_us, fpm_wakeup_cb wakeupCb) } // SDK turns on forced modem sleep in idle task #ifdef HAVE_ESP_SUSPEND - esp_delay(1); + esp_delay(10); #else - delay(1); + delay(10); #endif return true; } @@ -269,9 +269,18 @@ void EspClass::forcedLightSleepEnd(bool cancel) esp8266::InterruptLock lock; timer_list = saved_timer_list; } + saved_wakeupCb = nullptr; wifi_fpm_close(); wifi_fpm_set_sleep_type(saved_sleep_type); saved_sleep_type = NONE_SLEEP_T; + if (cancel) { + // let the SDK catch up in idle task +#ifdef HAVE_ESP_SUSPEND + esp_delay(10); +#else + delay(10); +#endif + } } void EspClass::autoModemSleep() { From affc7fcdcf86fb53ffff8253a712e258db08118b Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Sun, 25 Apr 2021 18:13:40 +0200 Subject: [PATCH 25/39] Fix serial bitrate in example. --- libraries/esp8266/examples/AutoSleepDemo/AutoSleepDemo.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/esp8266/examples/AutoSleepDemo/AutoSleepDemo.ino b/libraries/esp8266/examples/AutoSleepDemo/AutoSleepDemo.ino index 59b072892c..ae717ddbae 100644 --- a/libraries/esp8266/examples/AutoSleepDemo/AutoSleepDemo.ino +++ b/libraries/esp8266/examples/AutoSleepDemo/AutoSleepDemo.ino @@ -62,7 +62,7 @@ void handleNotFound() { } void setup() { - Serial.begin(74800); + Serial.begin(74880); while (!Serial); delay(100); Serial.println(); From 6015cec68fd58ffe75216ceb7564db70998636de Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Sun, 25 Apr 2021 20:51:18 +0200 Subject: [PATCH 26/39] Debounce the button GPIO in order to prevent crashes due to IRQ storms. --- .../examples/ForcedLightSleep/ForcedLightSleep.ino | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/libraries/esp8266/examples/ForcedLightSleep/ForcedLightSleep.ino b/libraries/esp8266/examples/ForcedLightSleep/ForcedLightSleep.ino index 145e18de9e..28fa6faf74 100644 --- a/libraries/esp8266/examples/ForcedLightSleep/ForcedLightSleep.ino +++ b/libraries/esp8266/examples/ForcedLightSleep/ForcedLightSleep.ino @@ -27,6 +27,7 @@ void IRAM_ATTR wakeupPinIsr() { // For edge-triggered IRQ. + detachInterrupt(WAKE_UP_PIN); schedule_function([]() { Serial.println("GPIO went from HI to LO"); }); @@ -71,14 +72,19 @@ void loop() { // If the GPIO is in the state that will cause a wakeup on attaching the interrupt, // it cannot trigger a wakeup later, but any sleep duration will be honored. bool wakeupPinIsHigh = digitalRead(WAKE_UP_PIN); - // the GPIO might still bounce to LOW between both digital reads, disabling wakeup + delayMicroseconds(5000); + wakeupPinIsHigh &= digitalRead(WAKE_UP_PIN); + delayMicroseconds(5000); + wakeupPinIsHigh &= digitalRead(WAKE_UP_PIN); + // the GPIO might still bounce to LOW after this but before sleep is full engaged, + // disabling wakeup after all if (wakeupPinIsHigh) { attachInterrupt(WAKE_UP_PIN, wakeupPinIsrWE, ONLOW_WE); } - wakeupPinIsHigh &= digitalRead(WAKE_UP_PIN); digitalWrite(LED_BUILTIN, HIGH); // turn the LED off so they know the CPU isn't running ESP.forcedLightSleepEnd(!wakeupPinIsHigh); digitalWrite(LED_BUILTIN, LOW); // turn on the LED + // retry immediately if the GPIO was found not ready for entering sleep if (wakeupPinIsHigh) { gotoSleep.reset(); } From b6e14f6f38f80a8535f4ab86e69aa185ed61ad9d Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Mon, 26 Apr 2021 10:06:36 +0200 Subject: [PATCH 27/39] Add documentation for the new ESP sleep modes to libraries.rst --- doc/libraries.rst | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/doc/libraries.rst b/doc/libraries.rst index 625935554b..f0b8f78cd7 100644 --- a/doc/libraries.rst +++ b/doc/libraries.rst @@ -77,6 +77,20 @@ Some ESP-specific APIs related to deep sleep, RTC and flash memories are availab ``ESP.deepSleepInstant(microseconds, mode)`` works similarly to ``ESP.deepSleep`` but sleeps instantly without waiting for WiFi to shutdown. +``ESP.forcedModemSleep(microseconds, callback)`` immediately puts the chip into forced ``MODEM_SLEEP``. A microseconds duration after which the sleep mode returns to the automatic sleep mode that was effective before this call can be given, a value of 0 or 0xFFFFFFF turns off that timeout. The optional callback function will be invoked when the forced modem sleep ends. + +``ESP.forcedModemSleepOff()`` immediately returns the chip to the automatic sleep mode in effect before the preceeding call to ``ESP.forcedModemSleep``. + +``ESP.forcedLightSleepBegin(microseconds, callback)`` works in tandem with ``ESP.forcedLightSleepEnd(cancel)`` to put the chip into forced ``LIGHT_SLEEP``. A microseconds duration after which the sleep mode returns to the automatic sleep mode that was effective before this call can be given, a value of 0 or 0xFFFFFFF turns off that timeout. The optional callback function will be invoked when the forced light sleep ends. Forced light sleep halts the CPU, in addition to the timeout, it can be awakened via GPIO input. Between the calls to ``ESP.forcedLightSleepBegin`` and ``ESP.forcedLightSleepEnd`` any GPIOs to use for wakeup can be set up. + +``ESP.forcedLightSleepEnd(cancel)`` causes the chip to enter the forced light sleep mode that was set up as described for ``ESP.forcedLightSleepBegin``. The optional cancel argument, if true, prevents the sleep mode transition from occuring and returns to the automatic sleep mode that was effective before ``ESP.forcedLightSleepBegin``. + +``ESP.autoModemSleep()`` immediately puts the chip into automatic ``MODEM_SLEEP``. + +``ESP.autoLightSleep()`` immediately puts the chip into automatic ``LIGHT_SLEEP``. + +``ESP.autoSleepOff()`` returns the chip to the automatic sleep mode that was effective before the preceding call to either ``ESP.autoModemSleep`` or ``ESP.autoLightSleep``. + ``ESP.rtcUserMemoryWrite(offset, &data, sizeof(data))`` and ``ESP.rtcUserMemoryRead(offset, &data, sizeof(data))`` allow data to be stored in and retrieved from the RTC user memory of the chip respectively. ``offset`` is measured in blocks of 4 bytes and can range from 0 to 127 blocks (total size of RTC memory is 512 bytes). ``data`` should be 4-byte aligned. The stored data can be retained between deep sleep cycles, but might be lost after power cycling the chip. Data stored in the first 32 blocks will be lost after performing an OTA update, because they are used by the Core internals. ``ESP.restart()`` restarts the CPU. From ad454ea8ea83aed0677052a9cbd5db4777e147be Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Mon, 26 Apr 2021 10:12:14 +0200 Subject: [PATCH 28/39] Fix headline in documentation as well. --- doc/libraries.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/libraries.rst b/doc/libraries.rst index f0b8f78cd7..06d8ac4cbe 100644 --- a/doc/libraries.rst +++ b/doc/libraries.rst @@ -71,7 +71,7 @@ An ESP8266 port of SoftwareSerial library done by Peter Lerup (@plerup) supports ESP-specific APIs ----------------- -Some ESP-specific APIs related to deep sleep, RTC and flash memories are available in the ``ESP`` object. +Some ESP-specific APIs related to the deep, modem, and light sleep modes, RTC and flash memory are available in the ``ESP`` object. ``ESP.deepSleep(microseconds, mode)`` will put the chip into deep sleep. ``mode`` is one of ``WAKE_RF_DEFAULT``, ``WAKE_RFCAL``, ``WAKE_NO_RFCAL``, ``WAKE_RF_DISABLED``. (GPIO16 needs to be tied to RST to wake from deepSleep.) The chip can sleep for at most ``ESP.deepSleepMax()`` microseconds. If you implement deep sleep with ``WAKE_RF_DISABLED`` and require WiFi functionality on wake up, you will need to implement an additional ``WAKE_RF_DEFAULT`` before WiFi functionality is available. From 74951fd408f334a7923c85194a30d0a3fcb2a906 Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Mon, 26 Apr 2021 13:07:24 +0200 Subject: [PATCH 29/39] By review comment. --- doc/libraries.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/libraries.rst b/doc/libraries.rst index 06d8ac4cbe..430d0ed994 100644 --- a/doc/libraries.rst +++ b/doc/libraries.rst @@ -81,7 +81,7 @@ Some ESP-specific APIs related to the deep, modem, and light sleep modes, RTC an ``ESP.forcedModemSleepOff()`` immediately returns the chip to the automatic sleep mode in effect before the preceeding call to ``ESP.forcedModemSleep``. -``ESP.forcedLightSleepBegin(microseconds, callback)`` works in tandem with ``ESP.forcedLightSleepEnd(cancel)`` to put the chip into forced ``LIGHT_SLEEP``. A microseconds duration after which the sleep mode returns to the automatic sleep mode that was effective before this call can be given, a value of 0 or 0xFFFFFFF turns off that timeout. The optional callback function will be invoked when the forced light sleep ends. Forced light sleep halts the CPU, in addition to the timeout, it can be awakened via GPIO input. Between the calls to ``ESP.forcedLightSleepBegin`` and ``ESP.forcedLightSleepEnd`` any GPIOs to use for wakeup can be set up. +``ESP.forcedLightSleepBegin(microseconds, callback)`` works in tandem with ``ESP.forcedLightSleepEnd(cancel)`` to put the chip into forced ``LIGHT_SLEEP``. A microseconds duration after which the sleep mode returns to the automatic sleep mode that was effective before this call can be given, a value of 0 or 0xFFFFFFF turns off that timeout. The optional callback function will be invoked when the forced light sleep ends. Forced light sleep halts the CPU, in addition to the timeout, it can be awakened via GPIO input. Between the calls to ``ESP.forcedLightSleepBegin`` and ``ESP.forcedLightSleepEnd`` any GPIOs except GPIO16 to use for wakeup can be set up. ``ESP.forcedLightSleepEnd(cancel)`` causes the chip to enter the forced light sleep mode that was set up as described for ``ESP.forcedLightSleepBegin``. The optional cancel argument, if true, prevents the sleep mode transition from occuring and returns to the automatic sleep mode that was effective before ``ESP.forcedLightSleepBegin``. From 0a7027265b3c267f02fbe64c690304f18c9cc894 Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Tue, 27 Apr 2021 14:21:17 +0200 Subject: [PATCH 30/39] ESP.deepSleep[Instant] refactoring to match the setting of RF mode in user_rf_pre_init Since WiFi is disabled at boot by default, and the above change to ESP.deepSleep..., (sample) sketches for special RF modes on resume from deep sleep may need fixing or documentation update. That's not done by this commit alone. --- cores/esp8266/Esp.cpp | 8 ++++---- cores/esp8266/Esp.h | 4 ++-- .../ESP8266WiFi/examples/WiFiShutdown/WiFiShutdown.ino | 4 ++-- .../esp8266/examples/LowPowerDemo/LowPowerDemo.ino | 10 +++++----- libraries/esp8266/examples/LowPowerDemo/README.md | 6 +++--- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/cores/esp8266/Esp.cpp b/cores/esp8266/Esp.cpp index 0dc40398aa..af72848bf0 100644 --- a/cores/esp8266/Esp.cpp +++ b/cores/esp8266/Esp.cpp @@ -115,16 +115,16 @@ void EspClass::wdtFeed(void) system_soft_wdt_feed(); } -void EspClass::deepSleep(uint64_t time_us, WakeMode mode) +void EspClass::deepSleep(uint64_t time_us) { - system_deep_sleep_set_option(static_cast(mode)); + system_deep_sleep_set_option(__get_rf_mode()); system_deep_sleep(time_us); esp_suspend(); } -void EspClass::deepSleepInstant(uint64_t time_us, WakeMode mode) +void EspClass::deepSleepInstant(uint64_t time_us) { - system_deep_sleep_set_option(static_cast(mode)); + system_deep_sleep_set_option(__get_rf_mode()); system_deep_sleep_instant(time_us); esp_suspend(); } diff --git a/cores/esp8266/Esp.h b/cores/esp8266/Esp.h index 5b45b17a6d..cb79e6b259 100644 --- a/cores/esp8266/Esp.h +++ b/cores/esp8266/Esp.h @@ -91,8 +91,8 @@ class EspClass { static void wdtDisable(); static void wdtFeed(); - static void deepSleep(uint64_t time_us, RFMode mode = RF_DEFAULT); - static void deepSleepInstant(uint64_t time_us, RFMode mode = RF_DEFAULT); + static void deepSleep(uint64_t time_us); + static void deepSleepInstant(uint64_t time_us); static uint64_t deepSleepMax(); static bool forcedModemSleep(uint32_t duration_us = 0, void (*wakeupCb)() = nullptr); diff --git a/libraries/ESP8266WiFi/examples/WiFiShutdown/WiFiShutdown.ino b/libraries/ESP8266WiFi/examples/WiFiShutdown/WiFiShutdown.ino index 69502de862..213453371c 100644 --- a/libraries/ESP8266WiFi/examples/WiFiShutdown/WiFiShutdown.ino +++ b/libraries/ESP8266WiFi/examples/WiFiShutdown/WiFiShutdown.ino @@ -47,7 +47,7 @@ void setup() { WiFi.mode(WIFI_OFF); Serial.println("Cannot connect!"); Serial.flush(); - ESP.deepSleep(10e6, RF_DISABLED); + ESP.deepSleep(10e6); return; } } @@ -69,7 +69,7 @@ void setup() { Serial.println("Done."); Serial.flush(); - ESP.deepSleep(10e6, RF_DISABLED); + ESP.deepSleep(10e6); } void loop() { diff --git a/libraries/esp8266/examples/LowPowerDemo/LowPowerDemo.ino b/libraries/esp8266/examples/LowPowerDemo/LowPowerDemo.ino index 5d1fb2fb44..9daa97db12 100644 --- a/libraries/esp8266/examples/LowPowerDemo/LowPowerDemo.ino +++ b/libraries/esp8266/examples/LowPowerDemo/LowPowerDemo.ino @@ -291,8 +291,8 @@ void runTest7() { Serial.println(F("going into Deep Sleep now...")); printMillis(); // show time difference across sleep testPoint_HIGH; // testPoint set HIGH to track Deep Sleep period, cleared at startup() - ESP.deepSleep(10E6, WAKE_RF_DEFAULT); // good night! D0 fires a reset in 10 seconds... - // if you do ESP.deepSleep(0, mode); it needs a RESET to come out of sleep (RTC is disconnected) + ESP.deepSleep(10E6); // good night! D0 fires a reset in 10 seconds... + // if you do ESP.deepSleep(0); it needs a RESET to come out of sleep (RTC is disconnected) // maximum timed Deep Sleep interval ~ 3 to 4 hours depending on the RTC timer, see the README // the 2 uA GPIO amperage during Deep Sleep can't drive the LED so it's not lit now, although // depending on the LED used, you might see it very dimly lit in a dark room during this test @@ -309,7 +309,7 @@ void runTest8() { 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() - ESP.deepSleep(10E6, WAKE_RFCAL); // good night! D0 fires a reset in 10 seconds... + ESP.deepSleep(10E6); // good night! D0 fires a reset in 10 seconds... Serial.println(F("What... I'm not asleep?!?")); // it will never get here } @@ -322,7 +322,7 @@ void runTest9() { 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() - ESP.deepSleepInstant(10E6, WAKE_NO_RFCAL); // good night! D0 fires a reset in 10 seconds... + ESP.deepSleepInstant(10E6); // good night! D0 fires a reset in 10 seconds... Serial.println(F("What... I'm not asleep?!?")); // it will never get here } @@ -335,7 +335,7 @@ void runTest10() { 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() - ESP.deepSleepInstant(10E6, WAKE_RF_DISABLED); // good night! D0 fires a reset in 10 seconds... + ESP.deepSleepInstant(10E6); // good night! D0 fires a reset in 10 seconds... Serial.println(F("What... I'm not asleep?!?")); // it will never get here } diff --git a/libraries/esp8266/examples/LowPowerDemo/README.md b/libraries/esp8266/examples/LowPowerDemo/README.md index f080348116..8d02fdab81 100644 --- a/libraries/esp8266/examples/LowPowerDemo/README.md +++ b/libraries/esp8266/examples/LowPowerDemo/README.md @@ -71,13 +71,13 @@ Similar to timed Deep Sleep, but it wakes with an interrupt at the next line in Similar to ESP.deepSleep(0). The chip sleeps at 0.4 mA amperage until it is woken by an external interrupt. The only allowed interrupts are high level and low level; edge interrupts won't work. If you have a design that needs to be woken more often than every 2 seconds then you should consider using Forced Light Sleep. For sleep periods longer than 2 seconds, Deep Sleep will be more energy efficient. The chip wakes after an interrupt in about 3 to 5.5 mS (regardless of CPU speed), but WiFi was turned off to enter Forced Light Sleep so you will need to re-initialize it if you are using WiFi. Any user timers (including PWM) will keep the chip from going fully into Forced Light Sleep, and amperage will be ~ 2 mA with timers enabled. -### Test 7 - Deep Sleep, wake with RF_DEFAULT +### Test 7 - Deep Sleep -In Deep Sleep almost everything is turned off, and the chip draws ~ 20 uA. If you have D0/GPIO16 connected to RST, you can use the RTC timer to wake the chip up at a timed interval. You can also wake it solely with an external RESET with ESP.deepSleep(0, wake option), which disconnects the timer. Waking with RF_DEFAULT means it will do an RFCAL if it needs to. Doing **ESP.deepSleep(time)** without the mode variable uses this wake mode. These first two Deep Sleep tests use the standard Deep Sleep function, so the WiFi connection is closed and the modem turned off, which takes up to 270 mS before Deep Sleep begins. Deep Sleep ends with a RESET, and the boot time after that is ~ 130 mS. Any Deep Sleep less than 2 seconds is wasting energy due to the modem shut-off and boot times, and Forced Light Sleep will be a better choice as it recovers in < 5.5 mS from the previous program state. The Deep Sleep tests will not go into Automatic Modem Sleep because delay() is not used. +In Deep Sleep almost everything is turned off, and the chip draws ~ 20 uA. If you have D0/GPIO16 connected to RST, you can use the RTC timer to wake the chip up at a timed interval. You can also wake it solely with an external RESET with ESP.deepSleep(0), which disconnects the timer. Doing **ESP.deepSleep(time)** without the mode variable uses this wake mode. These first two Deep Sleep tests use the standard Deep Sleep function, so the WiFi connection is closed and the modem turned off, which takes up to 270 mS before Deep Sleep begins. Deep Sleep ends with a RESET, and the boot time after that is ~ 130 mS. Any Deep Sleep less than 2 seconds is wasting energy due to the modem shut-off and boot times, and Forced Light Sleep will be a better choice as it recovers in < 5.5 mS from the previous program state. The Deep Sleep tests will not go into Automatic Modem Sleep because delay() is not used. Note that a RESET during Deep Sleep (either external or from D0/GPIO16) does not clear the GPIO pins; some of them hold their previous state. It's unknown how much else survives a reset, as it's not well documented. -### Test 8 - Deep Sleep, wake with RFCAL +### Test 8 - Deep Sleep, wake with RFCAL - BREAKING CHANGE: MUST use __get_rf_mode overrides to alter WAKE modes Identical to the test above, but the modem always does an RF power calibration when booting. In normal use, most people would do WAKE_RF_DEFAULT instead to avoid the extra RFCAL power burst coming out of Deep Sleep if it's not needed. Note that most of the time both of these modes (WAKE_RF_DEFAULT and WAKE_RFCAL) do a 100 mS long RFCAL *before* going into Deep Sleep (the RFCAL after Deep Sleep is much shorter). If the modem is shut down, this long RFCAL doesn't happen. From df7e909cf7239c274c500b2203c2ac5985c34f39 Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Wed, 28 Apr 2021 02:20:55 +0200 Subject: [PATCH 31/39] More comprehensive description of the usage of forcedLightSleep. --- doc/libraries.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/libraries.rst b/doc/libraries.rst index 430d0ed994..294b667f81 100644 --- a/doc/libraries.rst +++ b/doc/libraries.rst @@ -81,9 +81,9 @@ Some ESP-specific APIs related to the deep, modem, and light sleep modes, RTC an ``ESP.forcedModemSleepOff()`` immediately returns the chip to the automatic sleep mode in effect before the preceeding call to ``ESP.forcedModemSleep``. -``ESP.forcedLightSleepBegin(microseconds, callback)`` works in tandem with ``ESP.forcedLightSleepEnd(cancel)`` to put the chip into forced ``LIGHT_SLEEP``. A microseconds duration after which the sleep mode returns to the automatic sleep mode that was effective before this call can be given, a value of 0 or 0xFFFFFFF turns off that timeout. The optional callback function will be invoked when the forced light sleep ends. Forced light sleep halts the CPU, in addition to the timeout, it can be awakened via GPIO input. Between the calls to ``ESP.forcedLightSleepBegin`` and ``ESP.forcedLightSleepEnd`` any GPIOs except GPIO16 to use for wakeup can be set up. +``ESP.forcedLightSleepBegin(microseconds, callback)`` works in tandem with ``ESP.forcedLightSleepEnd(cancel)`` to put the chip into forced ``LIGHT_SLEEP``. A microseconds duration after which the sleep mode returns to the automatic sleep mode that was effective before this call can be given, a value of 0 or 0xFFFFFFF turns off that timeout. The optional callback function will be invoked when the forced light sleep ends. Forced light sleep halts the CPU, in addition to the timeout, it can be awakened via GPIO input. Between the calls to ``ESP.forcedLightSleepBegin`` and ``ESP.forcedLightSleepEnd`` any GPIOs except GPIO16 to use for wakeup can be set up. Care must be taken not to allow the chip to enter the idle task before the call to ``ESP.forcedLightSleepEnd(cancel)``, so for instance no direct or indirect calls to ``delay()`` are possible. Otherwise the forced light sleep may engange too early, breaking the required logic of the tandem calls. -``ESP.forcedLightSleepEnd(cancel)`` causes the chip to enter the forced light sleep mode that was set up as described for ``ESP.forcedLightSleepBegin``. The optional cancel argument, if true, prevents the sleep mode transition from occuring and returns to the automatic sleep mode that was effective before ``ESP.forcedLightSleepBegin``. +``ESP.forcedLightSleepEnd(cancel)`` causes the chip to enter the forced light sleep mode that was set up as described for ``ESP.forcedLightSleepBegin``. The optional cancel argument, if true, prevents the sleep mode transition from occuring and returns to the automatic sleep mode that was effective before ``ESP.forcedLightSleepBegin``. This can be used, for instance, if setting up the level-triggered GPIO interrupts for wakeup fails. ``ESP.autoModemSleep()`` immediately puts the chip into automatic ``MODEM_SLEEP``. From 1af138066a92bc55803d25c65c6c307d004d8177 Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Wed, 28 Apr 2021 02:25:20 +0200 Subject: [PATCH 32/39] Be more specific about ForcedLightSleepEnd. --- doc/libraries.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/libraries.rst b/doc/libraries.rst index 294b667f81..46ddfd5e42 100644 --- a/doc/libraries.rst +++ b/doc/libraries.rst @@ -83,7 +83,7 @@ Some ESP-specific APIs related to the deep, modem, and light sleep modes, RTC an ``ESP.forcedLightSleepBegin(microseconds, callback)`` works in tandem with ``ESP.forcedLightSleepEnd(cancel)`` to put the chip into forced ``LIGHT_SLEEP``. A microseconds duration after which the sleep mode returns to the automatic sleep mode that was effective before this call can be given, a value of 0 or 0xFFFFFFF turns off that timeout. The optional callback function will be invoked when the forced light sleep ends. Forced light sleep halts the CPU, in addition to the timeout, it can be awakened via GPIO input. Between the calls to ``ESP.forcedLightSleepBegin`` and ``ESP.forcedLightSleepEnd`` any GPIOs except GPIO16 to use for wakeup can be set up. Care must be taken not to allow the chip to enter the idle task before the call to ``ESP.forcedLightSleepEnd(cancel)``, so for instance no direct or indirect calls to ``delay()`` are possible. Otherwise the forced light sleep may engange too early, breaking the required logic of the tandem calls. -``ESP.forcedLightSleepEnd(cancel)`` causes the chip to enter the forced light sleep mode that was set up as described for ``ESP.forcedLightSleepBegin``. The optional cancel argument, if true, prevents the sleep mode transition from occuring and returns to the automatic sleep mode that was effective before ``ESP.forcedLightSleepBegin``. This can be used, for instance, if setting up the level-triggered GPIO interrupts for wakeup fails. +``ESP.forcedLightSleepEnd(cancel)`` causes the chip to enter the forced light sleep mode that was set up as described for ``ESP.forcedLightSleepBegin``. The optional cancel argument, if true, prevents the sleep mode transition from occuring and returns to the automatic sleep mode that was effective before ``ESP.forcedLightSleepBegin``. This can be used, for instance, if setting up the level-triggered GPIO interrupts for wakeup fails. It returns after waking up, unless is it cancelled, in which case it returns immediately. ``ESP.autoModemSleep()`` immediately puts the chip into automatic ``MODEM_SLEEP``. From a9d9cda98f61c3b72b7e0ecb9051d0aba95b0c08 Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Wed, 28 Apr 2021 02:39:22 +0200 Subject: [PATCH 33/39] Describe forced light sleep initiation in more detail. --- doc/libraries.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/libraries.rst b/doc/libraries.rst index 46ddfd5e42..b053fd4c0d 100644 --- a/doc/libraries.rst +++ b/doc/libraries.rst @@ -81,9 +81,9 @@ Some ESP-specific APIs related to the deep, modem, and light sleep modes, RTC an ``ESP.forcedModemSleepOff()`` immediately returns the chip to the automatic sleep mode in effect before the preceeding call to ``ESP.forcedModemSleep``. -``ESP.forcedLightSleepBegin(microseconds, callback)`` works in tandem with ``ESP.forcedLightSleepEnd(cancel)`` to put the chip into forced ``LIGHT_SLEEP``. A microseconds duration after which the sleep mode returns to the automatic sleep mode that was effective before this call can be given, a value of 0 or 0xFFFFFFF turns off that timeout. The optional callback function will be invoked when the forced light sleep ends. Forced light sleep halts the CPU, in addition to the timeout, it can be awakened via GPIO input. Between the calls to ``ESP.forcedLightSleepBegin`` and ``ESP.forcedLightSleepEnd`` any GPIOs except GPIO16 to use for wakeup can be set up. Care must be taken not to allow the chip to enter the idle task before the call to ``ESP.forcedLightSleepEnd(cancel)``, so for instance no direct or indirect calls to ``delay()`` are possible. Otherwise the forced light sleep may engange too early, breaking the required logic of the tandem calls. +``ESP.forcedLightSleepBegin(microseconds, callback)`` works in tandem with ``ESP.forcedLightSleepEnd(cancel)`` to put the chip into forced ``LIGHT_SLEEP``. A microseconds duration after which the sleep mode returns can be given, a value of 0 or 0xFFFFFFF turns off that timeout. The optional callback function will be invoked when the forced light sleep ends. Forced light sleep halts the CPU, in addition to the timeout, it can be awakened via GPIO input. Between the calls to ``ESP.forcedLightSleepBegin`` and ``ESP.forcedLightSleepEnd`` any GPIOs except GPIO16 to use for wakeup can be set up. Care must be taken not to allow the chip to enter the idle task before the call to ``ESP.forcedLightSleepEnd(cancel)``, so for instance no direct or indirect calls to ``delay()`` are possible. Otherwise the forced light sleep may engange too early, breaking the required logic of the tandem calls. -``ESP.forcedLightSleepEnd(cancel)`` causes the chip to enter the forced light sleep mode that was set up as described for ``ESP.forcedLightSleepBegin``. The optional cancel argument, if true, prevents the sleep mode transition from occuring and returns to the automatic sleep mode that was effective before ``ESP.forcedLightSleepBegin``. This can be used, for instance, if setting up the level-triggered GPIO interrupts for wakeup fails. It returns after waking up, unless is it cancelled, in which case it returns immediately. +``ESP.forcedLightSleepEnd(cancel)`` causes the chip to enter the forced light sleep mode that was prepared by the preceeding ``ESP.forcedLightSleepBegin``. The optional cancel argument, if true, prevents the sleep mode transition from occuring. This can be used, for instance, to return immediately if setting up the level-triggered GPIO interrupts for wakeup fails. Otherwise, it returns after waking up from forced light sleep. On return, the automatic sleep mode that was effective before the call to ``ESP.forcedLightSleepBegin`` is activated. ``ESP.autoModemSleep()`` immediately puts the chip into automatic ``MODEM_SLEEP``. From e8798ac3ff8a1d0d5d5ccc1550d326b6b52af420 Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Mon, 17 May 2021 12:58:24 +0200 Subject: [PATCH 34/39] Add neverSleep and neverSleepOff to Esp class, use them in core and libs where feasible. --- cores/esp8266/Esp.cpp | 18 ++++++++++++++++++ cores/esp8266/Esp.h | 6 ++++++ cores/esp8266/Updater.cpp | 2 +- doc/libraries.rst | 4 ++++ .../ESP8266WiFi/src/ESP8266WiFiGeneric.cpp | 12 ++++++++++++ libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.h | 14 ++------------ tests/host/common/MockEsp.cpp | 6 ++++++ 7 files changed, 49 insertions(+), 13 deletions(-) diff --git a/cores/esp8266/Esp.cpp b/cores/esp8266/Esp.cpp index af72848bf0..cb03731dc8 100644 --- a/cores/esp8266/Esp.cpp +++ b/cores/esp8266/Esp.cpp @@ -300,6 +300,24 @@ void EspClass::autoSleepOff() { saved_sleep_type = NONE_SLEEP_T; } +void EspClass::neverSleep() { + const auto active_sleep_type = wifi_get_sleep_type(); + if (NONE_SLEEP_T == active_sleep_type) { + return; + } + wifi_fpm_close(); + saved_sleep_type = active_sleep_type; + wifi_set_sleep_type(NONE_SLEEP_T); +} + +void EspClass::neverSleepOff() { + if (NONE_SLEEP_T == saved_sleep_type) { + return; + } + wifi_set_sleep_type(saved_sleep_type); + saved_sleep_type = NONE_SLEEP_T; +} + /* Layout of RTC Memory is as follows: Ref: Espressif doc 2C-ESP8266_Non_OS_SDK_API_Reference, section 3.3.23 (system_rtc_mem_write) diff --git a/cores/esp8266/Esp.h b/cores/esp8266/Esp.h index cb79e6b259..fabca8b0d8 100644 --- a/cores/esp8266/Esp.h +++ b/cores/esp8266/Esp.h @@ -114,6 +114,12 @@ class EspClass { /// it would have to be restored explicitly. static void autoSleepOff(); + static void neverSleep(); + /// Any prior sleep type is restored, but only as automatic. + /// If any forced sleep mode was effective before neverSleep, + /// it would have to be restored explicitly. + static void neverSleepOff(); + static bool rtcUserMemoryRead(uint32_t offset, uint32_t *data, size_t size); static bool rtcUserMemoryWrite(uint32_t offset, uint32_t *data, size_t size); diff --git a/cores/esp8266/Updater.cpp b/cores/esp8266/Updater.cpp index 91f2a9e6ae..5599f6ce08 100644 --- a/cores/esp8266/Updater.cpp +++ b/cores/esp8266/Updater.cpp @@ -109,7 +109,7 @@ bool UpdaterClass::begin(size_t size, int command, int ledPin, uint8_t ledOn) { _md5 = MD5Builder(); #ifndef HOST_MOCK - wifi_set_sleep_type(NONE_SLEEP_T); + ESP.neverSleep(); #endif //address where we will start writing the update diff --git a/doc/libraries.rst b/doc/libraries.rst index b053fd4c0d..9c7358087e 100644 --- a/doc/libraries.rst +++ b/doc/libraries.rst @@ -91,6 +91,10 @@ Some ESP-specific APIs related to the deep, modem, and light sleep modes, RTC an ``ESP.autoSleepOff()`` returns the chip to the automatic sleep mode that was effective before the preceding call to either ``ESP.autoModemSleep`` or ``ESP.autoLightSleep``. +``ESP.neverSleep()`` immediately puts the chip into ``NONE_SLEEP`` mode. + +``ESP.neverSleepOff()`` returns the chip to any automatic sleep mode that was effective before the preceding call to ``ESP.neverSleep``. + ``ESP.rtcUserMemoryWrite(offset, &data, sizeof(data))`` and ``ESP.rtcUserMemoryRead(offset, &data, sizeof(data))`` allow data to be stored in and retrieved from the RTC user memory of the chip respectively. ``offset`` is measured in blocks of 4 bytes and can range from 0 to 127 blocks (total size of RTC memory is 512 bytes). ``data`` should be 4-byte aligned. The stored data can be retained between deep sleep cycles, but might be lost after power cycling the chip. Data stored in the first 32 blocks will be lost after performing an OTA update, because they are used by the Core internals. ``ESP.restart()`` restarts the CPU. diff --git a/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp b/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp index 35245f17a2..7d3d2350e3 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp +++ b/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp @@ -348,6 +348,18 @@ bool ESP8266WiFiGenericClass::setSleepMode(WiFiSleepType_t type, uint8_t listenI return ret; } +bool ESP8266WiFiGenericClass::setSleep(bool enable) { + if (enable) + { + ESP.neverSleepOff(); + } + else + { + ESP.neverSleep(); + } + return true; +} + /** * get Sleep mode * @return sleep_type_t diff --git a/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.h b/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.h index d0525176bd..3307a35f82 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.h +++ b/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.h @@ -85,21 +85,11 @@ class ESP8266WiFiGenericClass { bool setSleepMode(WiFiSleepType_t type, uint8_t listenInterval = 0); /** - * Set modem sleep mode (ESP32 compatibility) + * Set ESP866 to never sleep or return to previous mode (ESP32 compatibility) * @param enable true to enable * @return true if succeeded */ - bool setSleep(bool enable) - { - if (enable) - { - return setSleepMode(WIFI_MODEM_SLEEP); - } - else - { - return setSleepMode(WIFI_NONE_SLEEP); - } - } + bool setSleep(bool enable); /** * Set sleep mode (ESP32 compatibility) * @param mode wifi_ps_type_t diff --git a/tests/host/common/MockEsp.cpp b/tests/host/common/MockEsp.cpp index 39583c3245..455f8824c6 100644 --- a/tests/host/common/MockEsp.cpp +++ b/tests/host/common/MockEsp.cpp @@ -125,6 +125,12 @@ void EspClass::autoLightSleep() { void EspClass::autoSleepOff() { } +void EspClass::neverSleep() { +} + +void EspClass::neverSleepOff() { +} + void EspClass::restart() { From 3fc7d23049dd3d0240086724f542dfaddf03ffb2 Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Sun, 13 Mar 2022 20:42:46 +0100 Subject: [PATCH 35/39] Apply suggested style fixes. --- .../examples/AutoSleepDemo/AutoSleepDemo.ino | 13 ++++++------ .../ForcedLightSleep/ForcedLightSleep.ino | 7 ++++--- .../ForcedModemSleep/ForcedModemSleep.ino | 5 +++-- .../examples/LowPowerDemo/LowPowerDemo.ino | 12 +++++------ tests/host/common/MockEsp.cpp | 20 ++++++------------- 5 files changed, 26 insertions(+), 31 deletions(-) diff --git a/libraries/esp8266/examples/AutoSleepDemo/AutoSleepDemo.ino b/libraries/esp8266/examples/AutoSleepDemo/AutoSleepDemo.ino index ae717ddbae..13f158ae09 100644 --- a/libraries/esp8266/examples/AutoSleepDemo/AutoSleepDemo.ino +++ b/libraries/esp8266/examples/AutoSleepDemo/AutoSleepDemo.ino @@ -35,9 +35,9 @@ // enter your WiFi configuration below const char* AP_SSID = "SSID"; // your router's SSID here -const char* AP_PASS = "PSK"; // your router's password here +const char* AP_PASS = "PSK"; // your router's password here -uint32_t timeout = 30E3; // 30 second timeout on the WiFi connection +uint32_t timeout = 30E3; // 30 second timeout on the WiFi connection esp8266::polledTimeout::oneShotMs wifiTimeout(timeout); // 30 second timeout on WiFi connection ESP8266WebServer server(80); @@ -63,7 +63,8 @@ void handleNotFound() { void setup() { Serial.begin(74880); - while (!Serial); + while (!Serial) + ; delay(100); Serial.println(); WiFi.mode(WIFI_STA); @@ -83,9 +84,9 @@ void setup() { } WiFi.setAutoReconnect(true); - //if (MDNS.begin("esp8266")) { - // Serial.println("MDNS responder started"); - //} + // if (MDNS.begin("esp8266")) { + // Serial.println("MDNS responder started"); + // } server.on("/", handleRoot); diff --git a/libraries/esp8266/examples/ForcedLightSleep/ForcedLightSleep.ino b/libraries/esp8266/examples/ForcedLightSleep/ForcedLightSleep.ino index 28fa6faf74..a373fcff5d 100644 --- a/libraries/esp8266/examples/ForcedLightSleep/ForcedLightSleep.ino +++ b/libraries/esp8266/examples/ForcedLightSleep/ForcedLightSleep.ino @@ -54,10 +54,11 @@ void wakeupCallback() { void setup() { Serial.begin(74880); - while (!Serial); + while (!Serial) + ; delay(100); - pinMode(LED_BUILTIN, OUTPUT); // activity and status indicator - digitalWrite(LED_BUILTIN, LOW); // turn on the LED + pinMode(LED_BUILTIN, OUTPUT); // activity and status indicator + digitalWrite(LED_BUILTIN, LOW); // turn on the LED pinMode(WAKE_UP_PIN, INPUT_PULLUP); // polled to advance tests, interrupt for Forced Light Sleep attachInterrupt(WAKE_UP_PIN, wakeupPinIsr, FALLING); } diff --git a/libraries/esp8266/examples/ForcedModemSleep/ForcedModemSleep.ino b/libraries/esp8266/examples/ForcedModemSleep/ForcedModemSleep.ino index cd2f62200d..6a99bd0860 100644 --- a/libraries/esp8266/examples/ForcedModemSleep/ForcedModemSleep.ino +++ b/libraries/esp8266/examples/ForcedModemSleep/ForcedModemSleep.ino @@ -35,9 +35,10 @@ void wakeupCallback() { void setup() { Serial.begin(74880); - while (!Serial); + while (!Serial) + ; delay(100); - pinMode(LED_BUILTIN, OUTPUT); // activity and status indicator + pinMode(LED_BUILTIN, OUTPUT); // activity and status indicator digitalWrite(LED_BUILTIN, LOW); // turn on the LED } diff --git a/libraries/esp8266/examples/LowPowerDemo/LowPowerDemo.ino b/libraries/esp8266/examples/LowPowerDemo/LowPowerDemo.ino index 9daa97db12..5bc076a4a0 100644 --- a/libraries/esp8266/examples/LowPowerDemo/LowPowerDemo.ino +++ b/libraries/esp8266/examples/LowPowerDemo/LowPowerDemo.ino @@ -289,9 +289,9 @@ void runTest7() { // 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 - testPoint_HIGH; // testPoint set HIGH to track Deep Sleep period, cleared at startup() - ESP.deepSleep(10E6); // good night! D0 fires a reset in 10 seconds... + printMillis(); // show time difference across sleep + testPoint_HIGH; // testPoint set HIGH to track Deep Sleep period, cleared at startup() + ESP.deepSleep(10E6); // good night! D0 fires a reset in 10 seconds... // if you do ESP.deepSleep(0); it needs a RESET to come out of sleep (RTC is disconnected) // maximum timed Deep Sleep interval ~ 3 to 4 hours depending on the RTC timer, see the README // the 2 uA GPIO amperage during Deep Sleep can't drive the LED so it's not lit now, although @@ -309,7 +309,7 @@ void runTest8() { 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() - ESP.deepSleep(10E6); // good night! D0 fires a reset in 10 seconds... + ESP.deepSleep(10E6); // good night! D0 fires a reset in 10 seconds... Serial.println(F("What... I'm not asleep?!?")); // it will never get here } @@ -322,7 +322,7 @@ void runTest9() { 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() - ESP.deepSleepInstant(10E6); // good night! D0 fires a reset in 10 seconds... + ESP.deepSleepInstant(10E6); // good night! D0 fires a reset in 10 seconds... Serial.println(F("What... I'm not asleep?!?")); // it will never get here } @@ -335,7 +335,7 @@ void runTest10() { 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() - ESP.deepSleepInstant(10E6); // good night! D0 fires a reset in 10 seconds... + ESP.deepSleepInstant(10E6); // good night! D0 fires a reset in 10 seconds... Serial.println(F("What... I'm not asleep?!?")); // it will never get here } diff --git a/tests/host/common/MockEsp.cpp b/tests/host/common/MockEsp.cpp index 455f8824c6..4b6afa907f 100644 --- a/tests/host/common/MockEsp.cpp +++ b/tests/host/common/MockEsp.cpp @@ -100,9 +100,7 @@ bool EspClass::forcedModemSleep(uint32_t duration_us, void (*wakeupCb)()) return true; } -void EspClass::forcedModemSleepOff() -{ -} +void EspClass::forcedModemSleepOff() { } bool EspClass::forcedLightSleepBegin(uint32_t duration_us, void (*wakeupCb)()) { @@ -116,21 +114,15 @@ void EspClass::forcedLightSleepEnd(bool cancel) (void)cancel; } -void EspClass::autoModemSleep() { -} - -void EspClass::autoLightSleep() { -} +void EspClass::autoModemSleep() { } -void EspClass::autoSleepOff() { -} +void EspClass::autoLightSleep() { } -void EspClass::neverSleep() { -} +void EspClass::autoSleepOff() { } -void EspClass::neverSleepOff() { -} +void EspClass::neverSleep() { } +void EspClass::neverSleepOff() { } void EspClass::restart() { From abd17a7b01ff13eda5c46a0cdaabf94c458a4cc9 Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Sun, 12 Mar 2023 02:22:09 +0100 Subject: [PATCH 36/39] There may exist code expecting this macro definition to overwrite the weak symbol. --- cores/esp8266/Esp.h | 2 ++ cores/esp8266/core_esp8266_phy.cpp | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/cores/esp8266/Esp.h b/cores/esp8266/Esp.h index fabca8b0d8..f32d077171 100644 --- a/cores/esp8266/Esp.h +++ b/cores/esp8266/Esp.h @@ -57,6 +57,8 @@ enum RFMode { RF_DISABLED = 4 // disable RF after deep-sleep wake up, just like modem sleep, there will be the smallest current. }; +#define RF_PRE_INIT() void __run_user_rf_pre_init() + // compatibility definitions #define WakeMode RFMode #define WAKE_RF_DEFAULT RF_DEFAULT diff --git a/cores/esp8266/core_esp8266_phy.cpp b/cores/esp8266/core_esp8266_phy.cpp index c655805a84..29ea4afc98 100644 --- a/cores/esp8266/core_esp8266_phy.cpp +++ b/cores/esp8266/core_esp8266_phy.cpp @@ -355,7 +355,7 @@ extern int __get_adc_mode(void) extern void __run_user_rf_pre_init(void) __attribute__((noinline, weak)); extern void __run_user_rf_pre_init(void) { - return; // default do noting + return; // default do nothing } #if (NONOSDK >= (0x30000)) From b8a95de395ba9310450a4cbf9e0ce43f83c326a3 Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Wed, 21 Jun 2023 21:28:13 +0200 Subject: [PATCH 37/39] Reviewer noticed that esp_suspend is part of the core for awhile now and should be used unconditionally. --- cores/esp8266/Esp.cpp | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/cores/esp8266/Esp.cpp b/cores/esp8266/Esp.cpp index cb03731dc8..2a2d655d4b 100644 --- a/cores/esp8266/Esp.cpp +++ b/cores/esp8266/Esp.cpp @@ -167,11 +167,7 @@ bool EspClass::forcedModemSleep(uint32_t duration_us, fpm_wakeup_cb wakeupCb) return false; } // SDK turns on forced modem sleep in idle task -#ifdef HAVE_ESP_SUSPEND esp_delay(10); -#else - delay(10); -#endif return true; } @@ -256,11 +252,7 @@ void EspClass::forcedLightSleepEnd(bool cancel) { if (!cancel) { // SDK turns on forced light sleep in idle task -#ifdef HAVE_ESP_SUSPEND esp_suspend(); -#else - esp_yield(); -#endif } #ifdef DEBUG_SERIAL walk_timer_list(); @@ -275,11 +267,7 @@ void EspClass::forcedLightSleepEnd(bool cancel) saved_sleep_type = NONE_SLEEP_T; if (cancel) { // let the SDK catch up in idle task -#ifdef HAVE_ESP_SUSPEND esp_delay(10); -#else - delay(10); -#endif } } From dd6ea93254a9497f72d2da5bf5786d680910256d Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Sat, 24 Jun 2023 20:13:46 +0200 Subject: [PATCH 38/39] Implement RAII-patterned token class for ForcedLightSleep begin()/end(). --- cores/esp8266/Esp.h | 30 +++++ .../ForcedLightSleep/ForcedLightSleep.ino | 107 ++++++++++-------- 2 files changed, 87 insertions(+), 50 deletions(-) diff --git a/cores/esp8266/Esp.h b/cores/esp8266/Esp.h index f32d077171..4d50f6eaa1 100644 --- a/cores/esp8266/Esp.h +++ b/cores/esp8266/Esp.h @@ -293,4 +293,34 @@ class EspClass { extern EspClass ESP; +/// RAII helper token class for forcedLightSleepBegin()/End(). +/// Cast to bool to check that forced light sleep can commence. +/// The forced light sleep is entered when the token goes out of scope. +/// Call cancel() to prevent sleeping. +class ESPForcedLightSleepToken { +private: + ESPForcedLightSleepToken() = delete; + ESPForcedLightSleepToken(const ESPForcedLightSleepToken&) = delete; + ESPForcedLightSleepToken& operator=(const ESPForcedLightSleepToken&) = delete; + + bool isArmed = false; + +public: + ESPForcedLightSleepToken(uint32_t duration_us, void (*wakeupCb)()) { + isArmed = ESP.forcedLightSleepBegin(10 * 1000 * 1000, wakeupCb); + } + ~ESPForcedLightSleepToken() { + if (isArmed) ESP.forcedLightSleepEnd(false); + } + operator bool() { + return isArmed; + } + void cancel() { + if (isArmed) { + ESP.forcedLightSleepEnd(true); + isArmed = false; + } + } +}; + #endif //ESP_H diff --git a/libraries/esp8266/examples/ForcedLightSleep/ForcedLightSleep.ino b/libraries/esp8266/examples/ForcedLightSleep/ForcedLightSleep.ino index a373fcff5d..f1f56628fa 100644 --- a/libraries/esp8266/examples/ForcedLightSleep/ForcedLightSleep.ino +++ b/libraries/esp8266/examples/ForcedLightSleep/ForcedLightSleep.ino @@ -26,70 +26,77 @@ // you can use any GPIO for WAKE_UP_PIN except for D0/GPIO16 as it doesn't support interrupts void IRAM_ATTR wakeupPinIsr() { - // For edge-triggered IRQ. - detachInterrupt(WAKE_UP_PIN); - schedule_function([]() { - Serial.println("GPIO went from HI to LO"); - }); + // For edge-triggered IRQ. + detachInterrupt(WAKE_UP_PIN); + schedule_function([]() { + Serial.println("GPIO went from HI to LO"); + }); } void IRAM_ATTR wakeupPinIsrWE() { - // Wakeup IRQs are available as level-triggered only. - detachInterrupt(WAKE_UP_PIN); - schedule_function([]() { - Serial.println("GPIO wakeup IRQ"); - }); - wakeupPinIsr(); - // reattach falling edge IRQ in loop + // Wakeup IRQs are available as level-triggered only. + detachInterrupt(WAKE_UP_PIN); + schedule_function([]() { + Serial.println("GPIO wakeup IRQ"); + }); + wakeupPinIsr(); + // reattach falling edge IRQ in loop } void wakeupCallback() { - schedule_function([]() { - Serial.println("wakeup callback was performed"); - }); - // return to falling edge IRQ, otherwise level-triggered IRQ with wakeup - // would get called unexpectedly while awake. - attachInterrupt(WAKE_UP_PIN, wakeupPinIsr, FALLING); + schedule_function([]() { + Serial.println("wakeup callback was performed"); + }); + // return to falling edge IRQ, otherwise level-triggered IRQ with wakeup + // would get called unexpectedly while awake. + attachInterrupt(WAKE_UP_PIN, wakeupPinIsr, FALLING); } void setup() { - Serial.begin(74880); - while (!Serial) - ; - delay(100); - pinMode(LED_BUILTIN, OUTPUT); // activity and status indicator - digitalWrite(LED_BUILTIN, LOW); // turn on the LED - pinMode(WAKE_UP_PIN, INPUT_PULLUP); // polled to advance tests, interrupt for Forced Light Sleep - attachInterrupt(WAKE_UP_PIN, wakeupPinIsr, FALLING); + Serial.begin(74880); + while (!Serial) + ; + delay(100); + pinMode(LED_BUILTIN, OUTPUT); // activity and status indicator + digitalWrite(LED_BUILTIN, LOW); // turn on the LED + pinMode(WAKE_UP_PIN, INPUT_PULLUP); // polled to advance tests, interrupt for Forced Light Sleep + attachInterrupt(WAKE_UP_PIN, wakeupPinIsr, FALLING); } using oneShotYieldMs = esp8266::polledTimeout::timeoutTemplate; oneShotYieldMs gotoSleep(2000); void loop() { - if (gotoSleep && ESP.forcedLightSleepBegin(10 * 1000 * 1000, wakeupCallback)) { - // No new timers, no delay(), between forcedLightSleepBegin() and forcedLightSleepEnd(). - // Only ONLOW_WE or ONHIGH_WE interrupts work, no edge, that's an SDK or CPU limitation. - // If the GPIO is in the state that will cause a wakeup on attaching the interrupt, - // it cannot trigger a wakeup later, but any sleep duration will be honored. - bool wakeupPinIsHigh = digitalRead(WAKE_UP_PIN); - delayMicroseconds(5000); - wakeupPinIsHigh &= digitalRead(WAKE_UP_PIN); - delayMicroseconds(5000); - wakeupPinIsHigh &= digitalRead(WAKE_UP_PIN); - // the GPIO might still bounce to LOW after this but before sleep is full engaged, - // disabling wakeup after all - if (wakeupPinIsHigh) { - attachInterrupt(WAKE_UP_PIN, wakeupPinIsrWE, ONLOW_WE); + if (gotoSleep) { + // No new timers, no delay(), while RAII ForcedLightSleepToken exists. + // Only ONLOW_WE or ONHIGH_WE interrupts work, no edge, that's an SDK or CPU limitation. + // If the GPIO is in the state that will cause a wakeup on attaching the interrupt, + // it cannot trigger a wakeup later, but any sleep duration will be honored. + bool wakeupPinIsHigh = digitalRead(WAKE_UP_PIN); + { + ESPForcedLightSleepToken token(10 * 1000 * 1000, wakeupCallback); + if (token) { // if true, run user code to set up forced light sleep details + // debouncing the wake up pin + delayMicroseconds(5000); + wakeupPinIsHigh &= digitalRead(WAKE_UP_PIN); + delayMicroseconds(5000); + wakeupPinIsHigh &= digitalRead(WAKE_UP_PIN); + // the GPIO might still bounce to LOW after this but before sleep is full engaged, + // disabling wakeup after all + if (wakeupPinIsHigh) { + attachInterrupt(WAKE_UP_PIN, wakeupPinIsrWE, ONLOW_WE); + } + digitalWrite(LED_BUILTIN, HIGH); // turn the LED off so they know the CPU isn't running + if (!wakeupPinIsHigh) token.cancel(); + } + // RAII token gets destructed, going to sleep if all went well + } + digitalWrite(LED_BUILTIN, LOW); // turn on the LED + // retry immediately if the GPIO was found not ready for entering sleep + if (wakeupPinIsHigh) { + gotoSleep.reset(); + } + // restore falling edge IRQ + attachInterrupt(WAKE_UP_PIN, wakeupPinIsr, FALLING); } - digitalWrite(LED_BUILTIN, HIGH); // turn the LED off so they know the CPU isn't running - ESP.forcedLightSleepEnd(!wakeupPinIsHigh); - digitalWrite(LED_BUILTIN, LOW); // turn on the LED - // retry immediately if the GPIO was found not ready for entering sleep - if (wakeupPinIsHigh) { - gotoSleep.reset(); - } - // restore falling edge IRQ - attachInterrupt(WAKE_UP_PIN, wakeupPinIsr, FALLING); - } } From 6252680e34f2281809adab20ee8abaa2d5dd82b2 Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Sat, 24 Jun 2023 20:26:45 +0200 Subject: [PATCH 39/39] Code style fixes. --- .../ForcedLightSleep/ForcedLightSleep.ino | 110 +++++++++--------- 1 file changed, 55 insertions(+), 55 deletions(-) diff --git a/libraries/esp8266/examples/ForcedLightSleep/ForcedLightSleep.ino b/libraries/esp8266/examples/ForcedLightSleep/ForcedLightSleep.ino index f1f56628fa..91a1e285f3 100644 --- a/libraries/esp8266/examples/ForcedLightSleep/ForcedLightSleep.ino +++ b/libraries/esp8266/examples/ForcedLightSleep/ForcedLightSleep.ino @@ -26,77 +26,77 @@ // you can use any GPIO for WAKE_UP_PIN except for D0/GPIO16 as it doesn't support interrupts void IRAM_ATTR wakeupPinIsr() { - // For edge-triggered IRQ. - detachInterrupt(WAKE_UP_PIN); - schedule_function([]() { - Serial.println("GPIO went from HI to LO"); - }); + // For edge-triggered IRQ. + detachInterrupt(WAKE_UP_PIN); + schedule_function([]() { + Serial.println("GPIO went from HI to LO"); + }); } void IRAM_ATTR wakeupPinIsrWE() { - // Wakeup IRQs are available as level-triggered only. - detachInterrupt(WAKE_UP_PIN); - schedule_function([]() { - Serial.println("GPIO wakeup IRQ"); - }); - wakeupPinIsr(); - // reattach falling edge IRQ in loop + // Wakeup IRQs are available as level-triggered only. + detachInterrupt(WAKE_UP_PIN); + schedule_function([]() { + Serial.println("GPIO wakeup IRQ"); + }); + wakeupPinIsr(); + // reattach falling edge IRQ in loop } void wakeupCallback() { - schedule_function([]() { - Serial.println("wakeup callback was performed"); - }); - // return to falling edge IRQ, otherwise level-triggered IRQ with wakeup - // would get called unexpectedly while awake. - attachInterrupt(WAKE_UP_PIN, wakeupPinIsr, FALLING); + schedule_function([]() { + Serial.println("wakeup callback was performed"); + }); + // return to falling edge IRQ, otherwise level-triggered IRQ with wakeup + // would get called unexpectedly while awake. + attachInterrupt(WAKE_UP_PIN, wakeupPinIsr, FALLING); } void setup() { - Serial.begin(74880); - while (!Serial) - ; - delay(100); - pinMode(LED_BUILTIN, OUTPUT); // activity and status indicator - digitalWrite(LED_BUILTIN, LOW); // turn on the LED - pinMode(WAKE_UP_PIN, INPUT_PULLUP); // polled to advance tests, interrupt for Forced Light Sleep - attachInterrupt(WAKE_UP_PIN, wakeupPinIsr, FALLING); + Serial.begin(74880); + while (!Serial) + ; + delay(100); + pinMode(LED_BUILTIN, OUTPUT); // activity and status indicator + digitalWrite(LED_BUILTIN, LOW); // turn on the LED + pinMode(WAKE_UP_PIN, INPUT_PULLUP); // polled to advance tests, interrupt for Forced Light Sleep + attachInterrupt(WAKE_UP_PIN, wakeupPinIsr, FALLING); } using oneShotYieldMs = esp8266::polledTimeout::timeoutTemplate; oneShotYieldMs gotoSleep(2000); void loop() { - if (gotoSleep) { - // No new timers, no delay(), while RAII ForcedLightSleepToken exists. - // Only ONLOW_WE or ONHIGH_WE interrupts work, no edge, that's an SDK or CPU limitation. - // If the GPIO is in the state that will cause a wakeup on attaching the interrupt, - // it cannot trigger a wakeup later, but any sleep duration will be honored. - bool wakeupPinIsHigh = digitalRead(WAKE_UP_PIN); - { - ESPForcedLightSleepToken token(10 * 1000 * 1000, wakeupCallback); - if (token) { // if true, run user code to set up forced light sleep details - // debouncing the wake up pin - delayMicroseconds(5000); - wakeupPinIsHigh &= digitalRead(WAKE_UP_PIN); - delayMicroseconds(5000); - wakeupPinIsHigh &= digitalRead(WAKE_UP_PIN); - // the GPIO might still bounce to LOW after this but before sleep is full engaged, - // disabling wakeup after all - if (wakeupPinIsHigh) { - attachInterrupt(WAKE_UP_PIN, wakeupPinIsrWE, ONLOW_WE); - } - digitalWrite(LED_BUILTIN, HIGH); // turn the LED off so they know the CPU isn't running - if (!wakeupPinIsHigh) token.cancel(); - } - // RAII token gets destructed, going to sleep if all went well - } - digitalWrite(LED_BUILTIN, LOW); // turn on the LED - // retry immediately if the GPIO was found not ready for entering sleep + if (gotoSleep) { + // No new timers, no delay(), while RAII ForcedLightSleepToken exists. + // Only ONLOW_WE or ONHIGH_WE interrupts work, no edge, that's an SDK or CPU limitation. + // If the GPIO is in the state that will cause a wakeup on attaching the interrupt, + // it cannot trigger a wakeup later, but any sleep duration will be honored. + bool wakeupPinIsHigh = digitalRead(WAKE_UP_PIN); + { + ESPForcedLightSleepToken token(10 * 1000 * 1000, wakeupCallback); + if (token) { // if true, run user code to set up forced light sleep details + // debouncing the wake up pin + delayMicroseconds(5000); + wakeupPinIsHigh &= digitalRead(WAKE_UP_PIN); + delayMicroseconds(5000); + wakeupPinIsHigh &= digitalRead(WAKE_UP_PIN); + // the GPIO might still bounce to LOW after this but before sleep is full engaged, + // disabling wakeup after all if (wakeupPinIsHigh) { - gotoSleep.reset(); + attachInterrupt(WAKE_UP_PIN, wakeupPinIsrWE, ONLOW_WE); } - // restore falling edge IRQ - attachInterrupt(WAKE_UP_PIN, wakeupPinIsr, FALLING); + digitalWrite(LED_BUILTIN, HIGH); // turn the LED off so they know the CPU isn't running + if (!wakeupPinIsHigh) token.cancel(); + } + // RAII token gets destructed, going to sleep if all went well } + digitalWrite(LED_BUILTIN, LOW); // turn on the LED + // retry immediately if the GPIO was found not ready for entering sleep + if (wakeupPinIsHigh) { + gotoSleep.reset(); + } + // restore falling edge IRQ + attachInterrupt(WAKE_UP_PIN, wakeupPinIsr, FALLING); + } }