ESP32 Deep Dive · #15 of 52

Deep Sleep: Wake Sources, State, RTC Memory

Battery Life Is a State Machine With the Power Removed Between Steps

A six-cell AA battery holder with a barrel-plug lead.
Battery-powered electronics are not powered by averages. They are powered by the charge you spend in every wake-sleep cycle. · oomlout, CC BY-SA 2.0

The robot hand has a small wireless calibration puck. It wakes, samples temperature near the motors, sends one status packet, and disappears again. The product manager wants it to run for months from a small battery.

The firmware engineer says, "No problem, the ESP32 can deep sleep at microamps." That sentence is true in the same way that "a car can idle" is true: it ignores everything else attached to the system. Regulators leak. Sensors stay powered. Pull-ups burn current. Wi-Fi bursts are expensive. A forgotten LED can cost more than the sleeping microcontroller.

Deep sleep is not a pause button. It is a design pattern: do the expensive work quickly, save the minimum state needed for the next cycle, configure a wake source, turn most of the chip off, and accept that the next wake looks a lot like boot.

By the end, you can

  1. Calculate charge per wake-sleep cycle from active current, active time, sleep current and interval
  2. Explain why reducing active time can matter more than shaving a few microamps from sleep
  3. Choose timer, GPIO, touch or ULP wake based on what remains powered in deep sleep
  4. Use RTC memory for small state that must survive deep sleep, while treating normal RAM as lost
  5. Write firmware as a wake loop that handles cold boot, timer wake and external wake differently
  6. Measure the whole sleeping system instead of trusting only the microcontroller datasheet

The battery sees charge, not intent

The active current can be tens or hundreds of milliamps. The deep-sleep current can be microamps. The reason the system still works is not that the active current is small. It is that the active interval is short.

A current-versus-time graph showing a short high-current active burst and a long low-current deep-sleep interval.
Battery life follows the area under this curve. A narrow active burst can dominate the entire cycle even when sleep lasts minutes. · TooFoo original SVG

The useful unit here is charge per cycle. If the board wakes every five minutes, spends 2.5 s active at 90 mA, then sleeps the remaining 297.5 s at 12 uA, the charge is:

Qcycle=Iactivetactive+IsleeptsleepQ_\text{cycle} = I_\text{active}t_\text{active} + I_\text{sleep}t_\text{sleep} Qcycle=90 mA2.5 s+0.012 mA297.5 s229 mAsQ_\text{cycle} = 90\ \text{mA}\cdot2.5\ \text{s} + 0.012\ \text{mA}\cdot297.5\ \text{s} \approx 229\ \text{mA}\cdot\text{s}

The sleep part is only about 3.6 mA*s. The active burst is about 225 mA*s. In this example, making the wake code one second faster saves more than cutting sleep current by several microamps.

average: 0.763 mA cycle: 229 mA*s life: 109 days

Try these moves:

  1. Double the wake interval from 300 s to 600 s. The active charge is the same, but the average current falls.
  2. Raise active time from 2.5 s to 7 s. The battery life collapses faster than the sleep-current number suggests.
  3. Raise sleep current from 12 uA to 80 uA. Now leakage matters because the long interval has become expensive.
  4. Lower active current but leave active time long. You still pay for every second awake.

In a 5-minute sleep cycle, which change usually gives the biggest win when Wi-Fi active current dominates?

The wake source must stay alive

Deep sleep turns off the main CPU and much of the chip. Only the retained domains can notice the event that wakes the system. This is why wake-source selection is an electrical design decision, not just a firmware call.

Diagram of timer, RTC GPIO, touch pad and ULP program wake sources feeding the ESP32 RTC domain.
A wake source is only real if it remains powered and connected to a domain that is awake enough to listen. · TooFoo original SVG

Timer wake is the cleanest first design. It is perfect for "sample every five minutes" or "phone home once per hour." The RTC timer remains alive, wakes the chip, and the firmware runs the same short loop each time.

External wake is for events that cannot wait for the next timer tick: a lid opening, a magnet moving, a fault line asserting, or a user button. The circuit must hold a valid level while the chip sleeps and while it wakes.

Small reed-switch relay modules removed from equipment.
A reed contact or magnetic switch is a classic low-power wake source because it can create a clean external state without running a sensor processor. · Daniel Beardsmore, Public domain

Motion sensors are a different bargain. A PIR module can wake the system when a warm object moves, but the module itself consumes power. For a wall-powered device that is fine. For a coin cell it may be the main load. The wake source has to fit the battery, not just the feature list.

A PIR motion sensor module.
A sensor that stays awake for you is part of the sleep budget. Its idle current matters even when the ESP32 is off. · Dmitry G, CC BY-SA 3.0

Touch wake and ULP wake are powerful but easy to over-use. Touch depends on board geometry, enclosure material and noise. ULP lets a tiny coprocessor sample slowly while the main CPU sleeps, but it adds a second program and another set of limits. Use them when the hardware reason is clear.

A DHT22 temperature and humidity sensor module.
Periodic sensors fit timer wake well: power the sensor, let it settle, read it, then remove power if the sensor allows it. · Suyash Dwivedi, CC BY-SA 4.0

