How to Use ESP32 Deep Sleep Mode
In this tutorial, you'll learn about How to Use ESP32 Deep Sleep Mode. We cover key concepts, practical examples, and best practices.
The Problem
You put the ESP32 into deep sleep to save battery, but the power consumption is still too high, the device does not wake up, or it resets immediately after waking. Deep sleep configuration is error-prone and incorrect settings drain the battery unnecessarily.
Quick Fix
Fix 1: Basic Deep Sleep with Timer Wake
WRONG โ not keeping the wake stub in RTC memory:
void loop() {
// (never called because setup runs, then deep sleep is entered)
}
void setup() {
Serial.begin(115200);
esp_deep_sleep_start(); // enters sleep immediately
}
// (wakes up and resets from setup, but no timer was configured)
RIGHT โ configure the wake timer first:
#include <esp_sleep.h>
void setup() {
Serial.begin(115200);
Serial.println("Woke up!");
// Configure timer to wake after 10 seconds
esp_sleep_enable_timer_wakeup(10 * 1000000); // microseconds
Serial.println("Entering deep sleep");
Serial.flush();
esp_deep_sleep_start();
}
// Output (repeated every 10 seconds):
// Woke up!
// Entering deep sleep
Fix 2: External Wake from GPIO
WRONG โ using any GPIO without checking RTC compatibility:
esp_sleep_enable_ext0_wakeup(GPIO_NUM_5, 1); // GPIO 5 is not RTC-capable
// ESP_ERR_INVALID_ARG: GPIO is not an RTC GPIO
RIGHT โ use only RTC-capable GPIOs:
// ESP32 RTC GPIOs: 0, 2, 4, 12, 13, 14, 15, 25, 26, 27, 32, 33, 34, 35, 36, 37, 38, 39
esp_sleep_enable_ext0_wakeup(GPIO_NUM_33, 1); // wake on HIGH at GPIO 33
// or
esp_sleep_enable_ext1_wakeup(1ULL << GPIO_NUM_33, ESP_EXT1_WAKEUP_ANY_HIGH);
Fix 3: Touch Wake
esp_sleep_enable_touchpad_wakeup();
// (touch sensor values must be calibrated first)
touch_pad_init();
touch_pad_set_voltage(TOUCH_HVOLT_2V7, TOUCH_LVOLT_0V5, TOUCH_HVOLT_ATTEN_1V);
touch_pad_config(TOUCH_PAD_GPIO4_CHANNEL, 0);
Fix 4: Power Consumption Still High
WRONG โ entering deep sleep without disconnecting peripherals:
esp_deep_sleep_start();
// (external sensors, LEDs, and voltage regulators still draw power)
RIGHT โ measure and minimize current draw:
# Expected deep sleep current:
# ESP32 (no peripherals): ~5-10 ยตA
# With touch wake: ~50 ยตA
# With ULP co-processor: ~150 ยตA
# To achieve the lowest power:
# 1. Disconnect all external components during sleep
# 2. Use GPIO pin to control external power via MOSFET
# 3. Disable internal pull-ups: gpio_pullup_dis(GPIO_NUM_X)
# 4. Set unused GPIOs to LOW: gpio_set_level(GPIO_NUM_X, 0)
// Reduce power further:
esp_deep_sleep_pd_config(ESP_PD_DOMAIN_RTC_SLOW_MEM, ESP_PD_OPTION_OFF);
esp_deep_sleep_pd_config(ESP_PD_DOMAIN_RTC_FAST_MEM, ESP_PD_OPTION_OFF);
// (turns off RTC memory, wake stub cannot run)
Fix 5: Wake from Deep Sleep Only (No Software Reset)
WRONG โ cannot distinguish wake source from a cold boot:
void setup() {
// (same code runs on first boot and after wake)
}
RIGHT โ check the wake cause:
void setup() {
Serial.begin(115200);
esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause();
switch (cause) {
case ESP_SLEEP_WAKEUP_TIMER:
Serial.println("Woke up by timer");
break;
case ESP_SLEEP_WAKEUP_EXT0:
Serial.println("Woke up by external signal");
break;
case ESP_SLEEP_WAKEUP_TOUCHPAD:
Serial.println("Woke up by touch");
break;
default:
Serial.println("Cold boot (not a wake)");
}
}
Fix 6: Keep ULP Processor Running During Sleep
// The Ultra Low Power (ULP) co-processor can run sensor reads in deep sleep
ulp_set_wakeup_period(0, 100000); // run every 100ms
esp_sleep_enable_ulp_wakeup();
Use DodaTech's Power Profiler to measure ESP32 deep sleep current draw and optimize battery life for production deployments.
Prevention
- Use only RTC GPIOs for external wake.
- Disconnect or power off external peripherals during sleep.
- Measure actual current with a multimeter.
- Use wake cause detection to differentiate cold boot from wake.
- Keep wake stub code minimal (runs from RTC memory).
Common Mistakes with deep sleep
- Forgetting that lazy evaluation defers computation until the value is forced, causing space leaks with unevaluated thunks
- Using
returnto exit a function early instead of wrapping a pure value in the monad - Mixing let bindings with <- bindings in do notation, producing type errors
These mistakes appear frequently in real-world ESP32 code. DodaTech's contributors have identified these patterns through analysis of open-source projects and production systems.
Practice Exercise
Write a pure function that safely divides two integers using Maybe, then test it with edge cases like division by zero and negative numbers.
This exercise reinforces the concepts covered in this guide. Try implementing it before checking online solutions.
FAQ
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro