Skip to content

test(hardware): soak test panel — panel-mount controls on a printed or laser-cut faceplate, harnessed to two PanelGroups #291

Open

Description

@mottihoresh

Goal

A permanent, reusable fixture for the #290 soak and for every later release gate: one faceplate carrying one instance of each control class per electrical path (direct GPIO, MCP23017, ADS1115, I²C mux), wired to two PanelGroup_Base Rev 2 boards (#209) through their standard connectors only.

Why not a breadboard

The AxisBench rig showed all of its analog wander (500–800 counts on a wire jiggle) came from breadboard ground contact resistance. A 2–3 hour soak on push-in contacts would measure the contacts, not the firmware. Soldered, strain-relieved wiring removes that — and is how the real panels are built, so this doubles as a harness rehearsal.

Construction

  • Panel-mount parts across several small faceplates, 3D-printed or laser-cut; wiring soldered point-to-point.
  • Module boards already on hand (MCP23017, ADS1115, TCA9548A, DRV8833) on standoffs behind the panels.
  • Harness ends crimped to plug into the base board's own sockets: 2×7 → J11 (10 GPIO), JST-XH 8 → J_I2C1 / J_I2C2, JST-XH 7 → J_SR, Mini-Fit Jr 2×1 → J_BL1 / J_BL2. The panel unplugs cleanly.
  • Both J_I2C connectors keep their Rev 1 pinout, interrupts included. J11 carries 10 GPIO on both revisions; on 0.2.0 positions 7 and 11 are PB2 and PC14 (PB5 moved to ShiftBus, PA8 to J_I2C2 INT_A).
  • External pull-ups on every input — the firmware sets plain INPUT on GPIO and GPPU=0 on the MCP23017 (PinRef.cpp:205/209). 10 k per line, soldered inline under heat-shrink or on a small terminal strip per panel near the harness exit.
  • Analog RC (1 k series + 100 nF) at the connector end, as close to the MCU as the harness allows.
  • Put the board A J11 LED on a timer-capable pin (PA0–PA3, PB0, PB1). Costs nothing and lets that station be reflashed as LED or Dimmer without rewiring.
  • Grounds: each panel returns to its own board's GND pins; don't join the two boards' analog grounds across panels (the bus already commons them).
  • MCP23017 GPA7/GPB7 are output-only (silicon erratum) → 14 inputs per chip.
  • DRV8833 module: fit the EEP→VCC jumper (backwards silk — nSLEEP floats otherwise). VM = 5 V from a bench supply or a bus-harness tap; J11 carries only 3V3 and J_BL is 12 V (over the 10.8 V max).
  • Parts come from what is already in InvenTree; no new sourcing.

Faceplate — several small panels, not two big ones. Build to the cockpit panel standard
(docs/hardware/mechanical-standards.md): 146.05 mm wide, height a whole number of 9.525 mm
Dzus units
. Mounting is the cockpit's own wood-frame method — the Dzus grid is
used here for its dimensions, so panels interchange and a later one bolts on without re-cutting
anything.

