ESP32 Deep Sleep Tutorial: Low Power Modes & Wake-Up Sources (2026)

 

ESP32 Deep Sleep Tutorial: Low Power Modes, Wake-Up Sources & Real Battery Life (2026 Guide)


If your ESP32 project is plugged into a wall, power consumption is someone else's problem. The moment you put it on a battery, it becomes the only problem that matters. A board that stays fully awake will drain a fresh 18650 cell in under a day — but the same board, put to sleep correctly between readings, can run for months on the same battery.

This guide covers everything you need to actually ship a battery-powered ESP32 project: the four power modes, every wake-up source with working Arduino IDE code, why your measured current will never match the datasheet, and a real battery-life calculation you can redo for your own project.

Quick Answer

Deep sleep is the ESP32's lowest practical power mode. It shuts down the CPU cores, Wi-Fi, Bluetooth, and most RAM, keeping only the RTC (real-time clock) domain alive. A bare ESP32 module draws roughly 10 µA in this state, though a typical dev board with a USB chip and voltage regulator attached usually pulls several milliamps instead. You put the chip to sleep with esp_deep_sleep_start() after configuring at least one wake-up source — a timer, a GPIO pin, or the touch sensor — because without one, the board would sleep forever.

The Four ESP32 Power Modes, Compared

Espressif documents four operating states, and mixing them up is the most common reason a "low power" project still burns through a battery in days instead of months.

ModeWhat stays onTypical current draw (bare module)Wakes up in
ActiveEverything (CPU, Wi-Fi, BT)80–260 mA (Wi-Fi TX can spike higher)—
Modem sleepCPU + RAM, radio duty-cycled~15–20 mAInstant
Light sleepCPU paused, RAM fully retained~0.15–0.8 mA (130–240 µA class)Milliseconds, resumes exactly where it left off
Deep sleepOnly RTC controller + RTC memory~10 µA (module only)Full reboot, runs setup() again

Light sleep is the right choice when you need to resume mid-function — think of a task that pauses between radio packets. Deep sleep is the right choice for "wake up, do a job, go back to sleep" projects: environmental sensors, battery-powered doorbells, soil moisture monitors, and similar. The rest of this guide focuses on deep sleep, since that's what almost every battery project on a microcontroller actually needs.

One detail competitors' tutorials tend to skip: deep sleep isn't a reboot in the software sense — the bootloader runs again — but any variable declared with RTC_DATA_ATTR survives it, because that memory segment is one of the few things the RTC domain keeps powered.

Setting Up a Timer Wake-Up

This is the most common wake source: sleep for a fixed interval, take a sensor reading, publish it, sleep again.

cpp
#define uS_TO_S_FACTOR 1000000ULL  // microseconds to seconds
#define TIME_TO_SLEEP  600         // seconds (10 minutes)

RTC_DATA_ATTR int bootCount = 0;   // survives deep sleep, resets on power loss

void setup() {
  Serial.begin(115200);
  delay(500);

  bootCount++;
  Serial.println("Boot number: " + String(bootCount));

  // 1. Do the actual work here: read a sensor, publish over Wi-Fi/MQTT, etc.

  // 2. Configure the wake source
  esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);

  // 3. Go to sleep
  Serial.println("Going to sleep now.");
  Serial.flush();
  esp_deep_sleep_start();
}

void loop() {
  // never reached — the board reboots into setup() on wake
}

Two things trip people up here. First, esp_deep_sleep_start() never returns, so anything after it never runs — loop() is dead code on a pure deep-sleep design. Second, forgetting Serial.flush() before sleeping can cut off the last line of your debug output, which wastes an hour of troubleshooting a board that was actually working fine.

External and Touch Wake-Up Sources

Timer wake-up covers polling projects, but plenty of designs need to wake on an event instead — a PIR motion sensor, a door reed switch, or a physical button.

EXT0 — single GPIO pin (RTC-capable pins only):

cpp
#define WAKEUP_PIN GPIO_NUM_33

esp_sleep_enable_ext0_wakeup(WAKEUP_PIN, 1);  // wake when pin goes HIGH
esp_deep_sleep_start();

EXT1 — multiple GPIO pins, any-high or all-low:

cpp
esp_sleep_enable_ext1_wakeup(BIT(GPIO_NUM_33) | BIT(GPIO_NUM_25),
                              ESP_EXT1_WAKEUP_ANY_HIGH);
