Chapters

25 Low-Power Sensors: Duty Cycles and Fusion

sensing
power-management
low-power
networks
reliability

25.1 Start With the Decision

A node sleeps for most of the hour, then burns its budget during radio joins and sensor fusion. Average current must include every wake cost before firmware is called low power.

25.2 Route Overview

This is part 2 of 3. Review Low-Power Sensors: Energy Budgets for the preceding evidence.

25.3 Learning Objectives

  • Compute average current from sleep, sensing, processing, and transmit states.
  • Compare buffering and sensor-fusion accuracy against their energy cost.

25.4 Chapter Roadmap

  • Checkpoint: Duty Cycle Is Average Current
  • Power Budget Calculation
  • Interactive Power Budget Calculator
  • Optional Buffered-Transmission Pattern
  • Transmission Buffering Calculator
  • Putting Numbers to It
  • Checkpoint: Optimize the Dominant Term First
  • Transmission Buffering Check
  • Try It: ESP32 Power Mode Explorer
  • Sensor Fusion Tradeoffs
  • Sensor Fusion Basics
  • Complementary vs Kalman Fusion
  • Smart vs Raw Sensor Processing
  • Try It: Complementary Filter Tuning
  • Checkpoint: Fusion Costs Power Too
  • Common Power Pitfalls
  • Set Wake Sources Before Sleep
  • Optional Wake-Source Pattern
  • Read Registers After Conversion
  • Optional Data-Ready Pattern
  • Knowledge Check
  • Key Takeaway
  • For Kids: Meet the Sensor Squad!
  • Wi-Fi vs LoRaWAN Power
  • Label the Diagram
  • Code Challenge
  • Power, Accuracy, Field Hardening
  • Next Power Practice

Physics PhoebeCheckpoint: Duty Cycle Is Average Current

You now know:

  • A low-power loop is wake source, sensor read, optional transmit, and return to sleep.
  • The calculator combines 5 active seconds at 150 mA with a 15 minute period and 0.01 mA sleep current.
  • Deep sleep shifts the design question from peak current to cycle average.

Once the cycle is defined, battery life is accounting.

25.5 Power Budget Calculation

20 min | Advanced | P06.C09.U03c

Understanding power budgets is essential for designing battery-powered sensor nodes. Let us calculate the battery life for a typical environmental monitoring node.

25.5.1 Example: Weather Station Power Analysis

System Configuration:

Start by eSP32 with BME280 sensor. Then wi-Fi transmission every 15 minutes. Finally 2000 mAh battery.

Current Consumption:

StateCurrentDuration per Cycle
Sensor Read30 mA3 seconds
Wi-Fi Connect + TX170 mA2 seconds
Deep Sleep10 uA895 seconds

Calculation:

Start by sensor read energy: 30 mA x (3 / 3600) h = 0.025 mAh per cycle. Then wi-Fi transmit energy: 170 mA x (2 / 3600) h = 0.094 mAh per cycle. Next sleep period energy: 0.01 mA x (895 / 3600) h = 0.0025 mAh per cycle. After that total per 15-minute cycle: 0.12 mAh. Continue by cycles per day: 96. Continue by daily consumption: 96 x 0.12 = 11.5 mAh/day, plus about 0.24 mAh/day sleep = 11.7 mAh/day total. Finally battery life: 2000 mAh / 11.7 mAh/day = about 171 days.

Interactive Power Budget Calculator
Active energy
Sleep energy
Total per cycle
Cycles per day
Daily consumption:
Battery life:

25.5.2 Transmission Buffering Strategy

The biggest power consumer is Wi-Fi/LoRa transmission. Buffering multiple readings before transmitting dramatically reduces power consumption.

Using the detailed breakdown from our example: Start by sensor reading: 30 mA for 3 seconds = 0.025 mAh. Finally wi-Fi transmission: 170 mA for 2 seconds = 0.094 mAh.

Original approach: 96 transmissions/day (one per 15-minute reading)

Buffered approach (transmit every 2 hours): 12 transmissions/day (8 readings per transmission)

Optional Buffered-Transmission Pattern
#include <esp_sleep.h>

#define READINGS_PER_TX 8

// RTC memory survives deep sleep (normal RAM is lost on wake)
RTC_DATA_ATTR float tempBuffer[READINGS_PER_TX];
RTC_DATA_ATTR int bufferIndex = 0;

void setup() {
    // Read sensor (fast, low power)
    tempBuffer[bufferIndex++] = bmp.readTemperature();

    if (bufferIndex >= READINGS_PER_TX) {
        // Transmit all buffered readings
        connectWiFi();
        sendAllReadings(tempBuffer, READINGS_PER_TX);
        disconnectWiFi();
        bufferIndex = 0;
    }

    // Configure wake source and enter deep sleep
    esp_sleep_enable_timer_wakeup(15 * 60 * 1000000ULL);  // 15 minutes
    esp_deep_sleep_start();
}

void loop() {
    // Not used - device resets after deep sleep
}
Transmission Buffering Calculator
Original: no buffering
Reads: /day
Transmissions: /day
Total:
Buffered: readings
Reads: /day
Transmissions: /day
Total:
Transmission reduction:
Battery life improvement:
Putting Numbers to It

Using the detailed breakdown: sensor reading (30 mA, 3s) and Wi-Fi transmission (170 mA, 2s) are separate operations.

Original approach (96 transmissions/day):

Start by Sensor reads: 96×30mA×33600h=2.496 \times 30\text{mA} \times \frac{3}{3600}\text{h} = 2.4 mAh/day. Then Wi-Fi TX: 96×170mA×23600h=9.196 \times 170\text{mA} \times \frac{2}{3600}\text{h} = 9.1 mAh/day. Next Sleep: 0.24\approx 0.24 mAh/day. Finally Total: 11.7 mAh/day → Battery life: 200011.7=171\frac{2000}{11.7} = 171 days.

Buffered approach (12 transmissions/day, 8 readings per transmission):

Start by Sensor reads: 96×30mA×33600h=2.496 \times 30\text{mA} \times \frac{3}{3600}\text{h} = 2.4 mAh/day (unchanged). Then Wi-Fi TX: 12×170mA×23600h=1.112 \times 170\text{mA} \times \frac{2}{3600}\text{h} = 1.1 mAh/day. Next Sleep: 0.24\approx 0.24 mAh/day. Finally Total: 3.7 mAh/day → Battery life: 20003.7=541\frac{2000}{3.7} = 541 days.

Result: Wi-Fi energy drops from 9.1 to 1.1 mAh/day (87.5% reduction), extending battery life by 3.1× (171 → 541 days). Transmission buffering is the single most effective optimization.

Physics PhoebeCheckpoint: Optimize the Dominant Term First

You now know:

  • In the weather-station example, sensor read is 30 mA for 3 seconds, Wi-Fi transmit is 170 mA for 2 seconds, and sleep is 895 seconds.
  • Sending every reading produces 96 transmissions per day; buffering 8 readings cuts that to 12.
  • That drops Wi-Fi energy from 9.0 to 1.1 mAh/day and moves battery life from about 171 or 172 days to 541 days.

Transmission Buffering Check

25.5.3 Power Optimization Hierarchy

Before optimising individual instructions, inspect Figure 25.1 to rank the actions that dominate an IoT sensor’s energy budget. The hierarchy keeps radio use, awake time, and peripheral choices in proportion.

Power bars compare Wi-Fi TX, active CPU, sensor reads and sleep modes. Wi-Fi dominates, making reduced radio active time the main optimization target.
Figure 25.1: Power optimization hierarchy: Wi-Fi transmission dominates the power budget

Read Figure 25.1, start with avoiding unnecessary wireless transmissions, then move through longer sleep intervals, shorter active work, and lower-level component tuning. The order connects engineering effort to likely energy return, preventing small microamp savings from distracting from costly radio or wake-time behaviour.

