projects/ janelia/ Mouse Joystick/ Sources/ mouse-joystick-pcb/ .agents/ PLANS.md/

Archived page

Mouse Joystick PCB Plan

.agents/PLANS.md of mouse-joystick-pcb, rendered; the Markdown is beside it.

Use this file for work that is multi-step, risky, or likely to span multiple sessions.

Documentation — complete for v1.0 (2026-10-15)

The documentation program run from kicad-libs (its PLANS, 2026-10-12 to 2026-10-15) is finished for this board: the fifteen placed block sheets regenerated from the library with the copper kept, a page per placed block (schematic, parts with sourcing, ports, the place on the board, the recorded netlist check), the four subsystem pages and the MCU pin allocation from kicad/board.yaml and its recipes, the dimensioned outline and mounting hole drawing, the BOM page with its LCSC and datasheet links, the board view with the model and numbered blocks, and the Pages site. make docs is a no-op until a KiCad source changes; make docs-check is the gate before a tag; make site builds the site strictly. The hand-written pages were reviewed the same day: docs/design-block-map.md is a pointer page, the routing pages of September 2026 are grouped as the routing record in the site's navigation. What a new revision changes in the board is the bring-up items below.

Active Task — v1.0 unit A findings from the A4/A5 bring-up (2026-10-06)

Measured on unit A (E4654C159F274534) with mouse-joystick-firm's new examples/BrakeAndTorque; numbers in docs/bringup-log.md → "v1.0 unit A — Stages A4 and A5". Three board-level items came out of it, none of which the firmware can settle:

Open Item — Two of four v1.0 units ignore the USB 1200 baud touch (opened 2026-10-13)

Unit B (board 7, E4654C159F404534, 2026-10-07) and unit D (board 9, E4654C159F234234, 2026-10-12) enter BOOTSEL on the 1200 baud touch at most once and then never again under the delivery build (mouse_joystick_qp_rev2): PlatformIO's upload fails The RP2040 device returned an error: <unknown> or leaves the board running, a pyserial open at 1200 baud returns a broken pipe on the DTR ioctl, and picotool reboot -f finds no reset interface. Units A and C take the touch every time with the same build. Manual BOOTSEL (RJ-45 and USB out, button, USB in) then picotool load -x --bus B --address N works first time. Record: docs/bringup-log.md, "Two things that bit" under the desk rig, and the unit B session of 2026-10-07.

Completed Task — Session handoff and routing timing (2026-09-21)

The handoff update changes documentation only. No new routing run or board change is needed to start the next session.

Completed Task — Connector access and BNC spacing (2026-09-21)

See connector access report. The current ZIP supersedes the earlier 14 mm BNC package. The initial routing rescue below retained all placements; this later mechanical correction changes three. No order has been placed. Actual mating cable/plug and enclosure fit remain separate from electrical DRC; the FH12 requires its specified FFC.

Completed Task — Routing rescue (2026-09-21)

See completion report. Experiments and native reports are under tmp/routing-rescue/; manufacturing outputs go to fab/routing-rescue-4layer/. The historical routing blockers below are superseded by these results. Optional circuit/bring-up work is not implied complete by routing or DRC.

Completed Board Task — InstrumentPort Tap Resistors (opened 2026-09-19)

Completed before routing; library shunt follow-up remains below. Split every InstrumentPort observation branch off its functional net with a series resistor, so the long runs to J1501 become separate, deprioritisable, separately-classed nets. Twenty-four taps across seven design blocks and the /mcu/ sheet.

The full specification — values, nets, owning blocks, as-built source pin names, part numbers, naming rules and traps — is docs/instrument-port-tap-resistors.md. That document is written to be executed from a fresh session; this is the checklist.

Headline decisions, so they are not re-litigated:

Done 2026-09-20. All twenty-four taps are placed and netlist-verified; the INSTRUMENT netclass is in place. Routing completed on 2026-09-21, tracked above.

Historical pre-routing findings (see completed routing above for disposition; remaining one-layer vias are recorded warnings, not opens):

Historical Task — v1.0 Layout And Routing (opened 2026-09-18)

Superseded by the completed four-layer routing above on 2026-09-21. The following is the prior planning record, not the current board status.