esp_deep_sleep_start();

Touch pin wake-up:

cpp
touchSleepWakeUpEnable(T0, 40);  // GPIO4 on most boards, threshold 40
esp_deep_sleep_start();

Only RTC-capable GPIOs (roughly pins 0, 2, 4, 12–15, 25–27, 32–39 on the original ESP32) work as wake sources — a Wi-Fi tutorial's pinout diagram won't tell you this, but the datasheet's RTC GPIO table will. You can also combine sources: set a timer and an external pin, and check esp_sleep_get_wakeup_cause() after boot to see which one actually fired.

cpp
esp_sleep_wakeup_cause_t reason = esp_sleep_get_wakeup_cause();
switch (reason) {
  case ESP_SLEEP_WAKEUP_EXT0:   Serial.println("Woke via EXT0"); break;
  case ESP_SLEEP_WAKEUP_TIMER:  Serial.println("Woke via timer"); break;
  default:                      Serial.println("Fresh power-on boot"); break;
}

A Practical Project: Battery-Powered MQTT Sensor Node

Deep sleep is most useful combined with networking, and this is where a lot of tutorials stop short — they show the sleep code in isolation, without the connectivity that makes it a real project.

If you've already worked through the ESP32 WiFi tutorial and the ESP32 MQTT tutorial on this site, the pattern is a straightforward extension:

  1. Wake on timer.
  2. Reconnect Wi-Fi (this alone can take 1–3 seconds and dominates your active-time power budget — a fixed IP and skipping DHCP shaves real time off it).
  3. Read the sensor.
  4. Publish the reading over MQTT.
  5. Disconnect cleanly (WiFi.disconnect(true); WiFi.mode(WIFI_OFF);) before sleeping — skipping this step is a common reason boards draw more current than expected even after esp_deep_sleep_start() is called.
  6. Re-arm the timer and sleep.

For a project like the ESP32-C6 indoor air-quality monitor covered elsewhere on this site, this cycle is what turns a wall-powered demo into something you can mount on a wall with a battery and forget about for months.

Deep Sleep Current Across ESP32 Variants

Not all ESP32 chips sleep the same way, and this matters more now that boards like the ESP32-C6 and ESP32-C5 are becoming common project boards.

ChipDeep sleep, RTC on (datasheet, bare module)Notes
ESP32 (original, dual-core)~10 µALargest wake-source selection (touch, EXT0/EXT1, ULP)
ESP32-S3~7 µA (RTC on), ~1 µA (RTC off)ULP-RISC-V coprocessor can run sensor polling during sleep
ESP32-C3~5 µA (datasheet); users commonly measure hundreds of µA on dev boardsRISC-V single-core; fewer RTC GPIOs than the original ESP32
ESP32-C6 / ESP32-C5Single-digit µA class, radio-offMatter/Thread and dual-band Wi-Fi radios add active-mode draw, making sleep discipline more important, not less

The gap between the datasheet number and what you'll measure on a real board is not a bug in your code — it's the USB-UART bridge chip, the voltage regulator's quiescent current, and the power LED, none of which the datasheet's bare-module figure accounts for. On a stock DevKitC-style board, expect single-digit milliamps rather than single-digit microamps unless you either build a bare-module PCB or physically remove/bypass the regulator and USB chip.

Common Mistakes That Quietly Drain the Battery

  • Leaving Wi-Fi or Bluetooth radios "on" in software even though the chip is asleep — always call WiFi.disconnect(true) and WiFi.mode(WIFI_OFF) before sleeping if you enabled them.
  • Powering peripherals from a GPIO pin left floating during sleep — sensors, SD cards, and displays can backfeed current through an undriven pin. Drive the pin explicitly LOW, or use a MOSFET to fully cut power to the peripheral.
  • Forgetting RTC_DATA_ATTR on variables you expect to survive sleep — a normal global resets to its initial value on every wake, because deep sleep is a real reboot of everything outside the RTC domain.
  • Brownout resets on wake, especially when Wi-Fi reconnects — the radio's current spike can sag a weak battery or thin wiring enough to trigger the brownout detector. A larger decoupling capacitor near the board's 3.3V pin and thicker battery leads usually fix this.
  • Testing power draw through a USB cable — a USB connection alone can add milliamps of quiescent current from the host port and onboard USB chip, making every reading meaningless until you disconnect it and measure only the battery leads.
  • Assuming the datasheet current applies to your dev board — treat the datasheet figure as the theoretical floor for a bare module, not a prediction for the board sitting on your desk.