RTC memory is a small notebook

After deep sleep, ordinary RAM is not something to trust. Code starts again, global variables are initialized again, and the program has to inspect the wake reason. The state that must survive belongs in RTC memory or in nonvolatile storage, depending on how important and how write-heavy it is.

Diagram showing normal RAM lost across deep sleep while RTC memory retains a small state record.
Use RTC memory for small, temporary state. Treat true power loss as a separate case and rebuild safely. · TooFoo original SVG

Good RTC state is small and disposable:

  1. Boot count.
  2. Last sensor sample.
  3. Backoff timer after a failed transmission.
  4. Calibration coefficients copied from flash.
  5. A version number or checksum so stale state can be rejected.

Bad RTC state is a whole application database. If the information must survive battery removal, save it to flash or external storage with a wear strategy. If the information can be recomputed after a cold boot, do not make the sleep path fragile by preserving too much.

A CR2032 backup battery on a laptop motherboard.
Backup domains are a familiar idea: keep only the tiny state that needs to survive while the main system is off. · Raimond Spekking, CC BY-SA 4.0
Practice 1 core

A board wakes every 10 minutes. It draws 120 mA for 4 s, then sleeps at 20 uA for the remaining time. Estimate the charge per cycle and say which term dominates.

Show worked solution

Active charge is 120 * 4 = 480 mA*s. Sleep time is 596 s, and sleep current is 0.020 mA, so sleep charge is about 11.9 mA*s. Total is about 492 mA*s, and the active term dominates.

Firmware is a wake loop

A deep-sleep program should be written as a loop that can start from several causes. Was this a true power-on reset? A timer wake? A GPIO wake? A brownout after a weak battery sagged during Wi-Fi transmit? Those are different stories.

Loop diagram showing boot, wake reason, RTC restore, sense and transmit, save state, and enter deep sleep.
Deep-sleep firmware is an event loop with most of the chip powered off between iterations. · TooFoo original SVG

On every wake, do the same disciplined sequence:

  1. Read and log the wake reason as early as possible.
  2. Check whether RTC state is valid for this firmware version.
  3. Bring up only the peripherals needed for this cycle.
  4. Do the measurement or event handling.
  5. Transmit or store the result.
  6. Put external sensors and loads into their lowest safe state.
  7. Save the small state record.
  8. Configure the next wake source.
  9. Enter deep sleep.

If the board sometimes wakes and immediately sleeps again, log why. If it wakes from reset instead of the timer, log that separately. Debug output is expensive, but a few early serial lines during development can save days of guessing.

Why should deep-sleep firmware read the wake reason before doing normal work?

Measure the whole sleeping system

The datasheet number is for a chip in a specified condition. Your board includes a regulator, pull resistors, sensors, level shifters, LEDs, flash, leakage paths and maybe a USB bridge. A real product battery sees all of it.

A USB multimeter measuring voltage and current.
A USB power meter is convenient for active currents, but many cannot resolve true microamp sleep current. · Jacek Rużyczka, CC BY-SA 4.0

For active current, a USB meter or bench supply readout may be enough to find large problems. For sleep current, the measurement tool becomes part of the experiment. A handheld multimeter on its microamp range can add burden voltage. A scope plus current probe may miss long sleep intervals. A dedicated power profiler is often the right tool when you need to see both the Wi-Fi spike and the sleep floor.

An A23 battery in a holder mounted to stripboard.
Small batteries make measurement honesty unavoidable. A few hidden microamps become real shelf-life loss. · Morahman7vn, CC BY-SA 4.0

The measurement checklist is blunt:

  1. Remove or disable status LEDs before claiming a sleep number.
  2. Measure with sensors powered and unpowered, not only with the ESP32 alone.
  3. Verify the regulator's quiescent current at the actual battery voltage.
  4. Check pull-up and divider currents that run during sleep.
  5. Trigger real wake events, not only timer wake in a firmware example.
  6. Record temperature if leakage or battery chemistry matters.

Key takeaways

  • Deep sleep saves power when the wake path is short and the sleeping system is truly quiet.
  • Charge per cycle is Iactivetactive+IsleeptsleepI_\text{active}t_\text{active}+I_\text{sleep}t_\text{sleep}; peak current alone is not the answer.
  • Wake sources must remain powered and connected to an awake domain: timer, RTC GPIO, touch or ULP.
  • RTC memory is for small temporary state. It is not a substitute for nonvolatile storage after battery removal.
  • Measure the whole board, including regulators, sensors, pulls, dividers and debug hardware.
When to choose light sleep instead

Deep sleep is best when wake latency can be hundreds of milliseconds and normal RAM does not need to survive. Light sleep is useful when latency matters and RAM retention is worth the extra current. The engineering choice is not "lowest current number wins." It is the lowest energy that still meets the response-time and state-retention needs of the product.

A sleeping product is still an active design. Its pins have reset states, its sensors have leakage, its firmware has memory boundaries, and its battery is counting every microamp-second. Treat deep sleep as a complete system mode, and the battery-life math starts behaving like engineering instead of hope.

full glossary →