- 
                Notifications
    You must be signed in to change notification settings 
- Fork 13.3k
Extending on the new WiFi-less startup and new power saving APIs in ESP class #7979
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
810fed9
              a1ceef3
              9538ef9
              1ecdfc1
              ef2bc95
              9891613
              ea94626
              7137d0f
              7fea539
              446aea7
              6a07829
              49c2ac0
              e7864f1
              79a7ee8
              788731e
              433cd17
              24efea2
              83e70ee
              c0326f0
              7c4879f
              01eb942
              382f6fd
              213a2e0
              56c40f3
              affc7fc
              6015cec
              b6e14f6
              ad454ea
              74951fd
              0a70272
              df7e909
              1af1380
              a9d9cda
              e8798ac
              3fc7d23
              abd17a7
              a6d1f73
              5a2c2db
              2d1cb1c
              9847e1f
              b8a95de
              dd6ea93
              6252680
              cd6d155
              File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
|  | @@ -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<int>(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<int>(mode)); | ||
| system_deep_sleep_set_option(__get_rf_mode()); | ||
| system_deep_sleep_instant(time_us); | ||
| esp_suspend(); | ||
| } | ||
|  | @@ -138,6 +138,174 @@ uint64_t EspClass::deepSleepMax() | |
|  | ||
| } | ||
|  | ||
| extern os_timer_t* timer_list; | ||
| namespace { | ||
| 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(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_set_opmode(NULL_MODE); | ||
| 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 | ||
| esp_delay(10); | ||
| 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(); | ||
| } | ||
| wifi_fpm_set_sleep_type(saved_sleep_type); | ||
| saved_sleep_type = NONE_SLEEP_T; | ||
| } | ||
| 
      Comment on lines
    
      +174
     to 
      +183
    
   There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Combine with begin() as a single operation? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Any calls to the SDK or modifying sleep modes by sidelining this API is undefined behavior. Simple as that. | ||
|  | ||
| #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 | ||
| if (!duration_us || duration_us > 0xFFFFFFF) { | ||
| duration_us = 0xFFFFFFF; | ||
| } | ||
| 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; | ||
| wifi_fpm_set_wakeup_cb([]() { | ||
|         
                  mcspr marked this conversation as resolved.
              Show resolved
            Hide resolved | ||
| if (saved_wakeupCb) { | ||
| saved_wakeupCb(); | ||
| saved_wakeupCb = nullptr; | ||
| } | ||
| esp_schedule(); | ||
| }); | ||
| #ifdef DEBUG_SERIAL | ||
| walk_timer_list(); | ||
| #endif | ||
| { | ||
| esp8266::InterruptLock lock; | ||
| saved_timer_list = timer_list; | ||
|         
                  devyte marked this conversation as resolved.
              Outdated
          
            Show resolved
            Hide resolved There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. -//- as modem - if we hide timer list, it should not be possible for user to overwrite this accidentally. Single operation instead of separate begin() -> end()? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This way, it worked/works, other ways, it didn't work/does work, i.e. doesn't enter the power saving modes properly. It was all carefully measured by the other guy at the time. Whatever happened to him :-( 
 I don't understand that sentence, I am sorry. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 
 Sadly, yeah, might be difficult to test at this point from our baseline of knowledge :/ 
 Something like InterruptLock. Imagine FpmThingy class that enables sleep on begin or construction, and disables on destruction. It may or may not be applicable to our internal usage, but seems like a better API for user side of things. Plus, it would not be necessary to expose too much methods to control it There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @mcspr I have added an RAII class and modified  There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Repeating the q from above - what is the difference between GPIO-activated sleep and timed one here? Is CPU really sleeping or we just simulate that it does? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OMG, you are asking questions that I cannot remember the answer to, except that I am 100% certain that we literally did hours of repetitive tests to show that power consumption dropped to the expected levels, and it didn't or the MCU became unreliable or the tables got corrupted if it wasn't done in exactly this - documented by Espressif - way. We really should not change a single line. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Right, but now we have a half-understood feature that maybe works? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure we are not talking past each other. The sleep isn't actived by the GPIO, but cancelled. Same for the timer. I'm sure that's what you meant. As far as it got measure two years ago, yes we were sure it did as advertised - sleeping to save power, resuming either by GPIO trigger - push of a button - or timeout. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @mcspr Is this now stalled again due to to unrelated workload, or have my changes and explanations been unconvincing so far? I would be totally relieved if this finally got merged, I am sure it will help people a lot, at least it might allow more specific reports about power-saving mode issues? | ||
| timer_list = nullptr; | ||
| } | ||
| return wifi_fpm_do_sleep(duration_us) == 0; | ||
| } | ||
|  | ||
| void EspClass::forcedLightSleepEnd(bool cancel) | ||
| { | ||
| if (!cancel) { | ||
| // SDK turns on forced light sleep in idle task | ||
| esp_suspend(); | ||
| } | ||
| #ifdef DEBUG_SERIAL | ||
| walk_timer_list(); | ||
| #endif | ||
| { | ||
| 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 | ||
| esp_delay(10); | ||
| } | ||
| } | ||
|  | ||
| void EspClass::autoModemSleep() { | ||
| wifi_fpm_close(); | ||
| saved_sleep_type = wifi_get_sleep_type(); | ||
| wifi_set_sleep_type(MODEM_SLEEP_T); | ||
| } | ||
|  | ||
| void EspClass::autoLightSleep() { | ||
| wifi_fpm_close(); | ||
| saved_sleep_type = wifi_get_sleep_type(); | ||
| wifi_set_sleep_type(LIGHT_SLEEP_T); | ||
| } | ||
|  | ||
| void EspClass::autoSleepOff() { | ||
| wifi_set_sleep_type(saved_sleep_type); | ||
| saved_sleep_type = NONE_SLEEP_T; | ||
| } | ||
|  | ||
|         
                  dok-net marked this conversation as resolved.
              Outdated
          
            Show resolved
            Hide resolved | ||
| 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) | ||
|  | @@ -531,7 +699,7 @@ 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): | ||
|  | @@ -787,7 +955,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<uint32_t *>(data), size) == SPI_FLASH_RESULT_OK; | ||
| }; | ||
|  | @@ -857,7 +1025,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) { | ||
|  | @@ -871,7 +1039,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) | ||
|  | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, perhaps, not necessary to expose to user? Isn't return signal enough of a result?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If there's no hurt in exposing it as an option, let's keep it this way, please.