Skip to content

fix(power): re-seed analog battery filter on charging-state change#11010

Open
truongsinh wants to merge 1 commit into
meshtastic:masterfrom
truongsinh:reseed-vbatt-filter
Open

fix(power): re-seed analog battery filter on charging-state change#11010
truongsinh wants to merge 1 commit into
meshtastic:masterfrom
truongsinh:reseed-vbatt-filter

Conversation

@truongsinh

Copy link
Copy Markdown

Problem

The analog battery voltage is intentionally smoothed (15-sample ADC average + a 0.5-coefficient low-pass filter in AnalogBatteryLevel::getBattVoltage()) to reject noise. A charging-state transition, however, steps the real voltage sharply (charge voltage vs loaded/rested voltage), and the filter blends that step in by halves per Power::runOnce poll (20 s) — so the reported voltage, and the SoC derived from it via the OCV table, keep tracking the previous power state for roughly 1–2 minutes after plugging or unplugging external power. Telemetry sent (or requested) shortly after a power change reports these stale values.

Fix

  • Power::readPowerStatus() tracks the resolved charging state across polls (checked after all detection variants, including the NRF_APM USB-regulator path) and, on a transition, calls a new HasBatteryLevel::notifyChargingStateChanged() hook, then takes an immediate fresh voltage/percent reading.
  • AnalogBatteryLevel implements the hook by bypassing the 5 s read throttle and replacing the filtered value with the next raw reading instead of blending it.

No behavior change when the charging state is steady (the filter path is identical), and battery sensors with their own gauge (PMU/fuel-gauge boards) are unaffected — the base-class hook is a no-op.

Testing

Tested on two Seeed T1000-E tracker cards (one flashed with this patch on the v2.7.26 base, plus a build from current master), with external power switched by a BLE-controlled inline USB power switch so the disconnect moment is timestamped:

  • While charging: battery_level=101, voltage ≈ 4.02 V (charge voltage) — unchanged.
  • External power removed at t=0: telemetry requested over the mesh returned a real SoC and a settled voltage that stayed flat across samples at t+2:02 / t+2:24 / t+2:46 (76% / 3.93 V on all three) — i.e. the reading had fully re-seeded within one poll, where the unpatched filter is still converging in that window.
  • 1 h combined runtime on both devices with the patch: mesh traffic, position, telemetry all normal; no resets.

🤝 Attestations

  • I have tested that my proposed changes behave as described.
  • I have tested that my proposed changes do not cause any obvious regressions on the following devices:
    • Heltec (Lora32) V3
    • LilyGo T-Deck
    • LilyGo T-Beam
    • RAK WisBlock 4631
    • Seeed Studio T-1000E tracker card
    • Other (please specify below)

I don't have the other listed devices; the touched code paths are shared (Power.cpp), with the analog re-seed only reachable on boards using AnalogBatteryLevel and only on a charging-state edge.

The analog battery voltage is intentionally smoothed (15-sample ADC average
plus a 0.5-coefficient low-pass filter) to reject noise. But a charging-state
transition steps the real voltage sharply (charge voltage vs loaded/rested
voltage), and blending that step in means the reported voltage - and the SoC
derived from it via the OCV table - keeps tracking the previous power state
for several 20 s polls (roughly one to two minutes) after plugging or
unplugging external power.

Detect the charging-state edge in readPowerStatus() (after all detection
variants, including NRF_APM, have resolved) and let the battery-level sensor
re-seed its filter with an immediate fresh reading, so telemetry sent shortly
after a plug/unplug reflects the new state within one poll.
@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 27c836ed-ee62-46b5-8db4-1d55c28d9c98

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

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