esphome / feature-requests

ESPHome Feature Request Tracker
https://esphome.io/
418 stars 26 forks source link

Support for Light sleep mode #141

Open ghvader opened 5 years ago

ghvader commented 5 years ago

Describe the problem you have/What new integration you would like

Support for ESP32 light sleep mode

Please describe your use case for this integration and alternatives you've tried:

ESPhome only supports deep sleep mode. i'm building battery powered sensors that are awoke by the use of PIR, waking from deep sleep activate the reset function and the whole board reboots upon motion detection. support for light sleep or alternative sleep modes that permit this behavior is requested. (the board should wake up, fast connect to wifi send the "motion detected" signal and not have to reboot in order to do so.)

Additional context

https://lastminuteengineers.com/esp32-sleep-modes-power-consumption/ https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/system/sleep_modes.html

OttoWinter commented 5 years ago

The highest contributor to power usage is by far the WiFi interface, AFAIK light sleep current is dwarfed by wifi. Have you looked into the wifi power save mode?

As light sleep would also require many core changes, I don't think this will be supported. However, esp-idf might support an automatic version of it at some point - so that might be an option once it ships. https://github.com/espressif/arduino-esp32/pull/1165#issuecomment-376717287

ghvader commented 5 years ago

@OttoWinter forgive me as i'm not a C programmer, so i may be misunderstanding, but if esp_deep_sleep_start() calls the deep sleep function and esp_light_sleep_start() enables light sleep, would that not be trivial to implement? "esp_light_sleep_start() function can be used to enter light sleep once wake-up sources are configured."

according to that 1st link, wifi power saving mode still use 3 - 20 mA while light sleep mode uses 0.8 mA in light sleep mode, i don't believe an entire board reset would be required upon receiving a signal from a configured wake up pin. appreciate you taking the time to look at my request either way.

OttoWinter commented 5 years ago

AFAIK, light sleep is not designed for what you want it to do: extended sleeps, it's designed for short sleeps of a few milliseconds.

I also don't understand why deep sleep wouldn't work for you. Have you tried setting static IP + fast_connect mode already? Connection time is pretty darn fast then.

ghvader commented 5 years ago

@OttoWinter unless i have something misconfigured. device goes into deep sleep mode and then when motion is detected, it wakes it up. the problem is, it doesn't wake it up. it does a full reset/reboot, even connecting fast is not sufficient, because you miss the initial motion. so you walk into a room, motion is detected, then the light turns on (is what is supposed to happen) instead the esp32 is rebooting from deep sleep mode, re-connecting and you have to stand in the dark moving around again, until it picks up your motion a 2nd time to then turn on the light.


19:27:57.320 -> [C][deep_sleep:034]:   Wakeup Pin: GPIO15 (Mode: INPUT)
19:27:58.984 -> [D][api:546]: Client 'Home Assistant 0.90.1 (192.168.x.xx)' connected successfully!
19:28:05.284 -> [I][deep_sleep:072]: Beginning Deep Sleep
19:28:10.618 -> ets Jun  8 2016 00:22:57
19:28:10.618 ->   NOTE::: motion is detected here - device reboots::::::
19:28:10.618 -> rst:0x5 (DEEPSLEEP_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
19:28:10.618 -> configsip: 0, SPIWP:0xee
19:28:10.618 -> clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
19:28:10.651 -> mode:DOUT, clock div:2
19:28:10.651 -> load:0x3fff0018,len:4
19:28:10.651 -> load:0x3fff001c,len:928
19:28:10.651 -> ho 0 tail 12 room 4
19:28:10.651 -> load:0x40078000,len:9280
19:28:10.651 -> load:0x40080400,len:5860
19:28:10.651 -> entry 0x40080698
19:28:11.054 -> [I][logger:119]: Log initialized
ghvader commented 5 years ago

I found another post where it was said that is how deep sleep works, by rebooting the device. which is why i thought light sleep would be more appropriate sleep method.

During light sleep mode, the CPU is paused by powering off its clock pulses, while RTC and ULP-coprocessor are kept active. This results in less power consumption than in modem sleep mode which is around 0.8mA. Before entering light sleep mode, ESP32 preserves its internal state and resumes operation upon exit from the sleep. It is known Full RAM Retention.

esp_light_sleep_start() function can be used to enter light sleep once wake-up sources are configured.

sandervandegeijn commented 5 years ago

Still, the light sleep would be nice. Tasmota also does this to lower power consumption, did anyone compare the energy consumption between Tasmota and esphomelib?

OttoWinter commented 5 years ago

@neographikal This feature request is about light sleep on ESP32 which is different from the WiFi modem light sleep mode (which already exists, see wifi page).

sandervandegeijn commented 5 years ago

Great thanks, will look into that :)