It mounts in a printed 20-unit frame (20 mounting positions = 190.50 mm of rail), which
doubles as the physical validation piece for the panel-blank generator (#334).

That frame is the binding constraint, and it rules out one panel per harness leg. #334 sets a
floor of N ≥ 4 (N = 3 collapses to one stud per side), so six panels need 24 units however
tightly they are drawn. Four panels, grouped by board rather than by connector, fill the frame
exactly:

Panel Units Carries Legs
P1 — board A 6 pot · ladder rotary · encoder · momentary · LED — then encoder · 2-pos toggle · 5-pos rotary · LED · pot J11 + J_I2C1 + J_I2C2
P2 — gauges 6 3× X27 — GPIO, MCP port, '595 J11 + J_I2C1 + J_SR
P3 — displays 4 3× SSD1306 — 2 muxed, 1 bare-bus J_I2C2 + J_I2C1
P4 — ShiftBus 4 encoder · ON-OFF-ON · 4 LEDs J_SR

20 units · 190.50 mm · 22 cutouts · 19 face stations. Board A is one plate and board B is
three, so there is one harness bundle per node instead of one per connector leg. A panel may
therefore carry more than one tail.

Four stations sit behind the plates with no cutout: both 12 V LED strings (the Dimmer sync
pair), the '165 home microswitch (behind the X27 it homes, on P2), and the TCA9548A. The
modules and the '165/'595 perfboard mount on standoffs behind their panel.

Stud geometry per mechanical-standards.md: columns at 4.7625 and 141.2875 mm
(span 136.525 = width − pitch), rows at 1.5 and N − 1.5 units. Every row in the
stack lands at (k + 0.5) × 9.525 mm.

Fastener: M5 socket cap — Ø5.6 clearance hole, Ø8.5 head (#334), not M6 and not the Ø6.6
Dzus-stud hole. The 4U panels decide it: their rows sit at 1.5 and 2.5 units, one pitch apart,
and an M6 head is Ø10 against a 9.525 mm pitch — it overlaps by 0.475 mm. M6 also overhangs the
side edge by 0.237 mm, since the column inset of 4.7625 is under a 5 mm head radius. M5 clears by
1.025 mm. The 6U panels have three pitches between rows and would tolerate M6, but the stack uses
one fastener throughout.

Cutout diameters — established 2026-10-25. Supplier catalogues (DAIER) plus a verified test
cut now give a first-cut number for every opening.

Station Cutout Locating feature Basis
R16-503 pushbutton ⌀15.8 none — round, rotation irrelevant cut and fitted
SR26 rotary (12-pos) ⌀9.75 ⌀3.2 at 9.5 cut and fitted
Toggle ×2 (MTS-10x-L1) ⌀12 ⌀3.2 at 9.5 DAIER catalogue
SR16 rotary (8-pos) ⌀9.0 slot 2.1 × 1.0 at 7.5 DAIER catalogue
X27 gauge ⌀4.6 shaft offset 6.0 from body centre, not concentric Juken datasheet
SSD1306 38 × 12 rear clearance — module spec
Pot / encoder ×5 M7×0.75 — cut a 7.0 / 7.2 / 7.4 ladder vertical key 1.2 × 2.6 WH148 drawing
5 mm LED holder cut a 7.8 / 8.0 / 8.2 ladder none vendor spread

Pots and encoders share one bushing and key, so a single geometry covers five stations. SR26 and
both toggles share a 9.5 mm key offset; SR16 shares nothing.

Corrections to earlier assumptions in this ticket:

  • The SR26's bushing is M9.3×0.75, not the M10×0.75 printed on the supplier quotation —
    M9.3 is what makes the verified ⌀9.75 hole fit.
  • No >12-position rotary exists in stock or in the Alpha SR26 reference (SR26 is 12-pos at
    30°, SR16 is 8-pos at 45°). The SoakRig sketch declares AnalogMultiPos(..., 16). Either the
    station drops to 12 and the constant changes, or a part gets bought. Does not block the P1 cut.
  • The 12 mm toggle figure came from Toggle12.FCStd, which was a test file that never existed
    in this repo
    — ⌀12.6 is withdrawn.

Faceplate is three layers in practice: BackPlate pass-through → Diffuser (bushing passes, nut
bears on its front face) → nut → FacePlate (clears the nut, so it sits recessed). Diameters are
not monotonic across the layers, and whether a part seats on the BackPlate or forward on the
Diffuser is a per-part decision driven by bushing length — the SR16's 4.6 mm bushing likely needs
the forward seat.

Still open: encoder key/body, toggle body depth, R16-503 clamp range and terminal span,
SSD1306 glass window, LED holder hole, perfboard outline, key projection heights, nut
across-corners and height. All in stock — a coupon settles most of them.

Coverage list — 23 stations

Every class at least once and every PinRef backend at least once, with extra instances only
where the backend genuinely changes behaviour (three X27s, three encoders, three LEDs) — not the
full class × backend cross-product. One deliberate duplicate: the second Dimmer, as a sync check.

Firmware modes are reflashes rather than extra hardware. Where a mode cannot coexist with the
main build it goes in an alternate build, listed below.

HID axis is out of scope for this rig. HID axes are controlIds 0x0010–0x001F, routed to
SimGateway's axis setters, so binding a pot to CTRL_ROLL instead of a DCSIN_* id is a
controlId swap on an existing station — available later as a reflash, not a station here.

No guarded switch is fitted. SwitchWithCover2Pos (#293) takes a single pin and fakes the
cover in firmware — open, 200 ms, then the switch; reversed for off — so it is electrically the
same one input as Switch2Pos. A physical guard would add a part and panel space without adding
anything testable, since the class never reads a cover contact. The MCP 2-pos toggle carries both
classes, one reflash apart.

The #293 check uses a synthetic pairing, because the A-4E-C has no control of this shape.
FirmwarePlan/11-open-issues.md records the class as used 0× by this aircraft: the only
cover in the jet, AFCS_1N2_COVER, guards AFCS_1N2, which is a 3-position switch
(mask 0x0018) that the mod does not gate on the cover, and the cover carries its own
DCSIN_AFCS_1N2_COVER. A guarded 3-position switch therefore needs no special class at all —
Switch3Pos on the switch plus an optional Switch2Pos on the cover, both already on this rig.
SwitchWithCover2Pos is kept purely for DCS-BIOS parity. Its constructor takes
(switchId, coverId, PinRef, reverse), and both are ordinary control ids — nothing requires the
second to be a real cover. Since no 2-position control in the A-4E has a paired cover
(EMERG_GEAR_REL has none), the check pairs ARM_STATION1 as the cover and ARM_STATION2 as
the switch
. Both are plain single-bit station-select switches, sit next to each other on the
armament panel, and share broadcast word 0x8500 (masks 0x0020 and 0x0040) — so the 200 ms
ordering is visible both on screen and as two bit changes in one word. Throw the toggle: station 1
flips, 200 ms later station 2 follows; off reverses the order. The pairing is not physically
meaningful in the aircraft; it verifies the class emits its two commands in the right order with
the right gap, which is all the gate asks.

Board A — 0.1.0 (board already assembled)

Path Components Classes exercised
J11 GPIO (6 of 10 pins) pot · rotary switch + resistor ladder · encoder (2 pins) · momentary AnalogInput (DCS-BIOS and HID axis), AnalogMultiPos, RotaryEncoder / RotaryAcceleratedEncoder, ActionButton
J11 GPIO out 1 LED LED
J_BL1 12 V LED string Dimmer (#288)
J_I2C1 MCP23017 encoder · 2-pos toggle · 5-pos rotary · 1 LED RotaryEncoder on MCP interrupt, Switch2Pos and SwitchWithCover2Pos (#293) on the same toggle, SwitchMultiPos, LED on MCP
J_I2C2 ADS1115 pot AnalogInput on the ADS path (also observes #269)

Board B — 0.2.0 (board on order)

Path Components Classes exercised
J11 GPIO (4 pins) X27 + DRV8833 NeedleGauge on direct GPIO
J_I2C1 MCP23017 (0x20) X27 + DRV8833, 4 coil pins on one port · 1× SSD1306 direct @0x3c NeedleGauge on the MCP batched-port path (moved from #137), DrumDisplay on a bare bus
J_SR '165 encoder · ON-OFF-ON · home microswitch RotaryEncoder on the ISR-sampled path, Switch3Pos, HomeMode::SENSOR
J_BL1 12 V LED string Dimmer (#288) — second instance, on purpose
J_SR '595 LEDs · X27.589 + DRV8833 LED on ShiftBus, NeedleGauge on the ShiftBus path
J_I2C2 TCA9548A (0x70) + 2× SSD1306 behind it DrumDisplay via I2cMux

IntegerOutput needs no hardware — its callback logs CANOPY_POS to DiagSerial.

I/O budget. J11 is 2×7: 10 GPIO (PB0 PA0 PB1 PA1 PB2 PA2 PA5 PA3 PC14 PA4) plus
2× +3V3 and 2× GND. Eight are ADC-capable and six of those are also timer-capable. Board A
needs 6 pins, board B needs 4 — comfortable on both. Interrupt lines are shared per pin
number, so an interrupt-driven encoder must not take both halves of PA0/PB0, PA1/PB1 or PA2/PB2.

Alternate firmware builds

Two variants cannot coexist with the main build because they occupy a station that is already
bound. Each board gets one alternate sketch; the variants on a board are independent stations, so
one alternate carries all of that board's changes.

Build Station Variant
board A alt MCP 2-pos toggle SwitchWithCover2Pos (#293) — same pin as Switch2Pos
board A alt J11 momentary Switch2Pos — the base class on the GPIO backend
board B alt GPIO X27 StepPattern::FULL_4STATE — same motor as SWITEC_6STATE

The momentary carries two classes because they are siblings, not a hierarchy:
ActionButton : InputBase emits one EVT_ACTION per debounced press edge, while
Switch2Pos : InputBase sends absolute 0/1 from the level. (SwitchWithCover2Pos is the one
that derives from Switch2Pos.) Binding Switch2Pos to a spring-return part is a deliberate
rig compromise — its header says the class wants a part that latches — so record that result as
class path proven, part not representative. Switch2Pos itself was hardware-verified in
Phase 4, so nothing unproven rides on it.

Plain RotaryEncoder in Rel mode is deliberately skipped: RotaryAcceleratedEncoder is a
RotaryEncoder with the momentum filter on, so the Rel dispatch path is already exercised by the
'165 encoder. Only Rel-without-the-filter is uncovered, which is marginal.

DCS bindings — chosen for reach, not realism

This is a test instrument, not a replica panel, so each station takes whichever sim control is
easiest to drive and confirm. That lands most of the list on lighting: those switches work cold
and parked and read back from the cockpit or an F2 external view, so the smoke test needs no
flying.

Station Binding Confirm by
J11 pot LIGHT_INT_INSTR (0xffff) instrument lights brighten
J11 LED caution-ladder bit master test
J11 ladder rotary TACAN_CHAN_MAJ (0x0780, 16 states) or MCL_CHAN_SEL (0x03e0, 32) switch animates; readout needs power
J11 encoder LIGHT_INT_BRIGHT (0x0c00, 4 states) in DIR mode console intensity steps
J11 momentary MASTER_TEST caution ladder lights
J_BL1 LED string (A) LIGHT_INT_INSTR (0xffff) instrument lights
J_BL1 LED string (B) LIGHT_INT_INSTR — same value as A both strings must track
MCP 2-pos toggle LIGHT_EXT_ANTICOLL (1 bit); reflashed as #293 → cover ARM_STATION1 + switch ARM_STATION2 F2 / station 1 then station 2 on the armament panel
MCP 5-pos rotary NAV_SEL (0x0038) or DOPPLER_SEL (0x0007) — 8 states, 5 used switch animates in the cockpit
MCP LED caution-ladder bit master test
MCP encoder ARC51_FREQ_1MHZ (0x3c00) radio readout
ADS pot LIGHT_INT_FLOOD_WHT (0xffff) floodlight
'165 encoder DEST_LAT_KNB doppler readout
'165 home microswitch none — hardware homing, mounts behind the X27 it homes needle parks
'165 ON-OFF-ON LIGHT_EXT_NAV (0x00c0, 3 states) F2
'595 LEDs caution-ladder bits master test
X27 (GPIO) CANOPY_POS (0xffff) watch the canopy
X27 (MCP) EXT_HOOK (0xffff) watch the hook
X27 ('595) D_FLAPS_IND (0x840e, 0xffff) watch the flaps
OLED × 2 (muxed, J_I2C2) CANOPY_POS, EXT_HOOK numbers track the animation
OLED × 1 (direct, J_I2C1) LIGHT_INT_INSTR — sync-pair target number vs the two strings

One press covers every LED station. The caution ladder is a single word at 0x845c with a
mask per lamp (D_SPOILER_CAUTION, D_SPDBRK_CAUTION, D_UTILHYD_CAUTION, D_CONTHYD_CAUTION,
D_FUELBOOST_CAUTION, D_FUELTRANS_CAUTION, D_RADAR_WARN, GEAR_LIGHT), so MASTER_TEST
lights the GPIO, MCP and '595 LEDs together — and exercises multi-mask fan-out from one
broadcast, which nothing else on the rig does. Verify on the first run that the mod's master test
actually drives the ladder
; that is assumed here, not confirmed.

The direct OLED sits on J_I2C1, not J_I2C2. hardware-standards.md forbids a same-address device on a trunk that also carries that address behind a mux — a bare 0x3C OLED beside the TCA9548A is shadowed whenever a channel is open. J_I2C1 carries only the MCP23017 at 0x20, so the bare-bus DrumDisplay path is proven there instead. Panel P4 already has a J_I2C1 tail for the MCP-driven X27, so this adds no harness leg.

One Dimmer per board, both on the same value — this is a sync check. The two strings are
the only duplicated class on the rig. Board A's pot drives LIGHT_INT_INSTR; both nodes receive
that broadcast independently and must light to the same brightness. Divergence between them is a
decode or timing fault that no single-node station can show. The direct OLED displays the same
value as a number, so the expected duty is readable next to the two actual brightnesses. One zone
per board only — J_BL2 stays unused.

Only four continuous (0xffff) targets are reachable, and all four are allocated:
LIGHT_INT_INSTR, LIGHT_INT_CONSOLE, LIGHT_INT_FLOOD_WHT, GUNSIGHT_KNB. The two remaining
encoders therefore bind to the radio and doppler, which need the set powered — not ramp-testable,
but the readout is on-screen.

Wide selectors are plentiful — fifteen outputs carry a mask of 3 bits or more, so both
multi-position stations get a target that matches the class rather than one that merely fits:
5-bit / 32-state (ARC51_FREQ_10MHZ, ARC51_FREQ_50KHZ, ARC51_FREQ_PRE, MCL_CHAN_SEL),
4-bit / 16-state (ARC51_FREQ_1MHZ, AWRS_QUANT, TACAN_CHAN_MAJ, TACAN_CHAN_MIN) and 3-bit /
8-state (ARM_FUNC_SEL, ARM_EMERG_SEL, AWRS_MODE, DOPPLER_SEL, NAV_SEL, RADAR_MODE,
SHRIKE_SEL_KNB). The resistor ladder is the >12-position case per the input-class heuristic,
so it takes a 16- or 32-state channel selector; the 5-position rotary takes an 8-state mode
selector. These selectors animate in the cockpit when switched, so the input is confirmable even
before the set is powered — only their drum readouts need power.

MASTER_TEST class check: confirm the mod models it as a toggle before using ActionButton
(momentary → toggle). A held push-to-test is a plain Switch2Pos, and the ActionButton item in
the checklist would then need a different control.

Three X27s, one per backend — each answers a different question. The MCP gauge is the
only configuration that ships today (APN-153's DRIFT needle: MCP23017 Port B GPB0–3 → DRV8833,
chosen because that needle is slow — hardware-standards.md puts slow needles at ≈163 °/s on
I²C-class). That is the one to put under the soak. The '595 gauge proves the fast path
— benched at 1,137 steps/s in #197 gate 2 and named in the standard for gauges above
≈150 °/s, but no board drives coils from a shift register yet, so this is where it gets
proven before a board commits copper. The direct GPIO gauge has no shipping consumer at all
(the only other DRV8833 board, Armament_MCU, is deprecated); it is kept as a diagnostic baseline
that isolates StepperMotor from an expander fault, and because board B's J11 is otherwise
idle. It is the first station to cut if those pins are ever needed.

Scope — what this rig must prove, and what it need not

#290 asks for "the single live check for every in-scope class", so the list is bounded by what
ships in 1.0.0:

AngleSensorInput (#294) is not a faceplate station. Both an AS5600 and an MT6701 are on
hand, but neither is a panel-mount part: the magnet has to sit in a knob, concentric with the axis
of rotation, with the sensor held behind it at a controlled airgap. That is a bespoke knob plus a
bushing plus a sensor mount — a mechanical design task, unlike every other station here, which is
a COTS part in a hole.

So it moves to its own small fixture: sensor, magnet, shaft, knob, no panel. #290 asks for a
live check of every in-scope class, not for every check to happen on this rig, so the gate is
still met. Sequence it as bench-the-sensor-loose → measure the real airgap and concentricity
tolerance → then model the jig; a jig built on datasheet figures gives a bad reading that cannot
be attributed between mount, sensor and firmware. GUNSIGHT_KNB is a real A-4 control, so the
knob is the prototype for that panel rather than throwaway work.

Reserved, not populated. A 9 g servo (ServoMotor, #132) and a 28BYJ-48 + ULN2003 are not
built for 1.0.0: #132 is unmilestoned and unwritten, and StepPattern has no unipolar member, so
neither would be testing anything that exists. Keep one timer-capable J11 pin and some '595
outputs free, and add a panel when those tickets land — which is the point of building to the
Dzus width in the first place.

Firmware

Split Firmware/Examples/E2E_DCS_Test ("one live instance of every control class", PR #130) into two node sketches (distinct NODE_IDs), one per board.

Added 2026-10-27, carried from #375: the SoakRig sketches (PR #339) must build on the bootloader layout. That means Firmware/shared/stm32.ini (app at 0x08001000, identity block), so the #290 soak runs the same layout as production. Board B (C8) came out ~2.3 KB over once the 4 KB bootloader was reserved; the decision (2026-10-26) is -flto rather than moving to the CB. Bench-check ShiftBus ISR timing (SHIFTBUS_ISR_HZ) after turning LTO on.

What SoakRig measures (for the #290 soak — added 2026-10-27)

The #389 bench proved the bridge side with emulated nodes: export words reaching CAN, 0 drops up to 4× the real sim rate. Emulated nodes only send frames; their loop never waits on anything. This rig's nodes do — I²C reads/writes, ShiftBus, real switches, outputs — so it measures the node side:

ShiftBus section (via J_SR, board B)

A small perfboard sub-panel on the XH-7 J_SR leg: one 74HC165 carrying an encoder, a 2-position toggle and the stepper home microswitch (exercises the ISR-sampled encoder path, SHIFTBUS_ISR_HZ), and one 74HC595 driving a few LEDs. Per the ShiftBus rules: bussed 10 k bias on all eight '165 inputs, 33 Ω series on SCK / LOAD / LATCH, LEDs ≤ 4 mA direct with series R.

Done when

Both boards pass a short live-DCS smoke test (every control moves its sim counterpart; every output follows the sim), ready for the #290 soak.

This smoke test is the single live check for the v1 classes — the other tickets defer to it instead of running their own sessions:

Build standard — proof of concept, not a reference design

Dev modules plus perfboard. Modules for every chip family that has one — MCP23017,
ADS1115, TCA9548A, DRV8833. The shift registers are the exception: 74HC165 / 74HC595 in DIP,
soldered to the perfboard.
Passives that need a home — pull-ups, the analog RC, the resistor
ladder — go on the same perfboard. No custom PCB, no SSOP parts anywhere on this rig.

This rig proves the firmware classes work against real controls. It is not an electrical
reference design, so component placement, trace length and grounding only need to be good
enough not to be the thing under test.

The one thing that is not negotiable is soldered connections — that is the whole reason this
exists rather than a breadboard. The AxisBench analog wander traced entirely to push-in contact
resistance, and a multi-hour soak on those would measure the contacts rather than the firmware.
Solder and strain-relieve; beyond that, tidy is enough.

Board pairing and revision independence

The A/B split is organisational, not directional. The tables below group inputs on A and
outputs on B, but there is no need to keep that — inputs and outputs can be mixed across both
boards, and arguably should be. A real panel carries switches and indicators on the same node,
so a mixed rig exercises a PanelGroup handling both directions at once, including a round trip
within one node rather than only across the pair. Split by whatever suits the faceplate layout
and connector capacity.

Only two constraints are real:

  1. The ShiftBus section needs a 0.2.0 board — J_SR does not exist on 0.1.0.
  2. Don't hang an output off J11 position 11 of a 0.2.0 board. There it is PC14, an OSC32
    pin with roughly 3 mA of drive — fine for an input, wrong for an LED or a coil. Position 7 is
    PB2, which is BOOT1 but is sampled only when BOOT0 is high, and BOOT0 is tied low, so it is an
    ordinary GPIO. On 0.1.0 the two positions are PB5/PA8 and both drive normally.

Everything else sits on either revision. Every panel on this rig is buildable now — plates, controls, modules and the '165/'595
perfboard. What is pending is the final hookup: board B's panels have nothing to plug into
until the Rev 2 boards are assembled. Every station has firmware — #293 and #294 both merged
2026-10-01. Board A can additionally be driven now, on the
already-assembled 0.1.0 board.

The panel is a long-lived instrument and must keep plugging into future revisions. It
depends on connector contracts, not on board pin assignments:

  • J_I2C1 / J_I2C2, J_BL, J_SR are interface classes — pin 6 is INT_A whatever board is
    on the other end. These legs are revision-proof as wired.
  • J11 is not a class — it is a raw MCU breakout, so its pin meanings move with the silicon:
    positions 7 and 11 are PB5/PA8 on 0.1.0 and PB2/PC14 on 0.2.0.

Positions 7 and 11 are therefore the only revision-volatile part of the rig. That is a
re-pin, not a redesign — crimped contacts pull and re-seat in a minute — so there is no
reason to leave them unused. Just know they are the ones to check against a new board.

Construction consequence: that only holds for crimped legs. J_I2C, J_BL and J_SR are
crimped JST-XH / Mini-Fit and re-pin per contact. J11 is specified as IDC ribbon above, which
is mass-terminated and cannot be re-pinned
— a revision that moves those pins means
re-terminating or building a new tail. For an instrument meant to outlive several board
revisions, crimped discrete wires on J11 are worth the extra build time. Decide at build.

Either way the rig plugs into 0.1.0, 0.2.0 and later revisions that keep the connector
standards, and the A/B revision split is a convenience rather than a constraint.

Parallel work — what is gated

Everything mechanical and all panel wiring can proceed now; all controls, modules and displays
are in stock. Only the harness-to-board hookup is gated — see above. Two part lines are on LCSC WM2610010130:

  • RES-1K-1PCT-0805 — the analog RC (1 k + 100 nF). Fit those filters last.
  • CONN-XHP-7P — the J_SR harness leg. The only Rev 2-specific connector.

Dev modules only on this rig — no SSOP parts. The bare IC-MCP23017-SSOP /
IC-TCA9548A-SSOP in InvenTree are for fabricated boards and are not what this panel uses.

Rig inventory — all on hand, mostly untracked

Bought outside LCSC, so none of it appears in any InvenTree shortfall:

Part Qty Acquired Needed by this rig
74HC165 DIP-16 20 2026-06-30 ShiftBus perfboard
74HC595 DIP-16 + 20 sockets 20 2026-06-30 ShiftBus perfboard — socketed, not soldered direct
MCP23017 module — Jun 2026 one per board
ADS1115 module 2 2026-05-22 1 (board A)
TCA9548A breakout 6 2026-06-19 1 (board B)
SSD1306 0.91″ OLED 10 2026-06-19 3 (board B) — tracked as OLED-0.91-I2C
DRV8833 module 10 — 3 — tracked, no IPN
Stepper X27.589 10 — 3 — tracked as STEP-X27-589; STEP-X27-168 is at zero, and the two differ only in shaft direction
ULN2003 + 28BYJ-48 6 sets 2026-08-15 reserved — not built for 1.0.0
SN65HVD230 module 3 2026-05-17 not needed on the panel

Not in the table: an AS5600 and an MT6701 are on hand but untracked, and neither is used on
the faceplate — see the angle-sensor note above. The stepper home microswitch is also absent from
the table; any microswitch or reed switch does.

Every quantity is covered several times over. The tracked IC-74HC165-SOIC (50) and
IC-TCA9548A-SSOP (10) are the SMD parts for fabricated boards — not what this rig uses;
there is no 74HC595 entry in InvenTree at all.

Not in the table: the stepper home microswitch for HomeMode::SENSOR. Any microswitch or
reed switch does; confirm one is on hand before the ShiftBus panel is built.

Reserved: the '595 leg can later drive a ULN2003 → 28BYJ-48, exercising the unipolar-stepper
path ('595 → ULN2003 → coils) on the same ShiftBus section. Out of scope until StepPattern
gains a unipolar member — see Reserved, not populated above.
Nothing will ever flag these short — the same blind spot that let the XH-7 housings reach zero
unnoticed. All of it is physically in hand, so no part of this rig is gated on procurement
except the two LCSC lines below.

Activity

  1. added this to the Firmware v1.0.0 milestone on Sep 21, 2026
  2. self-assigned this
    on Sep 21, 2026
  3. added theissue type on Sep 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

firmwareFirmware implementation workhardwarePCB, KiCad, and hardware designpriority: highCritical path; do next

Type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions