%%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#2C3E50', 'primaryTextColor': '#fff', 'primaryBorderColor': '#2C3E50', 'lineColor': '#16A085', 'secondaryColor': '#E67E22', 'tertiaryColor': '#7F8C8D'}}}%%
pie title Time Distribution for 5-Year Battery Life
"Deep Sleep (99.7%)" : 99.7
"Active (0.3%)" : 0.3
1600 Low-Power Design Strategies
1600.1 Learning Objectives
By the end of this chapter, you will be able to:
- Implement effective sleep mode strategies for microcontrollers
- Design duty-cycling schemes for optimal power efficiency
- Apply voltage and frequency scaling techniques
- Manage peripheral power consumption effectively
- Configure and use wake-up sources appropriately
- Optimize firmware for minimal energy consumption
1600.2 Low-Power Design Strategies
Achieving multi-year battery life requires systematic application of low-power design techniques at every level—hardware selection, circuit design, and firmware implementation.
1600.2.1 The Fundamental Principle: IoT Transceivers Are “Mostly Off”
A well-designed battery-powered IoT device spends 99%+ of its time in deep sleep. The goal is to:
- Minimize wake time - Complete all tasks as quickly as possible
- Maximize sleep depth - Use the lowest power mode that meets requirements
- Reduce wake frequency - Only wake when necessary
Rule of thumb: If your device is awake more than 1% of the time, you have optimization opportunities.
1600.2.2 Sleep Mode Hierarchy
Different sleep modes offer trade-offs between power savings and wake-up time:
| Mode | Typical Current | Wake-up Time | What’s Retained | Wake Sources |
|---|---|---|---|---|
| Run | 10-100 mA | N/A | Everything | N/A |
| Idle | 1-10 mA | Immediate | Everything | Any interrupt |
| Light Sleep | 100µA-1mA | 10-100 µs | RAM, registers | Fast GPIO, timer |
| Deep Sleep | 1-100 µA | 100-500 µs | RTC, wake logic | RTC, GPIO, touch |
| Hibernate | 0.1-10 µA | 1-10 ms | Wake logic only | Limited GPIO |
| Shutdown | 0.01-1 µA | Full reboot | Nothing | Power button |
1600.2.3 ESP32 Sleep Mode Example
#include "esp_sleep.h"
// Configure wake-up source (timer or GPIO)
esp_sleep_enable_timer_wakeup(3600 * 1000000ULL); // 1 hour
esp_sleep_enable_ext0_wakeup(GPIO_NUM_33, 1); // GPIO33 high
// Optional: Configure GPIO hold during sleep
gpio_hold_en(GPIO_NUM_LED);
// Enter deep sleep - does not return!
esp_deep_sleep_start();
// After wake-up, execution starts from beginning
// Check wake cause:
esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause();
switch (cause) {
case ESP_SLEEP_WAKEUP_TIMER:
// Timer-triggered wake
break;
case ESP_SLEEP_WAKEUP_EXT0:
// GPIO-triggered wake
break;
default:
// First power-on or reset
break;
}1600.2.4 STM32 Low-Power Modes
#include "stm32l4xx_hal.h"
// Enter STOP2 mode (1.1 µA typical)
HAL_PWREx_EnterSTOP2Mode(PWR_STOPENTRY_WFI);
// Enter STANDBY mode (0.3 µA typical)
HAL_PWR_EnterSTANDBYMode();
// Enter SHUTDOWN mode (30 nA typical!)
HAL_PWREx_EnterSHUTDOWNMode();
// Wake-up configuration for STANDBY
HAL_PWR_EnableWakeUpPin(PWR_WAKEUP_PIN1);
__HAL_PWR_CLEAR_FLAG(PWR_FLAG_WUF1);1600.3 Duty Cycling Strategies
1600.3.1 Time-Based Duty Cycling
Regular interval wake-ups for periodic sensing:
void duty_cycle_periodic(void) {
while (1) {
// Wake actions
read_sensors();
process_data();
if (should_transmit()) {
transmit_data();
}
// Calculate next wake time
uint32_t sleep_us = INTERVAL_SECONDS * 1000000;
// Enter deep sleep
esp_sleep_enable_timer_wakeup(sleep_us);
esp_deep_sleep_start();
}
}1600.3.2 Event-Driven Duty Cycling
Wake only when events occur:
void duty_cycle_event_driven(void) {
// Configure accelerometer for motion detection
config_accel_interrupt(MOTION_THRESHOLD);
// Configure GPIO wake-up
esp_sleep_enable_ext0_wakeup(ACCEL_INT_PIN, HIGH);
while (1) {
// Handle motion event
read_motion_data();
transmit_alert();
// Clear interrupt and sleep
clear_accel_interrupt();
esp_deep_sleep_start();
}
}1600.3.3 Adaptive Duty Cycling
Adjust interval based on conditions:
uint32_t calculate_adaptive_interval(sensor_data_t* data) {
static uint32_t base_interval = 60000; // 60 seconds
static float last_value = 0;
float change = fabs(data->value - last_value);
last_value = data->value;
// High change rate: sample more frequently
if (change > HIGH_CHANGE_THRESHOLD) {
return base_interval / 4; // 15 seconds
}
// Moderate change: standard interval
else if (change > LOW_CHANGE_THRESHOLD) {
return base_interval; // 60 seconds
}
// Stable: extend interval
else {
return base_interval * 4; // 4 minutes
}
}1600.3.4 Worked Example: Duty Cycle Optimization for 5-Year Battery Life
Scenario: You need to achieve 5-year battery life with a 2,400 mAh battery. Calculate the maximum duty cycle allowed.
Given:
- Battery capacity: 2,400 mAh
- Target lifetime: 5 years = 43,800 hours
- Active current: 80 mA (sensor read + LoRa TX)
- Sleep current: 10 µA
Step 1: Calculate maximum average current
\[I_{avg,max} = \frac{2400 \text{ mAh}}{43800 \text{ h}} = 0.0548 \text{ mA} = 54.8 \text{ µA}\]
Step 2: Calculate duty cycle
Using the duty cycle formula:
\[I_{avg} = I_{active} \times D + I_{sleep} \times (1 - D)\]
Where D is duty cycle (fraction of time active):
\[54.8 = 80000 \times D + 10 \times (1 - D)\] \[54.8 = 80000D + 10 - 10D\] \[44.8 = 79990D\] \[D = 0.00056 = 0.056\%\]
Step 3: Calculate active time per hour
\[T_{active} = 3600 \text{ s} \times 0.00056 = 2.02 \text{ seconds per hour}\]
Conclusion: To achieve 5-year battery life, the device can be active for only ~2 seconds per hour. This means:
- Sensor reading must complete in <1 second
- LoRa transmission must complete in <1 second
- No room for Wi-Fi (connection alone takes 2-5 seconds)
Optimization Strategies:
- Use hot-start GPS (1s vs 30s cold start)
- Pre-compute LoRa packet while sensor is reading
- Use spread factor SF7 (fastest) if range permits
- Transmit only deltas/changes, not every reading
1600.4 Peripheral Power Management
1600.4.1 GPIO Power Consumption
Unused or misconfigured GPIOs can consume significant power:
| Configuration | Current Draw |
|---|---|
| Floating input | 0-500 µA (oscillates) |
| Input with 10kΩ pull-up @ 3.3V | 330 µA |
| Input with 100kΩ pull-up @ 3.3V | 33 µA |
| Output low (no load) | ~0 µA |
| Output high driving LED @ 20mA | 20 mA |
| Internal pull-up enabled | 10-50 µA |
Best practice: Configure unused pins as output low or use internal pull-downs.
1600.4.2 Sensor Power Gating
Use GPIO or load switch to completely power off sensors:
#define SENSOR_POWER_PIN 25
void power_on_sensors(void) {
gpio_set_level(SENSOR_POWER_PIN, HIGH);
delay_ms(10); // Sensor startup time
}
void power_off_sensors(void) {
gpio_set_level(SENSOR_POWER_PIN, LOW);
}
void read_with_power_gating(void) {
power_on_sensors();
float temp = read_temperature();
float humidity = read_humidity();
power_off_sensors(); // Back to zero current!
process_and_transmit(temp, humidity);
}1600.4.3 Load Switch Selection
| Parameter | Low-Side Switch | High-Side Switch | Integrated Load Switch |
|---|---|---|---|
| Control | MCU GPIO | Level shifter needed | Direct MCU GPIO |
| Quiescent Current | 0 (MOSFET) | 0 (MOSFET) | 1-10 µA |
| Turn-on Time | 1-10 µs | 1-10 µs | 10-100 µs |
| Inrush Limiting | No | No | Often included |
| Example Parts | 2N7002 | Si2301 | TPS22917, SIP32431 |
| Cost | $0.02 | $0.05 | $0.20-0.50 |
1600.4.4 Clock and Voltage Scaling
Dynamic Voltage and Frequency Scaling (DVFS) reduces power:
\[P_{dynamic} \propto C \times V^2 \times f\]
- Halving frequency: 2× power reduction
- Halving voltage: 4× power reduction
- Halving both: 8× power reduction!
// ESP32 frequency scaling
#include "esp_pm.h"
// Configure power management
esp_pm_config_esp32_t pm_config = {
.max_freq_mhz = 240,
.min_freq_mhz = 80, // Scale down when idle
.light_sleep_enable = true
};
esp_pm_configure(&pm_config);
// STM32L4 low-power run mode (down to 2 MHz)
HAL_RCCEx_EnableLSECSS();
HAL_PWREx_EnableLowPowerRunMode();1600.5 Wake-Up Source Configuration
1600.5.1 Timer Wake-Up
Most common for periodic sensing:
// ESP32: Wake after 1 hour
esp_sleep_enable_timer_wakeup(3600 * 1000000ULL);
// STM32: Wake using RTC alarm
RTC_AlarmTypeDef alarm = {0};
alarm.AlarmTime.Hours = 1;
alarm.AlarmMask = RTC_ALARMMASK_DATEWEEKDAY;
HAL_RTC_SetAlarm_IT(&hrtc, &alarm, RTC_FORMAT_BIN);1600.5.2 External GPIO Wake-Up
For event-driven wake:
// ESP32: Wake on GPIO 33 going HIGH
esp_sleep_enable_ext0_wakeup(GPIO_NUM_33, 1);
// ESP32: Wake on ANY of multiple pins
uint64_t mask = (1ULL << GPIO_NUM_33) | (1ULL << GPIO_NUM_34);
esp_sleep_enable_ext1_wakeup(mask, ESP_EXT1_WAKEUP_ANY_HIGH);1600.5.3 Touch Wake-Up (ESP32)
// Configure touch pin for wake-up
touch_pad_config(TOUCH_PAD_NUM8, TOUCH_THRESHOLD);
esp_sleep_enable_touchpad_wakeup();1600.6 Communication Protocol Optimization
1600.6.1 Minimize Transmissions
// BAD: Transmit every reading
void bad_transmit_pattern(void) {
while (1) {
float temp = read_temperature();
transmit(temp); // Every 60 seconds
sleep(60);
}
}
// GOOD: Transmit only on change or periodically
void good_transmit_pattern(void) {
static float last_sent = 0;
static int readings_since_tx = 0;
while (1) {
float temp = read_temperature();
readings_since_tx++;
bool significant_change = fabs(temp - last_sent) > 0.5;
bool time_for_heartbeat = readings_since_tx >= 60; // 1 hour
if (significant_change || time_for_heartbeat) {
transmit(temp);
last_sent = temp;
readings_since_tx = 0;
}
sleep(60);
}
}1600.6.2 Batch Transmissions
#define BATCH_SIZE 10
#define READING_INTERVAL 60 // seconds
float readings[BATCH_SIZE];
int reading_index = 0;
void batched_transmit_pattern(void) {
// Read sensor
readings[reading_index++] = read_temperature();
// Transmit when batch is full
if (reading_index >= BATCH_SIZE) {
// One transmission for 10 readings!
transmit_batch(readings, BATCH_SIZE);
reading_index = 0;
}
sleep(READING_INTERVAL);
}1600.6.3 Protocol Selection Guide
| Requirement | Best Protocol | Reason |
|---|---|---|
| Ultra-low power, short range | BLE | 10-50 µJ per packet |
| Low power, long range | LoRa | Best range/power ratio |
| High bandwidth, power available | Wi-Fi | Fastest data transfer |
| Global coverage, no gateway | Cellular (LTE-M/NB-IoT) | Direct cloud connection |
| Mesh networking | Thread/Zigbee | Self-healing network |
1600.7 Firmware Optimization Techniques
1600.7.1 Efficient Interrupt Handling
// BAD: Long interrupt handler
void IRAM_ATTR bad_isr(void) {
read_sensor(); // DON'T: Slow I2C in ISR
process_data(); // DON'T: Computation in ISR
transmit_wireless(); // DON'T: Blocking TX in ISR
}
// GOOD: Minimal interrupt, deferred processing
volatile bool sensor_ready = false;
void IRAM_ATTR good_isr(void) {
sensor_ready = true; // Just set flag
}
void main_loop(void) {
while (1) {
if (sensor_ready) {
sensor_ready = false;
read_sensor();
process_data();
transmit_if_needed();
}
enter_light_sleep();
}
}1600.7.2 Optimize Memory Access Patterns
// BAD: Random access (cache misses)
for (int i = 0; i < 1000; i++) {
result += data[random_index[i]];
}
// GOOD: Sequential access (cache friendly)
for (int i = 0; i < 1000; i++) {
result += data[i];
}1600.7.3 Avoid Busy Waiting
// BAD: Busy wait (wastes power)
while (!sensor_ready) {
// CPU runs at full power doing nothing!
}
// BETTER: Sleep between checks
while (!sensor_ready) {
delay_ms(10); // May still waste power
}
// BEST: Interrupt-driven
enable_sensor_interrupt();
enter_sleep(); // Zero power until interrupt1600.8 Knowledge Check
Question 1: Which THREE energy optimization techniques provide the GREATEST battery life improvement for IoT devices? (Select all that apply)
Deep sleep provides the largest improvement - reducing from 80mA idle to 10µA is an 8,000× reduction. This single change can improve battery life from days to years. Peripheral management and duty cycling provide the next largest improvements (10-100×). Compiler flags and frequency changes provide modest improvements (1.1-2×).
Question 2: For a device requiring 5-year battery life on a 2,400 mAh battery with 80mA active current and 10µA sleep current, what is the maximum duty cycle?
Maximum average current = 2400mAh / 43800h = 54.8µA. Using the duty cycle equation: 54.8 = 80000×D + 10×(1-D), solving gives D = 0.056%. This translates to about 2 seconds of active time per hour, which is extremely tight - requiring careful optimization of all active operations.
1600.9 Summary
Key low-power design strategies:
- Sleep First: Design for 99%+ time in deep sleep
- Match Sleep Mode to Requirements: Use deepest mode that still allows required wake sources
- Power Gate Peripherals: Completely shut off sensors when not in use
- Minimize Wake Time: Optimize every active operation for speed
- Event-Driven Design: Wake on interrupts rather than polling
- Batch and Filter: Reduce transmission frequency and payload size
- Choose Protocols Wisely: BLE for low power, LoRa for range, avoid Wi-Fi when possible
1600.10 What’s Next
Continue to Energy Measurement and Profiling to learn how to measure and validate your power optimization efforts.