Skip to content

fw/services/battery: make the periodic fuel gauge save time-based - #1844

Draft
adipascu wants to merge 6 commits into
coredevices:mainfrom
peblum:charge-limit-save-cadence
Draft

adipascu wants to merge 6 commits into
coredevices:mainfrom
peblum:charge-limit-save-cadence

Conversation

@adipascu

@adipascu adipascu commented Aug 6, 2026 •

Copy link
Copy Markdown
Contributor

The charge limiter can park a couple of percent above the configured limit, because it only acts when the fuel gauge samples and the gauge samples once a minute. This removes that overshoot, and fixes the save cadence the faster sampling would otherwise disturb.

Important

Stacked on #1841, so the files view below includes its commits. Only the last two are new here, and their diff is the review surface.

One commit per section below, in that order.

Sampling the gauge faster while plugged in

At the 1C rapid charge rate a minute of charging is roughly 2% of capacity, so an 80% limit could pause at 81% on the display. While plugged in the next update is now rescheduled after 5 seconds, which bounds the overshoot to a fraction of a percent and keeps the displayed percentage fresh while charging. The extra samples cost nothing on USB power, and the one-minute timer stays as the unplugged cadence and as a backstop.

The gauge time base also keeps fractional seconds now. Truncating to whole seconds discards little of a one-minute interval but up to a fifth of a five-second one.

Making the periodic gauge save time-based

The periodic fuel gauge state save counted samples, which coupled it to the sample rate: with the 5-second cadence it would have run every 25 minutes while docked instead of the 5 hours it effectively is today. The save is now due when 5 hours have elapsed since the last one, whatever the sampling rate, and FUEL_GAUGE_SAVE_INTERVAL_S is named for what the value means. Saves triggered by battery state changes are unchanged.

Written with AI assistance (Claude), and running on my own Pebble Time 2.


Aside: I am currently available for firmware contracting or full-time work. Contact: adrian@pascu.be.

battery_set_charge_enable unconditionally cleared the emulated USB
connection, so pausing charging made the emulator report the charger
as unplugged and the connection could never be restored. Track a
separate charging-enabled flag and report charging only while it is
set.

Signed-off-by: Adrian Pascu <adrian@pascu.be>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
@adipascu
adipascu force-pushed the charge-limit-save-cadence branch from 2daffb5 to ee77728 Compare September 11, 2026 14:55
adipascu and others added 5 commits September 12, 2026 20:22
The one-shot charger enable after the first fuel gauge update ran
after the state change event was posted. The event handler runs on
KernelMain, which preempts KernelBG, so a consumer reacting to the
event by disabling the charger would be silently overridden when
KernelBG resumed. Enable the charger before posting the event.

Signed-off-by: Adrian Pascu <adrian@pascu.be>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Add a battery charge limit service that pauses charging once the
battery reaches a configurable percentage limit, reducing degradation
from sustained high charge levels. While plugged in the watch runs
from USB power, so the level holds without a resume threshold and
charging re-enables when the level is below the limit, the watch is
unplugged, or the limit is disabled. The limiter stands down while
charging is force-disabled from the debug prompt.

The chargeLimitPct preference (0 disables, 50-95 accepted, validated
on sync and at boot) is off by default, set from the phone via the
settings blob db, and applies immediately by forcing a battery state
update.

Based on an original patch by Shashvat Prabhu, reworked to a
configurable percentage with event-driven evaluation instead of a
60 s poll timer, with the nonexistent logging include and the
charging-stuck-off-after-unplug bugs fixed, and with unit tests
added.

Fixes coredevices#722

Co-authored-by: Shashvat Prabhu <shashvatprabhu2006@gmail.com>
Signed-off-by: Adrian Pascu <adrian@pascu.be>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Surface the configured battery charge limit as a read-only row in
Settings > System > Information, so the active limit can be checked
on the watch itself. Shows Off while the limit is disabled.

Signed-off-by: Adrian Pascu <adrian@pascu.be>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
The fuel gauge sampled once per minute regardless of charger state,
and the charge limiter only evaluates on those samples. At the 1C
rapid charge rate the level could run about 2% past the configured
limit between samples, pausing at 81% with an 80% limit set.

Reschedule the next update after 5 seconds whenever the watch is
plugged in. This bounds the limit overshoot to a fraction of a
percent and keeps the displayed percentage fresh while charging.
On USB power the extra samples cost nothing, and the one-minute
regular timer remains as the unplugged cadence and as a backstop
if an update errors out early.

The gauge time base now keeps fractional seconds: truncating to
whole seconds was negligible at a one-minute cadence but discards
up to a fifth of a five-second interval. Since the periodic fuel
gauge state save counts samples, it runs about every 25 minutes
instead of every 5 hours while plugged in, a harmless settings
file write.

Signed-off-by: Adrian Pascu <adrian@pascu.be>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
The periodic fuel gauge state save counted samples, coupling its
cadence to the sample rate: 300 one-minute samples put the idle
save at every 5 hours, and the FUEL_GAUGE_SAVE_INTERVAL_S name did
not match what the value counted. With the plugged-in 5-second
sampling the same count would run the idle save every 25 minutes
while docked, needless write amplification while parked at the
charge limit.

Compare elapsed time since the last save instead, keeping the
5-hour idle cadence regardless of sample rate. Saves triggered by
battery state changes are unchanged, and the first update after
boot still saves through its forced update.

Signed-off-by: Adrian Pascu <adrian@pascu.be>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
@adipascu
adipascu force-pushed the charge-limit-save-cadence branch from ee77728 to c1e9ee7 Compare September 12, 2026 18:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant