Skip to content

Deep Sleep Mode Configuration

Deep Sleep Mode Configuration

This section covers ESP32 deep sleep mode configuration for battery-powered e-paper display applications. Deep sleep dramatically reduces power consumption by shutting down the main CPU and most peripherals, keeping only the RTC (Real-Time Clock) active for timed wake-up. After completing this section, you will be able to:

  • Configure ESP32 deep sleep with timer wake-up
  • Calculate power savings between active and sleep modes
  • Implement periodic wake-refresh-sleep cycle for e-paper displays
  • Handle WiFi reconnection after deep sleep

Before starting this section, please ensure:

  • Basic ESP32 programming knowledge
  • Display is wired and working with GxEPD2 (see 02-03)
  • Power measurement tools (multimeter or power monitor)

The ESP32 has several power modes:

ModeCPUWiFi/BTRTCRAMTypical Current
ActiveRunningOnOnFull80-260 mA
Modem SleepRunningOffOnFull30-50 mA
Light SleepPausedOffOnFull5-10 mA
Deep SleepOffOffOnRTC only5-150 µA
HibernationOffOffOffOff0.5-5 µA

Deep sleep is the key to battery-powered operation: reducing consumption from ~100 mA (active) to ~10 µA (deep sleep) — a 10,000x reduction.

ESP32 supports these wake-up sources from deep sleep:

SourceDescriptionTypical Use
TimerRTC timer wakes after durationPeriodic data refresh
External (EXT0)GPIO level changeButton press
External (EXT1)Multiple GPIO ORMotion sensor
TouchTouch sensorUser interaction
ULP CoprocessorUltra-low-power sensor readingContinuous monitoring

For the e-paper display project, Timer wake-up is the primary mechanism.

Active Period (~10 seconds)
┌──────────────────────────────────────────────────────┐
│ 1. Wake from deep sleep │
│ 2. Reconnect WiFi (2-5 seconds) │
│ 3. Fetch data via MQTT (0.5 second) │
│ 4. Update display (3-5 seconds) │
│ 5. Enter deep sleep │
└──────────────────────────────────────────────────────┘
│ Timer wake-up (1 hour)
Deep Sleep (~10 µA)
#include <WiFi.h>
// Configuration
const char* ssid = "YOUR_SSID";
const char* password = "YOUR_PASSWORD";
const int SLEEP_HOURS = 1; // Wake up every hour
void setup() {
Serial.begin(115200);
Serial.println("Device waking up...");
// Connect WiFi
WiFi.begin(ssid, password);
int attempts = 0;
while (WiFi.status() != WL_CONNECTED && attempts < 20) {
delay(500);
Serial.print(".");
attempts++;
}
if (WiFi.status() == WL_CONNECTED) {
Serial.println("\nWiFi connected");
Serial.print("IP: ");
Serial.println(WiFi.localIP());
// TODO: Fetch data and update display here
delay(1000);
}
Serial.println("Entering deep sleep...");
// Configure deep sleep for 1 hour
esp_sleep_enable_timer_wakeup(SLEEP_HOURS * 3600 * 1000000ULL);
// Enter deep sleep
esp_deep_sleep_start();
}
void loop() {
// Never reaches here — device goes to sleep in setup()
}

Important: In deep sleep mode, loop() never runs. All logic must be in setup().

Full implementation combining display update and deep sleep:

#include <WiFi.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>
#include <GxEPD2_BW.h>
// Configuration
const char* ssid = "YOUR_SSID";
const char* password = "YOUR_PASSWORD";
const char* mqtt_server = "192.168.1.100";
const char* mqtt_topic = "factory/weather/data";
// Deep sleep settings
const unsigned long SLEEP_INTERVAL_US = 3600 * 1000000ULL; // 1 hour
// E-Paper pins
#define EPD_CS 5
#define EPD_DC 17
#define EPD_RST 16
#define EPD_BUSY 4
// Display constructor
GxEPD2_BW<GxEPD2_213_B72, GxEPD2_213_B72::HEIGHT> display(
GxEPD2_213_B72(EPD_CS, EPD_DC, EPD_RST, EPD_BUSY)
);
WiFiClient espClient;
PubSubClient client(espClient);
// Data variables
char temperature[8] = "--.-";
int humidity = 0;
char description[32] = "Loading...";
void callback(char* topic, byte* payload, unsigned int length) {
char buffer[256];
if (length >= 256) return;
memcpy(buffer, payload, length);
buffer[length] = '\0';
JsonDocument doc;
DeserializationError error = deserializeJson(doc, buffer);
if (!error) {
strlcpy(temperature, doc["temperature"] | "--.-", sizeof(temperature));
humidity = doc["humidity"] | 0;
strlcpy(description, doc["description"] | "Unknown", sizeof(description));
}
}
void updateDisplay() {
display.init(115200);
display.setRotation(1);
display.fillScreen(GxEPD_WHITE);
// Display temperature (large)
display.setFont(&FreeMonoBold18pt7b);
display.setCursor(10, 50);
display.print(temperature);
display.print(" C");
// Display humidity
display.setFont(&FreeMonoBold12pt7b);
display.setCursor(10, 80);
display.print("Humidity: ");
display.print(humidity);
display.print("%");
// Display description
display.setFont(&FreeMono9pt7b);
display.setCursor(10, 105);
display.print(description);
display.display();
display.powerOff(); // Turn off display power after update
}
void setup() {
Serial.begin(115200);
Serial.println("[WAKE] Starting...");
// Connect WiFi
WiFi.begin(ssid, password);
int retries = 0;
while (WiFi.status() != WL_CONNECTED && retries < 30) {
delay(500);
Serial.print(".");
retries++;
}
if (WiFi.status() == WL_CONNECTED) {
Serial.println("\n[WAKE] WiFi connected");
// Connect MQTT and fetch data
client.setServer(mqtt_server, 1883);
client.setCallback(callback);
if (client.connect("ESP32_Display_DeepSleep")) {
client.subscribe(mqtt_topic);
// Request fresh data by publishing to a trigger topic
client.publish("factory/weather/request", "update");
// Wait for data with timeout
unsigned long timeout = millis() + 5000;
while (millis() < timeout) {
client.loop();
delay(10);
}
}
// Update display
updateDisplay();
// Disconnect WiFi before sleep
client.disconnect();
WiFi.disconnect(true);
WiFi.mode(WIFI_OFF);
} else {
Serial.println("\n[WAKE] WiFi failed, using cached data");
updateDisplay(); // Show cached/blank display
}
Serial.println("[SLEEP] Entering deep sleep...");
// Configure wake-up timer
esp_sleep_enable_timer_wakeup(SLEEP_INTERVAL_US);
// Enter deep sleep
esp_deep_sleep_start();
}
void loop() {
// Never executed
}

