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.
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.
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#defineuS_TO_S_FACTOR 1000000ULL
#defineTIME_TO_SLEEP 600 // Sleep for 10 minutes (600 seconds)// RTC memory variable to track boot cycles across deep sleep cyclesRTC_DATA_ATTRintbootCount = 0;
constchar* ssid = "YOUR_WIFI_SSID";
constchar* password = "YOUR_WIFI_PASSWORD";
constchar* apiKey = "YOUR_WRITE_API_KEY";
voidsetup() {
Serial.begin(115200);
delay(10);
// Increment boot countbootCount++;
Serial.printf("Boot Cycle Count: %d\n", bootCount);
// Connect to Wi-FiWiFi.begin(ssid, password);
intretries = 0;
while (WiFi.status() != WL_CONNECTED && retries < 20) {
delay(500);
Serial.print(".");
retries++;
}
if (WiFi.status() == WL_CONNECTED) {
Serial.println("\nConnected. Sending telemetry data...");
WiFiClientclient;
HTTPClienthttp;
// Read sensor valuesfloattempVal = 24.5; // Replace with real sensor read callStringurl = "http://api.thingspeak.com/update?api_key=" + String(apiKey) +
"&field1=" + String(tempVal, 2);
http.begin(client, url);
inthttpCode = 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 sourceesp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
Serial.println("Setup complete. Entering deep sleep...");
Serial.flush();
// Enter deep sleepesp_deep_sleep_start();
}
voidloop() {
// 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
Configuring the RTC timer wakeup source allows the ESP32 to shut down its primary power domains, reducing current draw to microamps between readings.
Bypassing high-draw components on the development board (like the USB controller) prevents unnecessary battery drain, enabling months of runtime.
Using RTC recovery memory (`RTC_DATA_ATTR`) preserves variables across deep sleep cycles, allowing you to track boot cycles and diagnostic metrics without writing to flash.
What This Accomplishes
Reduces average power consumption by 99%, making your sensor node suitable for battery-powered, long-term deployments.
Teaches developers how to analyze and optimize power budgets, which is a critical skill for building real-world, wireless IoT systems.