OC
OceanRemote
Low-code IoT platform

๐Ÿ’ค ESP32 Deep Sleep Won't Wake - Complete Fix Guide

โš ๏ธ Symptom: Your ESP32 enters deep sleep but never wakes up, or wakes up immediately after going to sleep. Battery-powered projects drain faster than expected.

๐Ÿ” Common Causes of Deep Sleep Wake Issues

  • ๐Ÿ“Œ No Wake Source Configured โ€“ esp_sleep_enable_timer_wakeup() not called
  • ๐Ÿ“Œ Wrong GPIO for External Wake โ€“ Using RTC IO pin incorrectly
  • ๐Ÿ“Œ Wake Level Mismatch โ€“ HIGH vs LOW trigger level
  • ๐Ÿ“Œ RTC Memory Corruption โ€“ Variables stored in RTC memory not preserved
  • ๐Ÿ“Œ Touch Sensor Not Calibrated โ€“ Touch wake thresholds wrong
  • ๐Ÿ“Œ ULP Program Not Running โ€“ ULP coprocessor program not loaded or configured

๐Ÿ“Š ESP32 Deep Sleep Wake Sources

Wake SourcePower ConsumptionBest For
Timer Wake5-150ยตARegular interval readings
External Wake (GPIO)5-150ยตAButton press, motion sensor
Touch Wake5-150ยตATouch buttons
ULP Coprocessor25-150ยตASensor monitoring in sleep
Timer + GPIO5-150ยตABoth interval and external

1๏ธโƒฃ Timer Wake - Basic Working Example

The simplest deep sleep configuration:

void setup() {
    Serial.begin(115200);
    delay(1000);
    
    // Do your work here
    Serial.println("Woke up! Taking sensor reading...");
    
    // Configure wake up after 10 seconds (10,000,000 microseconds)
    esp_sleep_enable_timer_wakeup(10 * 1000000);
    
    Serial.println("Entering deep sleep for 10 seconds...");
    
    // Go to deep sleep
    esp_deep_sleep_start();
}

void loop() {
    // This will never run
}
๐Ÿ’ก Note: The loop() function is never called in deep sleep projects. All code goes in setup().

2๏ธโƒฃ External GPIO Wake - Button or Sensor Trigger

Wake ESP32 when a GPIO pin changes state:

Which GPIOs can wake ESP32?

GPIOsWake TypeNotes
0, 2, 4, 12, 13, 14, 15, 25, 26, 27, 32, 33RTC IOCan wake from deep sleep
Other GPIOsNon-RTCCannot wake from deep sleep!
// GPIO 33 is an RTC IO pin - can wake from deep sleep
#define WAKE_PIN 33

void setup() {
    Serial.begin(115200);
    delay(1000);
    
    // Configure wake pin
    // Wake when pin goes HIGH (button press)
    esp_sleep_enable_ext0_wakeup(WAKE_PIN, HIGH);
    
    // Or use ext1 for multiple pins:
    // esp_sleep_enable_ext1_wakeup(BIT(WAKE_PIN), ESP_EXT1_WAKEUP_ANY_HIGH);
    
    Serial.println("Going to sleep. Press button on GPIO 33 to wake.");
    
    esp_deep_sleep_start();
}

void loop() {}
โš ๏ธ Critical: Only RTC GPIO pins (0,2,4,12-15,25-27,32,33) can wake ESP32 from deep sleep!

3๏ธโƒฃ Touch Wake - Capacitive Touch Buttons

// Touch pins: T0(GPIO4), T1(GPIO0), T2(GPIO2), T3(GPIO15), 
// T4(GPIO13), T5(GPIO12), T6(GPIO14), T7(GPIO27), T8(GPIO33), T9(GPIO32)

#define TOUCH_PIN T0  // GPIO4

void setup() {
    Serial.begin(115200);
    delay(1000);
    
    // Calibrate touch threshold (run once, save value)
    int touchValue = touchRead(TOUCH_PIN);
    Serial.print("Touch value: ");
    Serial.println(touchValue);
    
    // Set threshold lower than normal reading
    // Normal: ~50, Touched: ~20
    touchSleepWakeUpEnable(TOUCH_PIN, 40);
    
    Serial.println("Touch to wake...");
    esp_deep_sleep_start();
}