Autorouting state, 2026-09-20: not routed. Best result 95 of 246 connections left unrouted, across five runs. Before attempting another run, read docs/autoroute-log.md — it records what each configuration produced so runs are not repeated — and the reusable reference ../kicad-libs/docs/freerouting-autorouting.md, which documents freerouting v2.4.1's settings surface, its verified defaults and the four that matter for design-block boards.

Do not re-litigate these; all measured:

Gated on the tap-resistor task above as of 2026-09-19. Splitting 24 nets changes the ratsnest and orphans copper already routed to the port, so finishing the route first would be work done twice. Pouring GND and fixing the four voltage-bearing clearances are independent and can proceed either way.

The schematic is done and ERC-clean; the board is placed with 2200 tracks in and 149 connections left. State, numbers and reasoning are in .agents/handoff.local.md. This is the checklist.

Blocking, in order:

Not blocking, but cheapest now:

Upstream dependency, unchanged: the DIG0/DIG1 marker pair is still not frozen in ../board-interfaces, so hold FW_TRIG0/FW_TRIG1.

Library dependency: ../kicad-libs's tmc5130 still ships 0.12 Ω shunts against this board's 0.15 Ω. Do not re-place that block until it is fixed upstream.

Completed Task — Digital InstrumentPort documentation (2026-09-12)

Session continuation, 2026-09-13: Start from the InstrumentPort handoff. The subdivision observations are now saved in the canonical draft. DIG0/1 is the latest beacon-placement recommendation, not a decision; DIG14/15 was the earlier unfrozen proposal. The v1.0 adoption/fallback gate remains open.

The new design direction is a 50-pin, 32-channel read-only digital port with integrated SN74LV541ARKSR buffers, enabled by hardware default on +3.3V_MCU. See docs/digital-instrument-port-migration.md and the canonical board-interfaces/docs/instrument-port-digital.md draft.

Verification: make test documentation/scaffold checks and git diff --check pass; new local links/anchors resolve. Only Markdown files changed. Board migration, new overlay allocation, and qualification remain pending.

This reopens the 2026-09-11 decision to keep 40 pins for v1.0; it does not claim the board is migrated or authorize an indefinite release delay. Prefer the new port if the reviewed block, adapter design, and board checks fit the order schedule; otherwise explicitly record a legacy-40 release fallback. Debug, analog, vertical FFC, and 25-pin implementations do not gate v1.0. Earlier "leave the InstrumentPort blocks alone" / "no new blocks" scope notes apply to the legacy fallback, not to an adopted 50-pin migration.

Active Task — v0.2 JLCPCB Fabrication Release (2026-08-21)

v0.3 Bring-Up — What To Test When The Boards Arrive (opened 2026-09-08)

v0.3 boards are inbound. The repo is already at (rev "1.0") as of 2de2847, so v0.3 is a measurement vehicle, not a shipping revision. Everything below is either a v1.0 decision that hardware can settle, or a measurement that has been owed since August.

v0.3 is electrically identical to v0.2 on the 36 V path — verified by diffing kicad/ at v0.2 against cab25b3. Every 36 V problem from v0.2 will reproduce, and the firmware staircase in PowerSequence is still required.

Stages 1-3 passed on the first v0.3 unit, 2026-09-09 — board 4, E4654C159F171E34

Stage 1 on USB alone (24 checks, 0 failed), then stages 2 and 3 with the motor, encoder, limit switch, 36 V, pyControl RJ-45, LA5032 and the angle BNC all connected. Rail up in 132 ms, TMC5130A VERSION = 0x11, vsense reads back 1, encoder follow ratio 1.0000, switch witnessed (engages 28.8°, 1.8° throw, active high), homing repeatable to 2.0 encoder counts at 0.200 rev/s approach / 0.010 rev/s creep. Full results in docs/bringup-log.md → Board 4.

Resolved on the way: the IOIN disagreement between boards. Five of those bits depend on what is plugged in, so the firmware's fixed expected value is wrong by construction. Empty limit-switch connector reads REFL_STEP = 1 (the MAX6816 drives its own pull-up high and v0.3 has no LIMIT_SW pull-down); fitted and at rest it reads 0, because the switch is normally closed. No assembly defect on any board. Fail-safe in the right direction: a broken switch cable reads as a permanent limit.