tdashmike commented 3 years ago

I would like to be able to do this: "If WiFi connection needs to be maintained, enable WiFi modem sleep, and enable automatic light sleep feature (see Power Management APIs). This will allow the system to wake up from sleep automatically when required by WiFi driver, thereby maintaining connection to the AP." from the link below. OttoWinter mentioned that WiFi modem light sleep mode is already implemented but I tried power_save_mode = HIGH and power consumption on my board is about 46mA while deep sleep is 1.6ma. According to the linked forum post below WiFi modem sleep should only consume ~3.5mA. I think this is in slow mode though but I have not found a way to control which mode to run at.

https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/sleep_modes.html

https://esp32.com/viewtopic.php?t=15281 https://lastminuteengineers.com/esp32-sleep-modes-power-consumption/

AJGDD commented 1 year ago

AFAIK, light sleep is not designed for what you want it to do: extended sleeps, it's designed for short sleeps of a few milliseconds.

light sleep is designed for short sleeps? where is that sentence in ESP32 espressif documentation?

mzakharo commented 1 year ago

I have another use-case that is preventing me form using ESPHome due to lack of light-sleep:

My ESP uses long (1 hour) periodic deep sleep to save battery. On my board (UM Feather S2), deep sleep cuts power to a connected sensor. I have a sensor (ORP) that needs to 'warm up' before taking measurement. ESP powers-on the ORP sensor for 1 minute. During this time, ESP enters light-sleep to save battery. When ESP wakes up from light sleep, it continues on where it left, takes ADC measurement, connects to WiFi and uploads the result. Cycle then repeats.

Can such a light/deep cycle be ever supported within ESPHome?

EternityForest commented 1 year ago

Doing this right would involve interrupts as in #120 I think.

Maybe WiFi state and sleep mode could be individually controllable, and interrupts could be their own separate thing, to avoid rearchitecture?

Rather than make BinarySensor use an interrupt or something, you just have a PinInterrupt integration with an option to wake up.

You could have a SleepController component that triggers going into light sleep at the end of N milliseconds or polling cycles since the last "Wake Up" action, and then automatically sleeps for N milliseconds or until something causes a wakeup.

The deep_sleep component could also be changed to reset it's timer on getting the signal.

It seems like this could mostly be done in a custom component, it's a pretty self contained kind of change not a major redesign, you'd just have to manually manage your sleep and wake timings.

@mzakharo Doesn't the ESP's real time clock keep going in deep sleep? Global variables can be made to stay in RAM. When you wake up, check if the heater(or whatever it actually is that warms up) flag is on in a lambda(Ideally save the time instead of just a flag in case you are woken up early).

If the heater is on, take a reading, turn it off, then go to sleep, if it's off, turn on the heater, set the flag, and sleep for a minute.

I wonder how hard it would be to make Number save its state in RTC memory so you didn't even need a lambda and it was a little cleaner?

mzakharo commented 1 year ago

I was able to implement light sleep with a custom component that starts before wifi is initialized. My biggest issue with ESPHome was with the sensor data upload: I was unable to figure out how to go to sleep as soon as sensor values were sent. I had to insert random delays in order to get my project working which seemed to me that ESPHome wasnt really designed to be run on battery\with sleep, not efficiently/reliably anyways.

Another big gap is lack of automatic update checking when sensor comes online. All firmware upgrade solutions for sleeping sensors I found online were lacking in convenience: you had to flip some flag. wait for sensor to come online, check for flag, then enter special update mode. cumbersome. complicated. I ended up implementing my own solution with micropython, which just checks for a binary payload on a MQTT topic that is retained. if it is available and md5sum does not match current firmware, sensor does automatic update. you can just write new fw with one MQTT publish command and leave it update itself.

To conclude, I think Light/Deep sleep requires a rethink of firmware update sequence, and ability to sleep as soon as all/so me/select QoS=1/2 messages from sensors have been delivered.

EternityForest commented 1 year ago

At least with the native API, I would imagine the big issue is with data still in the TCP buffer when you enter sleep?

Maybe we need a wake locks manager, nothing can go to sleep until all registered locks are cleared, as a global service any component can access?

mzakharo commented 1 year ago

Yes, indeed, both native API and MQTT would be still waiting/in progress sending the data when entering sleep. At least with MQTT QoS=1/2, entering sleep upon receiving a response from the server is an option, if such API was ever exposed.

EternityForest commented 1 year ago

I'd like to take a shot at this project, since I'm not a fan having to write custom Arduino applications from scratch for simple stuff, but I'm not sure what the best way to implement it is yet.

I think my top idea so far is to have a std::vector of functions, in the global application object, and an register_power_state_handler function, along with a request_power_state function.

The parameter is a bit field containing all the features (WiFi, power, display devices, sensors, high power actuators, etc) that some other component is requesting to be enabled or disabled.

If your component has no objection to this, you just return it as-is. If you need to do cleanup, you do it right there. If you are the one responsible for the subsystem, you turn that thing on or off as appropriate. Then you return the input.

If you don't want the system to sleep just yet, you return the input but with all the bits for the stuff you want to keep turned on.

Adding power management should be just a few lines of code for any component that wants to participate.

Then we'd have an all purpose request_power_state call that just calls all the handler functions, and returns the power state the other components find acceptable.

Then, we add a new sleep manager component. It is continually counting down, and when it reaches 0, it will go to the selected power state unless some other component blocks it. If so configured it will then wake up after a predefined period, again making the request_power_state call. It will also wake on HW interrupts.

It has an action to reset it's timer, which will prevent sleep for at least N seconds. It's also got on_wake and before_sleep actions, for very simply implementing a periodic sleep/wake.

It also has an action to immediately go to sleep regardless of timers, but that will still check with components to see if anyone wants to still be awake.

All in all, 2 new components and 2 global functions, and 1 new global data structure doesn't seem too bad, considering all the use cases like managing solar power.

I'm not sure what the set of power levels should be. I'm thinking:

Lightsleep, deep sleep, WiFi, Wifi low latency(No modem association stuff, for fast response), BLE(And all other submilliamp wireless), Sensors, LowPowerSensors, Motors, LEDs, NFC, GPS, Display

scottcopus commented 7 months ago

I think you should be able to call a lambda and with the esp_sleep_get_wakeup_cause() & esp_sleep_get_ext1_wakeup_status() functions to detect if a GPIO (the PIR sensor) triggered it to wake up, or if it was the timer, etc.

https://randomnerdtutorials.com/esp32-external-wake-up-deep-sleep/#:~:text=Code%20Multiple%20GPIOs%20%E2%80%93%20External%20Wake%20Up

nagisa commented 3 months ago

I have experimented with ESP-IDF light sleep implementation a little, and it actually works very well for what I want my ESP to do (effectively just proxy comparatively rarely changing GPIO state to MQTT, but need to retain the contents of RAM.) I had trouble with some state changes not going through to MQTT when using deep sleep in the past too.

