Low-Power Design & Energy Budgets

Battery-powered devices live or die by their energy budget. Learn MCU power modes (run, sleep, stop, standby, shutdown), dynamic vs static power, race-to-sleep, wake-up sources and latency, peripheral and GPIO leakage, duty-cycling radios and sensors, measuring µA currents, and building a complete battery-life budget.

A coin-cell sensor that should last five years has an average current budget of just a few microamps. Achieving that means the MCU spends > 99.9% of its time in deep sleep, wakes on an RTC alarm, does its work in milliseconds, and goes back to sleep — while every pin, pull-up and regulator is checked for leakage.

Power optimisation is a system job: firmware, hardware and even the choice of battery chemistry interact. The numbers here let you predict battery life before building anything.

An analogy

Think of a lighthouse keeper who naps between ships: the lighthouse is dark most of the time, flashes briefly when needed, and the keeper's alarm clock (RTC) or the ship's horn (pin interrupt) wakes him. The shorter the flashes and the deeper the naps, the longer the oil lasts.

Where the current goes

CMOS logic draws current mainly when switching (dynamic power ∝ frequency and V²) plus leakage (static power, grows with temperature). An MCU running at 64 MHz might draw ~6 mA; in STOP mode with RAM retained ~2 µA; in shutdown ~30 nA. External parts — LEDs, pull-ups, regulators, sensors, radios — often draw more than the MCU itself.

Power modes

Deeper modes save more but cost wake-up time and lose state:

Race to sleep and duty cycling

Because static and peripheral overheads are paid for as long as you are awake, it is often better to run fast and sleep sooner. Batch work (sample 10 readings, send once), use DMA and peripherals that operate autonomously in low-power modes (LPUART, LPTIM, comparator wake-up), and keep radios off as long as possible — a BLE advertisement or LoRa packet dominates most budgets.

Measuring and verifying

Use a source-measure unit or power profiler (e.g. Nordic PPK2, Joulescope) with µA–mA dynamic range, and integrate charge over a full cycle. Check leakage per GPIO, pull-up resistors (3.3 V / 10 kΩ = 330 µA!), regulator quiescent current, and debug interfaces left enabled (debug in low-power modes keeps clocks alive). Budget battery self-discharge, temperature derating and end-of-life voltage.

Formulas

Worked example: Sensor-node battery life

Given: Active 8 mA for 20 ms every 10 s; Sleep 2 µA; 1000 mAh

Find: Average current and life.

Answer: ≈ 18 µA → 6.3 years (before derating)

Worked example: Race to sleep

Given: Task = 1 M cycles; 16 MHz: 5 mA; 64 MHz: 17 mA (incl. 1 mA static)

Find: Charge per task.

Answer: Running at 64 MHz uses 15% less charge

Worked example: Hidden pull-up

Given: 10 kΩ pull-up on a button held to ground by a closed switch

Find: Current.

Answer: 330 µA — 150× the MCU's STOP current!

RTC-driven sense-and-sleep loop (STM32L4-style HAL)

int main(void)
{
    HAL_Init();
    clock_config_msi_4mhz();          /* low-power clock is enough      */
    gpio_all_analog();                /* unused pins: analog = lowest leak */
    rtc_wakeup_every(10);             /* wake every 10 s                */

    for (;;) {
        sensor_power(ON);             /* switch sensor supply via GPIO  */
        HAL_Delay(2);                 /* sensor settling                */
        int16_t t = sensor_read_temp();
        sensor_power(OFF);

        if (radio_needed(t)) radio_send(t);   /* radio: biggest cost    */

        HAL_SuspendTick();            /* stop SysTick waking us         */
        HAL_PWREx_EnterSTOP2Mode(PWR_STOPENTRY_WFI);   /* ~2 µA         */
        HAL_ResumeTick();
        clock_config_msi_4mhz();      /* clocks must be restored after STOP */
    }
}

Everything not needed is off: sensor power switched by a GPIO, unused pins set to analog mode, SysTick suspended. After STOP mode the clock configuration must be restored before continuing.

Common mistakes

Applications

Key takeaways

Going further