Still untested, and it is the headline item: the angle output. There is no DAC80501 driver in the firmware at all, so the BNC only shows the DAC's reset state. Nothing about the analog characterisation below can start until that driver exists.

Three things from stage 1 that change how the rest of this list is run:

Still owed on this unit and cheap: a cold power-on reset reason (needs a replug, so it reads power-on rather than software), and boot latency, which needs the LA5032 on FW_TRIG0/FW_TRIG1.

The two measurements that gate v1.0 design decisions

The rest of the angle-output characterisation

This is the headline reason v0.3 was ordered.

Carried-forward measurements this board can finally take

The question hardware cannot answer

v1.0 Board Changes (opened 2026-09-08, supersedes the v0.4 list below)

Status 2026-09-16: under way. The ordered task list is now .agents/handoff.local.md → Finish the schematic and Still to place, which is current; this section is the scope definition behind it. Blocks are no longer a prerequisite — all of them pass in ../kicad-libs and two are placed. What changed since this section was written:

Re-placing tmc5130 still damages a clean board: ../kicad-libs is 0.12 Ω against this board's 0.15 Ω, re-verified 2026-09-16.

SCOPE NARROWED 2026-09-08 — v1.0 only, no nice-to-haves

Decided by the user: only changes that directly affect v1.0. Everything else waits until v1.0 has been tested. The goal is to order v1.0 as soon as v0.3 testing allows. The lists below are kept for their reasoning; treat anything not named here as deferred.

Zero regression traps remain on this board. By not re-placing tmc5130 and not re-placing rp2040_gpio_isolation_sn74cb3q3245, two of the three evaporate without any work — the shunt can never revert, and the isolation block's 6 unconnected-copper errors never reach the board. The third (the torque block's missing .kicad_pcb) is library work, listed in ../kicad-libs's plan.

CORRECTED 2026-09-10 — the list below was wrong about two blocks. Commit aa68b17 ("Remove some design blocks for modifications", 2026-09-08) removed three sheets from this board, not one: the 36 V input block, rp2040, and quadrature_encoder_input_5v_sn74lvc2g17. So rp2040 is not "leave alone entirely" — it is out of the schematic and must be re-placed, and it has since been edited in ../kicad-libs (the RUN switch, done and clean). Same for the encoder block, which needs no design change but does need re-placing after the library scrub. The board schematic on disk therefore has no MCU and 126 components; that is deliberate teardown, not damage.

Blocks to re-place: hv_input_36V_terminal_2pos_5p08_protected_vertical (this is the LATCH fix — the block already exposes the port, no edit), analog_output_0_3v3_dac80501 (DAC to 3.3 V + I2C strap + ÷2 divider), rp2040 (RUN switch), and quadrature_encoder_input_5v_sn74lvc2g17 (no design change; re-place after the library scrub). Blocks to place new: torque_bridge_0_3v3_ina821, brake_current_sink_0_49ma_dac80501, hv_to_12v_tpsm365r6. Blocks to leave alone entirely: tmc5130, rp2040_gpio_isolation_sn74cb3q3245, usb2, both 5v_to_3v3_tps73733, hv_to_5v_tpsm365r6, limit_switch_debounced_input, pycontrol_behavior_port_rj45_boundary, instrumentation_signal_buffer_sn74lvc125a, the InstrumentPort boundary.

Verified 2026-09-08 and it removes a dependency: the isolation block already exposes all 30 channels as GPIO0-GPIO29 / ISO0-ISO29 port pairs, so ~{BRAKE_FAULT} routes through a spare channel with board-level wiring only — no block edit, no re-place.

Cut from v1.0 — revisit after v1.0 is tested

Kept despite the narrowing, and why

Two regression traps, not three — updated 2026-09-08. The tmc5130 shunt is no longer a library fix: the block now ships 0.12 Ω deliberately, as a placeholder, and sizing R4003/R4004 is this board's responsibility after placement. See Required below. Also verified that day: v1.0 needs no new design blocks — all 36 exist, including hv_to_12v_tpsm365r6.

Required

Decide, then do

Why an on-board debug MCU was rejected

