Basics
Runtime = usable capacity ÷ average current
A battery's mAh rating tells you how many milliamps it can deliver for how many hours: in theory a 2,000 mAh cell runs a 2 mA load for 1,000 hours. Real IoT devices don't draw a constant current, though. A sensor node sleeps at a few microamps most of the time, then wakes for a few seconds at tens or hundreds of milliamps to measure and transmit. So you first compute the average current over one complete cycle, then divide it into the capacity you can actually extract — the label value reduced by the cutoff voltage, aging and temperature. When comparing batteries of different voltages, compare energy in Wh (mAh × V ÷ 1000), not mAh.
Average load current: I_avg = (I_active × t_active + I_sleep × t_sleep) / (t_active + t_sleep) + I_always-on Switching regulator: I_batt = I_avg × V_load / (V_batt × η) (LDO or direct: I_batt = I_avg) Usable capacity: C_eff = C × usable fraction × aging factor × temperature factor Self-discharge: I_sd = C × (% per month) / 730 h Runtime: t = C_eff / (I_batt + I_sd)
Example 1 — an ESP32 sensor posting over Wi-Fi every 10 minutes
2,000 mAh LiPo; every 10 minutes (600 s) the board wakes for 3 s at an average 120 mA (Wi-Fi association plus transmit) and sleeps for 597 s at 15 µA whole-board. I_avg = (120 × 3 + 0.015 × 597) ÷ 600 = 0.615 mA. Dividing the label capacity gives 3,252 h (135 days), but with 90 % usable to cutoff and 80 % end-of-life capacity you have 1,440 mAh, and 3 %/month self-discharge adds 0.082 mA — about 2,066 h, or 86 days. The active burst is over 97 % of the load charge, so trimming Wi-Fi time from 3 s to 1 s nearly triples runtime, while shaving sleep current barely matters. Wake every minute instead and the average rises to 6 mA: under ten days.
Example 2 — a BLE beacon on a CR2032
225 mAh CR2032; advertising every second for 2 ms at 8 mA, 2 µA otherwise. I_avg = 8 × 0.002 + 0.002 × 0.998 ≈ 0.018 mA (18 µA). Because pulse loads reduce a coin cell's usable capacity, take 80 %: 180 mAh ÷ 0.0183 mA ≈ 9,800 h, about 1.1 years. Here the 2 µA sleep floor is 11 % of the total, so both sleep current and advertising interval matter.
Typical values
Battery chemistries — don't take the label at face value
| Type | Nominal V | Typical capacity | Cutoff | Self-discharge | Notes |
|---|---|---|---|---|---|
| Li-ion 18650 | 3.6–3.7 V | 2,500–3,500 mAh | 2.5–3.0 V | 2–3 %/month | Incl. protection IC; behind a 3.3 V LDO it drops out near 3.5 V |
| LiPo pouch | 3.7 V | Label (budget cells 70–80 %) | 3.0 V | 2–5 %/month | Permanent damage if over-discharged; protection required |
| AA alkaline | 1.5 V | 2,500–2,800 mAh (light load) | 0.9–1.0 V | 2–3 %/year | Only 1,000–1,500 mAh at 500 mA; poor in the cold |
| AA NiMH, low self-discharge | 1.2 V | 1,900–2,000 mAh | 1.0 V | 1–1.5 %/month | Standard NiMH loses 15–30 %/month |
| CR2032 | 3.0 V | 220–240 mAh | 2.0 V | ≈ 1 %/year | Rated at ~0.2 mA continuous; weak at pulses |
| ER14505 (Li-SOCl₂, AA size) | 3.6 V | 2,400–2,700 mAh | 2.0–3.0 V | < 1 %/year | 10-year IoT cell; buffer radio pulses with an HLC/supercap |
As a rough temperature guide, Li-ion delivers about 80–90 % of rated capacity at 0 °C (32 °F) and 50–70 % at −20 °C (−4 °F); alkaline cells fall off even faster below freezing. The aging factor depends on whether the device must still meet spec at end of life — Li-ion is conventionally 'worn out' at 80 % of rated capacity, so if you promise a service interval, design with 80 %.
Pitfalls
Common mistakes
- Using the chip's sleep current for the board: an ESP32 die sleeps at about 10 µA, but a typical dev board keeps drawing milliamps through its USB-UART bridge, an AMS1117-class LDO (≈ 5 mA quiescent) and the power LED. Always measure the finished board.
- Ignoring LDO dropout: a 3.3 V LDO with 200 mV dropout loses regulation once a Li-ion cell falls below 3.5 V, stranding the last 15–20 % of capacity. Lower the usable fraction to about 80 % or use a low-dropout or buck-boost converter.
- Mixing peak and average current: a 300 mA+ Wi-Fi transmit peak is a battery impedance and capacitor problem, not an average-current input. Enter the measured average over the active window.
- Comparing batteries in mAh across voltages: 3.7 V × 2,000 mAh (7.4 Wh) holds three times the energy of 1.2 V × 2,000 mAh (2.4 Wh).
- Forgetting self-discharge: for devices averaging a few microamps, self-discharge can exceed the actual load. A one-year product on standard NiMH will drain itself before the electronics do.
Low-power design
Getting long life from ESP32 and STM32 designs
- Fix the regulator first: an LDO with microamp quiescent current (HT7333 ≈ 4 µA, TPS7A02 in the 25 nA class) cuts sleep current by three orders of magnitude versus an AMS1117 at ≈ 5 mA.
- Shorten Wi-Fi sessions: a static IP, caching the last AP's channel and BSSID, and sleeping immediately after transmit can take an ESP32's wake time from seconds to a few hundred milliseconds — usually the single biggest win.
- Pick a low-power STM32 family: STM32L4/U5 Stop 2 sits around 1–2 µA with the RTC running, whereas general-purpose F1/F4 parts draw tens of microamps even in Stop. Choose L or U series from day one for battery products.
- Kill divider and pull-up leakage: a 100 kΩ + 100 kΩ battery-sense divider burns 21 µA at 4.2 V around the clock. Go to 1 MΩ-class values with a capacitor at the ADC pin, or switch the divider with a MOSFET.
- Measure properly: a multimeter's µA range has enough burden voltage to brown out the MCU when it wakes. A current profiler (Nordic PPK2, Joulescope, etc.) gives the true average over a full cycle.
Tip from KEMI's bench
When we design a battery-powered board, the first deliverable is a quiescent-current budget for every branch of the power tree — regulator Iq, sensor sleep currents, pull-ups, dividers and protection ICs, all in microamps. More often than not the peripherals, not the MCU, dominate the sleep floor. Put that total into the calculator's always-on field.
