Phase 1: Beginner 15 min read read

ESP32 Power Consumption Guide: Sleep Modes, Battery Life, and Optimisation

Master ESP32 power management — active mode, modem sleep, light sleep, deep sleep, and ULP. Calculate battery runtime and optimise your IoT device for months of operation.

Updated June 18, 2026

Power is the Hidden Design Constraint

When designing a mains-powered ESP32 project, power consumption rarely matters — the wall outlet supplies more current than any ESP32 configuration could demand. But the moment you target battery operation — a remote soil moisture sensor, a doorbell notifier, a wildlife camera trap — power consumption becomes the central design parameter. An ESP32 running at full speed with Wi-Fi active draws 80–240 mA, which would drain a 2000 mAh LiPo battery in less than a day. The same device, carefully managed with deep sleep, can run for months on the same battery.

This guide walks through every power mode the ESP32 supports, shows how to calculate realistic battery life, and gives you the practical code patterns to implement each mode in Arduino.

Understanding ESP32 Current Draw

The ESP32’s power consumption has three major contributors: the CPU cores, the Wi-Fi/Bluetooth radio, and peripheral clocks. Active current is dominated by the radio — a Wi-Fi transmit burst at maximum output power consumes up to 240 mA momentarily. The CPU running at 240 MHz adds about 30–40 mA on top. When you turn off the radio and the CPU, the remaining leakage from RTC circuitry, RTC memory, and crystal oscillators drops to microamp levels.

Mode CPU Wi-Fi Radio Current Wake Source
Active (TX) ON Transmitting 160–240 mA
Active (RX) ON Receiving 80–100 mA
Active (idle) ON OFF 30–40 mA
Modem Sleep ON Periodic 15–20 mA avg Timer
Light Sleep Paused Periodic 0.8–1.0 mA avg Timer, GPIO, UART
Deep Sleep OFF OFF 10–50 µA Timer, GPIO, ULP
Hibernate OFF OFF 2–5 µA GPIO only

Active Mode

Active mode is the default state after boot — both cores running, clocks at full frequency, Wi-Fi stack initialised. This is where your Arduino loop() executes. For mains-powered applications, you operate entirely in active mode. For battery-powered applications, you should minimise time in active mode and transition to sleep as quickly as possible after completing the task at hand.

You can reduce active mode power without sleeping by reducing CPU clock speed. Use setCpuFrequencyMhz(80) to drop from 240 MHz to 80 MHz — current drops roughly proportionally (from ~35 mA CPU to ~12 mA). At 80 MHz the ESP32 handles Wi-Fi, I²C sensors, and serial communication comfortably for most IoT tasks. At 10 MHz (the minimum stable frequency with Wi-Fi) you can reduce further but Wi-Fi may be unreliable.

Modem Sleep

Modem sleep is the simplest form of power saving and activates automatically when connected to a Wi-Fi access point and not transmitting. The radio powers down between the access point’s DTIM beacon intervals (every 100–1000 ms depending on router configuration). The CPU stays active. Average current drops from 80 mA to roughly 15–20 mA.

No code is required to enable modem sleep — it is on by default in the Arduino ESP32 core when connected. You can also force the radio off manually while keeping Wi-Fi association alive with WiFi.setSleep(true). This helps in sketches that poll a sensor on a schedule but only transmit occasionally.

Light Sleep

Light sleep pauses both CPU cores and most peripherals while retaining the contents of all SRAM. Wi-Fi association is maintained, and the chip can resume in under 1 ms without reconnecting to the network. Average current with Wi-Fi in light sleep is approximately 0.8–1 mA — a 100× reduction from full active mode. This mode is ideal for devices that must respond to incoming data quickly but spend most of their time idle.

// Enter light sleep for 2 seconds
esp_sleep_enable_timer_wakeup(2 * 1000000ULL);  // microseconds
esp_light_sleep_start();
// Execution resumes here after wake
Serial.println("Woke from light sleep");

Light sleep supports multiple wake sources simultaneously: a timer, a GPIO edge, a UART activity detect, or a Wi-Fi packet receipt. This makes it suitable for devices that sleep between transmissions but need to wake immediately if data arrives from the server.

Deep Sleep

Deep sleep is the most powerful battery-saving mode for devices that do not need to maintain a persistent network connection. The CPU, SRAM, and most peripherals are fully powered off. Only the RTC controller, RTC memory (16 KB total), and optionally the ULP co-processor remain active. Current drops to 10–50 µA depending on whether ULP is running and whether the 32.768 kHz crystal is active.

