22  Deep Sleep

Deep sleep is a power-saving mode on the ESP32 microcontroller that allows it to significantly reduce its power consumption by shutting down most of its components and functionalities. This mode is particularly useful for battery-powered applications where conserving energy is crucial.

22.1 Key Features of Deep Sleep

  1. Low Power Consumption:
    Deep sleep drastically reduces the chip’s power use. The current of a complete board also includes its regulators, LEDs and connected sensors.
  2. Preservation of Data:
    Variables explicitly placed in RTC (Real-Time Clock) memory can be retained during deep sleep. Waking restarts the application; ordinary RAM and peripheral state are not preserved.
  3. Wake-Up Sources:
    The ESP32 can be awakened from deep sleep by various events:
    • Timer: The internal RTC can be programmed to wake the device after a specified time interval.
    • External Pin: A change in the state of a designated GPIO pin can trigger a wake-up.
    • Touch Sensor: Touch pad sensors can be used to wake up the device.
    • ULP Coprocessor: The Ultra-Low-Power (ULP) coprocessor can run while the main CPU is in deep sleep and trigger a wake-up event based on sensor readings or other conditions.
  4. Application Examples:
    • Battery-Powered Sensors: Environmental sensors that need to periodically send data can spend most of their time in deep sleep, waking up only to take measurements and transmit data.
    • IoT Devices: Devices that need to conserve power, such as remote weather stations or smart home devices, can use deep sleep to extend battery life.

Try it · A battery budget

What uses the battery while you sleep?

Change the current and time spent in each state. Compare the share of time spent awake with its share of the battery charge used.

Average current
2.715 mA
Estimated runtime
24.6 days
Runtime vs always awake
29.5×

1,600 mAh usable · 589.3 hours estimated. Always awake: 20.0 hours.

Time and battery charge per cycleAwake for 3.3 percent of the time, using 98.2 percent of the charge.Time per cycleAwake 3.3% · asleep 96.7%Battery charge per cycleAwake 98.2% · asleep 1.8%
Orange: awakeBlue: asleep

One cycle = 2 s awake + 58 s asleep = 60 s.

Iavg = (80 × 2 + 0.05 × 58) / 60 = 2.715 mA

How is battery life estimated?

Average current = (awake current × awake time + sleep current × sleep time) / total cycle time. Convert µA to mA first: 1,000 µA = 1 mA. Usable capacity = rated capacity × usable fraction; runtime in hours = usable mAh / average mA.

The currents must be measured at the battery, including the board, sensors and regulator losses. The awake value is the average over the whole wake-up, measurement and transmission period. Sleep time is additional to awake time. This simple model excludes self-discharge, temperature and changing battery voltage, so very long runtimes are especially optimistic.

The defaults are illustrative, not measurements of the course board. Use measured total battery-side currents for your own project. Hardware configuration matters; see the ESP32 sleep-mode documentation.

22.2 Deep Sleep code

Download code
#define uS_TO_S_FACTOR 1000000ULL  /* Conversion factor for micro seconds to seconds */
#define TIME_TO_SLEEP  10        /* Time ESP32 will go to sleep (in seconds) */


void setup(){
  Serial.begin(115200);
  Serial.println("Good morning!");
  delay(2000); //Take some time to open up the Serial Monitor

  // turn on LED
  Serial.println("turn on LED");
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, HIGH);
  delay(2000); //Take some time to open up the Serial Monitor

  /*
  First we configure the wake up source
  We set our ESP32 to wake up every TIME_TO_SLEEP seconds
  */
  esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
  Serial.println("Setup ESP32 to sleep for every " + String(TIME_TO_SLEEP) +
  " Seconds");

  
  Serial.println("Going to sleep now");
  Serial.flush(); 
  esp_deep_sleep_start();
  Serial.println("This will never be printed");
}

void loop(){
  //This is not going to be called
}

22.3 Deep Sleep wake with touch

Adapted from here.

Download code
/*
Deep Sleep with Touch Wake Up
=====================================
This code displays how to use deep sleep with
a touch as a wake up source and how to store data in
RTC memory to use it over reboots

ESP32 can have multiple touch pads enabled as wakeup source
ESP32-S2 and ESP32-S3 supports only 1 touch pad as wakeup source enabled

This code is under Public Domain License.

Author:
Pranav Cherukupalli <cherukupallip@gmail.com>
*/

#if CONFIG_IDF_TARGET_ESP32
#define THRESHOLD 40   /* Greater the value, more the sensitivity */
#else                  //ESP32-S2 and ESP32-S3 + default for other chips (to be adjusted) */
#define THRESHOLD 5000 /* Lower the value, more the sensitivity */
#endif

