Guide · Low power · Battery
Run an ESP32 sensor for a year on one battery
An ESP32 with Wi-Fi on pulls ~160–260 mA and flattens a LiPo in hours. But a battery sensor doesn’t need to be awake — it wakes, reads, sends, and goes back to sleep. In deep sleep the same chip draws about 10 µA. That’s the difference between hours and a year.
The sleep modes, in current
- Active + Wi-Fi — ~160–260 mA (spikes higher on TX).
- Modem sleep — CPU on, radio off — tens of mA.
- Light sleep — ~0.8 mA, RAM and state preserved, fast wake.
- Deep sleep — ~10 µA, only the RTC stays alive.
- Hibernation — ~5 µA, almost everything off.
Deep sleep is the sweet spot for battery sensors: microamp draw, and it wakes on a timer or a pin.
The wake–work–sleep pattern
Deep sleep is not a pause — it’s effectively a reboot. The CPU stops, RAM is lost, and on wake your
sketch starts from setup() again. So you put the work in setup(), then sleep:
#include "esp_sleep.h"
#define uS_PER_S 1000000ULL
RTC_DATA_ATTR int bootCount = 0; // survives deep sleep in RTC memory
void setup() {
Serial.begin(115200);
bootCount++; // deep sleep = a fresh boot; setup runs again
// ... connect Wi-Fi, read sensor, send data ...
esp_sleep_enable_timer_wakeup(10 * 60 * uS_PER_S); // wake in 10 min
// esp_sleep_enable_ext0_wakeup(GPIO_NUM_33, 0); // ...or on a pin (PIR, reed)
esp_deep_sleep_start(); // everything below never runs
}
void loop() {} // unused — the work lives in setup() A little RTC memory (RTC_DATA_ATTR) survives sleep, so you can keep a
counter or last-state across wake-ups without touching flash.
What can wake it
- Timer — the workhorse: wake every N minutes to sample and report.
- A pin (ext0 / ext1) — a PIR, reed switch or button wakes it on an event.
- Touch — a capacitive pad.
- ULP co-processor — a tiny always-on core that can watch a sensor and wake the main CPU only when a threshold trips — for the absolute lowest average current.
The battery-life math
Average current is what matters. Say you wake for 2 s at 150 mA every
10 min and sleep at 10 µA the rest of the time. The awake share is 2 s / 600 s ≈ 0.33%,
so the average is roughly 0.0033 × 150 mA + 10 µA ≈ 0.5 mA. A 2000 mAh 18650 then lasts
~4000 hours — around five months, and cutting wake time or reporting less often pushes
it past a year.
The #1 trap: your dev board never reaches 10 µA
This catches everyone. A bare ESP32 module sleeps at ~10 µA — but a dev board also has an always-on power LED, a USB-serial chip (CH340/CP2102) and an AMS1117 regulator, each burning milliamps 24/7. Your board “sleeps” at 5–20 mA and the battery dies in days. Fixes: remove the power LED, use a board designed for low power, or run a bare module with an efficient regulator. See the power & regulators guide — the same AMS1117 that browns out under Wi-Fi also wastes your battery in sleep.
Squeeze more
- Shorten wake time — Wi-Fi reconnect is the big cost. A static IP and stored AP channel cut it from seconds to under a second.
- Report less often — every doubling of the interval nearly doubles battery life.
- Persist state in RTC memory instead of reconnecting or re-reading config each wake.
- Pick a low-power chip — an S2 or C3 idles lower than a classic ESP32; see the chip guide.
Deep sleep turns the ESP32 from a desk-bound, USB-powered board into a real wireless sensor you can stick on a wall and forget for a season.
Battery draining in days, not months? It’s usually the regulator and LED.