Skip to content

Fix unintended power-on, battery shutdown, and charging recovery - #279

Open
TobiasRoeddiger wants to merge 3 commits into
mainfrom
codex/battery-reliability
Open

TobiasRoeddiger wants to merge 3 commits into
mainfrom
codex/battery-reliability

Conversation

@TobiasRoeddiger

@TobiasRoeddiger TobiasRoeddiger commented Sep 16, 2026

Copy link
Copy Markdown
Member

This fixes several ways the earable could keep consuming battery after being turned off, or stop charging without recovering. The changes target the v2.7 board with the VARTA CP1454 A4X cell.

What changed and why

  1. Turning off on USB now stays in charging-only mode. Previously, button-off and automatic shutdown rebooted the device, and startup interpreted that reboot as a request to run the application again. The firmware now remembers the off request across reset and keeps audio, sensors and Bluetooth operation off while charging. This closes the “turned off, but running again” path.

  2. Startup always initializes the LED mode. Previously, an uninitialized value could select the custom LED mode, whose initial color is black. That could hide later charging/full-charge indications even while the device was running. Startup now explicitly initializes the complete LED state.

  3. Battery-only shutdown disconnects the system supply. Shutdown now requests the power chip's ship mode, stops audio and the network core, and falls back to processor System OFF if the ship command fails. Battery alerts no longer wake an intentionally off device. USB/button wake signals are armed only after cleanup, immediately before System OFF, so USB arriving during cleanup cannot trap shutdown in repeated interrupts.

  4. Low-battery protection runs periodically, including after USB is unplugged. Previously, periodic charger work stopped on unplug, leaving battery shutdown dependent on gauge interrupts. Checks now run on a dedicated battery queue, with one place deciding whether charging is allowed. Normal operation stops at 3.25 V and needs 3.35 V to restart. On USB, low voltage stops application loads but allows charging-only recovery. Failed safety readings stop normal operation on battery and inhibit charging on USB.

  5. Charger settings must actually take effect. Previously, failed writes were ignored; the chip could also acknowledge a voltage-setting write without applying it while the output was enabled. Startup now disables both output-enable sources before setting the peripheral supply to 3.3 V, checks communication errors, and reads settings back. Bus operations are serialized and respect the required delays. Unexpected charger resets are detected and repaired through charging-only startup. Reading status no longer accidentally undoes a charge inhibit.

  6. Fuel-gauge setup cannot wait forever or use frozen measurements. Configuration entry/exit now have time limits, write failures propagate, and charging rejects measurements while configuration mode is active. Profile checks use design capacity instead of learned full-charge capacity, avoiding unnecessary reprogramming as the cell ages. Programming is deferred during depleted-cell recovery. RAM verification also compares only the requested bytes, avoiding an out-of-bounds comparison.

  7. A charger safety-timer fault gets one controlled recovery. Previously, polling the fault did not clear it, so charging could remain stopped. Firmware now uses the charger's disable pin to recover once, only when battery readings and electrical conditions permit it. The hardware safety timer remains enabled. Retry/fault state survives reboots; a reboot after using the retry conservatively blocks charging until USB is disconnected, because the remaining timer budget is lost during startup.

  8. Storage errors and fatal errors no longer leave the device stuck running. A full SD card can return a successful zero-byte write; shutdown previously retried that forever. It now reports the error and still closes the file so shutdown can continue. Fatal errors on this board reboot into the battery/charging path instead of remaining in the debug panic loop.

Battery settings and recovery tool

Setting Before After / reason
Fast-charge current 110 mA 100 mA, matching the cell's charge profile
Maximum allowed gauge temperature 50 °C 45 °C; charging is permitted within 0–45 °C
Firmware undervoltage cutoff 3.0 V Still 3.0 V; it is now checked after programming
Recovery-tool undervoltage cutoff Effectively 2.6 V 3.0 V, matching firmware
Periodic check interval 10 s normal / 1 s fault Same defaults; configurable intervals bounded to 1–20 s to keep servicing the charger watchdog

The v2.7 charger temperature input is a fixed resistor divider, not a battery thermistor. Firmware continues to disable that input, now with verification. The recovery tool does the same and requires valid gauge temperature and safety flags before proceeding. Documentation now explains charging faults without promising that reconnecting USB will always recover the device.

Verification and remaining limits

  • Local signed firmware build and GitHub firmware build passed for 1bfc940.
  • 32 recovery-tool tests passed, plus local production-code checks covering 12 charger scenarios, 24 power-manager scenarios, gauge failures/timeouts/concurrency, and six SD shutdown cases. Large local test harnesses are not included in the PR.
  • Original-versus-fixed code checks reproduced the USB auto-off restart and verified charging-only startup after the fix. The custom-black LED behavior was reproduced separately; its occurrence in the reported incident is unproven.
  • Earlier board testing reproduced the original unintended restart at approximately 14 mA after unplugging. A preceding fixed build charged at +103 mA and kept the processor supply at 0 mV for five minutes after battery-only shutdown.

The final minimized image has not been flashed. Overnight retention and recovery from an actual 2.5 V cell still need hardware validation. The complete “fully charged → dark → deeply discharged while USB stayed powered” incident has not been reproduced, so this PR fixes the demonstrated failure paths without claiming that the entire historical cause is proven.

@github-actions

Copy link
Copy Markdown

Build output available:
openearable_v2_firmware.elf.zip
openearable_v2_fota.zip

@github-actions

Copy link
Copy Markdown

Build output available:
openearable_v2_firmware.elf.zip
openearable_v2_fota.zip

@github-actions

Copy link
Copy Markdown

Build output available:
openearable_v2_firmware.elf.zip
openearable_v2_fota.zip

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