void loop() {}

4๏ธโƒฃ Preserve Data Across Sleep with RTC Memory

Variables stored in RTC memory survive deep sleep:

// Variables in RTC memory keep their value across sleep
RTC_DATA_ATTR int bootCount = 0;

void setup() {
    Serial.begin(115200);
    delay(1000);
    
    // Increment boot counter (survives deep sleep)
    bootCount++;
    Serial.print("Boot number: ");
    Serial.println(bootCount);
    
    // Get wakeup reason
    esp_sleep_wakeup_cause_t wakeup_reason = esp_sleep_get_wakeup_cause();
    
    switch(wakeup_reason) {
        case ESP_SLEEP_WAKEUP_TIMER:
            Serial.println("Woke by timer");
            break;
        case ESP_SLEEP_WAKEUP_EXT0:
            Serial.println("Woke by GPIO button");
            break;
        case ESP_SLEEP_WAKEUP_TOUCH:
            Serial.println("Woke by touch");
            break;
        default:
            Serial.println("First boot or other wake");
    }
    
    // Configure 10 second timer wake
    esp_sleep_enable_timer_wakeup(10 * 1000000);
    
    Serial.println("Going to sleep...");
    esp_deep_sleep_start();
}

5๏ธโƒฃ ULP Coprocessor - Monitor Sensors in Deep Sleep

The ULP (Ultra Low Power) coprocessor can read sensors while main CPU sleeps:

// ULP example: Wake when temperature exceeds threshold
// This requires ULP RISC-V assembly programming (advanced)

// For simpler use, OceanRemote handles ULP automatically
// when you enable "Deep Sleep" mode in device configuration
๐Ÿ’ก OceanRemote users: Just enable "Deep Sleep" in your device configuration and set the wake interval. We handle all the low-level ULP programming for you!

6๏ธโƒฃ Debugging - Why Won't My ESP32 Wake?

Use this diagnostic sketch to verify wake configuration:

#include "esp_sleep.h"

void setup() {
    Serial.begin(115200);
    delay(1000);
    
    // Print wakeup reason from last sleep
    print_wakeup_reason();
    
    // Test: Configure timer wake
    esp_sleep_enable_timer_wakeup(10 * 1000000);
    
    // Test: Configure GPIO wake on pin 33
    // esp_sleep_enable_ext0_wakeup(GPIO_NUM_33, HIGH);
    
    Serial.println("Sleeping for 10 seconds...");
    esp_deep_sleep_start();
}

void print_wakeup_reason() {
    esp_sleep_wakeup_cause_t wakeup_reason = esp_sleep_get_wakeup_cause();
    
    switch(wakeup_reason) {
        case ESP_SLEEP_WAKEUP_UNDEFINED:
            Serial.println("First boot or reset (not wake from sleep)");
            break;
        case ESP_SLEEP_WAKEUP_EXT0:
            Serial.println("Wakeup caused by external signal using RTC_IO");
            break;
        case ESP_SLEEP_WAKEUP_EXT1:
            Serial.println("Wakeup caused by external signal using RTC_CNTL");
            break;
        case ESP_SLEEP_WAKEUP_TIMER:
            Serial.println("Wakeup caused by timer");
            break;
        case ESP_SLEEP_WAKEUP_TOUCHPAD:
            Serial.println("Wakeup caused by touchpad");
            break;
        case ESP_SLEEP_WAKEUP_ULP:
            Serial.println("Wakeup caused by ULP program");
            break;
        default:
            Serial.print("Wakeup reason: ");
            Serial.println(wakeup_reason);
    }
}

void loop() {}

