Skip to content

test(firmware): add SoakRig node sketches for the #291 soak rig - #339

Draft
mottihoresh wants to merge 16 commits into
mainfrom
mh/upbeat-visvesvaraya-5bbda6
Draft

mottihoresh wants to merge 16 commits into
mainfrom
mh/upbeat-visvesvaraya-5bbda6

Conversation

@mottihoresh

@mottihoresh mottihoresh commented Oct 15, 2026 •

Copy link
Copy Markdown
Collaborator

Held in draft until the #291 smoke test passes. Nothing here has run on hardware yet:
Board B needs a Rev 0.2.0 base (J_SR), those boards arrive Oct 15–22 and still need
assembly plus the #95 bring-up, and the faceplates are blocked on cutout measurements.
What is verified is that both boards compile, including every #if 0 variant block built
with the block enabled. Mark ready once the live-DCS smoke test on #291 passes.

Two PanelGroup node sketches for the #291 soak test rig, plus the CI bookkeeping that keeps them building.

The sketches

Firmware/Examples/SoakRig/ — BoardA (NODE_ID 1, genericSTM32F103C8) and BoardB (NODE_ID 2, genericSTM32F103C8). Between them they carry one instance of every v1 control class on every PinRef backend, which is the coverage contract #291's gate checks.

Board A Board B
Stations J11 GPIO, MCP23017 on J_I2C1, ADS1115 on J_I2C2, J_BL1 dimmer 3× X27 needle, ShiftBus section, 3× drum display, J_BL1 dimmer
Flash 73.1 % of 64 KB 96.6 % of 64 KB
RAM 18.5 % 28.0 %

Both boards are C8s — that is what the assembled bases carry. Board B is tight (U8g2 plus three StepperMotor instances; the FULL_4STATE alternate is 96.7 %), and the README lists the fallback if a change overflows: move a non-display station to board A, else rework board B to a CB.

Board A uses only J11 pins that exist on both base revisions, so it runs on the already-assembled 0.1.0 board today. Board B needs 0.2.0 for J_SR.

Alternate builds are #if 0 blocks, not extra hardware

Three of them, per #291's alternate-build table: SwitchWithCover2Pos (#293) and Switch2Pos on board A, StepPattern::FULL_4STATE on board B. Each compiles — verified by building with every block flipped on, not just by eye.

Two things worth calling out in review:

  • FULL_4STATE needs the config field overwritten after makeX27Config() returns. That helper hardcodes StepPattern::SWITEC_6STATE (StepperMotor.cpp:263) and takes no pattern argument, so a block that only passes its other parameters compiles, looks right, and runs the 6-state table — reporting an enum member proven while docs(firmware): v1.0 API + wire-format freeze review and compatibility policy #295 freezes it unexercised.
  • Board B's bare 0x3C panel sits on J_I2C2, the non-default trunk, deliberately. That selects the (U8G2&, const DrumReadout&, TwoWire&, …) overload added in feat(firmware): DrumDisplay takes a bus or a mux #318; on Wire the sketch would take the no-bus constructor and leave the overload untested against hardware. Both arrangements satisfy the hardware-standards shadowing rule, so coverage is the tie-breaker.

