Hibernation, the floor below sleep
The floor below deep sleep, at about 5 µA. Same function call — what changes is how much you switch off before making it, and how many ways there are left to wake up.
Four modes, and what each one gives up
Every step down is a list of things that stop being powered, and each of them takes a capability with it:
- Light sleep keeps RAM, so execution resumes at the next line. It is the only mode where that is true.
- Deep sleep loses RAM and restarts from
setup(), keeping 8 kB of RTC memory and the peripherals that can wake you. - Hibernation loses the RTC peripherals and RTC memory too. Only the timer and an external pin can wake it, and the board comes back exactly as if it had been unplugged.
It is the same function call
esp_deep_sleep_start() does both. The difference is what you switched off
first: turn off every RTC power domain and you are hibernating, leave one on and
you are in deep sleep. There is no separate API and no flag, which is why the
two get confused in every example on the internet.
The corollary is that hibernation is easy to lose by accident. Enable a touch wake source and the RTC peripherals must stay powered to notice a touch — the domain configuration is silently overridden, and the current doubles.
What it costs you
RTC memory is gone, so a boot counter or a last reading has to live in NVS — which means a flash write on every cycle, and flash has a finite write budget. At one wake an hour that is fine for decades. At one a minute it is not.
When to use it
When the wake is genuinely rare and the board runs on a cell rather than a pack. A sensor that reports twice a day spends almost all its life asleep, so halving the sleep current halves the battery drain — and there is nothing it needs to remember that a flash write cannot carry.
At one wake a minute, the awake time dominates so completely that the choice between 10 µA and 5 µA is not measurable. The next page has the arithmetic.
The code
There is no esp_hibernate_start(). Hibernation is deep sleep with every RTC power domain explicitly turned off, and that is what these four lines do.
#include <esp_sleep.h>
void setup() {
Serial.begin(115200);
delay(100);
Serial.println("hibernating for 30 s");
Serial.flush(); // the UART is about to lose power
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_OFF);
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_SLOW_MEM, ESP_PD_OPTION_OFF);
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_FAST_MEM, ESP_PD_OPTION_OFF);
esp_sleep_pd_config(ESP_PD_DOMAIN_XTAL, ESP_PD_OPTION_OFF);
esp_sleep_enable_timer_wakeup(30ULL * 1000000);
esp_deep_sleep_start(); // nothing after this line runs
}
void loop() {}With the RTC peripherals off, touch and the ULP cannot wake the chip — the hardware that would notice is unpowered. The timer and one external pin are all that is left.
MicroPython exposes deepsleep and not the power-domain controls, so true hibernation needs the esp32 module or a C build. What it does give you is the state that matters — anything in RAM is gone either way.
import machine, time
# RTC memory does not survive hibernation, so keep the counter in flash.
try:
with open("boots.txt") as f:
n = int(f.read())
except (OSError, ValueError):
n = 0
n += 1
with open("boots.txt", "w") as f:
f.write(str(n))
print("boot", n)
time.sleep(1) # let the print get out
machine.deepsleep(30_000)Storing the counter in a file rather than in RTC memory is the pattern that works in both modes, at the cost of a flash write per cycle. Budget those: flash has a finite number of them.
When it does not work
One RTC domain is still powered. Any enabled wake source keeps its domain alive — a touch wake source, an ext1 configuration, a pull-up you asked the RTC to hold. Turn the domains off after enabling only the timer.
With the RTC peripherals off, the only wake sources are the timer and ext0 or ext1 on a pin that is still powered. If the sketch enabled touch or ULP wake, that source is now dead and nothing will fire.
esp_deep_sleep_start cuts power immediately and the UART has a buffer. Serial.flush() before sleeping, and remember it blocks until the bytes are out.
Usually the board rather than the chip. The USB-serial chip, the power LED and the regulator's own quiescent current are all still there, and together they are hundreds of times the chip's 5 µA. Measure on battery, not on USB.
The simplest wake source and the one most projects use. Two numbers decide the battery life, and neither of them is the sleep current.
Waking on a timer →Edit this page — content/esp32/hibernation-mode.mdx
Discuss this article
Ask about this page. The answer stays here, on the page it belongs to, for whoever hits the same wall next.