-
Notifications
You must be signed in to change notification settings - Fork 13.3k
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
Extending on the new WiFi-less startup and new power saving APIs in ESP class #7979
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.
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.
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.