Decided 2026-09-08. Two variants were considered — a second MCU on the board for programming and debug, and a second FFC port letting an external debug MCU reach the target — and both are rejected. The reasoning, so it is not re-litigated:

Deferred to a later revision — do not design now

Purchases before layout, not after

Modelled 2026-09-08 against the whole v1.0 BOM, not part by part. Nothing is short: buy nothing for a 5-board build. Method — v0.3's fab BOM (173 placements) plus torque_bridge_0_3v3_ina821 (57), plus brake_current_sink_0_49ma_dac80501 (47), plus hv_to_12v_tpsm365r6 (12), plus the planned block deltas, plus the board-level TQ_AOUT BNC = 302 placements over 81 BOM lines. Every line mapped to a JLCPCB offer; none at zero stock. Tightest coverage in the entire board:

part /board stock boards
OPA197IDBVR C221351 7 298 42
2.2 µF GRM21BD72A225KE01L C2981733 2 90 45
DAC80501ZDGSR C1852033 2 98 49
HL-BNC-KHDA C19712152 2 98 49
ADS8689IPWR / INA821IDR / REF5010AIDR 1 ea 50 ea 50

Everything else clears 60 boards. Re-run the model rather than re-deriving it: the script pattern is one join of db/source/vendor_map_jlcpcb.csv and db/source/parts.csv against this repo's fab/*/jlcpcb-bom.csv plus the block component lists.

Next Revision — v0.4 Required Changes (opened 2026-09-02)

Superseded 2026-09-08 by the two sections above. The repo is at (rev "1.0") and there is no v0.4. This section is kept for its reasoning, not its task list — the rejected alternatives for the LATCH fix, the energy accounting behind the 108 µF ceiling, and the swap_lr and harness notes are all still correct and should not be re-derived. Work the v1.0 list.

Carry these forward. v0.2 and v0.3 are electrically identical on the 36 V path, so v0.3 boards will reproduce every problem below. Verified by diffing kicad/ at v0.2 against cab25b3 ("Generated v0.3 fab files"): R206/R1906 = 40 kΩ, LATCH strapped through 1 kΩ, C1213+C1214 = 2 × 100 µF, unchanged.

1. LATCH to a GPIO — blocking, and the reason this section exists

R203 (0402, 1 kΩ) straps the TPS1HTC100-Q1's LATCH to +3.3V_MCU, selecting latch-off. That prevents the 36 V rail from ever coming up. The switch trips its relative thermal shutdown 1-2 ms into charging +36V_SW's 208 µF, at only ~10-12 V, and with latch-off it never retries — so the rail lives on stored charge for 17-68 ms and collapses. Measured on both rig boards.

Change: move R203's far end from +3.3V_MCU to a spare GPIO. Keep the 1 kΩ RPROT. Firmware drives it low for the power-up ramp and high once the rail is up, which gets retry-based soft-start and latch-off for real faults. It also defaults to auto-retry when the MCU is unpowered or at reset, which is the safe default — that is exactly when the rail must be able to charge.

Spare GPIOs proven good by stage 1: GP15, GP16, GP17, GP18, GP20, GP22, GP23, GP27. (GP10/GP11/GP13 are the rev-1 DAC bus — do not use those.)

Rejected alternatives, with the reasoning so it is not re-derived:

2. Test points — the binding constraint all session

+36V_SW, +5V_SW, +3.3V_SW, +3.3V_MCU, +5V_MCU, and both sides of R1903. The entire 36 V diagnosis rested on energy accounting because reading +36V_SW means clipping onto C1213's can pads, and that measurement still has not been taken.

3. Not a required change, but know it before the next layout

The limit switch is at the positive end of travel and wired to REFL. Measured 2026-09-02 on board E4654C159F4D1334 during stage 3. REFL is the TMC5130A's left switch and stops negative motion, so SW_MODE.swap_lr has to be set or the hardware stop guards the direction that moves away from the switch. Firmware sets it, so nothing has to change on the board — but it is a mismatch between the wiring and the chip's natural sense, and a future revision or ../mouse-joysticks-pcb could avoid it by putting the switch at the negative end of travel or wiring it to REFR. Full note in docs/bringup-log.md.

