Closed nicoHarel closed 3 years ago
@gschorcht I guess you've seen this. :wink: Can /should we update ESP-IDF?
It seems the ESP-IDF version used by RIOT OS is too old and still uses the ancient C way of defining a function without arguments (no void inside the parenthesis).
That can probably be worked around by adding -Wno-old-style-definition
to CFLAGS.
You mentioned two problems:
Let me give some explanations to both problems.
1. Outdated version of the ESP-IDF.
When I started with porting RIOT for the ESP32, ESP-IDF version 3.0 beta was available, the version that is still in use. Although I had an update to version 3.1 or later on my ToDo list, I haven't done for the following reasons:
cpu/esp3
'.
The biggest problem with the ESP-IDF is that it can't be used as a standalone SDK as with other platforms for the following reasons:
Therefore, the only way to port RIOT for the ESP32 was to pick only that parts from ESP-IDF that are absolutely necessary to get RIOT running on ESP32. These are the system clock handling and SoC startup.
BTW, updating to the newest offical ESP-IDF release v4.0 wouldn't help to solve the problem with old style function definitions. It is still a problem in release v4.0, fore example here.
That was the reason why we couldn't use all header files directly from ESP-IDF but had to copy some to the vendor part of the code only to be able to fix these old style function declarations.
For all these reasons I haven't yet started an update. And to be honest, I will not start an update until ESP-IDF allows the use of newlib-nano.
2. Deep-sleep mode
I'm pretty sure that deep-sleep mode was already working with this old ESP-IDF version. But I didn't implement it since I'm not sure whether the deep-sleep mode is compatible with the concept of power management in RIOT.
The deep-sleep mode requires a warm-restart of the CPU to leave this mode so that RIOT will restart. That is, RIOT's system state and all data in kernel, drivers, ... are lost. Data that have to be preserved would have to be stored in RTC memory by the application.
The only sleep mode that would allow to preserve RIOT's system state is the light-sleep mode. But even if the CPU then consumes only 800 uA, together with other board components a power consumption of less than 1 mA would not be feasible.
Thanks for the detailed explanation! I agree on everything.
The only sleep mode that would allow to preserve RIOT's system state is the light-sleep mode. But even if the CPU then consumes only 800 uA, together with other board components a power consumption of less than 1 mA would not be feasible.
Reading this, I thought "1mA is 30 times lower than 30mA".
While reading the tests/periph_pm/README.md
, I wondered if it would be compliant with RIOT's power management to enter a power mode that requires a system reboot to exit the mode?
Ok thanks a lot for the clarifications. I was wondering why some of the ESP-IDF files were actually inside the riot project. In fact I started to copy some of those files (esp_timer_impl.h, ...) inside the Riot ESP folder myself.
I was afraid that updating the ESP-IDF would also mean reworking the cpu implementation of the ESP.
Updating the ESP-IDF seems out of the question then.
It should be working yes with the older version, except for the GCC compiler error about the old style function definition. But as kaspar030 mentioned, adding CFLAG options should be okay as a work around.
I understand what you're implying about the loss of data when going into deep mode. And what it means for RIOT, although I don't think that would be a problem for my application, I can imagine how that would be problematic as this "deep sleep behaviour" would be different from other MCU implemented ( yes ? ).
Light sleep might be okayish for me I guess but not great of course.
Yes there is a mention about resetting the CPU there. Although I don't think this has got any real value ?
If I'm being honest I was trying to add RIOT support for the lopy4 from Pycom. Because its got everything I need (Lora Chip + mesh + big flash + cheap...). But if using an ESP has too many drawbacks I might just as well use some other board (with an NRF52 MCU for example) and some Lora chip on the side.
Reading this, I thought "1mA is 30 times lower than 30mA".
Generally anything more than about 10uA is not considered "low-power" anymore. Even 100uA will break a great many applications.
While reading the
tests/periph_pm/README.md
, I wondered if it would be compliant with RIOT's power management to enter a power mode that requires a system reboot to exit the mode?
It would not be consistent with the intended functionality of pm_layered
to do that. Riot expects to return from pm_set()
with full ram retention and doing it any other way doesn't make sense given the context of what the modern mcus are able to do. Technically it's compliant in that pm_layered
only deals with how power states are entered, not left, but normally only the lowest power state requires a reset to wake from and normally this is only necessary if you need to achieve better than 0.5uA.
Anyway, I'm ~really~ hopefully? sure that it shouldn't be necessary to do that even for esp32. The defining difference between whether the system reboot is necessary is whether the mcu can retain ram at a low-power state. Any modern low-power mcu can retain ram for less than 1uA so the only time we should need to deal with a full reset is with mcus that don't have good low-power support.
Any modern low-power mcu can retain ram for less than 1uA so the only time we should need to deal with a full reset is with mcus that don't have good low-power support.
Ok, then ESP32 has a very poor low-power support:
This means that even the mode with the lowest power consumption needs 4.5 uA and requires a reset.
- Deep-Sleep mode at 6.5 uA and RTC memory can be retained
I'm suffering from not being familiar with esp32 but if it can resume from this state with good (aka most) ram retention then this is completely adequate. However if it's true that this ~5uA state only retains RTC ram then yes this means unfortunately that esp32 has very poor low-power support if it requires 800uA just to retain ram. That's an unmitigated disaster.
This has probably prompted me to investigate esp32 low-power support in the near future.
However if it's true that this ~5uA state only retains RTC ram then yes this means unfortunately that esp32 has very poor low-power support if it requires 800uA just to retain ram.
I can confirm only the ligh-sleep mode allows to retain the RAM content therefore needing at least 800µA to do so.
Edit: Wouldn't it be possible to consider one of the deep-sleep mode (6.5µA for example) as the lowest power state in pm_layered in RIOT and the light-sleep mode (800µA) as a "higher" deep-sleep mode ? I know it's not clean, but it's at least kind of a "power mode" support.
- Deep-Sleep mode at 6.5 uA and RTC memory can be retained
I'm suffering from not being familiar with esp32 but if it can resume from this state with good (aka most) ram retention then this is completely adequate. However if it's true that this ~5uA state only retains RTC ram then yes this means unfortunately that esp32 has very poor low-power support if it requires 800uA just to retain ram. That's an unmitigated disaster.
This has probably prompted me to investigate esp32 low-power support in the near future.
Deep-Sleep mode only retains RTC memory, the so called digital core including the SRAM is powered down in this mode. Detailed information can be found in ESP32 Technical Reference Manual, page 646.
I can confirm only the ligh-sleep mode allows to retain the RAM content therefore needing at least 800µA to do so.
That is horribly unfortunate. It reminds me of mcus from 8+ years ago. Although since esp32 is primarily a wifi chip, and wifi is not low-power, perhaps it shouldn't be surprising.
However, it's not incompatible with pm_layered
and if you can sustain 800uA then you could still use pm_layered
to control when this sleep state is entered. For that matter, you can also use pm_layered
to enter the lower states too, just with the understanding that the next WFI will reset the mcu.
Wouldn't it be possible to consider one of the deep-sleep mode (6.5µA for example) as the lowest power state in pm_layered in RIOT and the light-sleep mode (800µA) as a "higher" deep-sleep mode ? I know it's not clean, but it's at least kind of a "power mode" support.
Yes that's what I'd recommend doing. The difference between 6.5uA and 4.5uA won't be noticed by any practical application.
OK, I will have a look what is needed to implement these both power modes and what their impact on the platform code would be.
One further remark. There is a PR #12955 with extensive code deduplication, which has a major impact on the code in cpu/esp32
. I would prefer to merge this PR before making further changes in cpu/esp32
. Otherwise, a subsequent merge could become difficult.
Just for information, I have small test with deep sleep and RTC timer wakeup already running. At the moment, I'm thinking about how to use it as platform implementation for pm_layered
. ESP32 can also be woken up by several GPIOs. [Update] Deep sleep works also with GPIOs. As next step, I will see how to get light sleep working.
@gschorcht I have to agree. I currently port a library to the ESP32 for Arduino and it is the most annoying platform I ever worked with. I cannot recommend anybody to work with this platform. For prototyping it is kind of fine, but I wouldn't use it for real life products.
@nicoHarel If you need to use Lora, I would use the STM32F1/STM32F4 and a Lora module. Or maybe the STM32L series is more interesting for you. Depending on the requirements. It is not that complicated to create your own board. There are several ready to use modules out there. So, you basically just need to connect the STM32 module and Lora modul on one SBC together. But if the range of BLE is good enough, you can use the nRF52832/nRF52840 for your use-case. This would be even cheaper, since the nrf52 already has the radio on the IC.
@nicoHarel If you need to use Lora, I would use the STM32F1/STM32F4 and a Lora module. Or maybe the STM32L series is more interesting for you. Depending on the requirements. It is not that complicated to create your own board. There are several ready to use modules out there. So, you basically just need to connect the STM32 module and Lora modul on one SBC together. But if the range of BLE is good enough, you can use the nRF52832/nRF52840 for your use-case. This would be even cheaper, since the nrf52 already has the radio on the IC.
Yes, I'm trying to do something with a particle board (NRF52X). But by the looks of it, pm_layered isn't implemented either for those MCUs. The only function available is pm_off() which puts the MCU in "SYSTEM_OFF" mode, which is perfectly fine except I can't, for the life of me, wake up the NRF52840 after that (I tried to do so with a GPIO interrupt which normally should do the trick ?).
Yes, I'm trying to do something with a particle board (NRF52X). But by the looks of it, pm_layered isn't implemented either for those MCUs.
The NRF are special and do most of pm_layered automatically. This is not as optimized as it could be, but usable. Someone stated the particle is using ~500uA in idle, with examples/hello-world
. Enabling the internal DC-DC would make it go down another 100-150uA. It should be far below 100uA, but that's missing the mentioned optimization.
PR #13416 fixes the issue for ESP32 MCU. It implements the light-sleep as well as the deep-sleep mode. Since CPU including peripherals are stalled in these modes and can't be woken up by internal interrupts such as timers they can't be used as idle power mode. Therefore, they are blocked and have to be set explicitly using pm_set
.
But putting ESP32 into the light-sleep or deep-sleep mode is only one part of the problem. All ESP32 boards I know usually have a base load of more than 10 mA. You can measure this base load when you press the RESET
button. Pressing the RESET
button does nothing else than pulling down the CHIP_EN
pin so that ESP32 is powered off completely. The reason are the components around the ESP32 like the USB2UART bridge or the voltage regulator.
There are some battery powered ESP32 boards. Maybe the base load for such board is much better.
The NRF are special and do most of pm_layered automatically. This is not as optimized as it could be, but usable. Someone stated the particle is using ~500uA in idle, with
examples/hello-world
. Enabling the internal DC-DC would make it go down another 100-150uA. It should be far below 100uA, but that's missing the mentioned optimization.
That's what I also get here (~600µA in idle) with the hello-world project. With more "heavy" projects (gnrc, spi...) I get around 1.7mA in Idle. Not great. As you mentioned it should be way lower than this. People are measuring around 50µA with the System.sleep() function (not the deep sleep one) from Particle .
I've actually managed to wake up the NRF from SYSTEMOFF with a GPIO interrupt by setting the "sense" register of the pin myself. But it's pointless. The MCU reboots while waking up (meaning that, so does RIOT). So the current implementation is correct. It makes more sense to reset the board with the reset pin in that case...
PR #13416 fixes the issue for ESP32 MCU. It implements the light-sleep as well as the deep-sleep mode. Since CPU including peripherals are stalled in these modes and can't be woken up by internal interrupts such as timers they can't be used as idle power mode. Therefore, they are blocked and have to be set explicitly using
pm_set
.
Awesome :+1: thank you for your work ! Does that mean it can't be a awoken by a GPIO interrupt either ?
But putting ESP32 into the light-sleep or deep-sleep mode is only one part of the problem. All ESP32 boards I know usually have a base load of more than 10 mA. You can measure this base load when you press the
RESET
button. Pressing theRESET
button does nothing else than pulling down theCHIP_EN
pin so that ESP32 is powered off completely. The reason are the components around the ESP32 like the USB2UART bridge or the voltage regulator.There are some battery powered ESP32 boards. Maybe the base load for such board is much better.
Yes. I've noticed that too. The lopy4 is one of the battery powered board though. Every "non-essential" components are on a "programming shield". So that's fine with me.
Awesome thank you for your work ! Does that mean it can't be a awoken by a GPIO interrupt either ?
Yes and no. No, because GPIOs or better RTC GPIOs[*] can be used as wake-up source. Yes, because it's not a GPIO interrupt in the strict sense, where you could attach an ISR which is executed as soon as the MCU is woken up. Rather, you just define a list of GPIOs that can wake up the MCU when either all of them go LOW or one of them goes HIGH.
The GPIOs and the level that are used for the wake-up logic are defined by the environment variables ESP_PM_WUP_PINS
and ESP_PM_WUP_LEVEL
.
[*] RTC GPIOs are the GPIOs that are realized by the RTC module. In fact, these are all GPIOs that might be also used as ADC channels.
Yes. I've noticed that too. The lopy4 is one of the battery powered board though. Every "non-essential" components are on a "programming shield". So that's fine with me.
The board seems to be quite interesting. Do you have this board? If yes, may I ask you to measure the base load?
Yes. I've noticed that too. The lopy4 is one of the battery powered board though. Every "non-essential" components are on a "programming shield". So that's fine with me.
The board seems to be quite interesting. Do you have this board? If yes, may I ask you to measure the base load?
No problem, but what do you mean base load exactly ? With, for example, the hello-world project from RIOT OS ? If that is what you're looking for: 23mA with a LiPO batt (+3.8V) and the ESP32 frequency at 80MHz.
I know it seems like a lot, but compared to other ESP boards, it's actually quite low.
No problem, but what do you mean base load exactly ? With, for example, the hello-world project from RIOT OS ? If that is what you're looking for: 23mA with a LiPO batt (+3.8V) and the ESP32 frequency at 80MHz.
No, by base load I meant the current consumption when the ESP32 is not operating or disabled at all. That means the current consumption while you press the RESET
button.
When you press the RESET
button, ESP32's CHIP_PU
pin is pulled down and the current consumption of the EPS32 drops down to 0.1uA. The current consumption reduced by the current that flows over the pull-up resistor at the RESET
button should give the current consumption of the board itself (the base load).
When you press the
RESET
button, ESP32'sCHIP_PU
pin is pulled down and the current consumption of the EPS32 drops down to 0.1uA. The current consumption reduced by the current that flows over the pull-up resistor at theRESET
button should give the current consumption of the board itself (the base load).
Ok. I measure 0.1mA-0.2mA overall on the board. My current meter is a bit rubbish (can't measure anything lower than 0.1mA)... So maybe about 0.150mA, maybe lower, I don't know, it's hard to say. But it doesn't look like it's high at all.
Ok. I measure 0.1mA-0.2mA overall on the board. My current meter is a bit rubbish (can't measure anything lower than 0.1mA)... So maybe about 0.150mA, I don't know, it's hard to say. But it doesn't look like it's high at all.
That sounds really great. All boards I have here consume at least 11 mA.
May I ask you to try PR #13416 to see if you measure about the same in deep sleep and not more than 1 mA in light sleep mode? It would be really interesting whether it would work for you.
Ok. I measure 0.1mA-0.2mA overall on the board. My current meter is a bit rubbish (can't measure anything lower than 0.1mA)... So maybe about 0.150mA, I don't know, it's hard to say. But it doesn't look like it's high at all.
That sounds really great. All boards I have here consume at least 11 mA.
May I ask you to try PR #13416 to see if you measure about the same in deep sleep and not more than 1 mA in light sleep mode? It would be really interesting whether it would work for you.
No problem. I will try it this afternoon. I'm also very interested in the results anyway. The merge seems to blocked on your PR, but RIOT will still build, right ?
No problem. I will try it this afternoon. I'm also very interested in the results anyway. The merge seems to blocked on your PR, but RIOT will still build, right ?
Thanks. The merge is blocked because the PR first requires a review. RIOT will still build.
Ok, I've got a weird behaviour with your PR but maybe it's me or my board, I don't know.
I've tried a simple program (based on hello-world):
#include <stdio.h>
#include "periph/pm.h"
#include "xtimer.h"
int main(void)
{
puts("hello World!");
printf("You are running RIOT on a(n) %s board.\n", RIOT_BOARD);
printf("This board features a(n) %s MCU.\n", RIOT_MCU);
xtimer_sleep(5);
pm_set(ESP_PM_DEEP_SLEEP);
xtimer_sleep(5);
while(1);
return 0;
}
And this gives me the following output (with DEBUG flags turned on in the ESP32's and pm_layered's pm):
2020-02-24 15:21:21,076 # main(): This is RIOT! (Version: 2020.04-devel-661-g02ad1e-cpu/esp32/pm_layered)
2020-02-24 15:21:21,076 # hello World!
2020-02-24 15:21:21,084 # You are running RIOT on a(n) esp32-lopy4 board.
2020-02-24 15:21:21,085 # This board features a(n) esp32 MCU.
2020-02-24 15:21:21,086 # pm: setting mode 2
2020-02-24 15:21:21,088 # pm_set_lowest_normal enter.................. to normal sleep @8498
2020-02-24 15:21:23,077 # pm_set_lowest_normal exit from normal sleep @2006928
2020-02-24 15:21:23,078 # pm: setting mode 2
2020-02-24 15:21:23,084 # pm_set_lowest_normal enter.................. to normal sleep @2007035
2020-02-24 15:21:25,097 # pm_set_lowest_normal exit from normal sleep @4006933
2020-02-24 15:21:25,098 # pm: setting mode 2
2020-02-24 15:21:25,099 # pm_set_lowest_normal enter.................. to normal sleep @4007036
So I see all the "automatic" calls to "pm_set_lowest_normal" from pm_layered. But what I don't see, is the call to pm_set in the log. The program gets stuck here. Even if I change "ESP_PM_DEEP_SLEEP" with "ESP_PM_LIGHT_SLEEP".
Am I missing something ? Is my "pm_set()" calling incorrect ? pm_set() calls the function definition inside ESP32 pm module right ?
Edit: nevermind ! A simple delay fixes this. Turns out the cpu enter its sleep mode when calling "pm_set" before printing anything on the terminal. I'm measuring the currents right now.
Ok, the measurements: IDLE: 24mA ESP_PM_LIGHT_SLEEP: 4.5mA, a bit more than we anticipated I think. Something must still be on somewhere (board design maybe ?) ESP_PM_DEEP_SLEEP: can't measure anything more than the board (only displaying 0.1mA), pretty good !
The NRF are special and do most of pm_layered automatically. This is not as optimized as it could be, but usable. Someone stated the particle is using ~500uA in idle, with
examples/hello-world
. Enabling the internal DC-DC would make it go down another 100-150uA. It should be far below 100uA, but that's missing the mentioned optimization.
If you want to optimize in these areas, it probably makes more sense to have an external RTC with alarms. It should be possible to reach the 20uA. Maybe there are even more efficient RTCs, I don't know.
If you want to optimize in these areas, it probably makes more sense to have an external RTC with alarms. It should be possible to reach the 20uA. Maybe there are even more efficient RTCs, I don't know.
In my case the MCU will be woken up by an external LORA chip so no need for for an external RTC. But this means that, after all, the NRF52X has the same behaviour as the ESP32 (it needs a reset to exit deep sleep). The only difference being that I can choose the "wake up" pin on the ESP 32 with the PR #13416 which is not the case with the NRFs.
I don't know, I haven't made my mind yet. The NRF boards seems to be less power hungry. But the lopy 4 has an embedded SX1276 LORA chips (there's a RIOT OS driver for that one).
Ok @gschorcht I've got a question. You made the following statement:
PR #13416 fixes the issue for ESP32 MCU. It implements the light-sleep as well as the deep-sleep mode. Since CPU including peripherals are stalled in these modes and can't be woken up by internal interrupts such as timers they can't be used as idle power mode.
Now I have trouble understanding why is that. The ESP32 has an internal RTC which seems to remain active no matter what sleep modes the MCU enters. See this link (just an example): https://lastminuteengineers.com/esp32-sleep-modes-power-consumption/
I've also seen a function available inside the esp32 vendor folder (esp32/vendor/esp-idf/esp32/sleep_modes.c): "esp_deep_sleep(uint64_t time_in_us)" This functions calls "esp_deep_sleep_start()" (the one you call in pm.c I think) just after setting up a wake up timer.
I'm pretty sure that would mean it is possible to wake up from timer. Am i forgetting something ?
Have a good day.
You must not mix up timers and the RTC timer. Timers (module periph_timer) are part of the peripherals that are only active if the digital core is active (active or modem sleep). The RTC timer (module periph_rtc) is part of the RTC module which is also active in light or deep sleep and in hinbernation mode.
So the answer is: It is not possible to wake up the ESP32 by a peripheral timer (module periph_timer), but it is possible to wake it up by the RTC timer (module periph_rtc).
I know it is a bit more complicated to first set an RTC alarm with rtc_set_alarm
and then call pm_set
than to simply use esp_deep_sleep(10000)
. But this is the API used in RIOT.
Just for information: It's a bit confusing for ESP32 that the timer in the RTC module is called RTC timer. In fact, it's rather a RTT than a RTC. At the moment, the periph_rtc
module is an emulated RTC based on that RTT. However, I will soon provide an RTT implementation periph_rtt
soon so that the wake up time need not be set as an RTC alarm, but can be set as a simple counter offset
Alright, it seems that I misunderstood your original statement and thought you were talking about the RTC timers.
I know it is a bit more complicated to first set an RTC alarm with rtc_set_alarm and then call pm_set than to simply use esp_deep_sleep(10000). But this is the API used in RIOT.
I have absolutely no problem doing that. I just didn't know it was possible as I wasn't aware of the RTC module existence. Thanks for the tip.
Just for information: It's a bit confusing for ESP32 that the timer in the RTC module is called RTC timer. In fact, it's rather a RTT than a RTC
Agreed.
I have seen that the PR related to this issue has been merged, many thanks to everyone.
Also I thought I'd had bit more precision to the measurements I posted about the LOPY4 as @gschorcht seemed interested. With a lot more accurate current meter, a 3.9V charged LiPo battery, and nothing else except a capacitor in parallel with the battery to allow changing the caliber on the current meter:
Sleep modes | Current |
---|---|
IDLE | 27.4mA |
Light sleep | 4.21mA |
Deep sleep | 19.8µA |
So, it's still weirdly high for the light sleep. This doesn't really bother but i thought I'd share the results.
Try to force shut down on all peripherals (WiFi, Bluetooth, RTC RAM, RTC pins...), maybe something is running
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. If you want me to ignore this issue, please mark it with the "State: don't stale" label. Thank you for your contributions.
Description
I've noticed RIOT OS is not yet compatible with deep sleep modes on ESP32 and more generally on all ESPs. Unless I'm wrong, the only "energy saving" mechanism available consists in reducing the MCU frequency to 80MHz (which is the default clock on RIOT OS if I'm not mistaken).
At that frequency, most ESP boards current consumption is still around at least 30mA, which is unacceptable for most low energy applications. This is a decisive flaw for me as I need my ESP to draw less than a mA most of the time.
I've tried in a hurry to implement it myself, adding the right files (sleep_modes.c...) containing the functions definition needed (esp_deep_sleep_start...) but without success. It seems the ESP-IDF version used by RIOT OS is too old and still uses the ancient C way of defining a function without arguments (no void inside the parenthesis). GCC doesn't like that any more.
So the ESP-IDF need updating but doing so would have repercussions on the ESPs implementation in RIOT OS.
Proposition
Evaluate the impact of updating to a more recent version of the ESP-IDF.
If it doesn't seem to break absolutely everything about the ESP implementation, change the ESP-IDF tagged version in a branch and work from there.
Then implement the deep sleep functions inside RIOT OS.