This is the human-readable version of the wiring process — what it feels like at the bench,
component by component, during the from-scratch rewire. For the technical rulebook (board
priority, pairing rules, HV bank wiring), see
OPP hardware reference. For the exact bookkeeping steps Claude
follows to keep the config/registry/generated docs in sync, see
.claude/skills/wire-component/SKILL.md in the repo.
The short version: wire it, then figure out together exactly what got wired. Since this is a
full rewire — new pieces going to whatever board location is physically convenient, not
constrained by the old wiring — pre-planning every number down to the pin before touching a wire
doesn't fit how the work actually goes. Instead, the numbers get discovered right after the
physical connection is made, while it's still on the bench.
Dorus decides what a newly-wired object is called — "left slingshot," "VUK eject," "tilt
switch." Just a name at this point, no numbers. This is what shows up everywhere downstream:
components.yaml, the generated wiring guide, MPF's own device names.
If the component has a coil (a slingshot, a pop bumper, an eject, anything that fires or moves),
Dorus identifies which coil driver channel it's physically connected to — which board, which
bank, which numbered terminal. Read straight off the board's silkscreen or connector labels (see
Board silkscreen reference if a label needs decoding from a
photo).
This is the step that changed the most. Rather than assuming a switch's MPF name up front,
tools/wiring_test.py --monitor gets run against the live machine: it prints every switch's
current state, then streams every open/close as it happens in real time. Tap the switch (or
switches) that belong to this component, on the actual cabinet, and its real name and address
print immediately — no need to already know it. This is how "which switches did I just wire"
gets answered together, on the spot, instead of guessed at from a plan.
Start the session in probe mode for this (tools/mpf-session.ps1 -Action Start -NoBcp -Probe). Parts that aren't wired yet have no pin number (they live in hardware-draft.yaml, see
below), so most inputs are genuinely free, and without probes --monitor only sees switches MPF
already knows. Probe mode puts a temporary probe-<number> switch on every free input, so every
switch position on the machine is watched: tap the new switch and it prints NEW INPUT 2-3-5
(its address), and when you stop with Ctrl+C you get a list of every new input that fired.
Two passes, in order, both using tools/wiring_test.py:
--pulse-ms optionSee Wiring test tool for the exact commands, how the tool works and its
gotchas — confirmation prompts are a single Enter press, not typed text, so this whole loop stays
fast to repeat component after component.
Once a component's name, coil channel, and switch(es) are known and tested, they get recorded
into tools/hw_console/data/components.yaml and machinefolder/config/hardware-*.yaml — the
board-priority and pairing rules from the
OPP hardware reference still apply here, they just get checked
against the physical connection that was actually made, rather than dictating it beforehand.
python tools/hw_console/check_registry.py catches a collision or pairing-rule violation at this
point, and the wiring guide and status checklist regenerate automatically from the same data. That
bookkeeping is what .claude/skills/wire-component/SKILL.md walks through step by step.
hardware-draft.yaml)Parts that are planned (or even installed) but not wired to a controller yet have no pin
number at all. They're defined in machinefolder/config/hardware-draft.yaml on MPF's virtual
platform, so the game modes and tests can use their names without them taking up a real input.
Wiring one for real moves its entry from there into hardware-switches.yaml /
hardware-coils.yaml with the number you just found — so a new connection never collides with
a placeholder.
hardware-devices.yaml (drop bank, pop-bumpernumber: is the device's own name (e.g. number: s-toplane1),platform: virtual. Never a real-looking number. All other settings (type: NO, pulsename and draft: true in components.yaml (noconfigured flag readsno, and check_registry.py leaves it out of collision and pairing checks.hardware-draft.yamlcheck_registry.py flags a pair where one half is real and the other draft. A device entryautofire_coils); the Mid-Flipper'sflippers: entry gets added once its coil is real.config.yaml has hardware: platform: opp, virtual. The firstplatform:, so opp mustThe wire-component skill does this move, and keeps components.yaml and the generated docs in
step.