AngleSensorInput (#294) is compiled out: #291 moved it onto its own magnet-knob fixture, and a live instance would read a floating ADS channel for the length of the soak.

What is provisional

Pin and bit assignments, DCS bindings, and the stepper and angle-sensor calibration numbers all move once the panel is physically wired. The class-to-backend pairing is the part that must not drift. The README states which is which.

CI

Firmware/Examples/SoakRig was already picked up by the weekly full build, which rglobs Firmware/**/platformio.ini. PR builds use library_dependents, which listed no Examples/ project at all, so a library change on a PR would never have selected the rig. Both boards are now registered under the five libraries they depend on, and a DrumDisplay key was added — without one, a DrumDisplay change selected nothing.

Also drops ten Firmware/Prototype/* entries: that directory does not exist and has no history in this repo.

Verified by running the selector rather than reading the JSON: a PanelGroup change selects both boards, a DrumDisplay change selects BoardB plus its two real siblings, and full mode still enumerates all 35 projects.

The impact map is inaccurate well beyond this — most Firmware/Tests/* are missing from it, because they declare dependencies as file://../../Libraries/<Name> rather than bare names. That is being handled separately; this PR only adds what it needs and removes what is provably dead.

Refs #291

Two PanelGroup node sketches behind the #291 soak test panel. Between them they
carry one instance of every v1 control class on every PinRef backend.

BoardA (NODE_ID 1, STM32F103C8) takes the J11 GPIO stations, the MCP23017 on
J_I2C1, the ADS1115 on J_I2C2 and the J_BL1 dimmer zone. BoardB (NODE_ID 2,
STM32F103CB) takes the three X27 needles, the ShiftBus section and the three
drum displays; it needs the 128 KB part because U8g2 plus three StepperMotors
links at 94% of a C8.

Alternate builds are #if 0 blocks in the sketches rather than duplicated
hardware: SwitchWithCover2Pos (#293) and Switch2Pos on board A,
StepPattern::FULL_4STATE on board B. That last one needs the config field
overwritten after makeX27Config() returns, since the helper hardcodes
StepPattern::SWITEC_6STATE and takes no pattern argument.

AngleSensorInput (#294) is compiled out: #291 moved it onto its own magnet-knob
fixture, and a live instance would read a floating ADS channel for the length
of the soak.

Board B's bare 0x3C panel sits on J_I2C2, the non-default trunk, so that the
DrumDisplay TwoWire& overload from #318 is exercised against hardware before
#295 freezes the API.

Pin and bit assignments, DCS bindings and the stepper calibration numbers are
provisional and move when the panel is wired. The class-to-backend pairing is
the coverage contract #291's gate checks and must not drift.

Refs #291
The weekly full build already discovers Firmware/Examples/SoakRig, because
select-platformio-projects.py rglobs Firmware/**/platformio.ini. PR builds use
library_dependents instead, and that listed no Examples/ project at all, so a
library change on a PR never selected the soak rig.

Add both SoakRig nodes under the five libraries they depend on, and add a
DrumDisplay key — without one, a DrumDisplay change selects nothing. Its two
other real dependents are listed with it so the key is not misleading.

Drop ten Firmware/Prototype/* entries: that directory does not exist and has no
history in this repo.

Verified with the selector itself: a PanelGroup change now selects both boards,
a DrumDisplay change selects BoardB, and full mode still enumerates all 35
projects.
@mottihoresh
mottihoresh marked this pull request as draft October 15, 2026 17:09
mottihoresh added a commit that referenced this pull request Oct 15, 2026
The generator reads the committed map to carry full_build_paths over, which
is hand-maintained and cannot be derived. On an unresolved merge conflict that
read hit json.loads and failed with a raw traceback.

Two branches touching the map at once is the expected case, not a rare one --
#339 conflicts with this branch right now -- so turn that traceback into the
resolution: take either side, then regenerate over the top of it.
…a-5bbda6

# Conflicts:
#	.github/firmware-platformio-impact.json
Two station changes from the #291 rig owner.

Second 74HC595 and second 74HC165. Everything on the rig sat on chip 0, so
nothing under the soak crossed a cascade hop. Both chains are now two chips
deep and both chips in each chain carry live bits, so a dead QH'->DS or QH->SER
hop cannot pass as a merely quiet rig.

The caution ladder is split 2/2 across the '595 chips on purpose: all four
lamps come from one broadcast word, so a single MASTER_TEST press must fan out
across the hop inside one transfer, which a tidy chip-per-function split would
not test. The X27 coils stay together on chip 0 so a step is still one atomic
four-bit flip. On the '165 side the ON-OFF-ON moves to chip 1 rather than the
home microswitch, because it is polled every loop and a fault shows at once,
whereas the home switch is read only during the boot seek.

No setup code: configureAsInput/Output call noteInput/noteOutput, which grow
the chain to cover the highest chip declared, so a chip-1 PinRef is the whole
opt-in.

This is not the first exercise of the cascade — Firmware/Tests/ShiftBus already
has bench_probe595, bench_probe165 and frame_map spanning chips 0 and 1. What
is new is the cascade carrying live control classes under a multi-hour soak
rather than a standalone probe sketch.

R16-503 illuminated pushbutton. Board A's J11 momentary and its LED are now one
part (SW-PUSH-R16503-*): one 16 mm hole instead of two. No firmware change --
the lamp is driven from the sim, not from the contacts, so they remain two
independent stations on their own pins. Recorded in the wiring comments because
the pin map would otherwise read as two unrelated stations.

Refs #291
The assembled PanelGroup base 0.2.0 (BO-0014) has an STM32F103C8, not a CB.
Board B links at 96.6 % of a C8 (FULL_4STATE alternate 96.7 %), enough for the
soak. README records the fallback order if a change overflows: move a
non-display station to board A, else rework to a CB.
#359 landed the v1.0 API freeze on main. Two changes reach these sketches.

AnalogInput now takes (controlId, PinRef, const AnalogConfig&) and nothing
positional, so instrPot and floodPot lose their six-argument form. Both drop
their tuning entirely rather than restate it: hysteresis defaults to
HYSTERESIS_AUTO, which resolves to AnalogConfig::DCS_HYSTERESIS (1024) for a
DCSIN_* id -- exactly what they were passing. floodPot now declares identically
to instrPot, which is the point of the station pair: only the PinRef differs.

StepperConfig fields default to the X27 family, so the FULL_4STATE alternate
build is a plain designated initializer and the makeCanopy4StateConfig()
helper goes away. The comment keeps why it is spelled out rather than built
from makeX27Config(): that helper fixes the pattern, so the older form had to
overwrite the field after the call.

AngleSensorInput and the makeX27Config calls compile unchanged -- the new
AngleConfig is an optional trailing argument these sketches never passed.

Builds: board A 73.6 % flash of a C8, board B 48.4 % of a CB. Board B's
revision follows in the merge of fd31eeb.

Refs #291
…erge

Board A 73.6 % of a C8, board B 96.9 % (63,488 of 65,536). The FULL_4STATE
alternate now measures the same as the main build rather than 0.1 pt above it:
its StepperConfig is a static const that nothing references until you swap it
in, so the compiler drops it.

Refs #291
Merging main brought in its generated map, which does not know SoakRig exists
-- those projects live only on this branch -- so the HIDControls key came back
without them and the up-to-date check failed. Regenerated with
gen-platformio-impact-map.py --write; both boards are now listed under every
library they depend on, and --check passes.

Refs #291
…a-5bbda6

# Conflicts:
#	.github/firmware-platformio-impact.json
97.3 % of flash (63,756 of 65,536), up from 96.9 % before this merge and 96.6 %
when board B moved to the C8 -- about 230 bytes of library growth per merge from
main, with 1,780 bytes left. None of it is this sketch's own growth, so the
README now says to re-measure after every merge and to take the documented
fallback order rather than trim the sketch if one overflows.

Refs #291
The bootloader chain (#362-#367) gives every production STM32 image a 4 KB
bootloader at 0x08000000 and starts apps at 0x08001000, so a C8 app region is
61,440 B rather than 65,536. Both SoakRig projects now pull
Firmware/shared/stm32.ini for the identity-block linker script, the post-build
stamp, the app offset and the bootstrap target -- one level deeper than
Templates/Panels, so ../../../ rather than ../../.

Board B did not fit: 63,756 B against 61,440, over by 2,316. -flto links it at
55,020 B, 89.6 % of the region with 6,420 B spare, and drops RAM 5,752 -> 5,548.

LTO buys margin rather than clearing 2.3 KB once. Board B grew ~230 B per merge
from main (96.6 -> 96.9 -> 97.3 % of a C8 over two merges) and none of that is
this sketch's code, so the alternatives were measured and rejected: an F103CB
means reworking an assembled board, and trimming the unused DrumDisplay font
leaves 39 bytes, which one merge erases -- stackable, never standalone.

Verified .isr_vector lands at 0x08001000 on both boards, since PlatformIO keeps
reporting a percentage of 65,536 and its bar no longer means anything here. Both
alternate #if 0 builds still link: board A 51,104 B, board B 55,020 B.

README gains the flashing section (bootstrap once on a blank board, clearing
flash protection for Tests/ sketches, the 0x740-0x7FF bootloader CAN range) and
a soak-traffic section noting the extra 8-byte READY_n per node per SYNC_REQ
from #383, plus a standing note to bench LTO deliberately against the ShiftBus
ISR and the U8g2 statics.

Refs #291
…eat-visvesvaraya-5bbda6

# Conflicts:
#	.github/firmware-platformio-impact.json
Board A 48,884 B (79.6 % of the 61,440 B app region), board B 55,292 B
(90.0 %, 6,148 spare). Both up ~280 B from library growth in this merge, which
is the drift the LTO margin exists to absorb. isr_vector re-confirmed at
0x08001000 on both.

Refs #291

This branch has not been deployed

No deployments
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