Use RTC memory to preserve data across sleep cycles:

// RTC memory — preserved during deep sleep
RTC_DATA_ATTR int bootCount = 0;
RTC_DATA_ATTR char cachedTemperature[8] = "--.-";
RTC_DATA_ATTR int cachedHumidity = 0;
void setup() {
Serial.begin(115200);
// Increment boot counter
bootCount++;
Serial.print("Boot count: ");
Serial.println(bootCount);
// On first boot, use cached values
if (WiFi.status() != WL_CONNECTED) {
// Use cached data from RTC memory
Serial.print("Using cached temp: ");
Serial.println(cachedTemperature);
}
// ... update display and cache new data
strlcpy(cachedTemperature, temperature, sizeof(cachedTemperature));
cachedHumidity = humidity;
}

Make the sleep interval configurable:

// Sleep intervals in microseconds
#define ONE_MINUTE_US 60 * 1000000ULL
#define ONE_HOUR_US 3600 * 1000000ULL
#define SIX_HOURS_US 6 * 3600 * 1000000ULL
#define ONE_DAY_US 24 * 3600 * 1000000ULL
// Select interval based on use case
const unsigned long SLEEP_INTERVAL = ONE_HOUR_US;
// Dynamic interval — change based on conditions
void configureSleep(bool dataReceived) {
if (dataReceived) {
// Normal interval
esp_sleep_enable_timer_wakeup(ONE_HOUR_US);
} else {
// Retry sooner if data fetch failed
esp_sleep_enable_timer_wakeup(5 * ONE_MINUTE_US);
}
}

To measure actual power consumption:

Terminal window
# Measure current with multimeter
# 1. Set multimeter to DC mA range
# 2. Connect in series with ESP32 power line
# 3. Monitor during active and sleep phases
Expected measurements:
Active (WiFi + Display): 100-200 mA (~10 seconds)
Deep Sleep: 10-50 µA (~1 hour)
  • ESP32 enters deep sleep and wakes up at the configured interval
  • “Boot count” increments each wake cycle
  • Display updates correctly after each wake
  • WiFi reconnects successfully after deep sleep
  • Power consumption drops to µA range in sleep mode

Issue 1: ESP32 Won’t Wake from Deep Sleep

Section titled “Issue 1: ESP32 Won’t Wake from Deep Sleep”

Symptoms:

  • Device never wakes up
  • Display doesn’t update

Solutions:

  • Verify the timer configuration: esp_sleep_enable_timer_wakeup()
  • Check if GPIO wake-up is interfering
  • Try a shorter interval (10 seconds) for testing
  • Ensure EN pin is not being held low

Symptoms:

  • WiFi connection fails on wake-up
  • Need to power cycle to restore WiFi

Solutions:

// Force WiFi to reinitialize properly
WiFi.disconnect(true); // Delete stored credentials
WiFi.mode(WIFI_OFF); // Turn off WiFi hardware
delay(100);
WiFi.mode(WIFI_STA); // Set to station mode
WiFi.begin(ssid, password);

Symptoms:

  • After wake-up, display shows old data briefly

Solutions:

  • Call display.fillScreen(GxEPD_WHITE) before drawing new content
  • The e-paper’s bistable property means it retains the previous image until overwritten
  • Minimize active time — Everything in setup(), no delays in loop()
  • Disconnect WiFi before sleep — Saves power and avoids connection issues on wake
  • Use RTC memory for counters and cached data across sleep cycles
  • Test with short intervals (10 seconds) during development
  • Don’t use delay() extensively in active phase — Every millisecond counts
  • Avoid deep sleep with peripherals powered — Disconnect power to sensors
  • Don’t use WiFi scan before connect — It adds 3-5 seconds of active time
  1. Deep sleep reduces power from ~100 mA to ~10 µA — enabling battery-powered operation
  2. Timer wake-up is the primary mechanism for periodic display updates
  3. All logic must be in setup() — loop() never executes after deep sleep
  4. RTC memory persists data (but not variables) across sleep cycles
  5. Minimize active time — Design the wake cycle to be as brief as possible
  6. For a 1-hour refresh cycle, the ESP32 is in deep sleep ~99.7% of the time

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