If this does not convince you still, another straightforward example where light sleep excels is it always maintains a working network connection, so it is still plenty possible to have devices sip power and yet still promptly react to external commands (or pings, or OTAs, or whatever else you may have,) which is currently impossible with deep_sleep.

Here's some code I wrote for the experiments: https://gist.github.com/nagisa/8d3af3ff8bfd9563d673bb458dfd491d

For the most part lots of code for correct deep sleep should be possible to share with light sleep as well. For instance I'm seeing reboot after OTA take a longer time than usual (a couple seconds) which suggests that there is a missing lock preventing a sleep in that situation, likely because FreeRTOS decides that the system is idle. This sort of lock would be necessary for a correct deep sleep implementation too.

ned14 commented 1 month ago

Here's some code I wrote for the experiments: https://gist.github.com/nagisa/8d3af3ff8bfd9563d673bb458dfd491d

This is very useful, thank you. This gets us far closer to a proper https://esphome.io/components/external_components implementing this.

For the most part lots of code for correct deep sleep should be possible to share with light sleep as well. For instance I'm seeing reboot after OTA take a longer time than usual (a couple seconds) which suggests that there is a missing lock preventing a sleep in that situation, likely because FreeRTOS decides that the system is idle. This sort of lock would be necessary for a correct deep sleep implementation too.

Could not the power lock be held open for maybe five seconds after boot to solve this? Also, perhaps https://esphome.io/components/esphome.html#esphome-on-shutdown should take the power lock too?

Re: my own need for light sleep, all my ESPHome installations are PoE powered ESP32s. I don't use the Wifi at all, just ethernet. I'm more concerned about heat generation than power consumption, even with the R42 resistor removed and Wifi disabled my Olimex ESP32-POE boards do get warm because the CPU is basically spinning and the PoE conversion circuit multiplies the ESP32 power consumption by an inefficiency factor. Therefore reducing the ESP32 by three quarters has an outsize effect on heat generation - the boards drops from ~52 C down to ~30 C.

What would be super great is an opt-in component which enables light sleep and also pulls all unused GPIOs to high to reduce current leakage. Yes saving 150 mA may not seem like much from a purely ESP32 perspective, but it's a half watt of heating on its own and more again after the PoE conversion circuitry has multiplied it.

Also, for those ESPHome devices running on a 3.7v lithium battery charged by a window mounted solar panel, lopping off 150 mA is huge.

ned14 commented 1 month ago

I thought I'd do some testing of my Olimex ESP32-POE boards with the light sleep code posted above. This board uses the ESP32-WROOM-32, so it's a much older variant. Power readings are from the managed switch powering the board, and they are for the entire board including its four LEDs, its 0.5w 'burn' resistor for PoE keepalive, and power conversion circuitry (and obviously, running an ethernet connection).

I can confirm light sleep was definitely activated because the USB port stops working as expected. If I had to take a guess, I'd say either the increased 1000 Hz tick rate or the ethernet running undoes the power savings and actively makes things worse. So, unfortunately, for my specific ESPHome hardware I don't think light sleep is going to help.

What I think I actually need is to replace that 0.5 watt burn resistor with something less dumb. PoE cuts off power unless 0.5 watts is drawn for at least 60 milliseconds every 400 milliseconds. I don't know the consequences of the ESPHome deep sleep component having the ESP32 wake every 340 milliseconds and immediately return to deep sleep, it feels like it would introduce a lot of flash wear as you'd need to store a counter over reboot cycles. Incidentally, if you desolder the burn resistor, default ESPHome consumes 0.947 watts. Perhaps removing the four LEDs as well would get power consumption low enough to keep the PoE alive without needing deep sleep. I kinda feel some sort of power burn on a timer custom piece of hardware would just solve everything here.

My testing config is attached: power_tester_light_sleep.zip