# The battery budget of an always-on sensor app > Four phones on a shelf, airplane mode, screen off, and five weeks of overnight battery logs to find out what each sensor really costs an always-on app. 2026-07-27 · 19 min read · engineering, wheria, physics · by ntan (ntan) for uranashel Canonical HTML: https://uranashel.com/blog/battery-budget-sensors.html --- Wheria's most-requested feature sounds small on the store page: the app notices you parked without you opening it. Behind that sentence sits a pipeline that has to sense something during every hour your phone rides in a pocket. 16 hours a day, roughly. A parking app that flattens a battery gets uninstalled by Thursday, and the OS battery-usage screen is the exact mechanism people use to decide whom to blame. So before we committed to [the indoor pipeline](https://uranashel.com/blog/building-wheria-indoor.html), we measured what continuous sensing actually costs on hardware we own. These are the bench notes from that exercise. Method in one line: 4 phones (iPhone 13, iPhone 15, Pixel 6, Galaxy A54), airplane mode, screen off, one sensor configuration per night, battery percentage logged every 5 min, 3 nights per configuration plus one 24 h weekend run each, about 5 weeks of nights in total. ## A shelf of phones, screens off The rig is embarrassingly simple. Four phones lie face down on a wooden shelf. Airplane mode kills every radio. A small logger app samples the reported battery percentage every 5 min and appends a line to a CSV. Each night runs one configuration: baseline first, then one sensor added at a fixed rate. In the morning the phones charge back to 80% and the next configuration starts. Two honesty notes on the instrument. Battery percentage is a fuel-gauge estimate with a granularity of 1%. Over an 8.5 h night, one step of 1% works out to 0.12%/h, which on a 4,400 mAh pack corresponds to roughly 5 mA. Anything cheaper than 5 mA is invisible in a single night, so the sub-mA figures below come from averaging 3 nights and from the 24 h runs. Second, fuel gauges go nonlinear near full charge, so every run starts at 80% and we trim the first 30 min. Baseline drain with everything off came out at 0.9–2.1% per night across the fleet, Pixel 6 the thirstiest. Every figure that follows is a delta above that phone's own baseline. ## What each sensor drains These are system-level deltas: chip plus driver plus the CPU wakeups needed to deliver samples to an ordinary app process. Datasheet figures for the bare chips run 10–100× lower, and that gap is most of what this post is about. - **Accelerometer, 50 Hz:** 1.4–2.6 mA depending on phone. At 100 Hz: 1.9–3.1 mA. Single-digit mA-class, and almost none of it is the chip — the transducer itself draws about 20 µA. The rest is delivery. - **Gyroscope, added on top:** +2–4 mA. A MEMS gyro keeps a proof mass vibrating continuously, so the physics bills you before the software does. - **Magnetometer, 25 Hz:** 0.5–0.9 mA. Cheap. - **Barometer, 1 Hz:** below the 5 mA single-night floor and below the averaged floor too. Datasheet-class µA. Effectively free, which is why Wheria can afford [continuous floor math](https://uranashel.com/blog/barometer-parking-math.html) at all. - **GPS, continuous fix:** 27–38 mA. GNSS is receive-only, so airplane mode leaves it alive; those phones spent their nights on a window sill for sky view. - **Audio, 48 kHz capture plus our FFT chain:** 9–14 mA. Moderate. Fine for Estua on a bedside charger, wrong for anything that runs unplugged all day. ## From milliamps to percent per hour The conversion is one division. A battery of capacity `C` mAh under a constant drain of `I` mA loses `100·I/C` percent per hour. Minimal fix preserving all downstream arithmetic: replace with "For a nominal 4,400 mAh pack that is `I/44`". (Fuller alternative: change to 5,000 mAh / `I/50` and cascade: ~6 mA single-night floor, 0.02%/h per mA, 0.48%/day, GPS 14%/day, audio 5.8%/day, and 'division by 50' in the closing paragraph.): each continuous milliamp costs 0.023%/h, or 0.55% per day. Strictly the budget should be kept in energy, mWh against pack voltage, because sensor rails and the battery sit at different potentials; at phone scale the mAh shortcut lands inside the fuel gauge's own noise, so we use it. Now the list above turns into consequences. A 50 Hz accelerometer at 2 mA is 1.1% per day, real but invisible next to a screen. Continuous GPS at 30 mA is 0.68%/h, 16% per day, and that is the app the battery screen names by Wednesday. The audio pipeline at 12 mA sits between them at 6.5% per day. The whole discipline of background sensing is arranging never to pay the expensive rows continuously. ## Duty cycling: sense only when something happens Every cost above is a rate, so total drain is rate times time switched on. The duty factor is where the budget is won. A phone spends most of its day stationary on a desk, and parking happens twice on a commute day. Wheria therefore runs as a ladder of states, each rung more expensive and shorter-lived than the one below: ``` IDLE: // coprocessor subscriptions only, ~0 extra on significant-motion wakeup -> CLASSIFY CLASSIFY: // accel 50 Hz, 20 s burst activity == automotive -> DRIVING else -> IDLE DRIVING: // barometer 1 Hz + activity transitions automotive->walking within 90 s -> PARK_CHECK PARK_CHECK: // full pipeline, capped at 3 min step detector + heading + floor delta plausible park: save spot -> IDLE ``` On a two-commute day the full pipeline runs 8–15 min in total. Call it 0.2 h at 6 mA: 1.2 mAh, under 0.03% of the pack. The CLASSIFY bursts add about as much again. Nearly the entire background day is spent in IDLE, and IDLE has to cost almost exactly nothing. Which brings us to the coprocessor. ## Living inside the motion coprocessor Phones already run a small always-on core — Apple's motion coprocessor, the sensor hubs in Qualcomm and Exynos parts — that counts steps and classifies activity for the OS around the clock, on power the OS has already decided to spend. Subscribing to its outputs costs the subscriber approximately nothing, because the marginal work is a callback. Auto parking detection therefore has to be built out of exactly those primitives: significant-motion wakeups, activity-transition events (automotive to walking is the one we care about) and significant-location change. Our own math runs only inside the short windows those events open. The alternative is streaming 50 Hz accelerometer data to our own process all day and running a smarter detector than the OS provides. We tried that prototype for a week. It cost 2.3 mA around the clock on the Pixel 6, about 1.3% per day before doing anything useful, and it kept the application processor out of deep sleep, which the OS punishes with throttled delivery and eventually a spot on the battery screen. Coprocessor events are coarser and arrive 30–90 s late. For parking that is acceptable, because the signal we need next is the walk away from the car, and [the step detector](https://uranashel.com/blog/step-detection-imu.html) gets its data during those minutes anyway. ## Batching and sensor-hub delivery Inside the windows where we do stream, delivery style matters as much as sample rate. Sensor chips write into a hardware FIFO in the hub, and the OS can hand samples over one at a time or in batches. At 100 Hz delivered per-sample, the application processor takes 100 wakeups per second and never reaches its deep idle states. Batched at 4 s intervals, 400 samples per delivery, the same stream measured 0.4 mA against 2.2 mA per-sample on the Pixel 6. Same data, same math, 5× cheaper, at the price of 4 s of latency. So the background states run fully batched, and the live navigation screen runs unbatched, where a 4 s lag would show up in the heading arrow. That mode does not need the frugality anyway: the display itself draws on the order of 80–120 mA, two orders of magnitude above the sensors it is showing. Once the screen is on, sensor power is a rounding error. ## The budget we ship Wheria's background target is 1.5% per day. The fleet measures 0.7–1.3% depending on how much driving a day contains. Getting there meant cutting things that worked. Continuous magnetometer logging for garage fingerprinting cost 0.7 mA around the clock and was cut in favor of sampling only inside PARK_CHECK. Periodic GPS polling to catch highway driving was replaced entirely by activity transitions, which removed the single largest line item. The 100 Hz always-on stream became 50 Hz bursts after the detector proved insensitive to the difference. None of this arithmetic is deep. It is division by 44 applied ruthlessly, plus a refusal to pay any continuous cost the coprocessor already pays on our behalf. The result is a feature on [the Wheria page](https://uranashel.com/apps/wheria.html) that sounds like magic and is mostly bookkeeping: the app knows where you parked, and the battery screen never learns our name. --- uranashel · [Home](https://uranashel.com/) · [Apps](https://uranashel.com/apps.html) · [Lab](https://uranashel.com/lab.html) · [About](https://uranashel.com/about.html) · [Blog](https://uranashel.com/blog/) · [Developers](https://uranashel.com/developers/) · [API docs](https://uranashel.com/docs/) · [Privacy](https://uranashel.com/privacy.html) Machine-readable: [llms.txt](https://uranashel.com/llms.txt) · [sitemap.xml](https://uranashel.com/sitemap.xml) · [openapi.json](https://uranashel.com/openapi.json) · [API](https://uranashel.com/api/v1/)