Practice Lab Guide

Project 8: Battery-Powered Low-Power Sensor Node

Optimize ESP32 current draw using deep sleep states, duty cycling calculations, and linear regulator bypass hardware.
Domain
Power Optimization
Difficulty
⭐⭐⭐⭐☆ (Advanced)
Course Module
Power Management & Optimization
Deliverables
Low-Power Firmware, Power Consumption Profile
1. Low-Power Hardware Bypass & Duty Cycle Timeline

Standard microcontroller development boards are not optimized for low-power battery operation. The onboard USB-to-UART converter chip and high-quiescent-current linear regulators continuously draw power, draining batteries in days even during deep sleep. The schematic below shows a hardened low-power hardware design. We bypass the board's USB regulator by feeding a 3.7V LiPo battery directly into a high-efficiency 3.3V Low-Dropout (LDO) regulator (HT7333), which connects directly to the ESP32 3V3 rail. This setup reduces deep sleep current draw from 15mA down to just 15µA.

LiPo BATTERY 3.7V Nominal Capacity: 1000mAh + - LDO REGULATOR HT7333 Quiescent: 4µA VIN GND VOUT ESP32 (Bypassed USB Controller) 3.3V Pin Powers CPU Cores USB Port (COM) DISCONNECTED
2. Part 1: Step-by-Step Individual Code Implementation & Math Steps

Follow these detailed steps to implement deep sleep on your ESP32, verify compile times, and complete the battery life calculations.

STEP 1

Enable Timer Wakeup in Firmware setup

Configure the ESP32 real-time clock (RTC) controller to wake up the system after a defined interval.

C++ Code snippet to write: #define uS_TO_S_FACTOR 1000000ULL /* Conversion factor for micro seconds to seconds */ #define TIME_TO_SLEEP 600 /* Time ESP32 will go to sleep (in seconds) */ esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
We configure the RTC timer to register a wakeup event, converting the sleep duration from seconds to microseconds using a 64-bit unsigned integer value.
STEP 2

Trigger Deep Sleep Mode

Instruct the ESP32 CPU cores to shut down all primary domains and enter deep sleep mode.

C++ Code snippet to write: Serial.println("Entering deep sleep state now..."); Serial.flush(); esp_deep_sleep_start();
We flush the serial buffer to ensure transmission completes before calling `esp_deep_sleep_start()`, which powers down the CPU cores, Wi-Fi radio, and RAM.
STEP 3

Compile and Flash Firmware

Connect your board to your computer and flash the low-power firmware using the Arduino IDE.

IDE GUI Operations: 1. Open the Arduino IDE. 2. Select your target port ("/dev/ttyUSB0" on Linux VM or "COM3" on Windows). 3. Click the circular Upload arrow icon in the toolbar. 4. Open the Serial Monitor and verify the debug logs: "Wakeup number: 1" "Temp: 24.5 C" "Entering deep sleep state now..."
We upload the firmware and monitor the serial logs to verify that the ESP32 boots, reads sensor data, prints debug messages, and enters deep sleep.
STEP 4

Configure Multimeter to Measure Low-Power Current

Measure the current draw during the active cycle and deep sleep states to evaluate the power savings.

Multimeter Steps: 1. Switch the power supply OFF. 2. Connect the black probe to "COM" and the red probe to the "mA/µA" jack. 3. Turn the dial to the DC Microamps range (marked with a µA symbol). 4. Place the multimeter in series between the LDO output (HT7333 VOUT) and the ESP32 3.3V pin. 5. Turn the power supply ON. 6. Record the current draw during active mode (~120mA) and deep sleep (~15µA).
We connect the multimeter in series and select the microamps range to measure the low current draw during deep sleep, verifying the efficiency of the LDO regulator.
STEP 5

Calculate Average Current Consumption

Calculate the average current draw over a complete cycle using the measured active and sleep currents.