The left and right joysticks use different harnesses so the boards and the firmware are identical. Decided 2026-09-02. The left mechanism is mirrored, so its harness swaps one motor coil's leads and the encoder's A/B channels; nothing else differs and the boards carry no left/right state. Tables, the reasoning, and the verification procedure are in docs/harness-pinouts.md.

Travel is only ~4000 microsteps, 28 deg at the motor shaft, bounded by the limit switch at the positive end and a mechanical stop at the negative end. Any future firmware or motion envelope has to fit inside it.

4. Carried forward unchanged

How To Check A New Revision For The Fix

One command. Flash mouse-joystick-firm examples/TMC5130Communication and send l:

s then runs the whole stage 2 sequence and prints which power-up method it used, so a bring-up log records the mechanism rather than leaving it to be re-derived.

Current Goal

Rev 0, rescoped 2026-08-13: get the interfaces on a board and send it to fabrication. RP2040 with GPIO isolation, one TMC5130A on a 4-pin motor terminal, one single-ended encoder on a 5-pin terminal read by the driver only, one debounced limit switch on a 2-pin terminal, a pyControl UART on RJ-45, and a 36 V switched supply input that doubles as a chip-reset button.

Rev 0 also carries InstrumentPort-40, moved back in on 2026-08-16 for firmware development — see Resolved below.

Deferred to rev 1: the 0-5 V analog angle output, the pyControl TTL event lines, and the PIO encoder cross-check. Their pins are reserved and no Rev 0 pin moves when they arrive. The PIO cross-check was since deleted outright — see 2026-08-16 below.

Session-level detail lives in .agents/handoff.local.md. Read it first.

The rig's two-joystick form factor question stays open. This board is the fast path to a testable system, not a commitment against ../mouse-joysticks-pcb.

Context

Source-of-truth sibling repositories:

Direction History

Resolved

Blocking Items

Two remain, and only the second can actually invalidate the board. Ranked.

  1. Joystick motor part number. Identified 2026-09-02: Lin Engineering WO-208-13-01D — NEMA 08, 1.8°/step, 4-lead bipolar, 0.6 A/phase, 6.5 Ω/phase, 3 oz-in, no gearbox, rear shaft. See docs/bringup-log.md. The "0.67 A" in the sentence below was an assumption and is superseded; Lin does not state whether 0.6 A is RMS or peak, which is exactly the open IRUN 17 (0.421 A) versus 24-25 (0.600 A) choice, and only the thermal measurement closes it. The bulk-capacitance question below can now be decided. Sets the motor connector, supply envelope, ILIM, and confirms whether 0.67 A is RMS or peak. Owner: Peter. Also settles the tmc5130 block's bulk-capacitance question, restated here so it survives a clone: the block ships 200 µF on VM (C1213+C1214, 2× 100 µF 50 V, OW-PASS-EEEHAH101UAP, D8×10.2 mm) sitting on switched +36V_SW, where docs/power.md bounds bulk at "tens of µF, not hundreds". 200 µF at 36 V is 130 mJ per HV_EN turn-on into an HTSSOP-14. Unresolved. Options: depopulate C1214 (65 mJ), keep both and verify a clean turn-on cold and hot at Gate B, or decide once the motor is identified. 50 V on 36 V is 1.39×, and 110 mA ripple each is thin for motor bulk.

Added 2026-08-17: this bulk is also what sets the off-state creep rate in the DIAG_EN finding below. At ~208 µF the rail creeps at ~1.2 V/s; depopulating C1214 would double that to ~2.4 V/s. Moving DIAG_EN to GP14 removes the creep entirely, so the two decisions are now independent — but do not reintroduce a fixed DIAG_EN pull-up while reasoning about bulk. 2. Encoder part. Largely resolved 2026-08-17 — no longer gates the schematic, and no longer gates fabrication. The receive path is decided and built: single-ended 5 V push-pull, conditioned by the new quadrature_encoder_input_5v_sn74lvc2g17 block. Confirmed against the encoder's own spec (4.5-5.5 V supply, 25-42 mA, VOH ≥ 2.4 V at −8/−5 mA, VOL ≤ 0.4 V, tr 100 ns / tf 50 ns), which closes against the SN74LVC2G17's VT+ ≤ 2.0 V and VT− ≥ 0.8 V with 400 mV of margin on both rails.

