controller_display.hpp¶
ControllerFaultDisplay — the D-4 controller-screen content.
This header declares 1 type (2 members).
Extracted from include/shulib/diag/controller_display.hpp — this page is that header's documentation, reformatted, so it cannot disagree with the code. Prose about how to think about the API lives in the user guide; worked recipes live in the cookbook; this page is the complete, mechanical list of what exists.
Contents¶
class ControllerFaultDisplay¶
The three rows the V5 controller's LCD shows when a run stops: a one-word state plus the run clock, the FIRST latched fault BY NAME, then battery and total fault count. Built for the student standing at the field with no laptop and a robot that just stopped — it converts "it died" into a fault name someone can act on. update() DIFFS: only rows whose text changed reach the device, because V5 text writes are slow and firmware-rate-limited, and the clock and battery quantize (0.1 s, 0.1 V) so jitter alone cannot force a repaint. The clock still ticks visibly, on purpose — a frozen screen and a crashed program must not look identical. Reads the latch and battery it is given; owns nothing, raises nothing, never throws.
class, declared at include/shulib/diag/controller_display.hpp:110.
ControllerFaultDisplay::ControllerFaultDisplay¶
ControllerFaultDisplay(hal::ILineDisplay& display, const FaultLatch& faults, const hal::IBattery& battery) noexcept
All three must outlive the display.
function, declared at include/shulib/diag/controller_display.hpp:113.
ControllerFaultDisplay::update¶
Refresh the screen from current state; call at any convenient cadence (every loop tick is fine — unchanged rows cost no device writes).
function, declared at include/shulib/diag/controller_display.hpp:119.
Design commentary, from the header¶
The header opens with the reasoning behind these shapes. It is reproduced here in full because a reference that only lists signatures teaches nobody why.
The header’s own reasoning — 41 lines
ControllerFaultDisplay — the D-4 controller-screen content (WS13, chunk C5).
The use case (diagnostics-plan D-4): a student at the field, no laptop, robot
stopped. The V5 controller's three-row LCD can say WHY — the latched first
fault and a one-word state — which converts "it just stopped" into a fault
name someone can act on. Disproportionately useful on competition day; nearly
free to build.
This class is the CONTENT side of the seam split (brief constraint 4): it
renders rows and pushes them through hal::ILineDisplay; the PROS controller
adapter is R1's glue. Content (each row pinned by test):
row 0: "OK t 12.3s" one-word state + run clock
"FAULT t 12.3s" …the word flips when anything has latched
row 1: "flt none" …or the FIRST fault: "flt ODO_STUCK"
row 2: "batt 12.4V n 0" battery + total fault count
Row 1's budget for a fault name is kCols - 4 ("flt ") = 15 characters, and the
longest spelling does NOT fit: MECHANISM_STALLED is 17, so it renders as
"flt MECHANISM_STALL" — the seam truncates rather than wraps, which is defined
behaviour, not a bug. This banner used to claim the longest were the two 15-char
codes and that the fit was "checked by static math here". There was no such
check, and the arithmetic went stale the day chunk F1 appended MECHANISM_STALLED.
The static math now EXISTS (kNameBudget below) and asserts the property that
actually matters: every fault name must stay DISTINGUISHABLE inside the budget,
so a truncated row still names exactly one code. A future code that collides in
its first 15 characters is a compile error here, at the line that renders it.
── The write discipline (why update() diffs) ──────────────────────────────────────
V5 controller text writes are SLOW and firmware-rate-limited (~50 ms per line
class of slow). A per-tick repaint would both starve the display and waste the
loop budget it shares. So update() rewrites ONLY rows whose content CHANGED —
steady state costs three string compares and zero device writes; the battery
row quantizes to 0.1 V so millivolt jitter cannot repaint it. The run clock
quantizes to 0.1 s: ~10 row-0 writes/second, inside any sane device budget,
and the seconds display is exactly why a student trusts the screen is LIVE
(a frozen screen and a crashed program must not look identical).
Reads the FaultLatch and battery it is given; owns nothing; raises nothing;
never throws. Single-task by contract, like the rest of diag/.
Last updated 2026-08-15 — published from the main release branch, commit fa545ff. This site tracks releases, so between them it can lag the repository — that lag is deliberate, and it is what keeps the development log off the public site. For the current state of the code, see the repository.