Mathematical Calculation (Duty Cycle): - Active current (I_active) = 120 mA - Active time (t_active) = 5 seconds - Sleep current (I_sleep) = 0.015 mA (15 µA) - Sleep time (t_sleep) = 595 seconds (10 minutes total cycle minus 5s active) Average Current Formula: I_avg = ((I_active * t_active) + (I_sleep * t_sleep)) / (t_active + t_sleep) I_avg = ((120 * 5) + (0.015 * 595)) / 600 I_avg = (600 + 8.925) / 600 = 1.015 mA
We calculate the average current consumption using the duty cycle formula, weighting the active and sleep currents by their respective durations in a complete cycle.
STEP 6

Estimate Total Battery Lifetime

Use the average current draw to estimate the battery life of a 1000mAh LiPo battery, applying a derating factor to account for self-discharge.

Lifetime Calculation: - Battery Capacity = 1000 mAh - Derating Factor = 0.85 (accounts for self-discharge and cell aging) - Average Current (I_avg) = 1.015 mA Expected Life Formula: Hours = (Capacity * Derating) / I_avg Hours = (1000 * 0.85) / 1.015 = 837.4 Hours Days Calculation: Days = Hours / 24 = 837.4 / 24 = 34.9 Days
We calculate the expected battery life by dividing the usable battery capacity by the average current draw, estimating the operational lifetime in days.
3. Active vs. Sleep Duty Cycle Current Waveform

The duty cycle waveform below illustrates the contrast in current draw between the short active cycle (when the ESP32 wakes, reads sensors, and transmits data) and the long deep sleep state.

Current (mA) Time (s) Active Cycle: 120mA (5s) Deep Sleep State: 15µA (595s) Active (5s) Total Duty Cycle Period: 600 Seconds (10 Minutes)
4. Part 2: Complete Low-Power Firmware Template

To implement this power-saving cycle, copy the C++ code below and upload it to your ESP32. The script configures the RTC wake timer and enters deep sleep after printing sensor values.

// Practice Project 8: Low-Power ESP32 Telemetry Firmware #include <WiFi.h> #include <HTTPClient.h> // Conversion factor for micro seconds to seconds #define uS_TO_S_FACTOR 1000000ULL #define TIME_TO_SLEEP 600 // Sleep for 10 minutes (600 seconds) // RTC memory variable to track boot cycles across deep sleep cycles RTC_DATA_ATTR int bootCount = 0; const char* ssid = "YOUR_WIFI_SSID"; const char* password = "YOUR_WIFI_PASSWORD"; const char* apiKey = "YOUR_WRITE_API_KEY"; void setup() { Serial.begin(115200); delay(10); // Increment boot count bootCount++; Serial.printf("Boot Cycle Count: %d\n", bootCount); // Connect to Wi-Fi WiFi.begin(ssid, password); int retries = 0; while (WiFi.status() != WL_CONNECTED && retries < 20) { delay(500); Serial.print("."); retries++; } if (WiFi.status() == WL_CONNECTED) { Serial.println("\nConnected. Sending telemetry data..."); WiFiClient client; HTTPClient http; // Read sensor values float tempVal = 24.5; // Replace with real sensor read call String url = "http://api.thingspeak.com/update?api_key=" + String(apiKey) + "&field1=" + String(tempVal, 2); http.begin(client, url); int httpCode = http.GET(); Serial.printf("HTTP Response Code: %d\n", httpCode); http.end(); } else { Serial.println("\nConnection failed. Skipping telemetry upload."); } // Configure the RTC timer wakeup source esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR); Serial.println("Setup complete. Entering deep sleep..."); Serial.flush(); // Enter deep sleep esp_deep_sleep_start(); } void loop() { // Loop is never executed in deep sleep mode. // At wakeup, execution restarts from setup(). }
5. Deliverables Summary

Created Artifacts

  • Low-power telemetry firmware: secured_mqtt.ino.
  • Power consumption profile log sheet detailing active vs. sleep current draws.
  • Theoretical battery life calculation model.

Verification Proof

  • Photograph of the multimeter reading showing a 15µA current draw during the ESP32 deep sleep cycle.
  • Screenshot of the Serial Monitor logging successive boot counts (1, 2, 3...) after each timer-triggered wakeup.
6. Closing Explanation: Why We Did This & What It Accomplishes

Architectural Intent & Operational Impact

Why We Did This

What This Accomplishes