// Wake after 30 seconds
esp_sleep_enable_timer_wakeup(30 * 1000000ULL);

// Optional: also wake on GPIO 33 going high
esp_sleep_enable_ext0_wakeup(GPIO_NUM_33, 1);

// Save data in RTC memory before sleeping
RTC_DATA_ATTR int readings_count = 0;
readings_count++;

esp_deep_sleep_start();
// ESP32 resets after this — setup() runs again on wake

Note the important distinction from light sleep: deep sleep performs a full reset. setup() runs again after wake-up. Variables declared with RTC_DATA_ATTR survive, but all other state is lost. Wi-Fi must reconnect on each wake cycle. This reconnection cost (typically 0.5–2 seconds at 80–170 mA) dominates power consumption for devices that wake frequently. Optimise by storing Wi-Fi channel and BSSID in RTC memory and using WiFi.begin(ssid, pass, channel, bssid) to skip the channel scan.

Wake-Up Causes and Optimising Reconnection

Call esp_sleep_get_wakeup_cause() at the start of setup() to determine why the ESP32 woke:

RTC_DATA_ATTR uint8_t wifi_channel = 0;
RTC_DATA_ATTR uint8_t wifi_bssid[6] = {};

void setup() {
  if (esp_sleep_get_wakeup_cause() == ESP_SLEEP_WAKEUP_TIMER) {
    // Use cached channel/BSSID to reconnect faster
    if (wifi_channel > 0) {
      WiFi.begin(SSID, PASS, wifi_channel, wifi_bssid);
    } else {
      WiFi.begin(SSID, PASS);
    }
    // After connect, cache the channel and BSSID
    wifi_channel = WiFi.channel();
    memcpy(wifi_bssid, WiFi.BSSID(), 6);
  }
  // read sensor, transmit, sleep...
}

This pattern reduces Wi-Fi reconnect time from 2–4 seconds to 0.5–1 second in stable environments, cutting average current consumption by 50% or more for 30-second wake cycles.

ULP Co-Processor for Sensor Polling

The ULP (Ultra Low Power) co-processor is a small 8 MHz processor that can execute simple programs while the main ESP32 cores are in deep sleep. Its primary use case is reading ADC values, checking GPIO states, or communicating via bit-bang I²C with sensors — tasks that happen frequently but do not require the full main core CPU.

Programming the ULP is more complex than writing Arduino sketches — it uses a simple assembly language or ULP RISC-V C code on the ESP32-S2 and S3. The payoff is dramatic: instead of waking the full core every 30 seconds to check a soil moisture sensor (100 mA for 0.5 seconds = 50 mAs per sample), the ULP reads the sensor at 25 µA continuously and wakes the main core only when moisture drops below a threshold — perhaps once per day. The energy savings can be 1000× or more for slow-changing environmental sensors.

Battery Life Calculation

For a sensor that wakes every 5 minutes, connects to Wi-Fi, reads a BME280 sensor, posts to MQTT, and sleeps again:

  • Active time: ~2 seconds at 120 mA average = 240 mAs = 0.067 mAh per cycle
  • Sleep time: 298 seconds at 15 µA = 4,470 µAs = 0.00124 mAh per cycle
  • Total per cycle: ~0.068 mAh
  • Cycles per hour: 12
  • Consumption per hour: 12 × 0.068 = 0.82 mAh
  • Battery life on 2000 mAh: 2000 / 0.82 ≈ 2440 hours ≈ 102 days

Apply a 0.7 efficiency factor for the LDO regulator, self-discharge, and LiPo capacity at temperature: ~71 days. Real-world results of 6–10 weeks are typical for this configuration.

Hardware Considerations for Low-Power Designs

Software sleep modes are only half the equation. Hardware choices matter equally: choose an LDO with low quiescent current (MCP1700 at 1.6 µA vs AMS1117 at 5–10 mA at no load — the AMS1117’s own idle current can exceed your ESP32’s deep sleep current); disable any power-on LEDs by removing or desoldering them; use sensors with shutdown modes and power them through a GPIO-controlled MOSFET so they draw zero current during sleep. On a custom PCB, add a 100 µF electrolytic capacitor across the 3.3 V rail to handle the initial power-on current spike without brownouting the regulator.

Frequently Asked Questions

Projects to Build

Put this knowledge to work — try one of these hands-on projects.