Skip to content

docs(firmware): bring specs back in line with the code (audit 2026-10-27) - #403

Merged
mottihoresh merged 2 commits into
mainfrom
mh/docs-spec-drift-audit-7eed8d
Oct 27, 2026
Merged

mottihoresh merged 2 commits into
mainfrom
mh/docs-spec-drift-audit-7eed8d

Conversation

@mottihoresh

@mottihoresh mottihoresh commented Oct 27, 2026 •

Copy link
Copy Markdown
Collaborator

Docs PR A from the 2026-10-27 audit of #375–#395. There is no tracking ticket. The published docs/ pages are PR B and are not in this PR.

Wording and comments only. Nothing changes in behaviour, wire format or frame structure, and no new reporting is added. Where the code overrode a decision, the original decision text stays word for word and an as-built note is added after it. This applies to D5, D7, D11, D21, D24, D30 and D32 (D32's note says the counters stay on the board).

Audit claims, verified against main and the PR or ticket behind each change

# Claim Verdict What the code does Fixed in
1 D5 describes a SYNC_REQ broadcast Holds Since #387 the bridge syncs one node at a time. SYNC_REQ byte 0 names the node, which ends its answer with READY(RESYNC) (#383); the bridge moves on after that READY or after 150 ms. There is no cold-boot SYNC_REQ: a node is queued on its first HB. D5 (as-built note); 06 (Owns line, DcsBios note); 09 (SYNC_REQ response); 10; 11; examples/panelbridge.md; the PanelBridge TechSpec (timelines, rules, setup, RX table, READY section); PanelBridge.h:70; PanelBridge.cpp:385,495; CANProtocol.h ReadyFlag comment; PanelBridge README; PanelGroup TechSpec (READY, SYNC_REQ rows); CANProtocol TechSpec (SYNC_REQ handling, onSyncReq, SYNC_REQ_LEN_TARGETED); Tests/PanelBridge/platformio.ini comments
2 D7/D24 and FirmwarePlan 10 describe export coalescing Holds Since #389 CTRL_BCAST is never coalesced (CANProtocol.cpp:433). A full ring drops the new frame and counts it. D7 table and rationale; D24 "keeps the newest state"; 10 (TX queue item)
3 The _NODE_STATUS v4 row is 38 hex, not 34 Holds NODE_STATUS_ROW_BYTES is 19 (#365 added the bootloader fields before v4 shipped) D21 (original "34 hex" kept, as-built note says 38); NodeStatus.h:35; NodeStatus TechSpec (said 17 bytes); PanelBridge README (said "18 chars"); SimGateway's longest-line figure (~42 → ~52 bytes) in SimGateway.h and 07
4 Boot frames are "lowest priority" Holds CTRL_BCAST is 0x7E0 (D31), below 0x740–0x7C1 12 (Update Protocol); BootProtocol.h:25. Those are the only two places.
5 Unconfigured boards: announce timing, one at a time Holds, reworded The interval is a fixed 1 s (ANNOUNCE_INTERVAL_MS) with the first announce at once; it is not randomized. "One at a time" is a host rule: the uploader binds the first announcement it hears. Two boards that start the same frame in the same bit get a bit error, so error frames and an automatic retransmit, not a lost announcement. 12 § Unconfigured boards
6 02 says the batch hold is "≤ 2 ms" Holds On the bridge, drain() flushes a lone packet after two passes and ≥ BATCH_HOLD_MS (2 ms). On nodes the hold is 0 ms. 02 (policy table and batching paragraph); D11 as-built; 10; CANProtocol.h drain/sendBatched doxygen; CANProtocol TechSpec (BatchState, deadline)
7 09: markBootHealthy placement, SYNC order, DIAG_ERR Holds markBootHealthy() is the last line of PanelGroup setup() (after READY) and of PanelBridge::setup() (after arming the roster). DIAG_ERR, and in fact all four DIAG_* types, are defined but nothing sends or parses them. 09 (both boot sequences; DIAG section rewritten as "DiagSerial diagnostics", constants retired); CANProtocol.h comment marks them retired, constants kept; FirmwarePlan README and firmware skill index line
8 "Never lost silently" → "counted on the board" Holds The phrase itself only appears in the #392 title. Where nearby wording implied the counters are reported (DIAG_ERR counter, "exposes"), the text now says txDropCount() / rxDropCount() / rxOverrunCount() are counted on the board and not reported over the bus. D7, D32; 02 (TX policy, Receive); 06; 10 (soak gate); CANProtocol TechSpec send(); CANProtocol.h send() doc; CANProtocol.cpp:266
9 TechSpecs: Bootloader, SimGateway, PanelBridge, PanelGroup, NodeStatus Holds NodeStatus was missing NODE_FORGET_REQ_ADDR (0xFF04). BEGIN [len] and END [crc] match the code; I added that len is unpadded, that a short frame gets no reply, and that ENDED crc is 0 on an early fail. Further findings are listed below. listed below
10 PanelGroup.h:85 vs #394; README listener names, "debug optional" Holds #394 updated 05 but left the forceReport() doc saying "emit … unconditionally". READMEs named listeners that #389 folded into ExportDispatcher. The README sketches turned debug on, but it is off by default and the templates ship with it off (#389). PanelGroup.h; PanelGroup TechSpec; PanelBridge and PanelGroup README samples now comment out setDebug(true) as bench-only

Other stale places found by grepping the same claims

  • PanelBridge TechSpec. The "DCS-BIOS Input Formatting" table still listed InputType::SWITCH/ACTION/…, which has been stale since RotaryEncoder relative dispatch (REL ±step + DIR INC/DEC) — source DCS dispatch from class, collapse InputMap to controlId→name #147. It now describes the ABS/REL/DIR/ACTION forms chosen by frame. The roster reply was described as Serial.flush()ed from setup(); fix(panelbridge): never block loop() on a roster reply — write it a few rows per loop #388 made it a row at a time from loop(). A HEALTH frame was said to queue a sync; only HB does. The Dependencies list was missing NodeStatus, BootProtocol, FirmwareImage and FirmwareVersion.
  • 06. Said the roster reply "holds its loop ~130 ms". That stopped with fix(panelbridge): never block loop() on a roster reply — write it a few rows per loop #388.
  • PanelBridge.cpp:232–244. Comments named the removed listeners (BridgeExportListener, NodeStatusReqListener, …). Same for PanelBridge.h testHandleExport, the output_submission test and D30.
  • PanelBridge bench-test headers (dual_integration, node_tracking, startup_sequence, model_time_resync). They expected a [BRIDGE] SYNC_REQ broadcast log line that no code prints, and the old "session change — SYNC_REQ" text. I rewrote them to the lines the code prints: [BRIDGE] READY node=1, node=1 alive, READY (resync) node=1, DCS session change — sync every node. The pass criteria follow those lines. A node that boots first only shows alive (from its HB). A lone board stops at error-passive, not bus-off. On a single board, the model-time test logs the session change but sends no SYNC_REQ, because no node is alive.
  • PanelGroup (PanelGroup.h, TechSpec, 05):
    • The boot order put baseline and ISR before configure(), and left out ShiftBus begin() and markBootHealthy().
    • The INTCAP model was stale: the cache is refreshed from a live port read, and INTCAP is read only to clear the line.
    • _lastHeartbeatMs was listed; the heartbeat moved to CANProtocol in refactor(firmware): move node heartbeat, HEALTH and master watchdog into CANProtocol #340.
    • The README loop() order was wrong.
    • 05 gets a footnote: SwitchWithCover2Pos's pair can trail READY(RESYNC).
  • "DCS-BIOS re-broadcasts full state after SYNC_REQ". This appeared in the AnalogOutput comments, the Dimmer and IntegerOutput dedup tests, and the Dimmer, LED and AnalogOutput TechSpecs. SYNC_REQ only asks for inputs. The repeats come from DCS-BIOS autosync.
  • The "~50 ms" output catch-up in D5, 06 and 09. A changed value arrives on the next frame, an unchanged one with autosync, which runs every few seconds. Late outputs that catch up are accepted (as decided).
  • Bootloader (BootProtocol.h, main.cpp header, Bootloader TechSpec, 12):
    • BEGIN erases every page up front (feat(firmware): Gateway uploader — staging and CAN flash #366), not "the first page", and not "as blocks reach them".
    • ANNOUNCE also goes out during an idle transfer.
    • OUT_OF_RANGE covers SET_NODE_ID too.
    • GO clears the crash-loop counter.
    • Filter, mailbox and poll_rx() details.
    • The stale "code ends at 0x08000EF0 / RAM ~196 B" line is removed.
    • The bridge catch-at-boot sequence.
    • The idle timeout applies only after a successful BEGIN.
  • SimGateway (README, SimGateway.h, Uploader.h, LinkRecovery.h, 07, TechSpec):
  • STM32Board TechSpec. The ReceiveFifoLocked comment referred to a "drop-oldest policy" that doesn't exist.
  • TechSpec/PanelGroup/Inputs/AnalogInput.md:210. Cited SimGateway.cpp:80-81 for the USB report interval; it is now at :82-83.
  • docs/firmware/build-flags.md:218. I changed the PanelBridge.h citation from :92 to :96 because the header grew. That is the only docs/ edit, and it keeps check_doc_refs.py green.

Dropped

None. Every listed claim held.

Resolved after review (user decisions, 2026-10-27)

  • Calibration vs upload exclusion (SimGateway). The code refuses only UPL_BEGIN during a calibration session. The code is being fixed in build(firmware): one release zip with all three image types; drop the flat files #402, so the docs' rule stays as written.
  • Bootloader footprint. 12 § Freeze and the Bootloader TechSpec no longer give exact byte counts. They now say "about 3.7 KB, roughly 400 B free", for both builds. The release workflow is unchanged.
  • CTRL_BCAST coalescing loss. It reads "about a fifth" in both 02 and the CANProtocol.cpp comment.
  • Older SimGateway drift, folded in:
    • TechSpec:
      • No library.json copy; the TechSpec points at the file instead.
      • Firmware/Templates/SimGateway/ replaces the path that doesn't exist.
      • The full test tree and every test env, including axis_cal_*, hid_enumeration and hid_stress.
      • HIDHatSwitch added to the Public API block.
    • README: Owns bullets for calibration, link health and the uploader; diagnostics() / calibration() rows in the API table; a complete Testing section.
  • Review fixes:

Not changed

  • model_time_resync.cpp:51 prints "SYNC_REQ expected" at run time. That is test output, not a comment, so I left it alone.

Verification

  • python3 tools/docs-drift/check_doc_refs.py passes. I also checked every file:line citation into a file this PR edits: apart from the build-flags.md one above, they all still point at the same text.
  • pio run passes for Templates/PanelBridge, Templates/PanelGroup and Templates/SimGateway.
  • Frozen bootloader. I built Firmware/Bootloader (bootloader and bridge envs) with and without the comment edits to main.cpp and BootProtocol.h. Both firmware.bin files are byte-identical (4096 B each).

…-27)

Wording and comments only: no behaviour, wire format or frame change.
Fixes the FirmwarePlan, TechSpecs, library READMEs and code comments the
2026-10-27 audit of #375-#395 found stale: per-node sequential sync and
READY(RESYNC) instead of a SYNC_REQ broadcast, no CTRL_BCAST coalescing,
the bridge's 2 ms batch hold, 38-hex _NODE_STATUS rows, boot frames above
CTRL_BCAST, drop counters kept on the board, the retired DIAG_* frames,
re-reports as state, and markBootHealthy() placement. Decisions D5, D7,
D11, D21, D24 and D30 get as-built notes where the code overrode them.
@mottihoresh mottihoresh added firmware Firmware implementation work docs Documentation site content labels Oct 27, 2026
…SimGateway drift

Restore the original D5, D7, D21 and D24 decision text word for word and
keep only the as-built notes (D32's addition becomes one too). Fix the
SYNC_REQ wording left in the PanelBridge test ini, ReadyFlag and the bench
headers, the uploader retry result, the 0x7C1 relay row, the lone-board
error-passive note and a stale SimGateway.cpp citation. Bootloader size
reads "about 3.7 KB, roughly 400 B free"; the coalescing loss reads "about
a fifth". Fold in the older SimGateway TechSpec and README drift: no
library.json copy, the real template path, every test env, HIDHatSwitch in
the API block, a complete Testing section and API table.
@mottihoresh
mottihoresh merged commit e4ced28 into main Oct 27, 2026
13 checks passed
@mottihoresh
mottihoresh deleted the mh/docs-spec-drift-audit-7eed8d branch October 27, 2026 13:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs Documentation site content firmware Firmware implementation work

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant