Chapter 7: Practical Examples

Example 1: Battery-Powered Temperature Sensor (ESP32)

This example reads a DS18B20 temperature sensor and publishes the value via MQTT, then enters deep sleep for 5 minutes. It targets a 3.7 V LiPo battery.

Design Goals

Power Budget

Phase Duration Current Energy
Boot + WiFi connect ~800 ms 120 mA 96 mJ
Read sensor 200 ms 20 mA 4 mJ
MQTT publish 300 ms 80 mA 24 mJ
Deep sleep ~298.7 s 0.05 mA 14.9 mJ
Total per cycle 300 s   138.9 mJ

Average current: 138.9 mJ / (300 s × 3.7 V) ≈ 0.125 mA → battery life ~666 days.

Code Structure

The full sketch is in examples/temp_sensor_mqtt/temp_sensor_mqtt.ino. Key patterns:

RTC_DATA_ATTR int bootCount = 0;
RTC_DATA_ATTR float lastTemp = 0.0f;

void setup() {
    bootCount++;

    // Configure static IP
    WiFi.config(STATIC_IP, GATEWAY, SUBNET);
    WiFi.begin(SSID, PASSWORD);

    // Read sensor while WiFi connects (parallel)
    float temp = readDS18B20();

    // Wait for WiFi (with timeout)
    waitForWiFi(5000);

    // Publish and disconnect
    publishMQTT(temp);
    WiFi.disconnect(true);

    lastTemp = temp;
    esp_sleep_enable_timer_wakeup(5ULL * 60 * 1000000);
    esp_deep_sleep_start();
}

Reading the sensor while WiFi is connecting saves ~200 ms of radio-on time.


Example 2: Data Logger with Minimal Power (Arduino Uno)

This example logs analog sensor readings to EEPROM every 10 minutes and dumps the log over serial on button press. It runs on two AA batteries (3 V).

Design Goals

Watchdog Wake Pattern

The AVR watchdog can only time 8 seconds maximum. For longer intervals, count watchdog wakes:

#include <avr/sleep.h>
#include <avr/wdt.h>
#include <EEPROM.h>

volatile int wdtCount = 0;
const int TARGET_WAKES = 75;  // 75 × 8s ≈ 10 minutes

ISR(WDT_vect) {
    wdtCount++;
}

void enableWDT8s() {
    MCUSR &= ~(1 << WDRF);
    WDTCSR |= (1 << WDCE) | (1 << WDE);
    WDTCSR = (1 << WDIE) | (1 << WDP3) | (1 << WDP0);  // 8s
}

void sleepNWDT(int n) {
    wdtCount = 0;
    enableWDT8s();
    while (wdtCount < n) {
        set_sleep_mode(SLEEP_MODE_PWR_DOWN);
        sleep_enable();
        sei();
        sleep_cpu();
        sleep_disable();
    }
    wdt_disable();
}

EEPROM Log Format

Address 0: uint8_t logCount
Address 1..N: each entry = 2 bytes (uint16_t ADC value)
const int EEPROM_BASE = 1;
const int MAX_ENTRIES = 50;

void logReading(uint16_t val) {
    uint8_t count = EEPROM.read(0);
    if (count >= MAX_ENTRIES) count = 0;  // ring buffer
    EEPROM.put(EEPROM_BASE + count * 2, val);
    EEPROM.update(0, count + 1);
}

Power Consumption

Phase Duration Current
Active (read + log) ~50 ms 8 mA
Power-down sleep ~599.95 s 0.0003 mA
Average 600 s cycle ~0.67 µA active portion + 0.3 µA sleep ≈ 1 µA

Two AA batteries (2500 mAh) would last: 2500 mAh / 0.001 mA = 2,500,000 hours — effectively limited by battery self-discharge (~5 years).


Example 3: ESP32 Memory Profiling Script

A Python script that monitors ESP32 heap usage over serial, useful for detecting memory leaks during development.

The full script is in examples/heap_monitor.py.

import serial, re, time

PORT = "/dev/ttyUSB0"
BAUD = 115200

with serial.Serial(PORT, BAUD, timeout=1) as ser:
    print("Monitoring heap... Ctrl+C to stop")
    while True:
        line = ser.readline().decode("utf-8", errors="ignore").strip()
        m = re.search(r"Free heap: (\d+)", line)
        if m:
            ts = time.strftime("%H:%M:%S")
            print(f"{ts}  free={m.group(1)} bytes")

Add to your ESP32 sketch:

void loop() {
    doWork();
    Serial.printf("Free heap: %u bytes\n", ESP.getFreeHeap());
    delay(1000);
}

If free heap decreases monotonically, you have a memory leak (likely a String not freed, a task not deleted, or a missing f.close()).


Quick Reference: Power Mode Comparison

Platform Mode Current Wake Latency
ATmega328P Active (16 MHz) 12 mA
ATmega328P Idle 3 mA <1 µs
ATmega328P Power-down 0.1 µA ~65 µs
ESP32 Active + WiFi TX 240 mA
ESP32 Active, no radio 20–80 mA
ESP32 Modem sleep 3–20 mA ~1 ms
ESP32 Light sleep 0.8 mA ~1 ms
ESP32 Deep sleep 10–150 µA ~350 ms
ESP32 Deep sleep + ULP ~100 µA ~350 ms

Library Platform Purpose
avr/sleep.h AVR Sleep modes
avr/power.h AVR Peripheral power control
avr/wdt.h AVR Watchdog timer
esp_sleep.h ESP32 All sleep APIs
Preferences.h ESP32 NVS key-value store
LittleFS.h ESP32 Filesystem
esp32-hal-cpu.h ESP32 CPU frequency scaling

Previous: Power Optimization Home