Advanced ESP32 Sleep Modes & Wake-Up Sources

ESP32 Touch Wake-up UART Wake-up ESP32 Variants ESP32 ESP32-S2 ESP32-S3 Advanced Wake-up Methods

Introduction to Advanced ESP32 Sleep Modes

This guide expands on our ESP32 Sleep Modes documentation to cover advanced wake-up sources, ESP32 model differences, and specialized implementation techniques for optimizing your IoT projects.

For battery-powered applications, understanding these advanced sleep mode features is crucial to achieving maximum power efficiency while maintaining responsive device behavior.

ESP32 Model Differences

Different ESP32 model variants have varying capabilities regarding sleep modes and available wake-up sources. These differences are important to consider when designing your application.

RTC GPIO Availability

ESP32 Model Available RTC GPIOs
ESP32 0, 2, 4, 12-15, 25-27, 32-39
ESP32-S2 0-21
ESP32-S3 0-21

Touch Sensitivity and Behavior

ESP32 Model Touch Sensitivity Multiple Touch Wake-up
ESP32 Higher value = more sensitive Yes
ESP32-S2 Lower value = more sensitive No (single pin only)
ESP32-S3 Lower value = more sensitive No (single pin only)

External Wake-up Methods

ESP32 Model ext1 Wake-up Modes
ESP32 ESP_EXT1_WAKEUP_ALL_LOW, ESP_EXT1_WAKEUP_ANY_HIGH
ESP32-S2/S3 ESP_EXT1_WAKEUP_ANY_LOW, ESP_EXT1_WAKEUP_ANY_HIGH

Note: When porting code between different ESP32 models, be sure to adjust your wake-up configurations to match the capabilities and behavior of the specific model you're using.

Disabling Wake-up Sources

To disable previously enabled wake-up sources, use the esp_sleep_disable_wakeup_source() function. This can be useful when you need to dynamically change which events can wake up your ESP32.

#include 

void setup() {
  Serial.begin(115200);
  delay(1000);
  
  Serial.println("ESP32 Wake-up Sources Demo");
  
  // First, enable timer wake-up
  esp_sleep_enable_timer_wakeup(10000000); // 10 seconds
  Serial.println("Timer wake-up enabled");
  
  // Then enable touch wake-up
  touchSleepWakeUpEnable(T3, 40);
  Serial.println("Touch wake-up enabled");
  
  // Now disable just the timer wake-up
  esp_sleep_disable_wakeup_source(ESP_SLEEP_WAKEUP_TIMER);
  Serial.println("Timer wake-up disabled");
  
  // Or disable all wake-up sources
  // esp_sleep_disable_wakeup_source(ESP_SLEEP_WAKEUP_ALL);
  // Serial.println("All wake-up sources disabled");
  
  // Enable a different wake-up source
  esp_sleep_enable_ext0_wakeup(GPIO_NUM_27, HIGH);
  Serial.println("External wake-up enabled");
  
  Serial.println("Going to sleep...");
  esp_deep_sleep_start();
}

void loop() {
  // Not used
}

Available wake-up source arguments:

  • ESP_SLEEP_WAKEUP_TIMER - Timer wake-up
  • ESP_SLEEP_WAKEUP_EXT0 - External wake-up (RTC_IO)
  • ESP_SLEEP_WAKEUP_EXT1 - External wake-up (RTC_CNTL)
  • ESP_SLEEP_WAKEUP_TOUCHPAD - Touch sensor wake-up
  • ESP_SLEEP_WAKEUP_ULP - ULP program wake-up
  • ESP_SLEEP_WAKEUP_GPIO - GPIO wake-up (light sleep only)
  • ESP_SLEEP_WAKEUP_UART - UART wake-up (light sleep only)
  • ESP_SLEEP_WAKEUP_ALL - All wake-up sources

External Wake-up Methods

The ESP32 provides three different methods for waking up using external GPIOs, each with different capabilities and use cases.

Method Function GPIO Support Sleep Modes Features
ext0 esp_sleep_enable_ext0_wakeup() RTC GPIOs only All sleep modes Simple single-pin wake-up
ext1 esp_sleep_enable_ext1_wakeup() RTC GPIOs only All sleep modes Multiple pins with logic operations (ANY, ALL)
GPIO gpio_wakeup_enable() Any GPIO Light sleep only Flexible GPIO wake-up, supports all pins

ext1 Wake-up Example (Multiple Buttons)

This example demonstrates how to use ext1 wake-up to wake the ESP32 when any of multiple buttons are pressed.

#include 
#include "esp_sleep.h"
#include "driver/rtc_io.h"

int counter = 0;
const int ledPin = 2;                      // GPIO pin for onboard LED
const gpio_num_t buttonPin1 = GPIO_NUM_26; // RTC IO for pushbutton 1
const gpio_num_t buttonPin2 = GPIO_NUM_27; // RTC IO for pushbutton 2
#define BUTTON_PIN_BITMASK(GPIO) (1ULL << GPIO)  // Macro for individual GPIO bitmask

// Create a bitmask for GPIO 26 and GPIO 27
uint64_t bitmask = BUTTON_PIN_BITMASK(buttonPin1) | BUTTON_PIN_BITMASK(buttonPin2);

// Method to print the GPIO that triggered the wakeup
void print_GPIO_wake_up(){
  uint64_t GPIO_reason = esp_sleep_get_ext1_wakeup_status();
  Serial.print("GPIO that triggered the wake up: GPIO ");
  Serial.println((log(GPIO_reason))/log(2), 0);
}

void setup() {
    Serial.begin(115200);
    pinMode(ledPin, OUTPUT);
    pinMode(buttonPin1, INPUT_PULLDOWN); // pull-down resistor
    pinMode(buttonPin2, INPUT_PULLDOWN); // pull-down resistor

    // Configure GPIO 26 and GPIO 27 as RTC IOs for EXT1 wake-up
    rtc_gpio_deinit(buttonPin1);
    rtc_gpio_deinit(buttonPin2);

    // Enable EXT1 wake-up source
    esp_err_t result = esp_sleep_enable_ext1_wakeup(bitmask, ESP_EXT1_WAKEUP_ANY_HIGH);

    if (result == ESP_OK) {
        Serial.println("EXT1 Wake-Up set successfully.");
    } else {
        Serial.println("Failed to set EXT1 Wake-Up as wake-up source.");
    }
}

void loop() {
    Serial.printf("Counter: %d\n", counter);
    counter++;

    digitalWrite(ledPin, HIGH); // LED on to indicate wake-up
    delay(2000);
    digitalWrite(ledPin, LOW);  // Turn off LED before going to sleep

    Serial.println("Going into light sleep mode");
    delay(500);
    esp_light_sleep_start();    // Enter light sleep

    // After wake-up, disable the hold function on the RTC GPIOs
    rtc_gpio_hold_dis(buttonPin1);
    rtc_gpio_hold_dis(buttonPin2);

    Serial.println("----------------------");
    Serial.println("Returning from light sleep");
    // Print the GPIO (button) that caused the wake-up
    print_GPIO_wake_up();
}

Circuit Diagram:

Connect pushbuttons to GPIO 26 and 27 as follows:

  • Connect one terminal of each button to GND
  • Connect the other terminal of the first button to GPIO 26
  • Connect the other terminal of the second button to GPIO 27
  • The code uses internal pull-down resistors, so no external resistors are needed

GPIO Wake-up Example (Any GPIO)

This method allows you to use any GPIO pin for wake-up, not just RTC GPIOs, but it only works with light sleep mode.

#include 
#include "esp_sleep.h"
#include "driver/gpio.h"

int counter = 0;
const int ledPin = 2;                      // GPIO pin for onboard LED
const gpio_num_t buttonPin1 = GPIO_NUM_26; // GPIO for pushbutton 1
const gpio_num_t buttonPin2 = GPIO_NUM_27; // GPIO for pushbutton 2

int wakeup_gpio; // Variable to store the GPIO that caused wake-up

// ISR for buttonPin1
void IRAM_ATTR handleInterrupt1() {
    wakeup_gpio = buttonPin1;
}

// ISR for buttonPin2
void IRAM_ATTR handleInterrupt2() {
    wakeup_gpio = buttonPin2;
}

void setup() {
    Serial.begin(115200);
    pinMode(ledPin, OUTPUT);
    pinMode(buttonPin1, INPUT_PULLDOWN); // pull-down resistor
    pinMode(buttonPin2, INPUT_PULLDOWN); // pull-down resistor

    // Configure GPIOs as wake-up source
    gpio_wakeup_enable(buttonPin1, GPIO_INTR_HIGH_LEVEL); // Trigger wake-up on high level
    gpio_wakeup_enable(buttonPin2, GPIO_INTR_HIGH_LEVEL); // Trigger wake-up on high level

    // Enable GPIO wake-up source
    esp_err_t result = esp_sleep_enable_gpio_wakeup();

    if (result == ESP_OK) {
        Serial.println("GPIO Wake-Up set successfully.");
    } else {
        Serial.println("Failed to set GPIO Wake-Up as wake-up source.");
    }

    // Attach interrupts to GPIO pins
    attachInterrupt(digitalPinToInterrupt(buttonPin1), handleInterrupt1, RISING);
    attachInterrupt(digitalPinToInterrupt(buttonPin2), handleInterrupt2, RISING);
}

void loop() {
    Serial.printf("Counter: %d\n", counter);
    counter++;

    digitalWrite(ledPin, HIGH); // LED on to indicate wake-up
    delay(2000);
    digitalWrite(ledPin, LOW);  // Turn off LED before going to sleep

    Serial.println("Going into light sleep mode");
    delay(500);
    esp_light_sleep_start();    // Enter light sleep

    Serial.println("----------------------");
    Serial.println("Returning from light sleep");

    // Print the GPIO that caused the wake-up
    Serial.printf("Wake-up caused by GPIO %d\n", wakeup_gpio);
}

Important Notes:

  • The GPIO wake-up method only works with light sleep, not deep sleep or hibernation
  • When using interrupts to determine which pin triggered the wake-up, make sure to use the IRAM_ATTR attribute for the interrupt handlers
  • You can use any GPIO with this method, not just RTC GPIOs

Touch Wake-up

The ESP32 can wake up from sleep when touch is detected on its touch-sensitive pins. This allows for creating touch-activated devices that consume minimal power when idle.

Touch Pin Mapping

ESP32 Touch Pins

  • T0 (GPIO 4)
  • T1 (GPIO 0)
  • T2 (GPIO 2)
  • T3 (GPIO 15)
  • T4 (GPIO 13)
  • T5 (GPIO 12)
  • T6 (GPIO 14)
  • T7 (GPIO 27)
  • T8 (GPIO 33)
  • T9 (GPIO 32)

ESP32-S2/S3 Differences

ESP32-S2 and ESP32-S3 have different touch pin mappings compared to the original ESP32. Please refer to the technical documentation for your specific model when mapping touch pins.

Additionally, these models can only wake up on a single touch pin (unlike the original ESP32 which supports multiple touch pins for wake-up).

Touch Wake-up Example

#include 

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

int counter = 0;
const int ledPin = 2;  // GPIO pin for onboard LED
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);
  pinMode(ledPin, OUTPUT);

  delay(1000);  //Take some time to open up the Serial Monitor

#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

}

void loop() {
  Serial.printf("Counter: %d\n", counter);
  counter++;

  digitalWrite(ledPin, HIGH); // LED on to indicate wake-up
  delay(5000);
  digitalWrite(ledPin, LOW);  // LED off before going to sleep

  Serial.println("Going into deep sleep...");
  delay(500);
  esp_deep_sleep_start(); 
  
  // If using light sleep instead, uncomment this block and comment out esp_deep_sleep_start() above
  /*
  Serial.println("Going into light sleep...");
  delay(500);
  esp_light_sleep_start();
  
  Serial.println("----------------------");
  Serial.println("Returning from light sleep");  
  delay(2000);
  
  print_wakeup_reason();
  print_wakeup_touchpad();
  */
}

Circuit Diagram:

For simple testing, connect a jumper wire to GPIO 15 (T3) or GPIO 27 (T7). Touching the wire will trigger the wake-up.

For a more reliable touch sensor:

  • Connect a jumper wire to the touch pin
  • Connect the other end to a small piece of aluminum foil or a metal surface
  • Optionally add a 1MΩ resistor between the touch pin and GND to reduce sensitivity to false triggers

Important Notes:

  • Touch sensitivity settings differ between ESP32 models
  • For ESP32: Higher threshold values increase sensitivity
  • For ESP32-S2/S3: Lower threshold values increase sensitivity
  • The reliability of touch wake-up depends heavily on your hardware design
  • A small capacitor (10-100nF) between the touch pin and ground can help filter noise

UART Wake-up

The ESP32 can wake up from light sleep when data is received on a UART port. This is useful for applications where the ESP32 needs to respond to serial commands from sensors or other microcontrollers while minimizing power consumption.

UART Wake-up Limitations:

  • UART wake-up is only available in light sleep mode, not deep sleep or hibernation
  • The ESP32 can be configured to wake up on any of its UART interfaces (UART0, UART1, UART2)
  • The wake-up is triggered by a specific number of signal transitions (edges) on the RX pin

