Battery-powered ESP32 projects live and die by their power consumption. In active mode the ESP32 draws ~240 mA — enough to drain a 1000 mAh LiPo in under 5 hours. With deep sleep correctly configured, that same battery can last months or even years.
Power Modes Compared
| Mode | Current | CPU | WiFi/BT | RAM | Wake sources |
|---|---|---|---|---|---|
| Active | ~240 mA | ON | ON | Full | — |
| Modem Sleep | ~15 mA | ON | OFF | Full | Any |
| Light Sleep | ~0.8 mA | Paused | OFF | Retained | Timer, GPIO, UART, Touch |
| Deep Sleep | ~10 µA | OFF | OFF | RTC only | Timer, EXT0, EXT1, Touch, ULP |
| Hibernate | ~2.5 µA | OFF | OFF | None | Timer only |
Timer Wake-up — Most Common Pattern
The ESP32 reads a sensor, publishes the data, then sleeps for N seconds. The RTC timer fires after the interval, causing a reset — setup() runs again from the top.
#include <WiFi.h>
#include <DHT.h>
#define DHT_PIN 4
#define DHT_TYPE DHT22
#define SLEEP_SEC 30 // wake every 30 seconds
#define uS_TO_S 1000000ULL
RTC_DATA_ATTR int bootCount = 0; // survives deep sleep in RTC RAM
DHT dht(DHT_PIN, DHT_TYPE);
void setup() {
Serial.begin(115200);
bootCount++;
Serial.printf("Boot #%d\n", bootCount);
dht.begin();
float t = dht.readTemperature();
float h = dht.readHumidity();
Serial.printf("Temp: %.1f°C Hum: %.1f%%\n", t, h);
// TODO: connect WiFi, publish MQTT, then disconnect
WiFi.disconnect(true);
WiFi.mode(WIFI_OFF);
esp_sleep_enable_timer_wakeup(SLEEP_SEC * uS_TO_S);
Serial.println("Entering deep sleep...");
esp_deep_sleep_start();
}
void loop() {} // never reached
GPIO (EXT0) Wake-up — Button or PIR
// ext0_wakeup.ino// Wake when GPIO 33 goes HIGH (e.g. PIR sensor trigger)
esp_sleep_enable_ext0_wakeup(GPIO_NUM_33, 1); // 1 = HIGH level
esp_deep_sleep_start();
Touch Pad Wake-up
// touch_wakeup.ino// T0 = GPIO 4 (touch pin)
touchAttachInterrupt(T0, [](){}, 40); // threshold = 40
esp_sleep_enable_touchpad_wakeup();
esp_deep_sleep_start();
Battery Life Calculation
A common IoT pattern: active for 2 seconds every 60 seconds.
| Phase | Duration | Current | Charge used |
|---|---|---|---|
| Active (WiFi, read, publish) | 2 s | 240 mA | 0.133 mAh |
| Deep sleep | 58 s | 0.010 mA | 0.000161 mAh |
| Total per cycle (60s) | ~0.133 mAh | ||
| 1000 mAh LiPo life | ~7,500 cycles ≈ 125 hrs ≈ 5 days | ||
Reduce active time to 500 ms and add an external 10 µA ultra-low-power MCU to handle wakeup logic and you can push this to weeks or months.
Pro Tips
- Disable Bluetooth at boot if unused:
btStop();saves ~30 mA - Lower CPU frequency:
setCpuFrequencyMhz(80);to cut active current by ~20% - Use static IP to skip DHCP handshake — saves ~500 ms of active time per cycle
- RTC GPIO: Only GPIO 0, 2, 4, 12–15, 25–27, 32–39 can be used as wake sources
- External pull-downs: floating GPIOs can cause phantom wake-ups — always pull unused wake pins to GND