fix: unbrick boot — revert -S6 Clear(), add reentrancy guard, -S7 #9
Reference in New Issue
Block a user
Delete Branch "fix/canmap-revert-clear-add-reentrancy-guard"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Replaces and supersedes #8 (closed). PR #8 (-S6) caused the teslacharger to stop responding to the ESP8266 web UI after flashing. Kyle had to re-flash an older firmware 3-5 attempts later to recover.
Root cause: infinite recursion via the HandleClear callback
PR #8 uncommented
canMap->Clear()insideMapChargerMessages().Clear()(libopeninv canmap.cpp:145-150) ends withcanHardware->ClearUserMessages(), which (canhardware.cpp:82-91) fires theHandleClear()callback registered in main.cpp:170-173 — andHandleClear()callsMapChargerMessages(). The chain:The canary check (
FindMap(Param::tmpobcmax)) doesn't break the recursion because tmpobcmax has already been wiped by step 2 of the first Clear; the second call sees the empty map and recurses again.Hard fault fires before main() reaches the terminal loop, so neither USART terminal nor the ESP8266 web UI ever come up. Recovery requires re-flashing a firmware that doesn't have this code path (older -S5 or earlier).
Fix
Re-comment
canMap->Clear()insideMapChargerMessages(). The safe behavior is back: on a stale-saved-map upgrade, this function early-returns and the chip boots normally with the old map. The actual rebuild now requires the user to manually issuecanclearvia the openinverter terminal or web UI — a single Clear fires HandleClear once, MapMessages adds the current set additively, Save persists.Added a static
in_progressreentrancy guard. Any future accidental Clear() inside this function or its callees becomes a no-op re-entry rather than a chip-bricking fault. Safe becauseMapChargerMessagesis only invoked frommain()and from theHandleClearcallback, both single-threaded contexts.Kept the canary as
Param::tmpobcmax— still useful as a schema marker (so a future fix that can safely auto-rebuild without recursion has the right canary). Just no longer auto-triggers a destructive rebuild.VERSTRS5 → S7 (skips S6 deliberately). Any bench reporting1.20.R-S6in the openinverter UI is the broken build and needs to be re-flashed. -S7 is visibly distinct.What this does NOT fix (separate follow-up needed)
src/chargercan.cpphas 73 AddRecv+AddSend calls trying to fit intolibopeninv's 50-slot CANPOS pool (MAX_ITEMS=50in canmap.h:31). When a rebuild does run (manualcanclearafter this is flashed),canmap.cpp:483silently returnsCAN_ERR_MAXITEMSfor the last 23 entries — including the SKUDAK 0x211 broadcasts at the tail.So even after this PR ships and the user issues
canclear, the app will still show 0 A / 0 °C in the STATUS screen until a Step-2 PR either:libopeninvto bumpMAX_ITEMSto 80 — accommodates the firmware as-is plus headroom.Recommend Option A for the immediate Step-2 PR, with Option B revisited if/when multi-OBC support is needed.
Test plan
make clean && make— zero new warnings. Size: 25,428 B text / 2,072 B data / 328 B bss (+12 / +0 / +4 vs -S5 baseline, accounting for the static guard).1.20.R-S7in the version field.in_progresspath, runsMapMessagesonce with overflow-truncation, and saves; the bench may or may not gain SKUDAK 0x211 entries depending on what fits in the pool first).canclearvia the openinverter web UI, power-cycle. Confirm the chip comes back up cleanly (no boot fault, no recursion). It may have a partially-populated map due to the still-unresolved pool overflow — that's Step 2's job.Postmortem links
/Users/bastian/.claude/plans/okay-now-that-we-re-abundant-hartmanis.md🤖 Generated with Claude Code
View command line instructions
Checkout
From your project repository, check out a new branch and test the changes.