UART Wake-up Example

#include 
#include 

int counter = 0;
const int ledPin = 2;         // GPIO pin for onboard LED
String receivedMessage = "";  // Variable to store the complete message

// 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;
    case ESP_SLEEP_WAKEUP_UART:     Serial.println("Wakeup caused by UART"); break;
    default:                        Serial.printf("Wakeup was not caused by light sleep: %d\n", wakeup_reason); break;
  }
}

void setup() {
  Serial.begin(115200);
  pinMode(ledPin, OUTPUT);

  delay(1000);  //Take some time to open up the Serial Monitor

  // Enable UART wake-up from light sleep
  uart_set_wakeup_threshold(UART_NUM_0, 3);  // 3 edges on U0RXD to wakeup
  esp_sleep_enable_uart_wakeup(0);           // UART0 (default Serial (includes Serial Monitor))
}

void loop() {
  Serial.printf("Counter: %d\n", counter);
  counter++;

  digitalWrite(ledPin, HIGH); // LED on to indicate wake-up
  delay(5000);
  digitalWrite(ledPin, LOW); // LED off before going to sleep

  Serial.println("Going into light sleep...");
  delay(500);
  esp_light_sleep_start(); 
  
  Serial.println("----------------------");
  Serial.println("Returning from light sleep");  
  delay(2000);
  
  print_wakeup_reason();
   // Clear the internal wake-up indication by sending some extra data
  Serial.write(' ');   // Send a single space character

 while (Serial.available()) {
    char incomingChar = Serial.read();  // Read each character from the buffer
    
    if (incomingChar == '\n') {  // Check if the user pressed Enter (new line character)
      // Print the message
      Serial.print("You sent: ");
      Serial.println(receivedMessage);
      
      // Clear the message buffer for the next input
      receivedMessage = "";
    } else {
      // Append the character to the message string
      receivedMessage += incomingChar;
    }
  }  
}

Using UART Wake-up for Sensor Integration

UART wake-up is particularly useful when interfacing with external sensors or modules that communicate via serial. Here's an example application:

Example Use Case: Motion Detector

  1. Connect a PIR motion sensor module with serial output to ESP32's UART1
  2. Configure the ESP32 to wake up when the PIR sensor sends motion detection data
  3. When motion is detected, the sensor sends data that wakes up the ESP32
  4. ESP32 reads the motion data, performs actions (notification, logging, etc.)
  5. ESP32 returns to light sleep to conserve power until the next motion event

This approach is more power-efficient than using GPIO interrupts because the ESP32 only wakes up when actual data is available to process.

UART1/UART2 Wake-up Example

To wake up on UART1 or UART2 instead of the default UART0, use the following configuration:

// For UART1
uart_set_wakeup_threshold(UART_NUM_1, 3);  // 3 edges to wake up
esp_sleep_enable_uart_wakeup(1);           // Enable wake-up on UART1

// For UART2
uart_set_wakeup_threshold(UART_NUM_2, 3);  // 3 edges to wake up
esp_sleep_enable_uart_wakeup(2);           // Enable wake-up on UART2

Optimizing Wake-up Behavior

For battery-powered applications, it's important to optimize not just the sleep power consumption but also the wake-up behavior to minimize active time.

Best Practices for Wake-up Optimization

  1. Minimize active time:
    • Complete tasks quickly after wake-up
    • Avoid unnecessary delays or serial prints during debugging
    • Return to sleep as soon as possible
  2. Choose appropriate wake-up sources:
    • Use deep sleep with timer for periodic sensors
    • Use light sleep with UART for sensor modules that send data
    • Use touch wake-up for user-triggered events
  3. Debounce external wake-up sources:
    • For mechanical buttons, add hardware debounce (RC filter)
    • For touch sensors, add appropriate threshold settings
    • For UART, set appropriate wake-up thresholds
  4. Check wake-up reason and handle accordingly:
    • Use esp_sleep_get_wakeup_cause() to determine what triggered wake-up
    • Only execute the code needed for that specific wake-up source
    • Skip unnecessary initialization for certain wake-up types

Optimized Wake-up Code Example

#include 

RTC_DATA_ATTR int bootCount = 0;
const int sensorPin = 36;     // Analog sensor pin
const int buttonPin = 27;     // External wake-up button pin
const int ledPin = 2;         // Indicator LED

