Skip to content

Deep Sleep Power Optimization

Deep Sleep Power Optimization

This section covers ESP32 deep sleep mode configuration and optimization for achieving ultra-low power consumption in battery-powered IoT button applications. By the end of this section, you will be able to:

  • Understand ESP32 power modes and their current consumption
  • Configure deep sleep with wake-on-button-press
  • Measure and optimize sleep current
  • Explain the sleep/wake cycle for button applications

Before starting this section, please ensure:

  • Completed 04-04. Button Circuit Design
  • Basic understanding of ESP32 power management features
  • Access to a multimeter for current measurements

The ESP32 offers multiple power modes with significantly different current consumption:

ModeCurrent (ESP32-C3)DescriptionUse Case
Active (WiFi on)~60-80 mACPU running, WiFi connectedNormal operation
Active (WiFi off)~15-25 mACPU running, no radioLocal processing
Modem Sleep~3-5 mACPU running, WiFi idleBrief pauses
Light Sleep~0.5-1 mACPU paused, can wake quicklyShort idle periods
Deep Sleep~5-10 µACPU off, RTC peripheral onlyExtended idle
Hibernation~2.5 µARTC memory off, only RTC timerMaximum savings

For the IoT button, deep sleep is the key mode — the device spends nearly all its life in this state, waking only briefly to send a message when the button is pressed.

In deep sleep mode:

  • CPU is powered off
  • Most RAM loses content (RTC slow memory retains data)
  • WiFi/BT are powered off
  • RTC peripherals remain active (GPIO, timer, ULP coprocessor)
  • Wake-up sources remain monitored (GPIO, timer, touch sensor)
┌─────────────────────────────────────────────────────────┐
│ Deep Sleep State │
│ │
│ ┌──────────────────┐ ┌──────────────────────────┐ │
│ │ CPU (OFF) │ │ WiFi/BT (OFF) │ │
│ └──────────────────┘ └──────────────────────────┘ │
│ │
│ ┌──────────────────┐ ┌──────────────────────────┐ │
│ │ RTC Memory │ │ RTC Peripherals │ │
│ │ (Retains data) │ │ (GPIO wake, Timer) │ │
│ └──────────────────┘ └──────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ Main Memory (SRAM) — Content lost │ │
│ └──────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
Wake-Up SourceConfigurationButton Applicability
EXT0RTC GPIO → level change✅ GPIO 2 → LOW wakes (button press)
EXT1Multiple RTC GPIOs → pattern⚠️ For multiple buttons
TimerRTC timer → time elapsed✅ Periodic wake for status check
TouchCapacitive touch sensor❌ Not needed for button
ULPUltra-low-power coprocessor❌ Overkill for simple button

Step 1: Configure Deep Sleep with GPIO Wake-Up

Section titled “Step 1: Configure Deep Sleep with GPIO Wake-Up”
#include <esp_sleep.h>
// Pin definitions
const int BUTTON_PIN = 2; // GPIO 2 for button (wake source)
const int LED_PIN = D10; // Built-in LED (GPIO 10 on XIAO)
void setup() {
Serial.begin(115200);
delay(100); // Allow serial to initialize
// Check wake-up reason
esp_sleep_wakeup_cause_t wake_reason = esp_sleep_get_wakeup_cause();
switch (wake_reason) {
case ESP_SLEEP_WAKEUP_EXT0:
Serial.println("Woke by button press");
break;
case ESP_SLEEP_WAKEUP_TIMER:
Serial.println("Woke by timer (should not happen in button mode)");
break;
default:
Serial.println("First power-on or unknown wake source");
break;
}
// Brief LED indication
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, HIGH);
delay(200);
digitalWrite(LED_PIN, LOW);
// Execute button action
performButtonAction();
// Go back to sleep
Serial.println("Going to deep sleep...");
Serial.flush();
delay(100); // Allow serial output to complete
// Configure wake source
esp_sleep_enable_ext0_wakeup((gpio_num_t)BUTTON_PIN, 0); // LOW = pressed
// Enter deep sleep
esp_deep_sleep_start();
}
void loop() {
// Never reaches here
}
void performButtonAction() {
// Connect to WiFi and send MQTT message
// This function will be detailed in sections 04-07 and 04-08
Serial.println("Button action executed");
}

