Project 3: Multi-Sensor Data Logger with Deep Sleep
Project 3: Multi-Sensor Data Logger with Deep Sleep
Project Overview
| Attribute | Value |
|---|---|
| Difficulty | Intermediate-Advanced |
| Time Estimate | 2-3 weeks |
| Main Language | C |
| Alternatives | MicroPython, Rust, Arduino C++ |
| Primary Book | Making Embedded Systems by Elecia White |
| Knowledge Areas | Power Management, RTC Memory, ESP-NOW, Battery |
What Youโll Build
A battery-powered remote sensor node that wakes up periodically, reads sensors, transmits data over WiFi or ESP-NOW, then goes back to deep sleepโlasting weeks to months on a single battery.
Physical Setup:
- ESP32 powered by 3.7V 2000mAh LiPo battery
- BME280 sensor for temperature, humidity, pressure
- Optional: soil moisture sensor, PIR motion sensor
- Weatherproof enclosure for outdoor use
Power Consumption Profile:
Deep Sleep Mode:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ
โ Current: 10-150ยตA (bare module) โ
โ Duration: 14:55 of every 15 minutes โ
โ RTC timer running, GPIO wake possible โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Active Mode (WiFi transmitting):
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ
โ Current: 160-260mA โ
โ Duration: 2-5 seconds per cycle โ
โ Sensor read, WiFi connect, transmit โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Expected Battery Life (2000mAh, bare module):
- Wake every 15 minutes โ 60-75 days
- Wake on motion only โ 300+ days
Learning Objectives
By completing this project, you will be able to:
- Configure ESP32 deep sleep modes and understand power consumption
- Preserve state across sleep cycles using RTC memory
- Implement efficient WiFi connection with channel/BSSID caching
- Use ESP-NOW for ultra-low-power wireless transmission
- Design a power budget and calculate expected battery life
- Handle edge cases in battery-powered systems (low voltage, failed transmissions)
- Implement graceful degradation when resources are limited
Deep Theoretical Foundation
ESP32 Sleep Modes
The ESP32 offers multiple power states, each with different trade-offs between power consumption and wake-up latency.
Sleep Mode Comparison
โโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโ
โ Mode โ Current Draw โ Wake Time โ RAM Preserved โ
โโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโค
โ Active โ 160-260mA โ N/A โ Yes โ
โ Modem Sleep โ 20-30mA โ <1ms โ Yes โ
โ Light Sleep โ 0.8-1.1mA โ <1ms โ Yes โ
โ Deep Sleep โ 10-150ยตA โ ~300ms โ RTC only (8KB)โ
โ Hibernation โ 2.5ยตA โ ~300ms โ RTC slow only โ
โโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโ
What Remains Powered in Deep Sleep
Deep Sleep State:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ
โ POWERED OFF: POWERED ON: โ
โ โโโ Main CPU cores (both) โโโ RTC controller โ
โ โโโ Main RAM (520KB) โโโ RTC fast memory (8KB) โ
โ โโโ WiFi/BT radios โโโ RTC slow memory (8KB) โ
โ โโโ Most peripherals โโโ RTC timer โ
โ โโโ Flash (in standby) โโโ ULP coprocessor โ
โ โโโ Touch sensors (optional) โ
โ โโโ GPIO wake circuits โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
RTC Memory: Preserving State Across Sleep
When entering deep sleep, all main RAM is lost. RTC memory is your lifeline for maintaining state.
RTC Memory Attributes
// Fast RTC memory - accessible from main code
RTC_DATA_ATTR uint32_t boot_count = 0;
RTC_DATA_ATTR float last_temperature = 0.0;
RTC_DATA_ATTR uint8_t failed_transmissions = 0;
// Slow RTC memory - accessible from ULP coprocessor
RTC_SLOW_ATTR uint32_t ulp_counter = 0;
// NOINIT - survives software reset but not power cycle
RTC_NOINIT_ATTR uint32_t debug_flag = 0;
| Attribute | Size | Survives Deep Sleep | Survives Reset |
|---|---|---|---|
| RTC_DATA_ATTR | 8KB | Yes | No |
| RTC_SLOW_ATTR | 8KB | Yes | No |
| RTC_NOINIT_ATTR | Part of above | Yes | Yes |
| Normal variables | 520KB | No | No |
Wake-Up Sources
ESP32 supports multiple wake-up sources that can be combined:
// Timer wake-up (most common)
esp_sleep_enable_timer_wakeup(15 * 60 * 1000000ULL); // 15 minutes in ยตs
// GPIO ext0 (single GPIO, any level)
esp_sleep_enable_ext0_wakeup(GPIO_NUM_33, 1); // Wake on HIGH
// GPIO ext1 (multiple GPIOs, any HIGH or all LOW)
esp_sleep_enable_ext1_wakeup(mask, ESP_EXT1_WAKEUP_ANY_HIGH);
// Touch pad
esp_sleep_enable_touchpad_wakeup();
// ULP coprocessor
esp_sleep_enable_ulp_wakeup();
// Enter deep sleep
esp_deep_sleep_start(); // Code execution stops here!
After wake-up, determine what caused it:
esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause();
switch (cause) {
case ESP_SLEEP_WAKEUP_TIMER:
printf("Woke from timer\n");
break;
case ESP_SLEEP_WAKEUP_EXT0:
printf("Woke from GPIO\n");
break;
case ESP_SLEEP_WAKEUP_UNDEFINED:
printf("First boot (power-on reset)\n");
break;
}
Power Budget Calculation
Designing a battery-powered device requires careful power budgeting.
Example: 15-Minute Wake Interval with WiFi
Given:
- Battery capacity: 2000mAh
- Wake interval: 15 minutes (900 seconds)
- Active time: 3 seconds per wake
- Sleep current: 150ยตA (DevKit) or 10ยตA (bare module)
- Active current: 200mA average
Calculation (DevKit board):
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Sleep energy per cycle: โ
โ 150ยตA ร 897s = 0.0374mAh โ
โ โ
โ Active energy per cycle: โ
โ 200mA ร 3s = 0.167mAh โ
โ โ
โ Total per cycle: 0.204mAh โ
โ โ
โ Cycles per day: 24h ร 4/hour = 96 โ
โ Daily consumption: 96 ร 0.204mAh = 19.6mAh โ
โ โ
โ Battery life: 2000mAh / 19.6mAh = 102 days โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Calculation (bare module, 10ยตA sleep):
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Sleep energy: 10ยตA ร 897s = 0.0025mAh โ
โ Active energy: 200mA ร 3s = 0.167mAh โ
โ Total per cycle: 0.169mAh โ
โ Daily: 96 ร 0.169 = 16.2mAh โ
โ Battery life: 2000mAh / 16.2mAh = 123 days โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Key insight: For infrequent wake intervals, active-mode power dominates. For frequent wake intervals, sleep-mode current matters more.
WiFi Fast Connect
Standard WiFi connection takes 2-5 seconds. You can reduce this dramatically:
Connection Time Breakdown
Standard Connection:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ WiFi init โ ~200ms โ
โ Scan all channelsโ ~1500ms โ Major time sink! โ
โ Associate with APโ ~500ms โ
โ WPA2 handshake โ ~300ms โ
โ DHCP request โ ~500ms โ Another time sink! โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ Total โ ~3000ms โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Optimized Connection:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ WiFi init โ ~200ms โ
โ Connect to cachedโ ~300ms (skip scan, use saved channel/BSSID)โ
โ channel/BSSID โ โ
โ WPA2 handshake โ ~300ms โ
โ Static IP โ ~0ms (skip DHCP) โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ Total โ ~800ms โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Implementation
// Store connection info in RTC memory
RTC_DATA_ATTR uint8_t saved_channel = 0;
RTC_DATA_ATTR uint8_t saved_bssid[6] = {0};
void fast_wifi_connect() {
wifi_config_t wifi_config = {
.sta = {
.ssid = "YourSSID",
.password = "YourPassword",
.channel = saved_channel, // Use cached channel
},
};
// Copy cached BSSID
if (saved_channel != 0) {
memcpy(wifi_config.sta.bssid, saved_bssid, 6);
wifi_config.sta.bssid_set = true;
}
// Use static IP (skip DHCP)
tcpip_adapter_dhcpc_stop(TCPIP_ADAPTER_IF_STA);
tcpip_adapter_ip_info_t ip_info = {
.ip = { .addr = IP4_ADDR(192,168,1,200) },
.gw = { .addr = IP4_ADDR(192,168,1,1) },
.netmask = { .addr = IP4_ADDR(255,255,255,0) },
};
tcpip_adapter_set_ip_info(TCPIP_ADAPTER_IF_STA, &ip_info);
esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config);
esp_wifi_start();
esp_wifi_connect();
}
// After successful connection, save for next time
void save_connection_info() {
wifi_ap_record_t ap_info;
esp_wifi_sta_get_ap_info(&ap_info);
saved_channel = ap_info.primary;
memcpy(saved_bssid, ap_info.bssid, 6);
}
ESP-NOW: Ultra-Low-Power Alternative
ESP-NOW is Espressifโs proprietary protocol for peer-to-peer communication without WiFi infrastructure.
ESP-NOW vs WiFi
WiFi Connection: ESP-NOW:
Phone/PC โโโ Router โโโ ESP32 ESP32 โโโโโโโโโโโโถ ESP32
(Direct, no router)
- Requires router - No router needed
- 2-5 second connection - Instant (10-50ms)
- Works with internet - Local only (250m range)
- Higher power (160-260mA) - Lower power (80-150mA)
- Unlimited payload - 250 bytes max
When to use ESP-NOW:
- Hub + sensor node architecture
- No internet required
- Maximum battery life critical
- Low data volume
ESP-NOW Implementation
// Initialize ESP-NOW (on sender)
esp_now_init();
// Add peer (receiver's MAC address)
esp_now_peer_info_t peer = {
.channel = 0,
.encrypt = false,
};
memcpy(peer.peer_addr, receiver_mac, 6);
esp_now_add_peer(&peer);
// Send data
typedef struct {
float temperature;
float humidity;
uint8_t battery_percent;
} sensor_data_t;
sensor_data_t data = { 22.5, 48.0, 87 };
esp_now_send(receiver_mac, (uint8_t*)&data, sizeof(data));
ESP-NOW Power Comparison
WiFi transmission cycle:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ WiFi connect: 200mA ร 2.5s = 0.139mAh โ
โ Data transmit: 200mA ร 0.5s = 0.028mAh โ
โ Total: 0.167mAh per transmission โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
ESP-NOW transmission cycle:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Radio enable: 150mA ร 0.05s = 0.002mAh โ
โ Data transmit: 150mA ร 0.05s = 0.002mAh โ
โ Total: 0.004mAh per transmission โ
โ โ
โ Savings: ~40x less energy per transmission! โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Battery Voltage Monitoring
Monitoring battery voltage prevents deep discharge damage and enables low-battery warnings.
Voltage to Percentage Mapping
LiPo Battery Discharge Curve:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Voltage โ Percentage โ Notes โ
โโโโโโโโโโโผโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ 4.20V โ 100% โ Fully charged โ
โ 4.10V โ 90% โ โ
โ 4.00V โ 80% โ โ
โ 3.90V โ 60% โ Discharge curve flattens here โ
โ 3.80V โ 40% โ โ
โ 3.70V โ 20% โ Low battery warning โ
โ 3.60V โ 10% โ Critical โ
โ 3.30V โ 0% โ Empty - do not discharge below this! โ
โโโโโโโโโโโดโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Reading Battery Voltage
ESP32โs ADC can only read up to 3.3V. Use a voltage divider:
Battery (4.2V max)
โ
[R1] 100kฮฉ
โโโโโโโโโโโโ ESP32 ADC (GPIO34)
[R2] 100kฮฉ
โ
GND
Vout = Vin ร R2/(R1+R2) = 4.2 ร 0.5 = 2.1V (safe for ADC)
float read_battery_voltage() {
int raw = adc1_get_raw(ADC1_CHANNEL_6); // GPIO34
float voltage = (raw / 4095.0) * 3.3; // ADC to voltage
voltage *= 2; // Compensate for divider
return voltage;
}
uint8_t voltage_to_percent(float voltage) {
if (voltage >= 4.20) return 100;
if (voltage <= 3.30) return 0;
// Linear approximation (more accurate: use lookup table)
return (uint8_t)((voltage - 3.30) / (4.20 - 3.30) * 100);
}
Project Specification
Hardware Requirements
| Component | Quantity | Purpose |
|---|---|---|
| ESP32 DevKit or bare module | 1 | Main MCU |
| BME280 Sensor | 1 | Environmental sensing |
| 3.7V 2000mAh LiPo | 1 | Power source |
| TP4056 module | 1 | Battery charging |
| 100kฮฉ resistors | 2 | Voltage divider |
| Weatherproof enclosure | 1 | Outdoor protection |
Wiring Diagram
Battery (3.7V LiPo)
โ
โโโโโโโโโโ TP4056 (charging module)
โ
โโโโโโโโ ESP32 VIN
โ
ESP32 DevKit โ
โโโโโโโโโโโโโโโ โ
โ VIN โโโโโโโโโโโโโโโโโโโโโโโ
โ GND โโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ โ
โ GPIO34 โโโโโฌโโ[100k]โโโ Battery+โ
โ โ โ โ
โ โ [100k] โ
โ โ โ โ
โ โ โโโโโโโโโโโโ GND โโโโโ
โ โ
โ GPIO21 โโโโโโโโโโโโโโโโโโ BME280 SDA
โ GPIO22 โโโโโโโโโโโโโโโโโโ BME280 SCL
โ 3.3V โโโโโโโโโโโโโโโโโโ BME280 VCC
โ GND โโโโโโโโโโโโโโโโโโ BME280 GND
โ โ
โ GPIO33 โโโโโโโโโโโโโโโโโโ PIR Motion (optional)
โ GPIO2 โโโโโ[220ฮฉ]โโโโโโโ LED (status)
โโโโโโโโโโโโโโโ
Functional Requirements
- Sensor Reading
- Read BME280 every wake cycle
- Calculate battery percentage
- Store in RTC memory for comparison
- Deep Sleep Management
- Sleep for configurable interval (5-60 minutes)
- Support timer and GPIO wake sources
- Maintain boot count in RTC memory
- Data Transmission
- Support both WiFi and ESP-NOW modes
- Retry failed transmissions (max 3 attempts)
- Queue data locally if transmission fails
- Power Optimization
- Fast WiFi connect (<1 second target)
- Minimize active time
- Enter low-power mode for sensors
- Error Handling
- Detect and report low battery
- Handle WiFi connection failures
- Implement watchdog timer
Solution Architecture
System State Machine
โโโโโโโโโโโโโโโโ
โ BOOT โ
โ (Reset) โ
โโโโโโโโฌโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโ
โ INITIALIZE โ
โ - Check wake โ
โ cause โ
โ - Restore โ
โ RTC data โ
โโโโโโโโฌโโโโโโโโ
โ
โโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโ
โ โ โ
โผ โผ โผ
โโโโโโโโโโโโโ โโโโโโโโโโโโโ โโโโโโโโโโโโโ
โ TIMER โ โ GPIO โ โ FIRST_BOOTโ
โ WAKE โ โ WAKE โ โ โ
โโโโโโโฌโโโโโโ โโโโโโโฌโโโโโโ โโโโโโโฌโโโโโโ
โ โ โ
โโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโ
โ READ_SENSORS โ
โ - Temperatureโ
โ - Humidity โ
โ - Battery โ
โโโโโโโโฌโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโ
โโโโโ TRANSMIT โ
โ โ - WiFi/ESP-NOWโ
โ โ - Send data โ
โ โโโโโโโโฌโโโโโโโโ
โ โ
โ โโโโโโโดโโโโโโ
โ โผ โผ
โ [Success] [Failure]
โ โ โ
โ โ retry < 3?
โ โ โโโโโโโดโโโโโโ
โ โ โผ โผ
โ โ [Yes] [No]
โ โ โ โ
โ โ โโโโโโโโโโโโโค
โโโโโโ โ
โผ
โโโโโโโโโโโโโโโโโ
โ STORE_LOCAL โ
โ (queue for โ
โ next cycle) โ
โโโโโโโโโฌโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโ
โ DEEP_SLEEP โ
โ - Set timer โ
โ - Save state โ
โ - Enter sleepโ
โโโโโโโโโโโโโโโโ
Data Flow
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Sensor Node โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ Wake โ
โ โ โ
โ โผ โ
โ โโโโโโโโโโโ โโโโโโโโโโโ โโโโโโโโโโโ โ
โ โ BME280 โโโโโโ Process โโโโโโ Format โ โ
โ โ I2C Readโ โ & Store โ โ JSON โ โ
โ โโโโโโโโโโโ โ in RTC โ โโโโโโฌโโโโโ โ
โ โโโโโโโโโโโ โ โ
โ โ โ
โ โโโโโโโโโโโ โ โ
โ โ Battery โโโโโโโโโโโโโโโโโโโโโโโโโค โ
โ โ ADC Readโ โ โ
โ โโโโโโโโโโโ โ โ
โ โผ โ
โ โโโโโโโโโโโโโ โ
โ โ Send โ โ
โ โ via WiFi/ โ โ
โ โ ESP-NOW โ โ
โ โโโโโโโฌโโโโโโ โ
โ โ โ
โ โผ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โ Wireless
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Hub / MQTT Broker / Cloud โ
โ โ
โ Receives: {"temp":22.4,"hum":48,"press":1013,"bat":87,"boot":50}โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Key Data Structures
// Preserved across deep sleep in RTC memory
typedef struct {
uint32_t boot_count;
float last_temperature;
float last_humidity;
uint8_t failed_transmissions;
uint8_t wifi_channel;
uint8_t wifi_bssid[6];
uint32_t pending_readings;
} rtc_state_t;
RTC_DATA_ATTR rtc_state_t state = {0};
// Sensor reading packet
typedef struct {
float temperature;
float humidity;
float pressure;
float battery_voltage;
uint8_t battery_percent;
uint32_t boot_count;
uint32_t timestamp;
} sensor_packet_t;
Phased Implementation Guide
Phase 1: Basic Deep Sleep (Day 1-2)
Goal: Device sleeps and wakes on timer
- Verify Deep Sleep Entry
void app_main() { printf("Boot! Going to sleep for 10 seconds...\n"); esp_sleep_enable_timer_wakeup(10 * 1000000); esp_deep_sleep_start(); }- Serial monitor should print โBoot!โ every 10 seconds
- Measure Sleep Current
- Use multimeter in series with battery
- DevKit: expect 10-15mA (disappointing)
- Bare module: expect 10-150ยตA (excellent)
- Add Boot Counter
RTC_DATA_ATTR uint32_t boot_count = 0; void app_main() { boot_count++; printf("Boot #%d\n", boot_count); // ... }
Checkpoint: Boot count increments across sleep cycles
Phase 2: Sensor Integration (Day 3-4)
Goal: Read and display sensor data each wake
- Initialize BME280
- Use I2C with short wires for reliability
- Put sensor in forced mode (single measurement)
- Read immediately after wake
- Optimize Sensor Power
- BME280 draws ~3.6ยตA in sleep mode
- Wake sensor, read, put back to sleep
- Consider powering sensor from GPIO for zero sleep current
- Store Last Reading in RTC
- Compare to previous reading
- Detect anomalies (sensor failure = reading = 0 or NaN)
Checkpoint: Serial shows sensor readings each boot
Phase 3: WiFi Transmission (Day 5-7)
Goal: Send data to server/MQTT broker
- Standard WiFi Connect
- Connect with SSID and password
- Time the connection (expect 2-5 seconds)
- Send MQTT/HTTP message
- Implement Fast Connect
- Cache channel and BSSID in RTC
- Use static IP
- Time improvement (target <1 second)
- Handle Failures
- Retry up to 3 times
- Store failed data in RTC for next cycle
- Increment failure counter
Checkpoint: Data appears on MQTT broker/server
Phase 4: ESP-NOW Alternative (Day 8-10)
Goal: Replace WiFi with ESP-NOW for better power
- Set Up Hub Device
- Separate ESP32 as receiver
- Print MAC address
- Forward to WiFi/MQTT
- Implement ESP-NOW Sender
- Add hub as peer
- Send sensor data struct
- Verify receipt (callback or timeout)
- Compare Power
- Measure active time with ESP-NOW vs WiFi
- Calculate new battery life estimate
Checkpoint: Hub receives data from sensor node
Phase 5: Power Optimization (Day 11-14)
Goal: Maximize battery life
- Battery Monitoring
- Add voltage divider circuit
- Read and report battery percentage
- Implement low-voltage shutdown
- Reduce Active Time
- Profile each step (sensor, WiFi, transmit)
- Optimize slow operations
- Target <2 seconds total active time
- Implement GPIO Wake
- Add PIR sensor or door switch
- Wake on motion instead of timer
- Calculate new battery life
Testing Strategy
Unit Tests
| Component | Test | Expected Result |
|---|---|---|
| Deep Sleep | Set 10s timer | Wakes after ~10s |
| RTC Memory | Increment counter | Persists across sleep |
| Sensor | Read BME280 | Valid temp (15-35ยฐC) |
| ADC | Read battery | 3.3-4.2V range |
| WiFi | Connect | Success in <5s |
| ESP-NOW | Send packet | Callback confirms |
Power Measurement
- Measure Sleep Current
- Set long sleep interval (5 minutes)
- Measure with ยตA-capable multimeter
- Record for battery life calculation
- Measure Active Current
- Use current sensing resistor (1ฮฉ)
- Monitor voltage across resistor
- Calculate peak and average current
- Validate Battery Life Estimate
- Run for 24 hours
- Measure voltage drop
- Compare to prediction
Stress Tests
- WiFi Failure Simulation
- Turn off router during operation
- Verify retry logic works
- Check data is queued for next cycle
- Low Battery Behavior
- Drain battery to 3.5V
- Verify warning triggers
- Check graceful shutdown
Common Pitfalls and Debugging
Sleep Current Too High
Problem: DevKit draws 10-15mA in deep sleep
- USB-UART chip stays powered
- Power LED draws current
- Voltage regulator has high quiescent current
Solutions:
- Use bare ESP32 module for production
- Remove power LED
- Use low-quiescent regulator (e.g., MCP1700)
WiFi Wonโt Connect After Sleep
Problem: First boot works, subsequent donโt
- Channel changed (router switched channels)
- BSSID changed (mesh router)
- DHCP lease expired
Solutions:
- Clear cached data if connection fails
- Fall back to full scan periodically
- Use static IP to avoid DHCP
Sensor Readings Are Wrong
Problem: Temperature reads 0 or NaN
- Sensor didnโt wake up properly
- I2C timing issues after sleep
- Sensor needs reset after deep sleep
Solutions:
- Add delay after power-on
- Re-initialize I2C after wake
- Power cycle sensor from GPIO
Battery Drains Quickly
Problem: Battery lasts days, not months
- Sleep current higher than expected
- Active time too long
- Too frequent wake-ups
Solutions:
- Measure actual current draw
- Profile each operation
- Increase sleep interval
- Use ESP-NOW instead of WiFi
Extensions and Challenges
Beginner Extensions
- Multiple Sensor Types
- Add soil moisture sensor
- Add light sensor (BH1750)
- Report all in same packet
- Alert Thresholds
- Wake immediately if temperature too high
- Send urgent alert
- Return to sleep
Intermediate Challenges
- Solar Power
- Add solar panel and charging circuit
- Implement MPPT or simple diode charging
- Run indefinitely outdoors
- Mesh Network
- Multiple sensor nodes
- Relay data through each other
- Extend range to 500m+
Advanced Challenges
- ULP Coprocessor
- Run simple program while main CPU sleeps
- Monitor sensor threshold
- Wake main CPU only when needed
- Achieve <5ยตA average current
- Secure Communication
- Encrypt ESP-NOW packets
- Implement AES-128 encryption
- Secure against replay attacks
Real-World Connections
Commercial Products
| Product | Your Project Skill |
|---|---|
| Nest Temperature Sensors | Deep sleep, battery optimization |
| Tile Trackers | BLE + ultra-low power |
| Agricultural Sensors | ESP-NOW mesh, solar power |
| Door/Window Sensors | GPIO wake, years of battery life |
Industry Applications
- Precision Agriculture: Soil sensors across fields
- Cold Chain Logistics: Temperature monitoring in transit
- Wildlife Tracking: GPS + sensor loggers
- Smart Buildings: Distributed sensor networks
Resources
Official Documentation
| Resource | URL |
|---|---|
| ESP-IDF Sleep Modes | docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/sleep_modes.html |
| ESP-NOW Guide | docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_now.html |
| ULP Coprocessor | docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/ulp.html |
Books
| Book | Author | Relevant Chapters |
|---|---|---|
| Making Embedded Systems | Elecia White | Ch. 11: Power |
| IoT Product Development | Sai Yamanoor | ESP-NOW chapter |
| ESP32 Technical Reference | Espressif | Ch. 28: RTC |
Self-Assessment Checklist
Fundamentals
- I can explain the difference between light sleep and deep sleep
- I understand what RTC memory is and how to use it
- I can calculate expected battery life from current measurements
- I know when to use WiFi vs ESP-NOW
Implementation
- Device sleeps with <200ยตA current (or measured and documented)
- Data transmits successfully on each wake
- Boot count increments correctly across power cycles
- Battery percentage is accurate
Production Ready
- Failed transmissions are retried and queued
- Low battery triggers appropriate action
- Device recovers from WiFi failures
- Active time is minimized (<3 seconds)
Interview Preparation
Be ready to answer these questions:
- โExplain the difference between light sleep, modem sleep, and deep sleep.โ
- Power levels, RAM preservation, wake latency, use cases
- โHow do you preserve data across deep sleep?โ
- RTC_DATA_ATTR, RTC fast/slow memory, NVS for persistent data
- โWhy does an ESP32 DevKit have higher sleep current than a bare module?โ
- Onboard components: regulator, USB-UART, LEDs
- โCalculate battery life given specific parameters.โ
- Show the sleep energy + active energy calculation
- โWhat is ESP-NOW and when should you use it?โ
- Peer-to-peer, no router, lower power, 250 bytes max, local only
Next Project: P04-audio-spectrum-analyzer.md - Real-Time Audio Spectrum Analyzer