Closed 2026-09-08. The part is a US Digital E4T-1000, 1000 CPR bench-confirmed against 51200 usteps/rev, so the C802/C803 filter value is confirmed correct with ~450× margin — see the closed item in the v0.3 section above, and do not re-derive it. One consequence to propagate: the E4T's CPR > 500 branch draws 34 mA typ / 42 mA max (against 25/30 for CPR ≤ 500) and drops output drive to IOL/IOH = 5 mA, so docs/power.md's 5 V-budget row still reading "Unknown — the encoder part is not identified" is stale and should be filled in with 42 mA worst case.

Superseded text, kept for the derivation: what still needs the part number, and only this: CPR and maximum shaft RPM, to confirm the C802/C803 filter value. They are fitted at 470 pF, which covers a channel frequency up to 44.9 kHz — f_ch = CPR × RPM / 60. Note the number to size against is the fastest motor-commanded move (homing, recentering), not how fast the mouse pulls; the animal is the slow case. A low-pass has no lower limit, so slower motion never requires a different value — only a faster axis would. Sizing table with R = 4.7 kΩ:

Cap f_c max f_ch atten @10 MHz
27 pF 1.09 MHz 685 kHz −19 dB
470 pF (fitted) 71.4 kHz 44.9 kHz −43 dB
1 nF 33.7 kHz 21.2 kHz −49 dB
2.2 nF 15.3 kHz 9.7 kHz −56 dB