Try It: ESP32 Power Mode Explorer

Current draw
Energy
What stays on
Wake-up time
Ratio vs Active (Wi-Fi TX):

25.6 Sensor Fusion Tradeoffs

15 min | Advanced | P06.C09.U03d

Sensor Fusion Basics

Core Concept: Sensor fusion combines data from multiple sensors (e.g., accelerometer + gyroscope + magnetometer) using algorithms like complementary filters or Kalman filters to produce more accurate and reliable measurements than any single sensor alone.

Why It Matters: Individual sensors have inherent limitations — accelerometers drift over time, gyroscopes accumulate integration errors, and magnetometers are affected by nearby metals — but fusing their outputs compensates for each sensor’s weaknesses while amplifying their strengths.

Key Takeaway: Start with a simple complementary filter (weighted average of fast and slow sensors) for orientation sensing — it requires only 5 lines of code and achieves 80% of Kalman filter accuracy without the computational complexity or tuning challenges.

Complementary vs Kalman Fusion

Option A: Complementary Filter - Simple weighted combination using high-pass filter on one sensor and low-pass filter on another (e.g., gyroscope + accelerometer for orientation)

Option B: Kalman Filter - Statistically optimal recursive estimator that models system dynamics, measurement noise, and process noise for state estimation

Decision Factors:

FactorComplementary FilterKalman Filter
Computational loadVery low (few multiplies)High (matrix operations)
Memory footprint~20 bytes200-2000 bytes
Tuning complexity1-2 parameters5-20+ parameters
AccuracyGood (80-90% of optimal)Optimal (by definition)
Sensor modelingAssumes fixed noiseAdapts to varying conditions
LatencyMinimalSlight (prediction step)
Implementation timeHoursDays to weeks

Choose Complementary Filter when: Constrained MCU (8-bit AVR, small ARM Cortex-M0); battery life critical; quick prototyping; simple sensor fusion (2-3 sensors); accuracy requirements modest.

Choose Kalman Filter when: Accuracy is paramount; sensors have varying reliability over time; need state prediction between measurements; tracking moving targets; fusion of many sensors (5+); adequate computational resources available.

Practical guideline: Start with complementary filter for proof-of-concept. If accuracy is insufficient, upgrade to Kalman.

Smart vs Raw Sensor Processing

Option A: Smart Sensors - Sensors with on-chip intelligence including calibration, digital output, threshold detection, and sometimes embedded ML (e.g., BNO055 with on-chip sensor fusion, MAX30102 with SpO2 algorithm)

Option B: Raw Sensors + External MCU - Basic analog/digital sensors where all processing, calibration, and fusion happens in your microcontroller (e.g., MPU6050 raw mode + custom fusion code)

Decision Factors:

FactorSmart SensorsRaw Sensors + MCU
Host MCU loadMinimal (data ready)High (continuous processing)
Algorithm controlVendor black-boxFull transparency
CalibrationFactory-setCustom per-unit possible
LatencyFixed (sensor-determined)Configurable
Cost per unitHigher ($5-$30)Lower ($1-$10)
Power (total system)Often lowerOften higher
Debugging visibilityLimitedFull access
Algorithm updatesFirmware upgrade (rare)OTA software update

Choose Smart Sensors when: Rapid development required; limited MCU resources; vendor algorithm is proven for your use case; consistent behavior across units needed.

Choose Raw Sensors when: Custom algorithms required (proprietary IP, research); maximum flexibility for parameter tuning; need visibility into intermediate values; cost-optimized high-volume production.

Try It: Complementary Filter Tuning
Filter weights
Error characteristics
Complementary filter
Kalman filter

Physics PhoebeCheckpoint: Fusion Costs Power Too

You now know:

  • A complementary filter starts cheaply: a few multiplies, about 20 bytes, and 80-90% of optimal accuracy.
  • A Kalman filter adds matrix operations, 200-2000 bytes, and 5-20+ tuning parameters.
  • Smart sensors trade higher part cost (\$5-\$30) for lower host MCU load; raw sensors cost less (\$1-\$10) but need more firmware.