Not all GPIOs can wake the ESP32 from deep sleep. Compatible RTC GPIOs on XIAO ESP32-C3:

GPIORTC Wake CompatibleRecommended For
GPIO 0YesBOOT button (not recommended)
GPIO 1YesButton
GPIO 2YesButton (recommended)
GPIO 3YesButton
GPIO 4YesStatus LED
GPIO 5YesStatus LED

To verify deep sleep current:

void setup() {
// ... normal setup ...
// Measure and report before sleeping
Serial.println("--- Power Report ---");
Serial.print("Active current: ~60-80 mA (WiFi on)\n");
Serial.print("Deep sleep current target: < 10 µA\n");
Serial.println("--------------------");
delay(50);
esp_deep_sleep_start();
}

Measurement method:

  1. Set multimeter to µA range
  2. Disconnect power
  3. Connect multimeter in series between battery + and XIAO VIN
  4. Press button to wake; observe current during active period
  5. Wait for deep sleep; read µA value on multimeter
Expected measurement sequence:
Time 0s: Wake → 60-80 mA (WiFi connect + MQTT publish)
Time 3-5s: Action complete → preparing sleep
Time 5s+: Deep sleep → ~5 µA
void maximizePowerSaving() {
// Disable WiFi before sleep (if not needed for next wake)
WiFi.disconnect(true);
WiFi.mode(WIFI_OFF);
}

The most impactful optimization is minimizing the time spent in active mode:

// Fast path — minimize delays in active mode
void optimizedButtonAction() {
unsigned long startTime = millis();
connectWiFi(); // ~1-3 seconds
publishMQTT(); // ~500ms
delay(10); // Allow MQTT to flush
WiFi.disconnect(true);
Serial.print("Active time: ");
Serial.print(millis() - startTime);
Serial.println(" ms");
// Immediate deep sleep — no unnecessary delays
}

For minimum sleep current, configure the button pin to avoid floating:

  • Internal pull-up is sufficient for most cases
  • For extremely low leakage, add a 10kΩ external pull-up resistor

The XIAO’s onboard regulator has a quiescent current of approximately 2-3 µA. This is included in the total sleep current measurement and cannot be eliminated.

  • Deep sleep current measures < 10 µA
  • Button press reliably wakes the device
  • Active time is minimized (< 5 seconds per press)
  • Device returns to deep sleep after action
  • No spontaneous wake-ups without button press

Issue 1: Higher Than Expected Sleep Current

Section titled “Issue 1: Higher Than Expected Sleep Current”

Symptom: Sleep current > 50 µA

Possible Causes:

  • GPIO floating (not configured as INPUT_PULLUP or OUTPUT)
  • Serial monitoring enabled (some boards keep USB active)
  • External components drawing current
  • Capacitors leaking

Solution:

// Check all GPIOs are properly configured before sleep
gpio_set_direction(GPIO_NUM_4, GPIO_MODE_OUTPUT);
gpio_set_level(GPIO_NUM_4, 0); // Set LOW to minimize leakage
gpio_set_direction(GPIO_NUM_5, GPIO_MODE_OUTPUT);
gpio_set_level(GPIO_NUM_5, 0);

Symptom: Device in deep sleep, button press does nothing

Possible Causes:

  • Wrong GPIO number for wake source
  • Button connected to non-RTC GPIO
  • Level configuration reversed (waking on HIGH instead of LOW)

Solution: Verify GPIO is in RTC wake range and test with:

// Temporarily test with timer wake-up instead
esp_sleep_enable_timer_wakeup(10 * 1000000); // Wake every 10 seconds
  • Minimize active time — connect WiFi, publish MQTT, sleep immediately
  • Disconnect WiFi before entering deep sleep
  • Use RTC GPIO pins for wake sources
  • Add serial flush before sleep to ensure complete output
  • Do not use delay() in setup unless necessary
  • Avoid leaving GPIO pins unconfigured
  1. Deep sleep reduces current to ~5 µA — the key to 100+ day battery life
  2. GPIO EXT0 wake-up allows the button press to wake the ESP32 instantly
  3. Minimizing active time is the most critical optimization
  4. Regular current measurement verifies power optimization is working
  5. All peripherals should be disabled before entering deep sleep

Target Audience: Alibaba.com IoT Pre-sales Engineers
Status: ✅ Completed