Verify at bring-up by scoping ENC_A_FILT during the fastest commanded move. Both channels must keep the same value so A/B propagation delay stays matched. Still open from the part choice: whether the encoder has an index channel — J1102 pin 2 is reserved for it, and ENCN is currently tied to GND through R1201. Also confirm the 25-42 mA draw lands in docs/power.md (it is ~7% of the TPSM365R6's 600 mA). 3. Board outline and mounting. ../mouse-joysticks-mech is a two-joystick assembly, so how a per-axis board mounts is a decision — but pick a rectangle and move on rather than blocking the fab order on it.

Both of the 2026-08-17 non-blocking carry-overs are now closed. Netclass patterns were extended from 108 to 124 so every rail, both motor phases and the sense returns route at their intended widths; and the missing RUN/SWD was resolved as a decision rather than a fix — deferred to a future board revision, since RP2040 RUN has an internal 50–80 kΩ pull-up and needs no external part. Both are recorded in Open Schematic Findings below, which is the tracked record. (.agents/handoff.local.md is gitignored and will not be present on a fresh clone — do not rely on it.)

Resolved 2026-08-13, no longer blocking:

Items 1-2 are shared with ../mouse-joysticks-pcb. Answer them once and record the answer in both.

Reopened and narrowed, 2026-08-12: the 36 V TVS. It belongs on HV_IN to protect the switch and the buck at hot-plug, not on the motor rail to protect the driver — nothing can do the latter at 36 V. SMBJ40A is the part to order, and the stocked SMF48A works for first boards, so it is not a fabrication blocker. See docs/power.md.

Settled 2026-08-17: no TVS is populated in Rev 0. Confirmed against the netlist. It stays a bench-measurement item, not a fabrication blocker — and it is what bounds the margin on C1213/C1214, which are 50 V parts on a 36 V rail (72% of rating). Measure the transient at the TMC5130A VS pins at Gate B.

Open Schematic Findings — re-verified 2026-08-17 (cleared for layout)

Third and final pre-layout review, run after the nine board-level termination resistors, the DIAG_EN rework, and the netclass patterns. This section is self-contained on purpose — it is the tracked record, and .agents/handoff.local.md is gitignored and will not survive a clone onto another machine. Everything needed to act is here.

Current verification state

ERC is clean: 0 violations at every severity. 142 components, no duplicate refdes, every part has a footprint. Zero undefined cross-domain nets. Netclass patterns extended from 108 to 124, so no current-carrying net routes at the 0.2 mm Default width. Every capacitor sits at or below 80% of its rated voltage.

Reproduce:

export ORB_WEAVER_KICAD_LIBS=$HOME/Repositories/orb-weaver/kicad-libs
kicad-cli sch erc -o /tmp/mj-all.rpt --severity-all kicad/mouse-joystick-pcb.kicad_sch
kicad-cli sch export netlist --format kicadxml -o /tmp/mj.xml \
  kicad/mouse-joystick-pcb.kicad_sch

ERC could not see most of what mattered here. Every finding below was found by walking the flattened netlist against manufacturer datasheets, and all of them kept passing ERC before and after the fix. Re-check by netlist, not by ERC.

No blockers remain. The schematic is cleared for layout.

Closed this session — keep the reasoning, do not re-derive

Pattern Netclass Width
*/HV_RAW, */HV_IN (retargeted) PWR_HV_ENTRY 1.5 mm
/+36V_SW PWR_HV_BUS 1.2 mm
/motor_encoder_limit/OA*, /motor_encoder_limit/OB* MOTOR_2A 0.8 mm
*/tmc5130/BRA, */tmc5130/BRB MOTOR_2A 0.8 mm
/+5V_*, */VBUS, */PYC_CONN_5V, */PYC_5V_FUSED PWR_5V 0.5 mm
/+3.3V_* PWR_3V3 0.3 mm
*/5VOUT, */tpsm365r6/VCC PWR_5V_LOCAL 0.2 mm
*/tmc5130/VCP, */tmc5130/CPO, */tmc5130/CPI BOOTSTRAP 0.3 mm

Verified that no net matches more than one netclass, so there is no precedence ambiguity. BRA/BRB are not Kelvin sense nets — on the TMC5130A they carry full coil current into the sense resistor, so they are sized with the motor phases. SENSE_KELVIN exists for the TMC5160's SRA/SRB, not for this part. MOTOR_2A (0.8 mm) was chosen over MOTOR_LOW (0.3 mm) because the motor part is still open; downgrade it once the motor is fixed.

Ref Value Net To Why
R403 10 kΩ PYC_UART_RX +3.3V_MCU UART RX must idle high; the RP2040's default pull-down looks like a permanent break
R1401 10 kΩ TMC_SPI_SCK GND define the U1701 buffer input when the switched domain is off
R1402 10 kΩ TMC_SPI_COPI GND as above
R1403 10 kΩ TMC_SPI_CIPO GND as above; also gives the instrument tap a clean idle-low
R1404 10 kΩ ~{TMC_CS} +3.3V_SW CSN must idle deasserted; a pull-down would assert chip select
R1405 10 kΩ LIMIT_SW GND define the U1801 buffer input
R1101 10 kΩ TMC_DIAG0 +3.3V_SW DIAG is open-drain at reset; datasheet requires ≤47 kΩ
R1102 10 kΩ TMC_DIAG1 +3.3V_SW as above
R1103 10 kΩ ~{TMC_DRV_EN} +3.3V_SW guarantees the power stage is disabled in hardware

Four decisions inside that table are load-bearing and easy to get wrong later:

  1. Pulls that must read HIGH reference +3.3V_SW, never +3.3V_MCU. Referencing the always-on rail back-powers +3.3V_SW to roughly 1.3 V through the TMC's protection diodes — defeating the HV_EN power-cycle and corrupting SW_RAIL_SENSE, and parking the buffer inputs at ~2.0 V, exactly the SN74LVC125A's VIH. This was built the wrong way once and corrected.
  2. +3.3V_SW is held at 0 V when off by R401/R402 (2 kΩ to GND), which is what makes a pull-up to that rail a valid LOW in the off state. If the SW_RAIL_SENSE divider is ever removed, four of these resistors stop defining their nets.
  3. Do not use 100 kΩ pull-downs on the TMC SPI lines, which is what docs/interface-map.md used to prescribe. The TMC's CSN/SCK/SDI/SDO are (tpu) with RPU = 132/166/200 kΩ; 100 kΩ against that parks the node at ~1.9 V, straddling the buffer's 2.0 V VIH and leaving chip select ambiguous. 10 kΩ gives 0.28 V, a clean LOW.
  4. ~{TMC_DRV_EN} needed R1103 even though DRV_ENN has an internal pull-up. Once firmware enables the bus switch, the RP2040's reset-state internal pull-down (RPD 50–80 kΩ) passes straight through the FET switch and fights the TMC's 132–200 kΩ tpu, giving 1.00–1.89 V — never a guaranteed HIGH, and at the worst corner a LOW, which enables the power stage with 36 V on VS. R1103 makes the worst case 2.75 V against a 2.31 V V_INHI, so "drivers disabled at boot" is a hardware guarantee rather than a firmware ordering requirement.

Resulting levels, verified:

Net Domain OFF Domain ON, bus isolated
TMC_SPI_SCK/COPI/CIPO 0 V 0.28 V (LOW)
~{TMC_CS} ≤0.24 V ≈3.4 V (HIGH, deasserted)
LIMIT_SW 0 V driven by U1301
TMC_DIAG0/1 0 V 3.3 V (open-drain idle)
~{TMC_DRV_EN} 0 V ≥2.75 V (power stage off)

Verified correct — do not re-derive

Checked and explicitly not defects — do not chase these

Carry into layout — decisions, not blockers

Milestones

  1. ~~Scaffold the repository and write the design record.~~ Done 2026-08-12.
  2. Answer blocking items 1-5.
  3. Prove hv_input_36V_terminal_2pos_5p08_protected in a scratch project with ERC. It has never been placed on any board.
  4. Compose the schematic from existing kicad-libs blocks, in the order in docs/rev0-plan.md.
  5. ~~Design the two sections Rev 0 needs that do not exist as blocks: the pyControl port boundary and the debounced limit input.~~ Done 2026-08-15 and 2026-08-16, both with saved PCB layouts, both still project-local pending bench verification. The event BNC group and the DAC output section move to rev 1 with the functions they serve.
  6. Freeze the GPIO allocation and the instrument overlay against the schematic.
  7. Run ERC/DRC, generate fabrication outputs, order.
  8. Bench bring-up against Gates B through D. The 36 V transient measurement at the TMC5130A VS pins is the one that justifies or condemns the supply choice.
  9. Native pyControl integration against Gate E, adapting pump_controller.py as the device-class template.
  10. Record results in docs/bringup-log.md, against the board serial.

Verification

Ready for active PCB work when:

Progress Log

Planned Task — OBS-HW-JOYSTICK: current-board qualification and future debug/trace planning (2026-09-29)

MCU comparison update, 2026-09-30: the local observability plan now links the stocked STM32G0B1CEU6 versus RP2040/RP2354B assessment and PIO UART tradeoffs. Future pin/peripheral budget studies can proceed independently; three STM32 UART pairs coexist statically with USB/SWD, but this is not a complete joystick allocation or qualification. PIO does not create spare physical pins. No current board design or active QP target changes; central Context plan owns check results.

Later 2026-09-30 update: the Debug-Trace-20 logical pinout is accepted for planning; see the local observability plan and owning interface contract. Physical connector/cable, driver inhibit and new board allocation remain open. RP2354B is now a candidate to assess against the nearly allocated RP2040 pin map; this does not select an MCU migration or change the active RP2040 QP target.

Direction update, 2026-09-30: the separate optional connector and reusable external RP2040 probe are future-revision evaluation work. This reopens only that planning scope from the dated 2026-09-08 rejection of a second FFC port; retain its historical rationale, the current manufactured-board restrictions and the absence of SWD on the existing board. OBS-PROBE owns external-probe qualification in instrument-control; a second UART bridge is not assumed. OBS-QP now prioritizes RP2040/Earle Philhower Arduino, QP 7.3.4/QV only. Previous Teensy build evidence remains valid within its recorded scope, with Teensy hardware deferred/nonblocking; STM32G0B1CEU6 is later. No RP2040 QP support or new hardware result is established by this target change.

The new board observability plan owns this workstream's detailed queue. The central roadmap owns cross-repository gates and parallel workstreams. Existing dated board decisions and measured bring-up records remain authoritative; this entry records intent and no new hardware validation.

Claim file ownership before parallel edits and reserve shared bench resources through the central session record before measurements. Offline fixture and documentation work can proceed in parallel. Keep current manufactured-board qualification separate from future revision design; do not edit kicad-libs for this documentation task. Report evidence-linked results and limits to the central roadmap owner before marking a gate complete.

Pure agent shell update (2026-10-08)