The last pass is operational safety: always wake, and read only after conversion finishes.

25.7 Common Power Pitfalls

Set Wake Sources Before Sleep

The Mistake: Calling deep sleep functions (ESP32’s esp_deep_sleep_start(), STM32’s HAL_PWR_EnterSTOPMode(), nRF52’s sd_power_system_off()) without configuring a valid wake source, causing the device to sleep forever and require manual reset or power cycle to recover.

Why It Happens: Deep sleep disables most peripherals and CPU activity to achieve microamp-level current consumption. Unlike light sleep, there is no automatic wake on timer expiration unless explicitly configured. Developers test with short delays during development, then remove the delay for “production” without adding proper wake sources, bricking the device.

The Fix: Always configure at least one wake source before entering deep sleep.

  • Use a timer wake source for periodic sensing.
  • Use an external GPIO wake source for buttons, interrupts, or event-driven sensors.
  • During development, keep a fail-safe timer wake source even if the final product wakes from an interrupt.

25.8 Optional Wake-Source Pattern

// WRONG: Entering deep sleep with no wake source (ESP32)
esp_deep_sleep_start();  // Device sleeps forever! Requires power cycle.

// CORRECT: Configure timer wake source (10 seconds)
esp_sleep_enable_timer_wakeup(10 * 1000000);  // 10 seconds in microseconds
esp_deep_sleep_start();

// CORRECT: Configure external interrupt wake (GPIO 33, active LOW)
esp_sleep_enable_ext0_wakeup(GPIO_NUM_33, 0);  // Wake when GPIO33 goes LOW
esp_deep_sleep_start();

// CORRECT: Multiple wake sources (timer OR button)
esp_sleep_enable_timer_wakeup(60 * 1000000);  // 60-second timeout
esp_sleep_enable_ext0_wakeup(GPIO_NUM_0, 0);  // Boot button wake
esp_deep_sleep_start();

Recovery Strategy: During development, always include a “fail-safe” wake source like a 5-minute timer even if primary wake is external interrupt.

Read Registers After Conversion

The Mistake: Reading sensor data registers immediately after triggering a measurement, before the sensor’s internal ADC has completed conversion, resulting in stale data from the previous measurement or invalid values.

Why It Happens: Sensors like the BMP280 (temperature/pressure), ADS1115 (precision ADC), and HX711 (load cell) have multi-millisecond conversion times. The datasheet specifies conversion time, but example code often uses fixed delays or omits waiting entirely. At 400kHz I2C, you can read registers in ~50us, but BMP280 needs 44 ms in ultra-high-resolution mode.

The Fix: Check the sensor’s data-ready signal or wait for the specified conversion time. The behavior to remember is: trigger measurement, wait until conversion is complete, then read the result.

25.9 Optional Data-Ready Pattern

// WRONG: Reading BMP280 immediately after trigger (44ms conversion!)
bmp.takeForcedMeasurement();
float temp = bmp.readTemperature();  // Returns PREVIOUS measurement!

// CORRECT: Wait for conversion time (check datasheet)
bmp.takeForcedMeasurement();
delay(44);  // Ultra-high resolution mode: 44ms
float temp = bmp.readTemperature();

// BETTER: Poll status register for data-ready flag
bmp.takeForcedMeasurement();
while ((bmp.getStatus() & 0x08) == 0) {  // Bit 3 = "measuring"; 0 = conversion done
    delayMicroseconds(100);  // Poll every 100us
}
float temp = bmp.readTemperature();

Conversion Times to Know: BMP280 standard: 8 ms, ultra-high: 44 ms. ADS1115 at 8 SPS: 125 ms, at 860 SPS: 1.2 ms. HX711 at 10 Hz: 100 ms.

25.10 Knowledge Check

Question 1: A battery-powered environmental monitoring node uses an ESP32 with BME280 sensor, transmitting readings every 15 minutes via Wi-Fi. Current consumption is: sensor read 30mA (3 seconds), Wi-Fi TX 170mA (2 seconds), deep sleep 10uA. Using a 2000mAh battery, approximately how long will the node operate?