โš ๏ธ Common Deep Sleep Mistakes

  • Using non-RTC GPIO for wake โ€“ Only specific pins can wake ESP32
  • Forgetting to enable wake source โ€“ Must call esp_sleep_enable_* before esp_deep_sleep_start()
  • Serial.print after sleep โ€“ Serial may not be ready immediately on wake
  • Not adding delay on wake โ€“ Add delay(1000) for Serial to initialize
  • Using WiFi in deep sleep โ€“ WiFi turns off in deep sleep
  • No pull-up/pull-down on wake pins โ€“ Floating pins cause random wakes

๐Ÿ”‹ Deep Sleep Power Consumption Reference

ModeCurrent DrawBattery Life (2000mAh)
Active + WiFi80-240mA8-25 hours
Modem Sleep15-30mA2-5 days
Light Sleep5-10mA8-16 days
Deep Sleep (timer)5-150ยตA1-5 months!
Hibernation2.5-5ยตA1-2 years

๐Ÿ”‹ Battery-Powered Temperature Sensor (Complete Example)

#include "esp_sleep.h"
#include "DHT.h"

#define DHTPIN 4
#define DHTTYPE DHT22

DHT dht(DHTPIN, DHTTYPE);
RTC_DATA_ATTR int bootCount = 0;

void setup() {
    Serial.begin(115200);
    delay(1000);
    
    bootCount++;
    
    // Print wake reason
    esp_sleep_wakeup_cause_t wakeup_reason = esp_sleep_get_wakeup_cause();
    if (wakeup_reason != ESP_SLEEP_WAKEUP_TIMER) {
        Serial.println("First boot - starting sensor readings");
    }
    
    // Read sensor (takes 2-3 seconds)
    dht.begin();
    float temp = dht.readTemperature();
    float humidity = dht.readHumidity();
    
    if (!isnan(temp)) {
        Serial.print("Temperature: ");
        Serial.print(temp);
        Serial.println("ยฐC");
        // Send to OceanRemote or other service here
    }
    
    // Configure timer wake for 5 minutes (5 * 60 * 1000000)
    esp_sleep_enable_timer_wakeup(300 * 1000000);
    
    Serial.print("Deep sleep for 5 minutes. Boot count: ");
    Serial.println(bootCount);
    
    esp_deep_sleep_start();
}

void loop() {}

โœ… Best Practices for Deep Sleep

  • Use RTC GPIO pins (0,2,4,12-15,25-27,32,33) for external wake
  • Add pull-up or pull-down resistors to wake pins to prevent false triggers
  • Store persistent data in RTC_DATA_ATTR variables
  • Add delay(1000) at start of setup() for Serial to initialize
  • Use esp_sleep_get_wakeup_cause() to debug wake sources
  • Consider hibernation mode for maximum battery life (2.5ยตA)
  • Test with short wake intervals (10 seconds) during development

โ“ Frequently Asked Questions

Q: How do I make ESP32 wake from deep sleep with a button?

A: Connect button to an RTC GPIO pin (0,2,4,12-15,25-27,32,33) with a pull-up resistor, then call esp_sleep_enable_ext0_wakeup(pin, LOW) for button to GND.

Q: What's the difference between deep sleep and hibernation?

A: Deep sleep (5-150ยตA) keeps RTC memory. Hibernation (2.5-5ยตA) loses RTC memory but consumes less power. Use hibernation if you don't need to preserve data.

Q: Why does my ESP32 wake immediately after going to sleep?

A: Your wake pin is already in the trigger state. Add a pull-up/pull-down resistor or invert the trigger level (HIGH vs LOW).

Q: Can I use WiFi in deep sleep?

A: No. WiFi turns off completely in deep sleep. Wake up, connect to WiFi, send data, then go back to sleep.

Q: Does OceanRemote support deep sleep?

A: Yes! OceanRemote's firmware generator has a "Deep Sleep" option. Just enable it and set your wake interval. We handle all the ULP and wake configuration for you. Generate your firmware โ†’

๐Ÿš€ Need a Battery-Optimized Solution?

OceanRemote's deep sleep mode includes:

  • โœ“ Configurable wake intervals
  • โœ“ Automatic sensor reading
  • โœ“ Data upload on wake
  • โœ“ Months of battery life
  • โœ“ No coding required
Generate Firmware โ†’

โœ… Quick Checklist