1596 Hands-On Lab: Power Monitoring and Sleep Modes
1596.1 Learning Objectives
By the end of this lab, you will be able to:
- Implement and verify ESP32 deep sleep modes
- Measure current consumption using Wokwi simulation
- Configure timer and GPIO wake-up sources
- Optimize code for minimum power consumption
- Calculate and validate battery life predictions
1596.2 Lab Overview
This hands-on lab uses Wokwi simulation to explore power management techniques for ESP32. While simulation cannot perfectly replicate real-world current measurements, it demonstrates the concepts and code patterns needed for low-power design.
1596.3 Hands-On Lab: Power Monitoring and Sleep Modes
This simulation demonstrates ESP32 deep sleep with timer and GPIO wake-up. The serial output shows current state transitions.
1596.3.1 Basic Deep Sleep Code
Copy this code into the Wokwi editor to explore deep sleep:
/*
* ESP32 Deep Sleep Example
* Demonstrates timer-based wake-up for ultra-low power operation
*
* Power Profile:
* - Active: ~40mA (with serial)
* - Deep Sleep: ~10µA
*
* Battery Life Estimate:
* 2000mAh / (10µA × 0.997 + 40mA × 0.003) ≈ 16,000 hours ≈ 1.8 years
*/
#include <esp_sleep.h>
#define LED_PIN 2
#define SLEEP_SECONDS 10
// Boot counter stored in RTC memory (survives deep sleep)
RTC_DATA_ATTR int bootCount = 0;
void setup() {
Serial.begin(115200);
delay(100);
// Increment and print boot count
bootCount++;
Serial.println("\n\n=== ESP32 Deep Sleep Demo ===");
Serial.printf("Boot count: %d\n", bootCount);
// Print wake-up reason
print_wakeup_reason();
// Configure LED
pinMode(LED_PIN, OUTPUT);
// Flash LED to show we're alive
for (int i = 0; i < 3; i++) {
digitalWrite(LED_PIN, HIGH);
delay(100);
digitalWrite(LED_PIN, LOW);
delay(100);
}
// Simulate sensor reading
Serial.println("Reading sensors...");
delay(500);
float temperature = 20.0 + (bootCount % 10);
Serial.printf("Temperature: %.1f°C\n", temperature);
// Simulate data transmission
Serial.println("Transmitting data...");
delay(1000);
Serial.println("Transmission complete.");
// Calculate and display power estimate
calculate_power_estimate();
// Configure deep sleep
Serial.printf("\nEntering deep sleep for %d seconds...\n", SLEEP_SECONDS);
Serial.println("See you after the nap!\n");
Serial.flush();
// Configure wake-up source
esp_sleep_enable_timer_wakeup(SLEEP_SECONDS * 1000000ULL);
// Disable peripherals to minimize sleep current
// (In real hardware, this reduces from 10µA to ~5µA)
// Enter deep sleep
esp_deep_sleep_start();
// This line will never execute
Serial.println("This should never print");
}
void loop() {
// Never reached - deep sleep restarts from setup()
}
void print_wakeup_reason() {
esp_sleep_wakeup_cause_t reason = esp_sleep_get_wakeup_cause();
Serial.print("Wake-up reason: ");
switch (reason) {
case ESP_SLEEP_WAKEUP_TIMER:
Serial.println("Timer wake-up");
break;
case ESP_SLEEP_WAKEUP_EXT0:
Serial.println("External GPIO wake-up (EXT0)");
break;
case ESP_SLEEP_WAKEUP_EXT1:
Serial.println("External GPIO wake-up (EXT1)");
break;
case ESP_SLEEP_WAKEUP_TOUCHPAD:
Serial.println("Touchpad wake-up");
break;
default:
Serial.printf("Other/Power-on (code %d)\n", reason);
break;
}
}
void calculate_power_estimate() {
// Estimate based on observed behavior
float active_time_ms = 1800; // ~1.8 seconds active
float sleep_time_ms = SLEEP_SECONDS * 1000;
float cycle_time_ms = active_time_ms + sleep_time_ms;
float active_current_ma = 40.0; // With Serial
float sleep_current_ma = 0.010; // 10 µA
float avg_current = (active_current_ma * active_time_ms +
sleep_current_ma * sleep_time_ms) / cycle_time_ms;
float battery_mah = 2000;
float life_hours = battery_mah / avg_current;
float life_days = life_hours / 24;
Serial.println("\n--- Power Estimate ---");
Serial.printf("Active time: %.1f ms (%.1f mA)\n", active_time_ms, active_current_ma);
Serial.printf("Sleep time: %.0f ms (%.3f mA)\n", sleep_time_ms, sleep_current_ma);
Serial.printf("Average current: %.3f mA\n", avg_current);
Serial.printf("Battery life (2000 mAh): %.0f hours (%.1f days)\n",
life_hours, life_days);
}1596.4 Challenge 1: Optimize for 5-Year Battery Life
Goal: Modify the code above to achieve 5-year battery life on a 2000mAh battery.
Constraints:
- Maximum average current: 2000mAh / (5 × 365 × 24h) = 0.046 mA = 46 µA
- Active current: 40mA (can’t reduce in simulation)
- Sleep current: 10µA
Calculate: What sleep interval is needed?
Hint: Use the duty cycle equation and solve for sleep time.
1596.5 Challenge 2: Multi-Source Wake-Up
Goal: Extend the code to support both timer AND GPIO wake-up.
// Add GPIO wake-up capability
#define BUTTON_PIN 33 // Use GPIO33 for wake-up
void configure_wakeup_sources() {
// Timer wake-up (periodic)
esp_sleep_enable_timer_wakeup(3600 * 1000000ULL); // 1 hour
// GPIO wake-up (motion sensor interrupt)
esp_sleep_enable_ext0_wakeup(GPIO_NUM_33, 1); // Wake on HIGH
}
// In setup(), determine which source triggered wake:
void handle_wakeup() {
esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause();
if (cause == ESP_SLEEP_WAKEUP_TIMER) {
// Periodic reading - send data
read_and_transmit();
} else if (cause == ESP_SLEEP_WAKEUP_EXT0) {
// Motion detected - immediate alert!
send_motion_alert();
}
}1596.6 Challenge 3: Adaptive Duty Cycling
Goal: Implement adaptive sampling based on temperature change rate.
RTC_DATA_ATTR float last_temp = 0;
RTC_DATA_ATTR int consecutive_stable = 0;
uint32_t calculate_next_interval(float current_temp) {
float delta = abs(current_temp - last_temp);
last_temp = current_temp;
if (delta > 2.0) {
// Rapid change - sample every minute
consecutive_stable = 0;
return 60;
} else if (delta > 0.5) {
// Moderate change - sample every 5 minutes
consecutive_stable = 0;
return 300;
} else {
// Stable - extend interval up to 30 minutes
consecutive_stable++;
return min(1800, 300 + (consecutive_stable * 60));
}
}1596.7 Key Takeaways from This Lab
After completing this lab, you should understand:
Deep Sleep is Essential: Moving from idle (15-80mA) to deep sleep (10µA) provides 1,000-8,000× power reduction
RTC Memory Survives Sleep: Use
RTC_DATA_ATTRto preserve variables across deep sleep cyclesWake-Up Sources: Timer for periodic sensing, GPIO for event-driven response
Duty Cycle Math: Use \(I_{avg} = I_{active} \times D + I_{sleep} \times (1-D)\) to calculate battery life
Adaptive Strategies: Adjust sampling rate based on conditions to optimize power vs responsiveness
1596.8 Additional Wokwi Projects to Explore
Power Monitoring with INA219:
https://wokwi.com/projects/new/esp32
Add an INA219 current sensor to measure actual power consumption.
Multi-Sensor Low-Power Node:
Combine temperature, humidity, and motion sensors with optimized sleep.
LoRa Low-Power Transmitter:
Use SX1276 simulation for long-range, low-power communication.
1596.9 Summary
This lab demonstrated:
- ESP32 deep sleep implementation and wake-up sources
- RTC memory for preserving state across sleep cycles
- Power calculation and battery life estimation
- Adaptive duty cycling strategies
- Multi-source wake-up configuration
Continue to Interactive Tools for calculators and simulation tools.