Answer: Approximately 170 days (5.7 months)

Calculation:

  • Sensor Read Energy: 30mA x (3/3600)h = 0.025 mAh per cycle
  • Wi-Fi TX Energy: 170mA x (2/3600)h = 0.094 mAh per cycle
  • Sleep Period Energy: 0.01mA x (895/3600)h = 0.0025 mAh per cycle
  • Total per 15-minute cycle: 0.12 mAh
  • Cycles per day: 96
  • Daily consumption: 96 x 0.12 + 0.24 (sleep) = 11.7 mAh/day
  • Battery life: 2000mAh / 11.7 mAh/day = 171 days

Question 2: What is the most effective way to extend battery life by 3x or more for the node described above?

Answer: Reduce Wi-Fi transmissions by buffering multiple readings

Wi-Fi transmission dominates the power budget (80%+ of energy). By buffering 8 readings and transmitting every 2 hours instead of every 15 minutes:

  • Wi-Fi transmissions drop from 96 to 12 per day (87.5% reduction)
  • Daily energy drops from 11.7 to 3.7 mAh (sensor: 2.4, TX: 1.1, sleep: 0.24)
  • Battery life extends from 171 days to 541 days (3.1x improvement)

Other options (ESP8266, larger battery, reduced sampling) provide smaller improvements.

Question 3: Why must you configure a wake source BEFORE calling esp_deep_sleep_start()?

Answer: Without a configured wake source, the device sleeps forever and cannot recover without a manual power cycle.

Deep sleep disables the CPU and most peripherals. There is no automatic timeout - the device will remain in sleep mode indefinitely (consuming ~10uA) until a wake event occurs. If no wake source is configured, no wake event can ever occur, and the device is effectively bricked until physically reset.

Always configure at least one wake source (timer, GPIO interrupt, touchpad, or ULP coprocessor) before entering deep sleep.

Key Takeaway

The power management hierarchy for battery-powered sensor nodes is: (1) minimize transmissions first (buffering yields 3x+ improvement), (2) use deep sleep between readings (reduces idle current 10,000x from mA to uA), (3) then optimize active-period current (sensor selection, clock speed). Tackling these in wrong order — such as choosing a lower-power MCU before reducing transmission count — delivers marginal gains while ignoring the dominant power consumer.

For Kids: Meet the Sensor Squad!

the battery had a big problem. “I only have so much energy,” she said. “If everyone keeps talking all the time, I will run out in just a few months!”

Temperature Terry felt bad. “But I need to measure the temperature!” the microcontroller had an idea: “What if Sammy takes his readings, but instead of shouting them to the cloud every single time, we save them up and send a big batch all at once?”

“Like saving up your drawings and mailing them all in one envelope instead of 8 separate letters!” Temperature Terry suggested.

“Exactly!” said Max. “The Wi-Fi radio uses the MOST energy — way more than Sammy reading the temperature. By sending one big message instead of eight small ones, Bella lasts THREE TIMES longer!”

Bella was relieved. “And between readings, everyone goes to DEEP SLEEP. That means I only use 10 microamps — that is like a tiny trickle instead of a fire hose!”

“But ALWAYS set an alarm before sleeping,” Max warned. “If you sleep without an alarm, you sleep FOREVER and someone has to unplug you and plug you back in!”

Wi-Fi vs LoRaWAN Power

Scenario: You are deploying 50 environmental sensors across a 5 km² agricultural area. Each sensor reads temperature and soil moisture every 15 minutes. Should you use Wi-Fi or LoRaWAN?

System Specifications:

  • ESP32 microcontroller
  • BME280 temperature/humidity sensor
  • Capacitive soil moisture sensor
  • 3.7V 2000 mAh Li-ion battery
  • Sensor reading interval: 15 minutes (96 readings/day)

