The nRF54L15 Wakes Up Too Often: How Advertising Jitter Silently Halves Your Battery Estimate

The spreadsheet said 3 years
Your spreadsheet said 3 years. The bench says 18 months, maybe less if the room’s cold. Same part, same firmware, same coin cell. Something’s eating current you didn’t budget for, and it’s not a defective nRF54L15.
The gap almost always lives inside one number: avg_current. When engineers estimate coin-cell life for low-power BLE wakeups, they build that average from clean, deterministic timing. The radio wakes exactly on the interval, stays on for the minimum active window, goes back to sleep. Tidy. Wrong.
Your model assumed a level of timing precision that the BLE spec forbids and the physics of a low-frequency clock can’t deliver. Let’s rebuild the estimate so it matches the bench instead of fighting it.
Where the standard nRF54L15 battery life estimate comes from
Everyone starts the same place. Charge per advertising event, times events per second, plus sleep current. Divide capacity by the result.
avg_I = (Q_event × f_adv) + I_sleep
life = C_battery / avg_IQ_event is the charge (in coulombs, or µA·s) burned during one advertising event: the radio warming up, transmitting on 3 channels, maybe listening for scan requests, then shutting down. f_adv is how often that happens, taken as 1 / interval. I_sleep is what the chip draws between events, dominated by the RTC and retained RAM.
This formula is fine. The inputs are where it goes wrong. Two silent assumptions ride along:
f_advequals exactly1/interval. Set a 100 ms interval, get 10 events per second.Q_eventreflects the minimum active time. The radio is on only as long as it strictly needs to be.
Both are optimistic. The first is off by a small, well-defined amount. The second can be off by a lot, and it’s the one that quietly doubles your current.
Assumption 1: BLE advertising jitter is not optional
The Bluetooth Core Spec mandates a pseudo-random delay, advDelay, added to every advertising event. It’s a value between 0 and 10 ms, redrawn each event. This exists so that two devices advertising at the same nominal interval don’t lock into a pattern where their packets collide on the same channel forever.
You don’t get to turn it off. It’s not a Nordic setting or an SDK flag. It’s in the protocol.
The direct effect on timing is small. Your nominal interval stretches by the average of that random draw:
Nominal interval: 100 ms
+ advDelay (0-10 ms, avg ~5 ms)
Effective avg: ~105 msHere’s the part people misread. That ~5 ms of average spacing actually helps your battery a hair, because events happen slightly less often. If jitter only shifted the spacing, it’d be a rounding error in your favor.
Then why does “jitter halves your battery” get repeated on forums? Because jitter rarely travels alone. The random advDelay means the scheduler can’t predict exactly when the next event fires relative to its own clock. Combine that uncertainty with a low-frequency clock that itself drifts, and the stack has to compensate by keeping the radio on longer. The spacing barely moves. The active window swells.
That’s the real cost, and it lives in the next assumption.
Assumption 2: RTC drift power widens the active window
The nRF54L15 times its sleep intervals off a low-frequency clock. When it’s time to wake for an advertising event (or worse, a connection event where it must receive), the scheduler has to be on and ready before the event actually needs to happen. It can’t wake late, or it misses the slot.
How early does it wake? That depends on how much the clock could have drifted since the last sync. The scheduler opens a guard window sized to the worst-case timing error. It then holds the radio in RX across that window to be safe. A wider guard window means more microseconds of active radio current per event, which inflates Q_event directly.
This is where your low-frequency clock source stops being a footnote.
Clock source | Tolerance | Guard impact
RC oscillator | ±250-500 | wide window
Crystal (XTAL) | ±20-50 | tight window
(ppm - confirm against the latest nRF54L15 datasheet)The internal RC oscillator saves you a crystal on the BOM, but its tolerance is roughly an order of magnitude looser than a 32.768 kHz crystal. At ±500 ppm, drift over a 1-second window is around ±500 µs. The stack has to guard against every microsecond of that uncertainty on both edges. Every extra microsecond of RX, multiplied across every event over years, climbs fast.
Two clarifications so you budget correctly.
For a pure connectionless advertiser with no scan response, the guard-window hit is smaller. You’re transmitting on a schedule you control, not chasing someone else’s clock. The drift penalty bites hardest on connections and scan-response listening, where you must receive inside a window aligned to a peer’s timing.
And the RC oscillator can be periodically calibrated against the high-frequency clock, which tightens its effective tolerance. Whether that’s enabled, and how often it runs, is a build-time choice that changes your real numbers. Check what your project actually configures before assuming the worst case.
The interval you set is not the active time you pay for. Jitter plus drift decouples the two, and the active time is what drains the cell.
Rebuilding the coin cell battery estimate honestly
Same formula, honest inputs. Use a Q_event you measured or pulled from the Nordic Online Power Profiler with your real active-time profile, not the theoretical minimum. Adjust f_adv down slightly for average advDelay. And remember that at long intervals, sleep current starts to dominate the average, so a sloppy I_sleep figure matters more than you’d think.
Here’s optimistic versus realistic on a ~200 mAh CR2032. These numbers are illustrative. The ratio is the point, not the absolutes. Don’t lift these µA values into your own model.
Optimistic Realistic
avg current ~7 uA ~14 uA
CR2032 (200mAh) ~3.2 yr ~1.6 yr
(illustrative - verify against the latest Nordic figures)Roughly 2x. Notice what moved: it’s the average current, driven mostly by a fatter Q_event, not by the small change in event spacing. If you built your original estimate on a generic “BLE battery life calculator” blog post that took the interval as gospel and used a best-case active current, this is exactly the gap you’d see.
The absolute years depend on your interval, your payload, your clock source, and your specific silicon revision. But build an estimate from minimum active time and perfect timing and you’ve got a floor, not a forecast. Treat a rosy spreadsheet as the best case you’ll never quite hit.
How to verify on your own bench
Stop arguing with the spreadsheet and measure.
Measure real event charge. Put a Power Profiler Kit II on the device and capture actual advertising events. Read the charge per event and the active-window duration directly. That single measurement replaces the most error-prone number in your model. Feed the same active profile into the Nordic Online Power Profiler. Treat that tool as your reference, not any static table (this one included).
Log actual advertising timestamps. Confirm your effective interval matches what you set plus average jitter. If it’s wildly off, you may have a scheduling issue on top of the expected advDelay.
Check your LF clock source. Open your build and confirm whether LFCLK is running on the RC oscillator or a 32.768 kHz crystal, and whether RC calibration is enabled. This one config line can move your guard window, and your battery life, more than any other change. If you find RC without calibration on a design targeting multi-year life, that’s your first thing to fix.
One hard caveat. The nRF54L15’s power figures have shifted across silicon revisions and documentation stages. Early-silicon and preview numbers for active-radio TX/RX current and sleep current aren’t the numbers you’ll ship against. Before you trust any current value, including the illustrative ones here, confirm active-radio and sleep currents against the latest Nordic datasheet and the current Online Power Profiler. Nordic’s DevZone threads on advertising current and clock accuracy are worth reading for the nuance, but the datasheet at your time of writing is the authority.
Trust the bench, not the cell reference
Advertising jitter is required by spec. Clock drift is physics. Neither is a bug you can patch out, and neither shows up in the clean version of the battery formula that most estimates start from.
Your spreadsheet is a floor. It tells you the life you’d get if timing were perfect and the radio only ever stayed on for the theoretical minimum, which it won’t. Measure the real event charge, confirm your clock source, rebuild the average current from what the bench actually shows, then add margin on top. Budget for the device you built, not the one the formula wishes you’d built.
Hubble Network connects BLE devices directly to satellites, so your advertising budget stretches to global reach without a gateway in sight. See how it works →