Calculating Real Battery Life

A rough but genuinely useful estimate only needs three numbers: sleep current, active current and duration per cycle, and how often you wake.

Example: a sensor node wakes every 10 minutes, stays active for 4 seconds (sensor read + Wi-Fi + MQTT publish) at an average 120 mA, and sleeps the rest of the time at 2 mA (realistic for a common dev board, not the bare-module datasheet figure).

  • Active charge per cycle: 120 mA × (4/3600) h ≈ 0.133 mAh
  • Sleep charge per cycle: 2 mA × (596/3600) h ≈ 0.331 mAh
  • Total per 10-minute cycle: ≈ 0.464 mAh
  • Cycles per day: 144
  • Daily consumption: ≈ 66.8 mAh
  • On a 2,000 mAh battery: ≈ 30 days, before accounting for the battery's own self-discharge and derating for temperature

Rerun this math with your actual measured currents — not the datasheet numbers — using a USB power meter or a multimeter in series with the battery. It's the single most reliable way to avoid the classic "six months on paper, four days in reality" outcome.

Frequently Asked Questions

What's the difference between light sleep and deep sleep on the ESP32?

Light sleep pauses the CPU while keeping all RAM and peripheral state intact, so execution resumes exactly where it stopped, typically drawing a few hundred microamps. Deep sleep powers down almost everything except the RTC domain, forces a full reboot on wake, and draws roughly 10 µA on a bare module — far lower, but at the cost of losing normal RAM contents.

Why does my ESP32 use way more power in deep sleep than the datasheet says?

The datasheet figure describes a bare module under lab conditions. A typical development board adds a USB-to-UART chip, a voltage regulator, and a power LED, each drawing continuous current regardless of the ESP32's own sleep state — which is why dev boards commonly measure in the low milliamps instead of microamps.

Do global variables survive ESP32 deep sleep?

No, unless they're declared with RTC_DATA_ATTR. Deep sleep powers down normal RAM, so ordinary variables reset to their initial values on wake. Only RTC memory, which stays powered, preserves state across the sleep cycle.

Can the ESP32 wake from deep sleep on more than one GPIO pin?

Yes, using esp_sleep_enable_ext1_wakeup() with a bitmask of RTC-capable GPIOs, configured to wake on any pin going high or on all pins going low. EXT0 supports only a single pin.

How do I know why my ESP32 woke up?

Call esp_sleep_get_wakeup_cause() immediately in setup() and compare the result against constants like ESP_SLEEP_WAKEUP_TIMER or ESP_SLEEP_WAKEUP_EXT0 to branch your logic based on the actual trigger.

Does Wi-Fi need to be turned off before deep sleep?

Deep sleep powers off the radio regardless, but explicitly calling WiFi.disconnect(true) and WiFi.mode(WIFI_OFF) beforehand avoids edge cases where the radio stack leaves residual current draw or delays the transition to sleep.

Which ESP32 wake-up sources exist besides timer and GPIO?

Touch pins, the ULP (ultra-low-power) coprocessor — which can poll a sensor independently while the main CPU sleeps — and, on some variants, a dedicated ULP-RISC-V core capable of more complex sensor logic during sleep.

Is deep sleep enough to run an ESP32 for a year on a coin cell?

Rarely on its own. A CR2032-class coin cell holds around 200–230 mAh and has limited peak current capability, which struggles against Wi-Fi's active-mode current spikes. Multi-month to multi-year runtimes on small cells generally require either a larger LiPo/AA pack, very infrequent wake cycles, or dropping Wi-Fi for a lower-power radio like BLE or LoRa for the active portion of each cycle.

Final Takeaway

Deep sleep is simple to enable and easy to get wrong in ways that only show up days after deployment, when the battery is already dead. Pick the mode that matches what your project actually needs to remember between cycles, wire the wake source deliberately, measure current on the real board rather than trusting the datasheet, and run the battery-life math before you seal the enclosure — not after.

Post a Comment

0 Comments
* Please Don't Spam Here. All the Comments are Reviewed by Admin.