RTC_DATA_ATTR int bootCount = 0;
touch_pad_t touchPin;
/*
Method to print the reason by which ESP32
has been awaken from sleep
*/
void print_wakeup_reason() {
  esp_sleep_wakeup_cause_t wakeup_reason;

  wakeup_reason = esp_sleep_get_wakeup_cause();

  switch (wakeup_reason) {
    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.printf("Wakeup was not caused by deep sleep: %d\n", wakeup_reason); break;
  }
}

/*
Method to print the touchpad by which ESP32
has been awaken from sleep
*/
void print_wakeup_touchpad() {
  touchPin = esp_sleep_get_touchpad_wakeup_status();

#if CONFIG_IDF_TARGET_ESP32
  switch (touchPin) {
    case 0:  Serial.println("Touch detected on GPIO 4"); break;
    case 1:  Serial.println("Touch detected on GPIO 0"); break;
    case 2:  Serial.println("Touch detected on GPIO 2"); break;
    case 3:  Serial.println("Touch detected on GPIO 15"); break;
    case 4:  Serial.println("Touch detected on GPIO 13"); break;
    case 5:  Serial.println("Touch detected on GPIO 12"); break;
    case 6:  Serial.println("Touch detected on GPIO 14"); break;
    case 7:  Serial.println("Touch detected on GPIO 27"); break;
    case 8:  Serial.println("Touch detected on GPIO 33"); break;
    case 9:  Serial.println("Touch detected on GPIO 32"); break;
    default: Serial.println("Wakeup not by touchpad"); break;
  }
#else
  if (touchPin < TOUCH_PAD_MAX) {
    Serial.printf("Touch detected on GPIO %d\n", touchPin);
  } else {
    Serial.println("Wakeup not by touchpad");
  }
#endif
}

void setup() {
  Serial.begin(115200);
  delay(1000);  //Take some time to open up the Serial Monitor

  //Increment boot number and print it every reboot
  ++bootCount;
  Serial.println("Boot number: " + String(bootCount));

  //Print the wakeup reason for ESP32 and touchpad too
  print_wakeup_reason();
  print_wakeup_touchpad();

#if CONFIG_IDF_TARGET_ESP32
  //Setup sleep wakeup on Touch Pad 3 + 7 (GPIO15 + GPIO 27)
  touchSleepWakeUpEnable(T3, THRESHOLD);
  touchSleepWakeUpEnable(T7, THRESHOLD);

#else  //ESP32-S2 + ESP32-S3
  //Setup sleep wakeup on Touch Pad 3 (GPIO3)
  touchSleepWakeUpEnable(T3, THRESHOLD);

#endif

  //Go to sleep now
  Serial.println("Going to sleep now");
  esp_deep_sleep_start();
  Serial.println("This will never be printed");
}

void loop() {
  //This will never be reached
}

22.4 Save values between sleeps

Recommended tutorial here.

Download code
/*
Good tutorial
https://randomnerdtutorials.com/esp32-save-data-permanently-preferences/
*/
#include <Preferences.h> // Include the Preferences library

#define uS_TO_S_FACTOR 1000000ULL  /* Conversion factor for microseconds to seconds */
#define TIME_TO_SLEEP  10          /* Time ESP32 will go to sleep (in seconds) */

Preferences preferences;           // Create a Preferences object

void setup() {
  Serial.begin(115200);
  Serial.println("Good morning!");
  delay(100); // Take some time to open up the Serial Monitor

  // Turn on LED
  Serial.println("Turn on LED");
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, HIGH);
  delay(2000);

  // Open Preferences in RW mode
  preferences.begin("my-app", false); // "my-app" is the namespace, false = read/write mode

  // Retrieve stored values
  int storedInt = preferences.getInt("myInt", 0);      // Default value is 0 if not set
  float storedFloat = preferences.getFloat("myFloat", 0.0); // Default is 0.0
  String storedString = preferences.getString("myString", "Default");

  Serial.println("Retrieved values from flash:");
  Serial.println("Int: " + String(storedInt));
  Serial.println("Float: " + String(storedFloat));
  Serial.println("String: " + storedString);

  // Increment and store new values
  storedInt++;
  storedFloat += 1.5;
  storedString = "Wake #" + String(storedInt);

  preferences.putInt("myInt", storedInt);
  preferences.putFloat("myFloat", storedFloat);
  preferences.putString("myString", storedString);

  Serial.println("Stored updated values in flash:");
  Serial.println("Int: " + String(storedInt));
  Serial.println("Float: " + String(storedFloat));
  Serial.println("String: " + storedString);

  // Close Preferences
  preferences.end();

  // Configure the wake-up source
  esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
  Serial.println("Setup ESP32 to sleep for every " + String(TIME_TO_SLEEP) + " seconds");

  Serial.println("Going to sleep now");
  Serial.flush();
  esp_deep_sleep_start();

  Serial.println("This will never be printed");
}

void loop() {
  // This is not going to be called
}