Why Your ESP32 Won't Go Below a Few Milliamps in Deep Sleep (And How to Actually Fix It)
I burned through four AA batteries in three days on a soil moisture sensor that was supposed to run for months. The code was right. esp_deep_sleep_start() was firing correctly. The datasheet said my chip should be sipping something like 10 microamps in deep sleep. My multimeter said otherwise — it said 9.6 milliamps. That's roughly a thousand times more than it should have been.
If you've measured your ESP32 in deep sleep and the number looks nothing like the datasheet, you're not doing anything wrong in your code. The problem is almost never software. It's the board.
Here's what's actually going on, how to find the exact culprit on your setup, and what to change so your project runs on a battery for months instead of days.
The gap between "datasheet current" and "real current"
Espressif's own numbers put ESP32 deep sleep current somewhere around 10 µA with RTC peripherals powered down, and even lower on newer chips like the ESP32-C3. That number is real — but it's the current of the bare silicon, measured on Espressif's own reference hardware with everything else stripped away.
Your dev board is not bare silicon. It's the chip plus a USB-to-serial converter, a 3.3V voltage regulator, status LEDs, pull-up resistors, and sometimes a boot button circuit — and several of those parts never sleep. People measuring popular dev boards in the wild routinely see anywhere from a few hundred microamps to well over 10 mA in "deep sleep," and the spread almost always comes down to which of those extra components is still awake.
So the real question isn't "why is my ESP32 broken" — it's "which part of my board is quietly staying on."
The five most common culprits (check in this order)
1. The onboard USB-to-UART chip
Most dev boards (DevKitC, NodeMCU-32S, most WROOM breakout boards) have a CP2102 or CH340 USB-serial chip so you can flash and monitor over USB. These chips are not designed for microamp-level idle current — some pull several milliamps just sitting there, powered, doing nothing.
Fix: Power the board through its 3.3V pin directly (bypassing the USB circuitry and the onboard regulator entirely) instead of through the USB port or 5V pin. This alone is usually the single biggest win you'll get.
2. The onboard voltage regulator (LDO)
Even once you cut USB out of the picture, if you're still feeding 5V into the board's VIN/5V pin, that power runs through an onboard linear regulator (AMS1117 and similar are common) to get to 3.3V — and a lot of these regulators have quiescent current in the milliamp range, not microamp range.
Fix: Same as above — feed 3.3V straight into the 3.3V pin, skipping the onboard regulator. If you need to step down from a battery, use a dedicated low-Iq regulator (something like the MCP1700 or a modern buck converter rated for low quiescent current) instead of the board's built-in one.
3. Status LEDs and RGB indicators
Newer boards (especially ESP32-C3/C6/S3 dev kits) often have an onboard addressable RGB LED for status. Even "off," these can draw close to a milliamp of standby current depending on the driver chip — enough on its own to blow your whole power budget.
Fix: Physically desolder the LED, or cut its supply trace if there's a solder jumper for it (check your board's schematic — some have one labeled specifically for this). If neither is possible, drive its control pin low and accept the loss, or switch to a bare module board without one.
4. Floating GPIO pins
Any GPIO left in INPUT mode and not explicitly pulled high or low can float, and a floating pin can leak current through internal pull resistors or externally connected circuitry — small per pin, but it adds up fast across a busy board.
Fix: Before calling deep sleep, explicitly set unused pins to a known state:
// Set any pins driving external circuitry to a defined low state
pinMode(UNUSED_PIN, OUTPUT);
digitalWrite(UNUSED_PIN, LOW);
// Power down RTC peripherals and memory domains you're not using
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_enable_timer_wakeup(TIME_TO_SLEEP_US);
esp_deep_sleep_start();Skip powering down RTC_SLOW_MEM if you're relying on RTC_DATA_ATTR variables to survive the sleep — powering that domain off wipes them.
5. External sensors and modules that never got told to sleep
That BME280, that SD card breakout, that relay module — if you're powering them from the ESP32's 3.3V pin directly, they stay powered the whole time the ESP32 is "asleep," and plenty of sensor modules pull far more than the ESP32 itself does at idle.
Fix: Switch external peripherals off the 3.3V rail with a MOSFET or a dedicated load switch, controlled by a GPIO you set high only when you need that sensor active. Cut power entirely between wake cycles instead of just telling the sensor to go into its own low-power mode.
How to actually measure it (so you know if a fix worked)
A regular multimeter's mA range usually can't resolve microamp-level current accurately. What you want is either:
- A USB power meter with a µA-resolution mode, or
- A bench multimeter set to a low current range, wired in series between your power source and the board — no USB cable connected, no serial monitor open, board running purely on battery or bench supply.
Measure right after the board enters deep sleep, not during the brief active window before it. If the number is still stuck in the milliamp range after fixing the USB/LDO path, work down the list above one item at a time and re-measure — isolating variables is the only way to know which change actually mattered.
What a properly optimized setup looks like
| Setup | Typical deep sleep current |
|---|---|
| Full dev board over USB power | Several mA to 10+ mA |
| Same board, powered via 3.3V pin directly | Tens to low hundreds of µA |
| Bare module, RGB LED removed, GPIOs defined, peripherals load-switched | Single-digit to low tens of µA |
That last row is the difference between a battery that lasts a week and one that lasts a year, for the exact same code.
FAQ
Why does my ESP32 draw milliamps instead of microamps in deep sleep? Almost always because of onboard board components that are still powered — most commonly the USB-to-serial chip and the voltage regulator — not because of anything in your sketch. Powering the board directly through its 3.3V pin fixes this in most cases.
Does deep sleep current change between ESP32, ESP32-C3, and ESP32-S3? Yes, slightly — different chip revisions have different rated deep sleep currents, with some of the newer RISC-V variants like the C3 rated a bit lower than the original ESP32. But on a real dev board, the board's own components usually dominate the total draw far more than the chip-to-chip difference does.
Will powering the ESP32 through its 3.3V pin damage it? No, as long as your supply is a clean, regulated 3.3V and stays within the chip's voltage tolerance. This is the standard way experienced makers power battery projects — it just means you lose the convenience of USB power and have to flash the board before disconnecting it.
Do I need external hardware to measure microamp currents? A basic multimeter can work if it has a dedicated low-current range, but most people get more reliable readings from a USB power meter with µA resolution or a bench supply with a built-in current display.
Does using RTC_DATA_ATTR variables affect power draw?
It keeps the RTC slow memory domain powered so your data survives sleep, which uses a small amount of extra current compared to powering that domain fully off — usually worth the tradeoff unless you're chasing the absolute lowest possible number.
If you're building a battery-powered ESP32 project and want the wake/sleep cycle itself dialed in — not just the sleep current — the RTC memory persistence patterns and timer wake-up setup covered here pair well with a proper deep sleep wake-source guide, which I'll be covering next.
.jpg)