ESP32 Deep Sleep & RTC GPIO: Complete Power Management Guide
Battery life is the #1 challenge in IoT devices. The ESP32's Deep Sleep mode drops power consumption from ~75 mA (active) to as low as 5 µA — a 15,000× reduction. This guide covers every wake source, which pins remain functional during sleep, the ULP co-processor, and real-world power optimization strategies.
ESP32 Power Modes Comparison
| Mode | Typical Current | CPU | RTC | ULP | RAM Retention |
|---|---|---|---|---|---|
| Active (Modem) | 75–160 mA | ✅ Running | ✅ | N/A | Full |
| Modem Sleep | 3–30 mA | ✅ Running | ✅ | N/A | Full |
| Light Sleep | 0.5–4.0 mA | ⛔ Paused | ✅ | ❌ | Full |
| Deep Sleep | 5–10 µA | ⛔ Off | ✅ | ✅ | RTC fast memory only |
| Hibernation | 2.5–5 µA | ⛔ Off | ⛔ Off | ❌ | None |
RTC GPIO Pins: What Works During Deep Sleep
In Deep Sleep, the main CPU and most peripherals are powered off. However, the RTC (Real-Time Clock) domain remains active and can control a subset of GPIO pins. These RTC-capable pins can be used for wake-up detection and ULP co-processor I/O.
| RTC GPIO | Standard GPIO | Function During Sleep |
|---|---|---|
| RTC_GPIO0 | GPIO36 (INPUT_ONLY) | Wake from ext0/ext1 |
| RTC_GPIO1 | GPIO37 (INPUT_ONLY) | Wake from ext0/ext1 |
| RTC_GPIO2 | GPIO38 (INPUT_ONLY) | Wake from ext0/ext1 |
| RTC_GPIO3 | GPIO39 (INPUT_ONLY) | Wake from ext0/ext1 |
| RTC_GPIO4 | GPIO34 (INPUT_ONLY) | Wake from ext0/ext1 |
| RTC_GPIO5 | GPIO35 (INPUT_ONLY) | Wake from ext0/ext1 |
| RTC_GPIO6 | GPIO25 | Wake, ULP I/O, ADC2 |
| RTC_GPIO7 | GPIO26 | Wake, ULP I/O, ADC2 |
| RTC_GPIO8 | GPIO33 | Wake, ULP I/O, touch T8 |
| RTC_GPIO9 | GPIO32 | Wake, ULP I/O, touch T9 |
| RTC_GPIO10 | GPIO4 | Wake, ULP I/O, touch T0, ADC2 |
| RTC_GPIO11 | GPIO0 | Wake, ULP I/O, touch T1, strapping |
| RTC_GPIO12 | GPIO2 | Wake, ULP I/O, touch T2, strapping |
| RTC_GPIO13 | GPIO15 | Wake, ULP I/O, touch T3, strapping |
| RTC_GPIO14 | GPIO13 | Wake, ULP I/O, touch T4 |
| RTC_GPIO15 | GPIO12 | Wake, ULP I/O, touch T5, strapping |
| RTC_GPIO16 | GPIO14 | Wake, ULP I/O, touch T6 |
| RTC_GPIO17 | GPIO27 | Wake, ULP I/O, touch T7 |
Wake Sources for Deep Sleep
The ESP32 can wake from Deep Sleep via five mechanisms:
1. Timer Wake-Up
The simplest method: wake after a fixed interval. Ideal for periodic sensor readings.
// ESP32 Timer Wake-Up Example — Wake every 10 seconds
#include <esp_sleep.h>
#define uS_TO_S_FACTOR 1000000ULL
#define TIME_TO_SLEEP 10 // seconds
void setup() {
Serial.begin(115200);
delay(100);
esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
Serial.println("Going to sleep for " + String(TIME_TO_SLEEP) + " seconds");
esp_deep_sleep_start();
}
void loop() { } // Never reached
2. External Wake (ext0)
Wake on a single RTC GPIO pin going HIGH or LOW. Uses very little power.
// ESP32 ext0 Wake-Up — Wake when GPIO33 goes HIGH
esp_sleep_enable_ext0_wakeup(GPIO_NUM_33, HIGH);
3. External Wake (ext1)
Wake when any combination of up to 8 RTC GPIO pins changes state. Most flexible for multiple wake triggers.
// ESP32 ext1 Wake-Up — Wake when GPIO33 OR GPIO32 goes HIGH
const uint64_t ext1_mask = (1ULL << GPIO_NUM_33) | (1ULL << GPIO_NUM_32);
esp_sleep_enable_ext1_wakeup(ext1_mask, ESP_EXT1_WAKEUP_ANY_HIGH);
4. Touch Wake-Up
Wake when a capacitive touch pad is touched. See our Touch Pins Guide for full details.
// ESP32 Touch Wake-Up
touchSleepWakeUpEnable(T0, 30); // GPIO4, threshold=30
esp_sleep_enable_touch_wakeup();
5. ULP Co-Processor Wake
The ULP (Ultra Low Power) co-processor can perform sensor readings while the main CPU is asleep, then wake the main CPU when a condition is met. See the ULP section below.
ULP Co-Processor: Computing While Asleep
The ESP32's ULP co-processor is a tiny RISC-V (ESP32-S2/S3) or custom (ESP32) processor that runs from the RTC slow memory while the main CPU and Wi-Fi/Bluetooth are powered down. It can:
- Read analog values from the ADC (ADC1 only — ADC2 is used by Wi-Fi)
- Read touch sensors
- Control RTC GPIO pins (digital I/O)
- Perform basic arithmetic and logic
- Execute code from 8 kB of RTC slow memory
- Wake the main CPU when a programmed condition is met
RTC Memory: Retaining Data Across Sleep Cycles
Data stored in RTC fast memory survives Deep Sleep. This is critical for counters, calibration values, and connection state:
// Data retained across Deep Sleep cycles
RTC_DATA_ATTR int bootCount = 0;
RTC_DATA_ATTR float calibrationOffset = 0.0;
void setup() {
bootCount++;
Serial.println("Boot count: " + String(bootCount));
// calibrationOffset persists across sleeps
}
Power Optimization Checklist
| Strategy | Savings | Implementation |
|---|---|---|
| Use Deep Sleep | ~75 mA → ~10 µA | esp_deep_sleep_start() |
| Disable unused peripherals | 10–30 mA | periph_module_disable() |
| Pull unused pins LOW | 1–5 mA | Set pinMode OUTPUT + digitalWrite LOW |
| Reduce flash frequency | 5–10 mA | Set to 40 MHz in menuconfig |
| Disable brownout detector | ~25 µA | esp_sleep_enable_timer_wakeup() |
| Use RTC GPIO for wake | ~1 µA vs touch | ext0/ext1 wake with pull resistors |
| Skip Wi-Fi reconnect every cycle | Significant | Store Wi-Fi creds in RTC memory |
Complete Deep Sleep Example: Battery-Powered Sensor
// ESP32 Battery Sensor - Wake, Read, Send, Sleep
#include <esp_sleep.h>
#include <WiFi.h>
#define uS_TO_S_FACTOR 1000000ULL
#define SLEEP_SECONDS 300 // 5 minutes
RTC_DATA_ATTR int bootCount = 0;
const char* ssid = "your-ssid";
const char* password = "your-password";
void setup() {
Serial.begin(115200);
bootCount++;
// Read sensor (ADC1 - safe with Wi-Fi)
int sensorValue = analogRead(36); // GPIO36 = ADC1_CH0
// Connect to Wi-Fi
WiFi.begin(ssid, password);
if (WiFi.waitForConnectResult() == WL_CONNECTED) {
// Send data to server
Serial.print("Boot:"); Serial.print(bootCount);
Serial.print(" Sensor:"); Serial.println(sensorValue);
WiFi.disconnect(true);
WiFi.mode(WIFI_OFF);
}
// Configure sleep
esp_sleep_enable_timer_wakeup(SLEEP_SECONDS * uS_TO_S_FACTOR);
Serial.println("Sleeping for " + String(SLEEP_SECONDS) + "s");
esp_deep_sleep_start();
}
void loop() { }
🔧 Explore GPIO Compatibility
Use our interactive pinout tool to find RTC-capable GPIO pins and check for strapping conflicts before designing your sleep circuit.
Open Interactive Pinout