// Time configurations
#define SLEEP_DURATION_NORMAL 1800   // 30 minutes in seconds
#define SLEEP_DURATION_ALERT 60      // 1 minute in seconds for alert mode
uint64_t sleepDuration = SLEEP_DURATION_NORMAL;

// Thresholds
#define SENSOR_THRESHOLD 2000        // Threshold for sensor readings
#define uS_TO_S_FACTOR 1000000ULL    // Conversion factor for seconds to microseconds

void setup() {
  // Only initialize serial for the first few boots (for debugging)
  if (bootCount < 5) {
    Serial.begin(115200);
    delay(100); // Brief delay for serial to initialize
    
    Serial.println("Boot count: " + String(bootCount));
    esp_sleep_wakeup_cause_t wakeup_reason = esp_sleep_get_wakeup_cause();
    Serial.println("Wake-up reason: " + String(wakeup_reason));
  }
  
  // Quick GPIO setup
  pinMode(ledPin, OUTPUT);
  pinMode(buttonPin, INPUT_PULLDOWN);
  
  // Increment boot count
  bootCount++;
  
  // Determine why we woke up and handle accordingly
  esp_sleep_wakeup_cause_t wakeup_reason = esp_sleep_get_wakeup_cause();
  
  switch(wakeup_reason) {
    case ESP_SLEEP_WAKEUP_EXT0: // Button was pressed
      handleButtonWakeup();
      break;
      
    case ESP_SLEEP_WAKEUP_TIMER: // Timer triggered wake-up
      handleTimerWakeup();
      break;
      
    default: // First boot or reset
      performInitialSetup();
      break;
  }
  
  // Configure wake-up sources for next sleep
  esp_sleep_enable_timer_wakeup(sleepDuration * uS_TO_S_FACTOR);
  esp_sleep_enable_ext0_wakeup(GPIO_NUM_27, HIGH);
  
  // Go to sleep
  if (bootCount < 5) {
    Serial.println("Going to sleep for " + String(sleepDuration) + " seconds");
    Serial.flush();
  }
  
  // Quick flash to indicate going to sleep
  digitalWrite(ledPin, HIGH);
  delay(50);
  digitalWrite(ledPin, LOW);
  
  esp_deep_sleep_start();
}

void loop() {
  // Never reached with deep sleep
}

void handleButtonWakeup() {
  // Button was pressed - user interaction needed
  digitalWrite(ledPin, HIGH); // Turn on LED
  
  // Read sensor and perform action
  int sensorValue = analogRead(sensorPin);
  
  // Process data and decide on next sleep duration
  if (sensorValue > SENSOR_THRESHOLD) {
    // Alert condition detected - sleep for shorter time
    sleepDuration = SLEEP_DURATION_ALERT;
    
    // Blink LED to indicate alert
    for (int i = 0; i < 5; i++) {
      digitalWrite(ledPin, LOW);
      delay(100);
      digitalWrite(ledPin, HIGH);
      delay(100);
    }
  } else {
    // Normal condition - standard sleep time
    sleepDuration = SLEEP_DURATION_NORMAL;
    delay(1000); // Keep LED on for a second
  }
  
  digitalWrite(ledPin, LOW); // Turn off LED
}

void handleTimerWakeup() {
  // Periodic wake-up for sensor reading
  
  // Quick sensor read without turning on LED to save power
  int sensorValue = analogRead(sensorPin);
  
  // Adjust sleep duration based on reading
  if (sensorValue > SENSOR_THRESHOLD) {
    sleepDuration = SLEEP_DURATION_ALERT;
    
    // Brief LED flash to indicate alert condition
    digitalWrite(ledPin, HIGH);
    delay(50);
    digitalWrite(ledPin, LOW);
  } else {
    sleepDuration = SLEEP_DURATION_NORMAL;
  }
}

void performInitialSetup() {
  // First boot initialization
  digitalWrite(ledPin, HIGH);
  
  // Perform any one-time setup
  sleepDuration = SLEEP_DURATION_NORMAL;
  
  delay(1000);
  digitalWrite(ledPin, LOW);
}

Power-Saving Techniques in this Example:

  • Serial output limited to first few boots to save power during normal operation
  • Different handling based on wake-up source (timer vs button)
  • Adaptive sleep duration based on sensor readings
  • Minimal LED usage to reduce power consumption
  • Quick processing and return to sleep
  • Use of RTC memory to track boot count and state across sleep cycles