The Battery Whisperer's Guide: Squeezing Every mAh from Your Tracker
When I first powered up our multi-radio tracker prototype—equipped with Wi-Fi, Bluetooth, LoRa, and GPS—I watched the battery plunge from full to flat in under 6 hours. That painful moment sparked a six-month deep dive into embedded power optimisation. What follows is not theory—it's a field manual.
These are the lessons learned from optimising a real-world ESP32-S3-based tracker. If you're working on battery-powered IoT devices with multiple radios and sensors, this guide will likely save you months of trial, error, and dead batteries.
The Hidden Power Vampires: Understanding Your Baseline

Most engineers begin with datasheet values to estimate battery life—but the field teaches you better. I did the same. On paper, our sleep current was under 50μA. In reality, it was 850μA. That single number changed everything. Every unmeasured consumption is a silent battery killer.
In our case, three missteps accounted for over 90% of our baseline current:
- A GPS module's "standby" backup rail drawing 150μA
- A leaky LDO that behaved beautifully under load but pulled 200μA at idle
- Unpowered I2C sensors leaking 500μA via pull-ups
Tracking this down required isolating each domain, switching power rails in software and hardware, and validating every consumption. We turned our 98-day idle lifetime into 545 days—before we even touched optimization logic.
Architecture for Micro-Power: The Foundation

We learned early that quiescent current matters—but power domain control defines capability.
Our three-domain strategy:
- Always-on: ESP32-S3, RTC domain, ultra-low-Iq LDO (TPS7A05 @ 25nA)
- Sensor domain: IMU, barometer, microphone powered via P-MOSFET switch
- Radio domain: GPS, LoRa isolated on separate enable lines
This let us completely power off domains, not just sleep peripherals. Example: microphone's analog front end still drew 50μA in standby—so we gated it with hardware. Over 24 hours, that alone saved 1.2mAh. Multiply that by weeks of runtime, and your math changes fast.
The biggest gain, however, came from the LDOs. Initially we chose ultra-low-Iq parts everywhere. But when our active draw peaked at 250mA during radio bursts, we saw dropout, ripple, and sluggish recovery. Switching to a higher-Iq LDO on the 3.3V rail (50μA vs 1μA) gave us 15% net battery savings by avoiding brownouts and inefficient conversions.
Radio Discipline: The 80% Solution

Radios make or break your battery. And it's not just how often you use them—it’s how intelligently you time them.
- GPS: Cold starts took up to 90s at 42mA. We implemented AGPS preload over Wi-Fi, stored ephemeris in RTC RAM, and used IMU data to throttle fix rates. If the dog wasn’t moving, no GPS. If it sprinted, faster fixes kicked in.
- Wi-Fi: Static IP, DNS caching, and DTIM interval control dropped our reconnect time from 5s to 1.6s. Connection reuse beat aggressive polling. For infrequent updates, we disconnected fully. For fast syncs, we stayed in modem sleep.
- LoRa: We dynamically tuned SF7–SF10 based on RSSI/SNR. This cut our TX power by up to 70% on short-range syncs. We logged SNR metrics and adapted on the fly. No more always-on SF10 "just in case."
Our radio state machine became smarter than our state diagram. Motion-gated GPS, battery-aware Wi-Fi upload intervals, and fallback logic for LoRa-only fallback when other radios failed.
MCU Power States: The Deep Sleep Religion

The ESP32-S3 deep sleep mode is the only mode that matters in low-duty devices. We achieved 11μA baseline draw using:
- RTC Timer + GPIO interrupt for scheduled and reactive wake
- ULP coprocessor running sub-5kHz polling loop (battery voltage + IMU event)
Wake intervals were tuneable based on activity. During periods of user motion (via IMU), the RTC timer fired every 2 minutes. In inactivity states, we backed it off to 10 or even 30 minutes.
The ULP let us avoid full wakeups entirely for 50–75% of cycles. Its 150μA active draw, running at 1% duty cycle, cost less than 2μA average.
Sensors and Peripherals: Death by a Thousand Cuts

Peripherals are sneaky. One 50μA standby line doesn’t seem much—until you multiply it by three sensors, 24 hours, and 15 days.
Flash, for instance, burned 50μA in idle until we started issuing 0xB9 (deep power-down) after every write. We now batch 20 readings in RAM and flush once every 10 minutes, waking the Flash chip briefly, then sleeping again.
Our IMU (ICM-20948) runs in accelerometer-only mode most of the time (8μA). Full 9-axis activation is conditional: real movement or a calibration window.
And audio? Off by default. We power it via MOSFET only for 2-second windows during specific user-defined scenarios (barking analysis, sound classification).
Firmware Architecture: Orchestrating Efficiency

Firmware is the glue between all these savings. Our scheduler divides each wake cycle into discrete slices:
- RTC Restore + Wake Source Check
- Fast Sensors (IMU, barometer)
- Decision Tree: GPS Fix?
- Logging
- Transmit (Wi-Fi/LoRa)
- Store context + Sleep
All blocking ops (e.g. GPS acquisition) have hardcoded timeouts. If GPS doesn’t fix in 60s, we drop. If Wi-Fi isn’t connected in 10s, we defer.
Everything adapts:
- Battery < 3.4V? Radio updates drop to once/hour.
- No motion? Skip GPS.
- Flash full? Switch to low-resolution logging.
Charging System Optimization
Charging should be smart—not fast. Our charger runs at 250mA, maximizing lithium battery longevity. Thermistor-controlled charge cutoffs at >45°C avoid thermal degradation.
We also disabled charge during radio-intensive sessions. Flash writes + GPS + charging = heat + brownouts. Better to charge gently when idle.
Real-World Results
- Quiescent draw: 850μA → 11μA
- GPS fix energy: 2.4mAh → 0.6mAh
- Wi-Fi TX: 450mAh → 85mAh
- 2000mAh battery: 8 hours → 15 days of real usage
Key Takeaways
- Measure aggressively
- Use deep sleep or nothing
- Treat GPS like a tiger: powerful, expensive, timing-sensitive
- Schedule radios like a network architect
- Use ULP as a real processor, not an afterthought
- Power off what you don’t need—literally
- Build firmware to adapt, fail gracefully, and recover predictably
Everything here was built to support Hoomanely’s core goal: seamless, non-invasive devices. We build systems that stay quiet when they should, alert when they must, and never ask users to micromanage charge cycles.
This battery-first design flows into all of our work—from environmental sensors to wellness wearables. The same architecture that lets a pet tracker last weeks is what will someday let a health patch run a full recovery cycle, or an environmental monitor live untethered for months.
This guide represents hundreds of hours of profiling and refinement. If you're building similar systems, these lessons are yours to borrow, remix, or improve upon. The best battery optimisation happens in the field, not the lab. Let's push the limit of what small power can do.