Option A: Wi-Fi Connectivity (same analysis as the Power Budget Calculation section)

StateCurrent (mA)Duration per CycleEnergy (mAh)
Deep Sleep0.0114 min 55 sec (895 sec)0.0025
Sensor Read303 sec0.025
Wi-Fi Connect + TX1702 sec0.094
Total per cycle15 min0.12 mAh

Daily consumption: 96 cycles × 0.12 mAh + 0.24 (sleep) = 11.7 mAh/day

Battery life: 2000 mAh / 11.7 mAh/day = 171 days (5.7 months)

Option B: LoRaWAN Connectivity

Note: At +14 to +20 dBm TX power (required for the 5-15 km range in this scenario), LoRaWAN modules such as the SX1276 draw 100-130 mA. We use 120 mA as a realistic value for long-range agricultural deployments.

StateCurrent (mA)Duration per CycleEnergy (mAh)
Deep Sleep0.0114 min 55 sec (895 sec)0.0025
Sensor Read303 sec0.025
LoRaWAN TX (+17 dBm)1202 sec0.067
Total per cycle15 min0.094 mAh

Daily consumption: 96 cycles × 0.094 mAh + 0.24 (sleep) = 9.3 mAh/day

Battery life: 2000 mAh / 9.3 mAh/day = 215 days (7.1 months)

Comparison Table:

MetricWi-FiLoRaWANWinner
Battery Life171 days215 daysLoRaWAN (1.3× longer)
TX Current170 mA120 mALoRaWAN (1.4× lower)
Range50-100m5-15 kmLoRaWAN (100× longer)
InfrastructureWi-Fi AP every 50m1 gateway for entire 5 km²LoRaWAN (simpler)
Cost per Node$5 (ESP32 only)$13 (ESP32 + LoRa module)Wi-Fi (cheaper)
Total Infrastructure Cost50× Wi-Fi APs @ $30 = $1,5001× LoRaWAN gateway @ $200 = $200LoRaWAN (much cheaper)

Decision: LoRaWAN wins for agricultural deployment due to:

  • 1.3× longer battery life (215 vs 171 days) with realistic TX power for 5+ km range
  • 100× longer range (15 km vs 100m) — covers entire 5 km^2^ farm with ONE gateway
  • $1,300 lower infrastructure cost ($200 vs $1,500)
  • Lower maintenance (fewer battery changes per year)

Note: At low TX power (+7 dBm, ~40 mA), LoRaWAN battery life extends to 400+ days, but range drops to 1-2 km. The advantage grows further with transmission buffering, since LoRaWAN’s lower per-TX energy cost compounds the savings.

When Wi-Fi Makes Sense:

  • Indoor deployment with existing Wi-Fi infrastructure (no gateway cost)
  • High data rate needs (>10 kB/transmission) — LoRaWAN limited to 242 bytes
  • Real-time applications (<1 second latency) — LoRaWAN has 1-5 second latency
  • Short range (<100m) with frequent communication (every minute)

Key Insight: For battery-powered outdoor IoT with infrequent small-payload transmissions, LoRaWAN’s lower TX current and vastly longer range make it the clear winner. Wi-Fi’s higher power consumption (170 mA) dominates the energy budget, and the infrastructure savings alone often justify LoRaWAN even before considering battery life.

25.10.1 Wi-Fi vs LoRaWAN Battery Life

Wi-Fi
Energy/cycle:
Daily:
LoRaWAN
Energy/cycle:
Daily:
Label the Diagram
Code Challenge

25.11 Power, Accuracy, Field Hardening

The main chapter above builds the low-power sensing loop and radio budget. The companion page checks the accuracy side of the same design: self-heating, sensor warm-up, settling delays, adaptive sampling, and field-hardening evidence.

Next Power Practice

Continue with Power Accuracy and Field Hardening to verify that battery-life optimizations do not bias the measurement chain.

25.12 Continue to the Next Part

Carry this evidence into Sensor Power: Accuracy and Field Hardening, which begins with Power Accuracy and Field Hardening.