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:
- [ ] A full-scale brake step overshoots to 62 mA (127 % of 48.78 mA) for
~5 ms and
~{BRAKE_FAULT}never asserts, although 62 mA is above the 56.4 mA ceiling (V_LIMIT0.564 V / 10 Ω). Overshoot begins above ~60 % of full scale (90 %: +26 %, 75 %: +15 %, 60 %: +1 %); the first millisecond slews 8–14 mA/ms at every amplitude, so it is the coil'sL di/dtagainst the drain headroom (coil ≈ 1–1.5 H) and the loop integrator winding up while the current cannot follow the ~2 ms command filter. Decide with a scope onV_SENSE(top ofR1816) andLIMIT_OUTduring a full-scale step: (a) the ceiling amplifier is slower than the excursion, (b) it limits butLIMIT_OUTnever falls belowU1803's threshold, or (c) the monitor overshoots and the coil does not. If (b), the fault indication is blind to exactly the event it was added for (the 2026-09-08 reasoning above). Firmware rampsBRAKEsetpoints meanwhile, so the next spin may also choose to slow the command filter or add anti-windup instead. - [ ] The torque shunt-calibration switch is inert on the as-built board:
TQ_CAL_ENlow / high / low moved the ADS8689 reading by 0.0 codes against a design step of 0.744 V (9522 codes). The 2026-10-04 review inkicad-libs/docs/torque-sensor-interface-design.mdpredicted it: the N-channel enhancement MOSFET's source sits atSIG_N≈ 5 V against a 3.3 V gate. Fix in the block (a P-channel or a level shift) for the next spin; until then calibration is weights on the lever arm (rig plan C3). - [ ] Torque rest reading 69–71 mV below the ideal 1.6667 V zero at
AMP_VOUT(−0.255 N·cm on the nominal scale), 7× the sensor's zero balance. Mechanism preload (the joystick against its stop through the coupling) or front-end offset (VEXC,VMID, INA821): free the joystick by hand and read again, or meterVEXC(10.000 V) andVMID(5.000 V). Not a defect until separated; tare handles it either way. - [x] Confirmed on the wire, for the record: I2C 0x48 and 0x49 both ACK;
brake DAC
GAIN0x0101 reads back,DEVID0x115; ADS8689RANGE_SEL0x0B reads back, SDO loopback patterns clean, noise 95–112 µV rms at 1 kHz; brake 0–48.80 mA linear (full scale 1.0065 V against 1.006 V design, the 12 V gate drive is sufficient); no brake-to-torque coupling at 0.1 mV;ALARM0x0041 at power-up (tripped high-AVDD flag from the+5V_FILTramp) clears on read;DEVICE_ID[15:0]reads 0x0010 where the datasheet says 0 (harmless, noted).
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.
- [ ] Decide whether this is the board's USB path or the firmware's CDC
handling. Board side: compare the four units' USB-C block (
J201, the series resistors, the ESD part) and the+5V_MCUrail under USB enumeration; a unit that enumerates but drops the control transfer that carries the line-coding change points at signal quality. Firmware side:mouse-joystick-firm/.agents/PLANS.mdholds the mirror item (the touch is honoured by the Arduino core's CDC, and the two failing units took it once from the self-test build). - [ ] Until then the firmware README documents the button procedure; a reflash of Rig 1's left board needs hands at the rig.
Completed Task — Session handoff and routing timing (2026-09-21)
- [x] Save a tracked handoff linking the completed PCB, mechanical report and current order package.
- [x] Preserve original run H timing lines: 21 min 43.702 s through optimization, excluding import/refill, local repairs and validation. That historical run used 31 configured threads and the older exporter; a fresh corrected export at one thread is not benchmarked.
- [x] Correct obsolete startup layout status and mark old routing checklists
as historical. The complete board is committed in
d5ae4b0. - [x] Recheck the shunts: board R1203/R1204 are 0.15 ohm, but reusable block R4003/R4004 remain 0.12 ohm. Correct the old checked library item below; preserve the correct board shunts on any future block replacement.
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)
- [x] Preserve the previously routed board and fabrication package under
tmp/connector-access/baseline/andbaseline-fab/. - [x] Move angle BNC J1101 left by 10 mm: 24 mm pitch with J901, common Y, board-relative centres (42, 55) and (66, 55) mm. A 16 mm plug allowance leaves an 8 mm gap; the approximate STEP proxy is not fit evidence.
- [x] Move motor J1102 left by 5 mm and encoder J1103 left by 2 mm. The top terminal row stays aligned, with drawn body gaps of about 4.1/2.6 mm. All connector origins remain on board-relative whole-mm coordinates.
- [x] Reserve a 7 mm outer-copper hardware area around each M3 mounting hole. Hole positions remain on the 90 by 70 mm rectangle. Review USB/RJ45/FFC access; document enclosure-specific cable, latch and lid allowances.
- [x] Repair routing, retain 0.8 mm motor tracks, refill and verify: zero DRC errors/opens, zero ERC errors/warnings, clearance regression passes. The existing warning counts remain 499 clearances and 20 dangling vias.
- [x] Match all 1,204 schematic pad identities; only the three intended footprints moved. All values, rotations and library IDs are unchanged.
- [x] Regenerate
fab/routing-rescue-4layer/, including the fabrication ZIP, 81 resolved BOM groups, 360 placements, mechanical drawing and coordinates.
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)
- [x] Preserve the original six-layer board under
tmp/routing-rescue/baseline/. - [x] Correct DSN plane/window nesting, unfracture filled polygons, constrain
duplicate-pad bridges to existing copper, and convert both
route/fixwith--protect-prerouted. Five regression tests pass. - [x] Import the existing run H and refill: its 100 Freerouting residuals were five real missing connections. Controlled new runs were stopped after this discovery; no completed new exporter benchmark is claimed.
- [x] Repair the five opens, exporter-created BNC shorts, HV clearances and QSPI width. Four layers, zero DRC errors and zero unconnected items.
- [x] Verify all 1,204 schematic numbered-pad identities against the PCB and preserve all 364 footprint positions, rotations, values and library IDs. User permits movement, preferring whole-mm and symmetric connectors; movement proved unnecessary. Project rules and netclasses are unchanged.
- [x] Promote the verified candidate to
kicad/mouse-joystick-pcb.kicad_pcb. - [x] Complete
make fabwith KiCad 10.0.6: ERC 0 errors/0 warnings, DRC 0 errors/0 unconnected pads. All 81 BOM groups resolve, 360 placements match. ZIP integrity and four-layer order verified. The remaining DRC warnings are 499 advisory clearances and 20 dangling vias. Clearance regression passes with no voltage-bearing findings.
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:
- 100 Ω (
OW-PASS-0402WGF1000TCE) on the fourteen clocked taps, 1 kΩ (OW-PASS-RC0402FR-071KL) on the ten slow control and status taps. Never mix values inside one bus — a chip select takes its clock's value. - The resistors go inside the owning design block, not on a wrapper sheet.
The pull-resistor doubling rule does not apply to series elements. The one
exception is
DAC_SDA/DAC_SCL, shared by two DAC blocks, which go on/mcu/. - Board nets take
_INST; block pins take_TAP. Active-low names close the overbar first —~{TMC_CS}_INST, never~{TMC_CS_INST}, or the netclass pattern misses five nets._MONand_ISOare both already taken. - Four nets get nothing —
FW_TRIG0,FW_TRIG1,PG_5V,PG_12Vhave the port as their only consumer.
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.
- [ ] Correct
../kicad-libs'stmc5130shunts to 0.15 Ω before future replacement of this board's driver block. Rechecked 2026-09-21: the library R4003/R4004 still read 0.12 Ω; the old checked item was inaccurate. Board R1203/R1204 correctly read 0.15 Ω and the current order is unaffected. - [x] Add the tap resistors and tap pins to the eight blocks in
../kicad-libs; add the two I2C taps on/mcu/. Eight, not seven: the spec's own table lists eight blocks plus the/mcu/sheet. Pins were named_INST, not_TAP— see As Built. - [x] Re-place the blocks here by hand; re-check
R1203/R1204. - [x] Wire the 24
_INSTnets to the port block'sDIGnpins. Every tap lands on the channel the spec assigns it, verified input-pin to output-pin on all four buffers. - [x] Add the
INSTRUMENTnetclass — 24 explicit assignments plus the/*_INSTpattern, 0.15 mm track and clearance,Default's via geometry unchanged. Proven live: three findings on_INSTnets at 0.1978 mm stopped being violations, including an overbar name. - [x] Verify by netlist export, not ERC. Each
_INSTnet has exactly two nodes; each functional net kept its bias and no longer reaches a buffer. - [x] Re-check designator citations across the docs. The
Jband was reassigned —interface-map.md's connector table is rewritten as built, and the pyControl RX pull-up isR401invalidation-plan.md,rev0-plan.mdandpower.md.bringup-log.mdis deliberately untouched.
Historical pre-routing findings (see completed routing above for disposition; remaining one-layer vias are recorded warnings, not opens):
- [ ] Two pairs of exactly stacked vias at (129.175, 84.5) on
GNDand (127.425, 84.5) on+36V_SW—holes_co_located, delete one of each pair. - [ ] Fifteen one-layer vias inside
rp2040_with_gpio_isolation_sn74cb3q3245(GPIO2-GPIO8,10-13,15,18,20,22). Redundant stubs, not breaks — those nets report no unconnected items. - [ ] Fifteen voltage-bearing clearance errors; see the severity split
recorded in
kicad/mouse-joystick-pcb.kicad_dru.
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:
- Locking tracks is harmful.
(type fix)cost 157 already-connected pins. - A track keepout over a GND plane kills fanout — zero vias placed.
- Without a keepout the plane gets cut — 48 non-GND segments on In1.Cu.
- Enlarging the board and moving blocks did not help — same ~60% rate, more work to do because inter-block copper was destroyed.
- The router used one layer out of four (+415 segments on F.Cu, +7 on a 96%-empty B.Cu). Default via cost 50 against trace cost 1.0–1.8. Tuning this has not been tried and is the largest untried lever.
- Never export with
zones=0. 51 of 323 GND terminals are exposed-pad thermal-via duplicates that only a pour can tie together.
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:
- [ ] Pour GND. There are no copper zones at all. 40 of the 149 unrouted
connections are
GND; 71 of 149 are power/ground distribution. Decide at the same time whether an inner layer becomes a power plane. - [ ] Re-run
make clearance-baselineafter pouring. The current baseline was taken with zero zones, so a post-route diff against it would be meaningless. - [ ] Fix four voltage-bearing clearances —
+36V_SW↔+5V_SW0.25 mm,+36V_SW↔PG_5V0.2919 mm ×2,+36V_SW↔PG_12V0.35 mm, and+36V_SW↔VCP0.353 mm ×2 (that last is ~10 V real differential, low priority). All routed copper, all movable. - [ ] Delete the duplicate via —
holes_co_located, two+36V_SWvias at the same point. - [ ] Hand-route HV entry, motor phases, bootstrap/
VCP, USB before the autorouter. Freerouting sees netclasses but none of the.kicad_drurules. - [ ] Autoroute the rest with
make freeroute, threads at 1, thenmake clearance-checkand read the diff.
Not blocking, but cheapest now:
- [ ] Add the two PPTC test points at
R303— a fitted-versus-jumpered polyfuse is otherwise undiagnosable, and pads are free before routing. - [ ] Decide
J1103pin 2 / encoder index:ENCNstays inside thetmc5130block and that pin is unconnected. - [ ] Regenerate
docs/interface-map.md→#### mcu Sheet Pinsfrom the schematic instead of maintaining it by hand.
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.
- [x] Document the migration and the v1.0 adoption/fallback release gate.
- [x] Replace the default-disabled recommendation for the new port, retaining the old pull-up/OE description as legacy hardware evidence.
- [x] Cross-link the old allocation and validation docs and check consistency.
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)
- [x] Confirm the schematic, PCB title block, and front silkscreen identify v0.2.
- [x] Confirm
J1901uses the correctedorb_weaver_connectors:RJ45_Molex_43860-0001_Horizontalfootprint. - [x] Generate the managed JLCPCB fabrication package with ERC and DRC enabled.
- [x] Verify the four copper layers, drill outputs, BOM/placement coverage, and Gerber-only upload archive.
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:
- Build
env:mouse_joystick_rev1for a v0.3 board, notenv:mouse_joystick. v0.3'sU501pads 13/14/16 routeGP10/GP11/GP13to the angle-output DAC, so v0.3 isMJ_BOARD_REV=1.platformio.inicalls that env "the rev 1 board (pcb HEAD)", which reads as if no fabricated board matches it. Bothplatformio.iniandsrc/BoardPins.hppin../mouse-joystick-firmstill say rev 0 covers "v0.1 and v0.2" and rev 1 is "HEAD"; neither mentions v0.3. Worth one comment edit before someone flashes the wrong env at a live SPI bus. - Designators rotated again, and the self test prints v0.1 names. The mapping
for every part stage 1 touches is tabulated in the bring-up log. The one that
matters most: the
LATCHstrap isR1903on v0.3, notR203. - Nothing in the angle-output list below is reachable on USB alone. The
DAC80501andOPA197sit on+5V_SW, so the whole headline characterisation needs 36 V connected. Stage 1 is the entire USB-only surface.
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
- [x] 1. CLOSED 2026-09-10 WITHOUT THE OneBox RECORDING — the ÷2 dividers are
GO on all three blocks. The user cannot take an OneBox recording now, so
the decision was made on data in hand: both channels are limited by
something other than the converter, and both arguments are
radius-independent. Angle is encoder-limited by 14.9× — one
encoder count (0.0900°) is 2.279 mV post-divider, ≈15 OneBox codes.
Torque is sensor-limited by 1.63× — the RTS-5's 0.05%
nonrepeatability is 3.3 codes pre-divider and still 1.63 post. So no
recording can change the answer, and the alternative (a 3.3 V precision
RRIO buffer) is a new part plus vendor mapping plus lead time against a
v1.0 the whole scope narrowing exists to get ordered. The ÷1 fallback
needs no extra land: both divider positions are populated 3.2 kΩ, so
reverting after rig testing is 0 Ω on top and omit the bottom — a BOM value
change, not a respin, at the cost of the 3.3 V single-fault bound.
Researcher input the same day, which confirmed rather than changed it:
peak pull force ~20 mN to ~140 mN, reward threshold a 5 mm pull,
lever arm 75-76 mm rig-measured (old-rig CAD says 72-73 mm). So 5 mm is
3.77°-3.82° ≈ 42 encoder counts, 5.9%-6.0% of the 64.174° swing, and
the mouse's whole force range sits at 4.3%-30% of the torque span.
CLOSED 2026-09-11 — the peak-pull distribution arrived and the angle
mapping stays full-swing. 1,319 successful trials: min 6.23 mm, median
12.15 mm, p95 14.41, p99 16.12, max 21.21 mm, SD 1.50 mm (CV 12%), i.e.
4.73°-16.10° or 7.4%-25.1% of the 64.174° swing. Narrowing buys nothing
because the encoder is the quantizer, not the OneBox: one count is
0.1186 mm of pull and the full-swing mapping already gives the OneBox 14.7
codes per count, so a 20 mm map's 2.08 extra bits sit below the encoder
floor. The trial-to-trial SD is 12.6 encoder counts wide. And the
researcher's own max (21.21 mm) exceeds the 20 mm they proposed, so a
narrowed map clips real pull peaks. Full derivation and the arithmetic:
docs/encoder-analog-output.md→ CLOSED 2026-09-11. Nothing further is owed from outside on this board. Original status follows. ~~HALF-SETTLED 2026-09-10. The span half is closed; the noise half needs the OneBox.~~ The gain/offset fit came out unity to within 0.03%, so there is no span error to preserve and the ÷2 divider costs exactly the predicted one bit and nothing more. What remains is strictly: is the recorded trace already OneBox-quantisation-limited with margin? That is a noise measurement at the 19 µV RMS level, which no scope on this bench can see — it needs the OneBox itself or another 16-bit capture. The op-amp purchase decision is still open, and still gated on the researcher question below rather than on anything measurable here. Original text: ~~Does the analog output span matter? — decides an op-amp purchase.~~ v1.0 halves every board-exiting analog output with a ÷2 divider so nothing can exceed 3.3 V on a pyControl pin (docs/onebox-analog-interface.md). That costs exactly one bit. The criterion is whether the recorded trace is already OneBox-quantization-limited with margin to spare. Record a static, homed, motor-idle angle trace at full span and look at the width of the value histogram. Predicted from the documented figures: board noise 19 µV RMS against theAD7616-P's ~44 µV RMS quantization, so the converter should dominate by about 2.3×. If it does, halving the span is undetectable and the divider wins. If the trace is much wider than a couple of codes, span is precious and a 3.3 V RRIO op amp is worth buying.- Angle is expected to be encoder-limited by a wide margin regardless: 713 counts over the 64.174° swing is 2.3 mV per count on a 1.650 V span against a 152.6 µV OneBox step, so the OneBox already resolves 15× finer than the encoder can distinguish.
- [~] 1b — the -0.19% figure is LOAD-DEPENDENT, noted 2026-09-10. That number assumes a 1 MΩ load, which the OneBox has. A 10 MΩ meter sees only -0.02%, about 0.34 mV on 1.650 V, which is at the Fluke's resolution and lost in drift — so measuring it with a meter alone tests the wrong thing. Either load the BNC with 1 MΩ to emulate the OneBox and then insert the 1.8 kΩ, or accept it as arithmetic: a -0.2% fixed gain error is fully absorbed by per-board calibration, and the Johnson noise of 1.85 kΩ is ~5.5 nV/√Hz. Leaning toward the arithmetic. Original text: ~~Validate the divider's source impedance without touching the board.~~ Put 1.8 kΩ in series in the BNC cable and confirm the DC reading moves only by the predicted −0.19% and the noise does not change. That is the only thing about the final ÷2 topology (1.85 kΩ source versus today's 249 Ω) the OneBox can tell apart.
- [~] 1c — NO gross motor coupling, measured 2026-09-10. Static setpoint at
1.650 V, scope AC-coupled at 10 mV/div on the BNC: 3.54 mV pk-pk with the
driver off, 3.55 mV with the chopper running, no increase under jogs or
under 6 fast approaches, and the same value with the BNC disconnected —
so that 3.5 mV is the scope's own floor. Bounds board noise plus coupling
at roughly < 200 µV RMS, about 4x the
AD7616-P's quantisation. A scope cannot reach the 19 µV prediction — its LSB is 312 µV at 10 mV/div and 31 µV even at 1 mV/div — so measurement 1's noise half needs the OneBox and nothing else will do. The chopper frequency was NOT swept; this is the default operating point only. Original text: ~~Check nothing else dominates first. Sweep the motor chopper~~ frequency while recording the angle channel. TheAD7616-P's antialias filter is first-order at ~39 kHz against fixed 30.3 kHz sampling, so out-of-band content folds in nearly unattenuated. A chopper-correlated artifact would be far larger than one bit and makes the span question academic until fixed. - [x] 2. DONE 2026-09-10 —
+36V_SWnever exceeded 37 V, so NO TVS is needed for the motor-driven case. Single-shot armed at 37 V, 1 V above nominal, through 7 hard-stop decelerations at 0.200 rev/s plus 6 reversals atIRUN17 = 0.421 A RMS, and two staircase turn-ons. Never triggered. That is 12 V of margin to the TMC5130A's 49 V inductive-load maximum, and it also bounds+36V_SWripple under 1 V. Matches the energy prediction (200 µF holds 130 mJ against ~2.2 mJ of coil energy, so ~0.4 V worst case). Data indocs/bringup-log.md-> MEASUREMENT 2 CLOSED. Does not cover supply-side events — the probe was downstream of the high-side switch, and hot-plug plus cable-inductance ringing at the input is what the TMC5130A datasheet's ringing warning is actually about. The entry TVS position and the driver's RC snubber footprint both exist, so that remains a populate decision.SMBJ40Aif ever wanted; stockedSMF48Acovers first boards.
The rest of the angle-output characterisation
This is the headline reason v0.3 was ordered.
- [ ]
GAINCANNOT BE READ BACK. Rewritten 2026-09-09 — the original text asked for something the hardware cannot do. TheDAC80501has no SDO pin: the VSSOP-10 pinout is VDD, VOUT, NC, AGND, SPI2C, SCLK, SYNC, SDIN, NC, VREFIO,SDINis typed Input in SPI mode, and "SDO" appears nowhere in the datasheet. Over SPI the part is strictly write-only. (TheGP12/SDOnote further down this file is the torque bridge's ADC, a different chip.) So:VREFIO= 2.500 V is a DMM measurement at the pin, not a firmware check. Still worth taking.GAIN=0x0101can only be verified by measuring the output: write a known code, measure the BNC, compare against the expected full scale. That is a DMM task at bring-up, not a register read.- The safety point stands and gets sharper.
GAINresets to0x0001— REF-DIV 0, BUFF-GAIN 1, which is a 5.0 V full scale on a 5 V VDD. The board needs 2.5 V full scale (×1.32 → 3.3 V at the BNC). Since theZvariant resets the DAC register to zero scale, the output is safe until data is written — so writingGAINbefore any DAC data is mandatory, and the driver must enforce that ordering itself rather than trust the caller. Note0x0000(REF-DIV 0, BUFF-GAIN 0) also gives 2.5 V full scale with less gain-up noise;0x0101is what this plan specified and there may be a v1.0 3.3 V headroom reason for it.
- [x] DECIDED 2026-09-10: v1.0 straps the DAC to I2C. Firmware is ready —
env:mouse_joystick_rev1_i2cin../mouse-joystick-firmbuilds the I2C path today, and both examples select the transport with-DMJ_DAC_I2C=1. It cannot be tested on any board that exists: on v0.3SPI2C(U2001pad 5) is tied directly to GND in copper, no strap resistor, so v0.3 is SPI-only without cutting a trace. Three reasons, and the bandwidth objection is now measured away:STATUS.REF-ALARMis unreadable over SPI. A reference alarm drives the output to 0 V while leaving the DAC codes intact, so it is indistinguishable from a legitimate zero.docs/encoder-analog-output.mdrequiresSTATUSto report the output invalid in that case, and SPI cannot.- The 4.5 mV floor measured 2026-09-10 makes the analog level a weak backup. It was supposed to be ~25 mV. The gap between a firmware fault and a dead output section is now 4.5 mV, halving to 2.25 mV at v1.0.
- Bandwidth does not bind. I2C at 1 MHz adds ~33 µs to a measured
527 µs worst-case angle loop — about 6%. See the 1 kHz loop result in
docs/bringup-log.md. Two DACs, ONE bus, no second peripheral needed.A0selects four addresses by what it is tied to: AGND 0x48, VDD 0x49, SDA 0x4A, SCL 0x4B. The angle output takes 0x48 and the brake takes 0x49, leaving two spare. It also saves 2 GPIOs — I2C needs SDA + SCL where SPI needs SCLK + SDIN + two separate chip selects — which matters on a revision also adding the torque bridge'sSDO,TQ_CONVST_CS,TQ_CAL_ENand~{BRAKE_FAULT}.
- [~] DESIGN BLOCK SPEC for the I2C strap — BRAKE BLOCK DONE 2026-09-10,
analog block still owed.
brake_current_sink_0_49ma_dac80501's schematic is complete and netlist-verified:SPI2CandA0each strapped with a 1 kΩ to+3.3V_FILT(I2C mode, address 0x49), the~{CS}port deleted,SCL/SDAexposed with no termination, andSDA's port shape changed tobidirectional. The three 10 kΩ SPI-idle resistors were deleted — that was missing from the original spec and is the part that would have broken the bus. No DNP parts, by the user's preference. Full as-built detail in../kicad-libs/docs/brake-current-sink-design.md→ Strapped To I2C.analog_output_0_3v3_dac80501is unchanged so far and takes 0x48, matching the firmware's existingkI2cAddrA0Agndselection. Original spec follows. Bothanalog_output_0_3v3_dac80501andbrake_current_sink_0_49ma_dac80501need it, and those two are the complete set — no other block contains a DAC80501. MODIFY IN PLACE. Do not fork SPI and I2C variants. Decided 2026-09-10. Neither block has an installed base to protect: the analog one is already on the re-place list above for the DAC-to-3.3 V change, and the brake one has never been placed. v0.3's SPI configuration is preserved in git at thev0.3tag and in the fabricated boards, so a live SPI block is not needed to reproduce it. Forking would double maintenance across ~57 and ~24 components over a four-pin difference, name the blocks on the wrong axis (this repo names by function + range + part, not interface), and invite placing the wrong variant. A dual-mode block was considered and rejected: pin 7's net changes from a hierarchical port to a static tie, which cannot cleanly be a populate option, and keeping the port either way makes the block ambiguous about what it is. BUNDLE the I2C change with the already-planned DAC-to-3.3 V change on the analog block, so it is re-placed ONCE. Two re-placements means two rounds of designator churn and two LCSC/Vendor-SKU scrubs —rp2040took 50 field issues from a two-part change. The brake block's DAC is already on+3.3V_FILT(checked atU36001pad 1), so it needs only the transport change, no rail move, and its bus is level-compatible with the RP2040 immediately. Five changes, both blocks:SPI2C(pin 5): GND -> the local VDD rail. Currently a hard copper tie to GND.SYNC/A0(pin 7) stops being a chip select and becomes a static address strap. Drop the~{DAC_CS}hierarchical port. Expose the address as a selectable strap inside the block — two 0402 positions,A0->AGND andA0->VDD, populate exactly one — so the same block can be placed twice at different addresses. Do NOT hard-wire one address.SCLK/SDIN(pins 6, 8) becomeSCL/SDA; rename the block's hierarchical ports to match.- PULL-UPS MUST BE BOARD-LEVEL, NOT IN THE BLOCK. Two instances each
carrying pull-ups would put them in parallel and halve the resistance.
Same class of trap as two
PWR_FLAGs on one net. The block exposesSCL/SDAwith no pull-ups; the board fits one pair. - Pull-ups reference the ALWAYS-ON 3.3 V rail, on the MCU side of the
bus switch — not the switched side, or they are dead whenever the
rail is off. Convenient that v1.0 already moves the DAC to 3.3 V, which
makes the bus level-compatible with the RP2040 with no translation.
Open question worth thinking about: open-drain I2C through the
SN74CB3Q3245bus switch. The CB3Q is a bidirectional FET switch and passes I2C fine, but the pull-up placement above is what makes the switch-open state sane. Verify on v1.0 hardware rather than assuming. This is new, it is a hardware change, and it is small.docs/encoder-analog-output.mdstates that "STATUSmust report the output as invalid" when the output is 0 V for either of two reasons — an unpowered output section, or a reference alarm from a misconfiguredREF-DIV. Both look exactly like a legitimate end of travel in a researcher's recorded dataset. Over SPI, firmware cannot detect the reference alarm at all, so that stated requirement is unimplementable as built. The unpowered case is inferable fromSW_RAIL_SENSE_DIV_2; the alarm is not. TheDACx0501does support register readback — includingDEVID,GAINandSTATUS.REF-ALARM— but only in I2C mode, selected by theSPI2Cstrap pin at power-up. The cost is bandwidth, and it does not bind: this design's update rate is already capped near 1 kHz by the 987 Hz filter's 266 µs group delay and the encoder-read loop, while I2C fast mode puts a 24-bit frame at roughly 30 µs. So I2C buys the alarm visibility the design doc asks for and costs nothing that matters. Change is a strap plus two pull-ups plus a different peripheral.
- [x] DONE 2026-09-10 on board 4, and the fit is essentially unity. Fluke 87
on DC volts at the BNC, four codes: 0 -> 4.5 mV, 993 -> 50.2 mV,
32768 -> 1.650 V, 65535 -> 3.300 V. One model fits all four —
unity gain to within 0.03%, +0.2 mV offset, hard clamp at 4.5 mV — and
every residual is inside the meter's resolution. Span is 1.650 V against
1.650 expected, so there is no per-board gain to calibrate out. The
warning that the fit "must be redone rather than rescaled" at v1.0 still
holds but costs almost nothing, because the only per-board number is the
clamp. Data in
docs/bringup-log.md-> ANGLE OUTPUT CHARACTERISED. This also half-settles measurement 1 below: there is no span error to preserve, so the ÷2 divider costs exactly the predicted one bit and nothing more. The remaining half is a noise question and needs a recording, not a meter. - [x] The output floor is 4.5 mV, NOT ~25 mV — and that breaks the
three-level out-of-band scheme. Measured 2026-09-10. The valid-range gap
improves (45.5 mV instead of 25), but the gap between a firmware fault
and a dead output section collapses from 25 mV to 4.5 mV, halving
again to 2.25 mV at v1.0. Both hardware failures read as a legitimate end
of travel, and the doc's mitigation is
STATUSreporting the output invalid — which over SPI cannot see a reference alarm. So this strengthens the I2C strap case below.- [ ] Decide: drive code 497 = 25 mV as the fault level instead of code 0. Firmware-only, free, restores the 25 mV separations deliberately, and survives the v1.0 halving at 12.5 mV (82 OneBox codes). Not applied — it changes what recorded data means.
- [ ] Correct
docs/encoder-analog-output.md, which asserts a ~25 mV floor and builds the out-of-band scheme on it. The 25 mV figure is measured wrong by 5.6x.
- [~] Partly answered 2026-09-10 without a dedicated capture. The live
1 kHz loop showed the filter needs ~2 ms to settle a step while
updates arrive every 1 ms, which is why the staircase is visible at all —
so the settling figure is corroborated even though the 266 µs group delay
itself was not timed against a trigger reference. A proper measurement
still wants
FW_TRIG1on a second scope channel, and the InstrumentPort FFC is occupied by the LA5032. Original text: ~~Verify the 266 µs filter group delay of the 2-pole 987 Hz Sallen-Key. Any latency measured throughAIN0includes it and it is not firmware latency. - [x] DONE 2026-09-10 —
ANGLE_OUT_MONreads 1.650 V, the same as the BNC. Fluke 87 onJ1501pin 32 against pin 31/33 GND, with the output parked at midscale. Topology traced in copper too:R20081 kΩ tapsAMP_VOUT, the op-amp side of the 249 ΩR2007, so the monitor reads the true output regardless of what loads the BNC — which would have caught the 50 Ω termination error immediately. Original text: ~~ConfirmANGLE_OUT_MONonJ1501pin 32 tracksANGLE_OUT. At v1.0 the monitor tap moves to the divider midpoint and the two read the same voltage, which removes a scale factor from the capture.
Carried-forward measurements this board can finally take
- [ ]
~{HV_FAULT}andHV_SNSneedGP14raised first. Firmware must assertHV_DIAG_ENbefore either reads anything at all. Without it neither works, and that has bitten before. - [ ]
+3.3V_MCUback-power check. Measure it with 36 V applied and both USB and pyControl unplugged. TheSN74LVC125Ahas noIoffand TI does not specify its leakage atVCC= 0, so this is unspecified rather than guaranteed. - [x] Encoder filter value — closed on paper 2026-09-08, no scope needed.
The fitted part is an E4T-1000: 1000 CPR, so
f_ch = 1000 × RPM/60, and the 470 pFC802/C803corner of 44.9 kHz is reached only at 2694 RPM at the motor shaft. Bring-up ranVMAX= 0.100 rev/s = 6 RPM (100 Hz), so the margin is ~450× and even a ten-fold faster axis stays clear. The encoder's own ceiling binds first anyway — max A/B frequency 100 kHz, and module max RPM = 6×10⁶/CPR = 6000 RPM at 1000 CPR. 470 pF is correct; do not re-derive. ScopingENC_A_FILTis now optional confirmation, not a gate. - [x] DONE 2026-09-09 —
IRUN17 wins, on case temperature alone. Taken on boardE4654C159F4D1334with a thermal camera, 66 min of continuous energisation atIRUN17 driven by repeatedgcycles: motor can plateaus at 43.3 °C in a ~25 °C room (+18 °C rise), TMC5130A at 45.5 °C,ot/otpwclear on every poll, no follow failures in ~1300 cycles.IRUN24 scales the rise by (0.600/0.421)² = 2.03 to ~62 °C in the same room, over the 60 °C case maximum, so it is rejected without a rebuild. Electrical duty was ~100%, not the 72% of motion time:cs_actualpolled 15 withstst= 1, because firmware never writesTPOWERDOWN. Real use is low duty (command, depower, wait, rehome), so this bounds rather than represents — the exception being a hold-against-force mode, still open with the researcher. Coil current was not measured (no clamp on the bench); the thermal half is decisive on its own. Data, method and three caveats indocs/bringup-log.md→ Thermal Survey. Original text below. ~~Coil current and motor case temperature — settles IRUN 17 versus 24-25, and does NOT need v0.3.~~ The motor and driver path are unchanged from v0.2 and two v0.2 boards are in hand, so this can be taken today. Measure coil current at IRUN 17 and read case temperature after a representative duty cycle. Copper loss is 2.31 W at 0.421 A and 4.68 W at 0.600 A (both phases, 6.5 Ω/phase) in an 82 g NEMA 8 whose datasheet case maximum is 60 °C (140 °F) against a 50 °C ambient maximum — so this is a real constraint, not a formality. That 60 °C figure is inconsistent with the Class B (130 °C) winding insulation quoted on the same datasheet page; worth a question to Lin Engineering as well as a thermocouple. This measurement is also what decides whether the shunt can move to a larger shunt at a later revision — see the Required item for which one, and note it is not the 0.33 Ω an earlier draft named. - [x] LATCH baseline — DONE 2026-09-09 on board 4, FAIL as expected. Cold,
the rail holds only 52-73 ms of 250 ms and
~{HV_FAULT}latches low.R1903is fitted, so v0.3 behaves exactly like v0.2 and the firmware staircase stays required. Stage 2 also passed in full on that board the same day: rail up in 132 ms, bus closed, TMC5130AVERSION= 0x11. Caution — the probe could report a false PASS, and did once: run while+36V_SW's 208 µF was still charged it presents the switch with no inrush.+36V_SWneeds 691 ms to bleed. Fixed in firmware; details indocs/bringup-log.md→ LATCH baseline. - [ ] Travel: set both hard stops deliberately and record them, rather than
measuring whichever position they were left in. Reframed 2026-09-09: the
stops on both joysticks are movable, both mechanisms have roughly
60° of range, and the gearing is 1:1 on both, so joystick degrees
and motor degrees are the same number. The 28° figure carried through this
repo and through firmware's
kMeasuredTravelMicrosteps= 4000 is a stop setting, not a mechanism property — seedocs/bringup-log.md→ The 28 deg travel figure is a hard-stop setting, not a mechanism property. The left's 64.174° full swing is consistent with ~60°. - [x] Hand-check both mechanisms — done 2026-09-09, neither is jammed. Both joysticks were reconnected and sit mid-travel; ~1300 motion cycles ran on the right with no follow failures.
- [ ] Hand-check the left mechanism's swing before commanding any motion —
it may have been left jammed on 2026-09-03. See
docs/bringup-log.md, "OPEN: the mechanism may have been left jammed". - [ ] Record which measurements were impractical for lack of a test point.
There are none anywhere on the board, and the 36 V diagnosis had to be done
by energy accounting because reading
+36V_SWmeans clipping ontoC1213's can pads. That list is the v1.0 test-point requirement.
The question hardware cannot answer
- [ ] Ask the researcher which box is the primary analog record. The OneBox is 16-bit over ±5 V, 152.6 µV per code, so a 1.650 V span is 13.4 bits and the ÷2 divider is free. pyControl's own ADC is 12-bit on a 3.3 V reference, 806 µV per code, so the same span is 11 bits and the divider hurts. This is the single input that decides whether to buy a 3.3 V RRIO op amp, and no measurement on v0.3 substitutes for it.
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:
rp2040+rp2040_gpio_isolation_sn74cb3q3245are replaced by the singlerp2040_with_gpio_isolation_sn74cb3q3245, built 2026-09-16, placed.- The 40-pin DUT boundary and all four
SN74LVC125Abuffers are replaced byinstrument_port_digital_50_dut_ffc_boundary_vertical— 32 channels, not 18, allocated indocs/interface-map.md.GP24was freed and immediately re-spent onHV_LATCH, which the bench had owed a pin since 2026-09-02. - Both DAC blocks are I2C (0x48 analog, 0x49 brake), not SPI, which freed
GP13and moved the torque ADC ontoSPI1. hv_to_12v_tpsm365r6joins the placement list — new to this board, and both the brake and torque blocks need+12V.- The PPTC is chosen:
1206L035/16YR, LCSCC126817, to be purchased. - Nine new board-level biases are owed, listed in
docs/interface-map.md→ Board-Level Input Biases. The I2C pull-ups are 4.7 kΩ, not 2.2 kΩ.
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
- The debug connector and SWD pads. No probe on hand; buys nothing today.
- Dropping
U603/U604— area/cost optimisation, not a defect. - The 1206 PPTC for
R303— the fitted jumper works; sizing unresolved. PYC_FAULT/EXT_FAULT_IN/~{RIG_FAULT}— already deferred below.
Kept despite the narrowing, and why
- Test points. They are pads: no BOM lines, no parts, no cost, and they
are what makes v1.0 testable — the whole point of ordering it. v0.2's 36 V
diagnosis had to be done by energy accounting because reading
+36V_SWmeans clipping ontoC1213's can pads. Decide schematic-symbol versus layout-only before layout, as below. ~{BRAKE_FAULT}— the one genuine judgement call, ~2 parts (SN74LVC2G17, 990 in stock, plus a 100 nF). It is not a feature for the experiment, it is instrumentation for the v1.0 test: without it a brake current-ceiling event is completely invisible to both the MCU and the capture, so if the limiter fires during v1.0 bring-up you would never know. Cutting it is defensible; cutting it means accepting that blind spot on first hardware.
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
- [ ] Size
R1203/R1204after placingtmc5130— the block default is wrong on purpose. (They areR4003/R4004inside the library block.) VALUE CLOSED 2026-09-09: 0.15 Ω withvsense= 1, and the 0.22 Ω 2512 branch below is dead. Taking 0.22 Ω neededIRUNsettled at 17 and a reduction above ~1.7:1.IRUN17 is settled (see the thermal result in the v0.3 section above), but the gearing is 1:1 on both joysticks, so the sweep would need motor overcurrent the thermal result rules out. No 2512 land change in the block or this board. The action that remains is only the swap from the block's 0.12 Ω placeholder at placement time — which is exactly the silent failure this item exists to prevent. Decided 2026-09-08. The library ships 0.12 Ω (OW-PASS-KRL1632E-M-R120-F-T5) as a placeholder because the correct value is a property of the motor. This board fits 0.15 Ω (OW-PASS-RLH12FTCR150) withvsense= 1, which is what both v0.2 units already carry and read back. Forgetting the swap is silent — no ERC, no DRC, no fault bit — and with firmware writing IRUN 17 a 0.12 Ω fit puts 0.511 A RMS, +21.4% into the coils. Record the shunt value, thevsensebit andIRUNin one place; they are one decision. Why 0.15 Ω, against the whole stocked field (derivation in thekicad-libsplan →tmc5130shunt policy): it is the only value/vsensepair that keeps both candidate operating points inside the CS range — 0.421 A on CS 17 and 0.600 A on CS 24, step 23.4 mA — and the choice between those two is still open. 0.22 Ω/vsense= 1 cannot reach 0.600 A at all; 0.33 Ω/vsense= 1 cannot reach either. It is also Trinamic's own worked example for the low-current branch ("0R15: 1.1 A peak"). If you ever move off 0.15 Ω, the destination is 0.22 Ω 2512 (JER2512F3R220, 500 in stock, 3 W, ±50 ppm) withvsense= 0 — and it is a mixed trade, not an upgrade. Gains: 131 mV of sense signal against 89 mV (+47%), tempco ±50 ppm/°C against the 0.15 Ω's ±100 ppm, and 1.60× headroom, the most of any stocked option. Costs: step coarsens 23.4 → 29.9 mA, the operating point drops from CS 17 to CS 13, and it is a 2512 land change in both the block and this board, whereBRA/BRBare routed tight per the TMC pin table. Take it only if the thermal result settles IRUN at 17 and the gear count comes back above ~1.7:1 so the calibration sweep needs no motor overcurrent. Corrected 2026-09-08: an earlier draft named 0.33 Ω here. It is at zero stock (OW-PASS-73WL4R330F,C2121350— catalogued and orderable,stock_total = 0) and on re-derivation it is worse than the 0.22 Ω anyway on tempco (±200 ppm), power rating (750 mW) and headroom (1.09×). Do not queue it as a purchase.0.36 Ω1206 is stocked but is a ±600 ppm/°C 250 mW thick-film chip resistor whose rating is exceeded at full scale — it is the polyfuse-position part, not a shunt. NoteV_SRTL/V_SRTHare typ-only in rev 1.21, so absolute current accuracy is unspecified whatever is fitted; this is a resolution-and-ceiling choice, not an accuracy one. - [x] Corrected 2026-09-08 —
docs/rev0-plan.mdis now clean. Seven edits, more than the fiveIRUNsites, because the root cause was upstream of them: the doc's stated "0.67 A per phase" motor rating was an assumption, and Open Decisions item 1 explicitly ruled out theWO-208-13-01Dthat turned out to be fitted. Every derived figure was re-computed rather than rescaled —IRUN28 → 17, peak sense voltage 163 → 101 mV (89 mV across the 0.15 Ω itself), peak dissipation 135 → 53 mW, thevsense= 0 hazard 1.23 A → 0.760 A, and the self-limiting claim "12% above the motor rating" → 25%. Two findings recorded while fixing it: 0.760 A exceeds thevsense= 1 ceiling of 0.749 A, so the wrong bit defeats the hardware limit outright; and the old "fix the library down to 0.15 Ω" instruction was reversed in place to "never re-placetmc5130". Original text below kept for the record. ~~Correct the staleIRUN = 28— this is the more dangerous half of the shunt problem, and it is in the docs, not the hardware.~~ Scope corrected 2026-09-08: only this repo'sdocs/rev0-plan.mdis stale.../mouse-joystick-firm's AGENTS.md was already fixed on 2026-09-02 — it now reads "IRUN <= 17(0.421 A RMS) until someone measures a coil current", names the WO-208-13-01D, and records that bring-up runs atIRUN = 8. Firmware is correct and already enforces the conservative reading.docs/rev0-plan.mdstill carriesIRUN = 28in five places (lines ~249, 251, 276, 525, 605), which on 0.15 Ω is 0.679 A RMS — 1.61× the conservative reading of the motor's 0.6 A/phase and 1.13× even the equal-heating reading. Fix those five, noting 24-25 as the alternative pending the thermal measurement. - [ ]
LATCHto a GPIO — the blocking defect.R19031 kΩ still straps theTPS1HTC100-Q1'sLATCHto+3.3V_MCU, selecting latch-off, which prevents the 36 V rail from ever coming up. The library block already exposesLATCHas a port, so this is a re-place plus one wire to a spare GPIO. Full reasoning and the rejected alternatives are in the v0.4 section below, which stays for that reasoning. - [ ] Re-placing that block also brings three fixes worth knowing about:
SNSgains a 1 kΩ + 10 nF filter behind the 249 ΩR_SNS(today the raw node goes straight intoGP28unfiltered),~{FAULT}gains a 10 kΩ RPROT, and theDIAG_ENRPROT goes 1 kΩ → 10 kΩ. - [ ] Move
~{HV_FAULT}offGP29. It is an open-drain digital input sitting on ADC3, and ADC pins are the board's scarce resource:GP26is rail sense,GP28isHV_SNS, andGP27/ADC1 is the only free one. Moving~{HV_FAULT}to a spare digital pin frees ADC3 forBRAKE_ISENSEand leavesGP27available. Costs nothing. - [ ] Add
hv_to_12v_tpsm365r6on the switched domain. Needed by the torque block, and now also by the brake's loop amplifier. Place its switching loop physically remote from the torque bridge front end — it exists to feed that block, which makes adjacency tempting. - [ ] Place
torque_bridge_0_3v3_ina821. Board-side obligations: the sensor BNC and theTQ_AOUTBNC are not in the block, and the ESD clamp's ground via must land on the BNC's ground pin or its clamping is dominated by connection inductance. Three GPIOs:GP12(SPI1 RX) forSDO, one forTQ_CONVST_CS, one forTQ_CAL_EN— the last is not optional in the sense that matters, since the in-block pull-down makes an unconnected port safe but leaves the shunt-cal circuit inert. - [ ] Place
brake_current_sink_0_49ma_dac80501. The coil terminal is in-block.BRAKE_ISENSEgoes to the freed ADC and toAIN1_P. Give the pass device as close to 6 cm² of drain copper as the zone allows, on the side away from the torque zone. - [ ] Wire
~{BRAKE_FAULT}back to the RP2040. Decided 2026-09-08, and the point is to buy information: monitor the brake's current-ceiling output on real hardware and let that decide whether the deferred inter-board~{RIG_FAULT}line is worth building. Without it there is no evidence either way. The brake block gains theSN74LVC2G17buffer and the port (see thekicad-libsplan); the board owes three things:- One GPIO, routed through the isolation bus switch (
sw), because the far end is powered from the switched domain — that is the rule, not a convenience. It arrives already at 3.3 V logic, so it needs a route but not anSN74LVC125Achannel. - An MCU-side termination. The buffer has
Ioff, so with+3.3V_FILTdown its output is high-Z and the isolated MCU-side pin floats. A 10 kΩ pull-down to GND is the right choice: per the domain rule, a pull that may read LOW goes straight to GND rather than to+3.3V_SW. The consequence is that the line reads asserted whenever the switched rail is off, which is the fail-safe direction — but firmware must therefore only interpret it whileSW_RAIL_SENSEsays the rail is up. Record it with the other nine board-level terminations. DIG12on the InstrumentPort, already allocated to~{BRAKE_FAULT}indocs/interface-map.md, so the capture sees the same edge the MCU does. Record the GPIO indocs/interface-map.md. Budget still fits: eight digital spares afterGP12goes to the torqueSDO, against six consumers —HV_LATCH,TQ_CONVST_CS,TQ_CAL_EN,BRAKE_CS, the relocated~{HV_FAULT}, and~{BRAKE_FAULT}— leaving two spares plusGP27/ADC1.
- One GPIO, routed through the isolation bus switch (
- [ ] INVERTED 2026-09-10 by the I2C change — this now says the opposite.
The DAC blocks' three 10 kΩ SPI-idle resistors are deleted, so the
board owns every bus termination. Fit two 2.2 kΩ pull-up pairs, one per
bus segment: the DAC-side pair referenced to the switched 3.3 V rail,
the MCU-side pair to
+3.3V_MCU. Not one always-on pair — an always-on pull-up reaching a DAC whoseVDDis at 0 V exceeds theDAC80501'sVDD + 0.3 Vdigital-input absolute maximum and back-feeds the block's+3.3V_FILTthrough the internal clamps. And the switch is open by default (~{BUS_SW_EN}=GP9, held isolated byR6017.87 kΩ), so a single MCU-side pair would leave the peripheral segment floating into two live DACs and three poweredSN74LVC125Amonitor inputs. Closed bus is then 1.1 kΩ, right for the 1 MHz FM+ the loop budget assumes; the DAC sinks 3 mA in FM+ so 1.5 mA/line is fine. ~~Do not add board-level pull-downs onSCKorSDI. Both DAC blocks already own one each, so a board carrying both already has 5 kΩ.~~ - [ ] Move the InstrumentPort DAC-bus monitor taps to the B (MCU) side of
U602. TodayU2101's threeSN74LVC125Amonitor inputs sit on the A (peripheral) side —U2101.2on~{DAC_CS},.5onDAC_SPI_SCK,.9onDAC_SPI_COPI— where the blocks' own 10 kΩ pulls used to hold them defined. With those gone and the bus switch open by default, powered LVC125 inputs would float. The B side is held by the always-on pull-up pair, and it is the waveform you actually want to decode. - [ ] Going to I2C frees one
U2101channel (3 of 4 used → 2 of 4) and two GPIOs. Record the two I2C pins and the freed channel indocs/interface-map.md, and drop the~{DAC_CS_BUF}row. - [ ] Add the
RUNreset switch — restored to Required 2026-09-08, with evidence. Lands via therp2040block change. Do it independently of the debug connector, which stays cut. The failure it fixes has already happened once, 2026-09-03:docs/bringup-log.md→ "STATUShung the core". The board stopped answering the link and stopped honouring the 1200 baud reset touch, so it could not be reflashed. Recovery neededSW501held through a power cycle with both USB and the RJ-45 unplugged — pulling USB alone leaves the board alive on pyControl's 5 V through the ORing diodes. With aRUNswitch that becomes: hold BOOTSEL, tapRUN, reflash. No cables touched, and no disturbing a rig where two interchangeable boards sit on ports 1 and 4 and calibration belongs to a board serial. Why once is enough to justify it: the recovery cost is structural, not a bug — it follows from the ORing diodes and the deliberately unswitchable MCU domain, so every future hang costs the same four steps. v1.0 also roughly doubles the surface for that exact bug class: theSTATUShang was reaching for SPI before the bus was up, and v1.0 adds theADS8689to sharedSPI1with an ordering constraint (first write after power-up must useSPI-00-S) and a mode that must match theDAC80501's falling-edge capture. And the log's own lesson is that this class is untestable in the shared corpus by construction, so it will always be found on hardware. Cost is lower than "one part":SW501is alreadyC7185056as a single-designator BOM line, so a second instance increments that line's quantity rather than adding a BOM line — nothing new for JLCPCB to source or place, no new footprint (orb_weaver_smd:SMT_1045822_WRE), no new symbol, 115 in stock. RP2040RUNhas an internal 50-80 kΩ pull-up so no external pull-up is needed. It is also the only cut item that cannot be added later without a respin. - [ ] Firmware, paired with the above: enable the RP2040 hardware watchdog.
Recorded in
../mouse-joystick-firm/.agents/PLANS.md. It covers the complementary case — a transient hang self-recovers unattended, in the rig, with no button — and theSTATUShang would have auto-recovered. They are complementary, not substitutes, and this is the part to get right: a watchdog reset does NOT enter BOOTSEL. Flash something reliably broken and the watchdog gives a reset loop that you still cannot reflash out of, so it makes that case harder to escape.RUN+ BOOTSEL is the escape hatch for "I just flashed something broken"; the watchdog is for "it wedged mid-session". Ship both. - [ ] Debug connector: land the pads, and treat the probe as a separate
purchase. Established 2026-09-08: there is no SWD probe on hand for
either candidate footprint, and no Tag-Connect or 1.27 mm 2×05
Cortex-Debug pattern exists in official
kicad-footprints10.0.6 or the local libraries, so the footprint must be authored either way. The probe is not a board problem —debugprobefirmware turns any spare RP2040 board into a CMSIS-DAP probe that OpenOCD and probe-rs speak, so SWD capability is a ~$4 purchase, not a respin. Land the pads regardless; they are close to free and they are the only route to a hard-fault post-mortem. Prefer Tag-Connect TC2030-style (pads plus three NPTH, zero BOM lines, so no per-board cost and nothing for JLCPCB to place) over the SMD header, which is a new part and an order. - [x] An on-board debug MCU is rejected — decided 2026-09-08. Recorded so it is not re-proposed. See Why an on-board debug MCU was rejected below.
- [ ] Test points. Decide before layout whether they are schematic
symbols (so the netlist carries them) or layout-only pads. The list:
+36V_SW,+5V_SW,+3.3V_SW,+3.3V_MCU,+5V_MCU, both sides ofR303,TMC_DIAG0,TMC_DIAG1,~{TMC_DRV_EN},HV_EN,HV_SNS,REFL,~{INSTR_BUF_OE},ENC_A,ENC_B,LIMIT_SW, plus in-blockVREFIO,AMP_VOUTandAOUT, andTQ_DRDYonce torque is in. - [ ] Netclasses for the new analog nets. Torque:
SIG_P,SIG_N,IN_P,IN_N,VEXC,VMID,INA_OUT,ATT_TAP,FILT_IN,AMP_VOUT,TQ_AOUT. Brake:V_SET,V_SENSE,V_LIMIT,BRAKE_ISENSE, the gate node and the coil nets. None of them may fall through toDefault, and re-check that no net matches more than one pattern. - [ ] Fix the title block. Every sheet still reads "Orb Weaver Design Block
Mining" and carries the mining project's
comment 1.revis already1.0. This is the fabrication drawing. - [ ] Board outline. 100 × 80 mm with 177 footprints today. The brake zone (36 passives, three SOT-23-5, a VSSOP-10, a DFN2020, two SOD-123FL, an X1SON, two ferrites, a 2-position vertical terminal, no electrolytic), the torque zone (57 parts) and the 12 V module are a large increase. Leave pads for an RC snubber from the brake pass-device drain to GND.
Decide, then do
- [ ] The pyControl event lines
GP20/GP22/GP23. The researcher will likely derive pull events from the analog or UART record instead, in which case all three come out along with theJ8/J9BNCs planned for them — real board area next to the new zones. Not forced by the pin budget: moving~{HV_FAULT}off ADC3 already resolves the squeeze. Confirm with the researcher, then remove them for the right reason. - [ ]
U603andU604are entirely unused andU602uses 1 of 8 channels. Three QFN-20s and three bypass caps for seven signals. The cheapest moment to drop two of them is before placement, by movingTMC_DIAG1to one ofU601's free channels. Note the old counter-argument is dead: the unused channels have their MCU-side pins tied to GND, so nothing is pre-wired. - [ ]
R303is a 0.36 Ω jumper in a 1206 polyfuse position on the pyControl 5 V branch. No PPTC exists in the parts DB. Size against v1.0's load, not rev 0's ~100 mA. Port 5 V is a shared 1.0 A that also feeds the controller's own logic regulator with no per-port fuse, so a fault here can reset the experiment controller. Add test points either side or a tripped fuse cannot be diagnosed in circuit. - [ ] TVS at the 36 V entry — populate decision, from v0.3 measurement 2.
- [ ] Encoder chain has no ESD device, contrary to
docs/design-block-map.md. The 4.7 kΩ series and 470 pF give some protection and theSN74LVC2G17has internal clamps. Decide and record. - [ ] Locking motor connector. Screw terminals on a rig that gets handled; deferred once already. Decide before the harness is rebuilt, not after.
- [ ] Silkscreen a unit identifier. Two interchangeable boards in one rig is a failure mode, and calibration belongs to a board serial rather than a joystick position.
- [ ] The bus switches run from
+3.3V_MCU, whereas theSN74CB3Q3245part note says to power them from the switched rail soIoffprovides isolation atVCC= 0. As built, isolation depends on the MCU holding~{BUS_SW_EN}high, whichR601makes the reset default. Reconcile the note or the rail. - [x]
~{BRAKE_FAULT}— decided 2026-09-08: it goes to a GPIO in v1.0. Moved up to Required below.
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:
- The observe half already exists and is better than a debug MCU would be.
Eleven live InstrumentPort Strands, an LA5032 with 32 channels, and
agent-drivable captures. The 2026-09-03 encoder confirmation — raw quadrature
against the driver's
X_ENCto 3-8 counts — is exactly the class of question a debug MCU would be bought for, and the port answered it. - The domain-state half already exists too. The UART protocol's
status,limitsandidreport the state that actually matters, while the system runs. A halted core cannot tell you why a follow check tripped. - Breakpoints are close to useless on this system. Halting mid-move leaves
the motor energised at a commanded position, the brake in an arbitrary state
and 36 V up. Every firmware problem hit so far — stall detection, follow-check
sensitivity, the
mechanical_stop_firsthoming fault, theENC_CONSTsign — is dynamic, and none is visible at a halt. - The register state that matters is not in the RP2040.
GSTAT,DRV_STATUS,IOINandCHOPCONFlive in the TMC5130A and are read over SPI regardless;IHOLD_IRUNis write-only, so no debugger can read it back. - Reflashing is already solved. The firmware self test has a
Bcommand that reboots into BOOTSEL, so the usual headline benefit of SWD is largely already covered; theRUNswitch closes the hung-or-unprogrammed remainder. - A stimulus path conflicts with a documented safety constraint.
docs/instrument-strategy.md→ Safety With Animals Present makes observe-only a deliberate property: the buffers are unidirectional,AOUT0/AOUT1are no-connect, and anything adding a drive path into the board "must be treated as a bench-only configuration and must not be the normal experiment arrangement". An on-board debug MCU is a permanently populated drive path, which is the arrangement that rule forbids. - What survives the rejection is worth keeping. The one real capability gap is
hardware-in-the-loop stimulus — faking encoder quadrature, asserting the
limit switch, driving fault lines to test firmware response automatically. If
that is ever wanted, build it the way this project already builds instrument
interfaces: a separate bench-only adapter board that plugs into an FFC port,
a sibling of
m2k_instrument_port_adapterandla5032_instrument_port_adapter, physically absent during animal work. Not an on-board part. Do not design it now — the InstrumentPort's capture side has not yet failed to answer a question.
Deferred to a later revision — do not design now
- [ ]
PYC_FAULT/EXT_FAULT_IN/ any hard-wired fault signal. Decided 2026-09-08: test v1.0 and the new blocks first, and add this only if the rig shows it is needed.~{BRAKE_FAULT}on a GPIO is the experiment that informs this — if a monitored ceiling event turns out to be frequent or consequential, that is the evidence for building a rig-level line; if it never fires in normal use, the deferral becomes permanent. What was established while looking, so it does not have to be re-derived:- No free RJ-45 pin can carry it on this breakout and these ports. Rig 1
is a Breakout 1.2 (
b = Breakout_1_2()), boards on ports 1 and 4. Read off../kicad-libs/tmp/pyControl_breakout_1_2r1_schematic.pdf: pins 3 and 7 arePOW-xA/POW-xB,TBD62083APGsink-driver outputs on every port; pin 5 isPOW-1Con port 1 (another driver output) andDAC-2on port 4 (pyboardX6/PA5, an analog DAC output); pin 8 is unconnected on 1.2. Pins 1 and 4 are the UART, 2 is ground, 6 is 5 V. The pin-5 table indocs/pycontrol-port-pinout.mdis the H7 breakout's and does not apply to this rig. - Breakout 1.2 has only two BNCs, and
BNC-2is the same net as port 6'sDIO-6B, so using it forecloses port 6. - The controller is not who needs the wire. The safety rule wants one
board's fault to stop the other, which is a board-to-board link: a single
shared active-low open-drain
~{RIG_FAULT}on a 2-position 3.5 mm terminal, pulled up to+3.3V_MCU(not+3.3V_SW), costs one GPIO because the same pin reads the wired-OR while high-Z, and replaces bothPYC_FAULTandEXT_FAULT_IN. A wired-OR cannot detect a dead board, so hardware would handle fault propagation and the UART heartbeat liveness. - If the controller must see it instead, port 2 is the least-bad donor:
no UART, nothing assigned, and
DIO-2A/DIO-2Bare two independent MCU GPIOs, so one spare port can carry both boards' fault lines via a Y-harness.
- No free RJ-45 pin can carry it on this breakout and these ports. Rig 1
is a Breakout 1.2 (
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.
- [x]
DAC80501ZDGSR— closed, the concern was stale. This bullet said "50 in stock… covers exactly 25 boards with no rework margin". Stock is 98, so it is 49 boards at 2 per board. - [x]
OPA197IDBVR— closed, no purchase needed. 7 per board confirmed by count (3 brake, 3 torque, 1 angle — the old "~2 torque" was low), against 298 in stock = 42 boards. The queued "100-150" is unnecessary. - [x]
RT0402BRD073K2L3.2 kΩ 0.1% / 25 ppm — closed. 1980 in stock against 6 per board for the three ÷2 dividers. - [x]
USBLC6-2P6— closed. The 2026-08-14 handoff warned "stock is 20… two per board caps the build at ten boards". Stock is now 1002. - [ ] A 1206 PPTC for
R303/R1903— the only genuinely pending purchase, and it is blocked on a spec rather than on money. Confirmed 2026-09-08: there is no fuse, polyfuse or PPTC anywhere in the catalog — zero hits on all three terms. Size the hold current against v1.0's load, not rev 0's ~100 mA, then create the internal part, vendor mapping, symbol and footprint. Buying before sizing is how the wrong hold current gets ordered. - [ ] Do not buy the TVS yet.
SMBJ40Ais not in the catalog; the stockedSMF48A(120) covers first boards. The populate decision comes from v0.3 measurement 2, so ordering now is buying ahead of the data that decides whether it is needed. - [ ] Do not buy the 3.3 V RRIO op amp yet — gated on v0.3 measurement 1 plus the researcher's answer. Nothing stocked qualifies.
- [ ] If the build is 25 boards rather than 5, top up four parts first.
ADS8689IPWR($4.19),INA821IDR($2.49) andREF5010AIDR($1.21) all sit at exactly 50, andDAC80501ZDGSR($5.02) at 98 — a 25-board run consumes half of each with no rework margin, and these are the lines with real lead-time exposure. At 5 boards, order nothing. - [x] Both parts are identified — closed 2026-09-08, resolved 2026-09-02.
This bullet was stale.
docs/bringup-log.mdidentifies the motor as a 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) and the encoder as a US Digital E4T-1000 single-ended (1000 CPR, 4000 quadrature counts/rev, 5 V push-pull), CPR bench-confirmed against 51200 usteps/rev. Datasheets indocs/datasheets/and../kicad-libs/tmp/. What the motor number did not settle is whether 0.6 A is RMS or peak — Lin does not say — so that sub-question survives as the IRUN 17 versus 24-25 choice, and only a thermal measurement closes it.
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 theLATCHfix, the energy accounting behind the 108 µF ceiling, and theswap_lrand 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:
- Tie
LATCHlow permanently. Works — proved it by depopulatingR203— but the part then auto-retries every 2 ms forever into a genuine fault, cycling the motor rail at ~500 Hz.docs/power.mdis right that a joystick twitching in an animal's paw is unacceptable. Fine as a bench configuration, wrong as a shipped one. - Halve the bulk instead. The switch delivers ~70 mJ before tripping, so
½CV² = 70 mJ at 36 V puts the one-burst ceiling at ~108 µF — depopulating
C1214lands on break-even, not under it. Real margin needs ≤ 50-60 µF, and that bulk is what absorbs motor inductive turn-off on a rail with 49 V of driver headroom and no TVS. Do not trade a protection for a convenience. - Raise
I_CL. FET energy is ½CV² regardless of the limit; the time is ∝ 1/I, so peak junction rise goes as roughly ½VI·Zθ(t) ∝ √I. A higher limit is hotter. Separately, checkR206still leaves motion headroom once the motor is known — 0.679 A RMS/phase draws an estimated 0.2-0.7 A from 36 V depending on winding resistance, against a 1.15 A limit.
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
- TVS footprint on
HV_IN— still a populate decision, still no evidence either way.docs/power.md→ Transient Headroom. - 1206 PPTC on the pyControl 5 V branch (
R1903is a jumper today), plus test points either side so a tripped fuse is diagnosable.
How To Check A New Revision For The Fix
One command. Flash mouse-joystick-firm examples/TMC5130Communication and
send l:
[FAIL] this board still needs the workaround→LATCHis still strapped. Nothing to do;PowerSequence::powerUp()uses the firmware staircase automatically.[PASS] LATCH fix is present→ a plainHV_ENassertion works. Then build with-DMJ_HAS_LATCH_GPIO=1 -DMJ_LATCH_GPIO=<n>, reading<n>off that revision's netlist — do not guess it,src/BoardPins.hpphas an#errorthat enforces this — andpowerUp()switches to the direct path and arms latch-off after the ramp.[!!] NO 36 V INPUT→ the rail never moved and the switch never reported. BelowV_S,UVLOR(5.0-5.75 V) the part does not operate, so this is an absent supply, not a tripped switch. CheckJ201is seated and the supply is on;J201pin 1 isGND, pin 2 is positive, and reversed leads are blocked byD201and look identical to this.
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:
../mouse-joysticks-pcb— the twin-axis board for the same rig; most of this repository's documentation derives from it../orb-weaver-context— cross-project context and the PCB development plan../board-interfaces— InstrumentPort-40 and, later, the pyControl connector profile../kicad-libs— symbols, footprints, database libraries, design blocks, vendor mappings, KiCad helper tooling../tmc51x0-driver-dev-pcb— the validated RP2040 + TMC5130A predecessor../mouse-joysticks-mech— the new-rig mechanical assembly, two joysticks~/Repositories/github/syringe-pump— pyControl module reference design~/Repositories/kicad/mouse-joystick-pcb— previous-generation prior art
Direction History
- 2026-08-10:
mouse-joystick-pcbcreated as one PCB per joystick, RP2040. - 2026-08-11: renamed to
mouse-joysticks-pcband rescoped to one board for both joysticks; Rev 0 descoped back to RP2040 to be buildable now. - 2026-08-12: this repository created to carry the single-axis design in parallel, because it can be finished sooner. 36 V input instead of 24 V, and nothing descoped for pin budget. Both repositories are live.
Resolved
- Rev 0 scope, 2026-08-13, amended 2026-08-16. Interfaces plus the means to
observe them; DAC and event lines deferred to rev 1, PIO encoder tap deleted.
docs/rev0-plan.md. - RP2040 GPIO allocation. Rev 0 connects 18 of 30 and reserves 8 more at
fixed numbers; full scope is 26 of 30. Spares are now four —
GP15,GP16,GP18,GP27;GP14was consumed byHV_DIAG_ENon 2026-08-17. No pin moves between Rev 0 and rev 1.TMC_REF_STIMwas deleted permanently and its pin pays for the bus-switch enable onGP9.docs/interface-map.md. - Two power domains, 2026-08-13. The high-side switch is a reset button:
everything resettable is on
+36V_SW→+5V_SW→+3.3V_SW, and the MCU is on+5V_MCU→+3.3V_MCUfed from USBVBUSor pyControl pin 6 through two ORing diodes. The board does not boot on 36 V alone, accepted. Exactly two5v_to_3v3_tps73733instances, one per domain.docs/power.md. - GPIO isolation rule. A GPIO goes through the bus switch if and only if its far end is on the switched domain — 7 channels in Rev 0, 12 at full scope. The pyControl UART bypasses, so the board stays reachable with the motor rail off. The oversized 4-package block is accepted as-is for schedule.
- pyControl pin 6 is 5 V, confirmed from the syringe-pump module netlist. Its current budget is still unmeasured and is a Gate A item.
- InstrumentPort-40 is in Rev 0, re-allocated 2026-08-16. Moved back in for
firmware development: the port carries both directions of the pyControl serial
link tapped at the RJ-45, the TMC5130A SPI bus, the two firmware triggers, and
ENC_A/ENC_B/LIMIT_SW— 11 of 18 digital Strands live. The other 7 are allocated and unrouted, 4 reserved for the rev 2 brake and torque sensor so those circuits inherit a pinout instead of renegotiating one. The overlay now follows the profile's own Preferred Digital Overlays table: TMC bus on the SPI slots, pyControl serial on the first UART pair, brake PWM and fault on the PWM/fault slots, torque front end on the I2C slots. - REVISED 2026-09-11 — the eviction is now a choice, not a forced move, and
the strand budget needs settling before v1.0 layout. The I2C transport
change cut both ways: it freed
DIG6(~DAC_CS) and dissolvedDIG11'sBRAKE_CSallocation, since I2C has no chip selects — but it also took the torque ADC'sSCK/SDIoff strands they used to share with the DAC bus, becauseSPI1now serves theADS8689alone. v1.0 tallies to 17 or 19 of 18 strands depending on whether those two get port visibility. Decide it explicitly here rather than discovering it at routing. User's decision, 2026-09-11: the TMC5130 has proven itself on encoder handling, soENC_A/ENC_Bare reconfirming a verified circuit and the strands are better spent verifying new ones. Recommended split: keepLIMIT_SW— homing is the field failure mode and the switch is a wearing mechanical part whose failure the driver's own registers cannot contradict — and giveENC_A/ENC_Btest pads instead of strands. If more are needed,TQ_CAL_ENis a static enable rather than an edge worth a channel, and the ADC'sSDIcarries only bring-up configuration writes. Reopened 2026-09-12: retaining InstrumentPort-40 v0.3 for v1.0 is now the explicit schedule fallback, not the only planned outcome. The new direction is 50 pins / 32 digital channels / 18 grounds with integrated, hardware-default-enabled buffers; it supersedes the earlier 30-signal / 10-return digital-40 proposal. Seedocs/digital-instrument-port-migration.mdfor the adoption gate. Neither the new board overlay nor hardware is done. Beacon correction: retain the current 64-bit serial payload; adding a profile-version field is not firmware-only compatibility. Record the profile with captures until a separately versioned frame/decoder extension exists. LegacyFW_TRIG0=GP25,FW_TRIG1=GP21reachTRIG0/TRIG1throughU1801and bypass the bus switch. Keep both marker channels in the new first 16-channel bank and retain the plannedFW_TRIGtest pads. Original legacy allocation rationale follows. DIG0,DIG1, andDIG11are swap slots.ENC_A,ENC_B, andLIMIT_SWhold three of the rev 2 brake/torque slots through Rev 0 and rev 1 and hand them over at rev 2. They earn the space because none of those signals reaches a GPIO in any revision, so the TMC5130A's own registers are firmware's entire account of position and homing and nothing can contradict them — capturing the raw signals beside the SPI traffic is the only check. These slots were chosen because they sit in the rev 2 buffer package, making the eviction as late as possible.ENC_Nstays on a test pad.- No new boundary pins on
pycontrol_behavior_port_rj45_boundary. An earlier draft called for two, so the port could tap the connector side ofR29001/R29002. Rejected 2026-08-16: 330 µA through 100 Ω is 33 mV, so a digital channel cannot tell the two sides apart, and the connector-side tap would put the buffer input on the unprotected side of the ESD clamp. The port uses the existingPYC_DIO_A/PYC_DIO_Bpins at five boundary pins. - But change those two pins from
passivetobidirectional. It is the only connector boundary block in the library with no driving pin —usb2andinstrument_port_40_dut_ffc_boundaryboth use bidirectional for logic — and left passive,PYC_UART_RXreaches the board with no driver on the net. - Signal directions are now in the allocation table, as a
Dircolumn, and everymcusheet pin is listed with its KiCad shape and what it mates with. TheDIO-A→ RX /DIO-B→ TX crossover is drawn explicitly; it is the one place on this board where the naming reverses.~{BUS_SW_EN}is the only Rev 0 connected GPIO needing nomcusheet pin — the bus switch is inside that sheet. GP24~{INSTR_BUF_OE}is now live. No pin moved.- ~~Four 100 kΩ pull-downs plus the
SW_RAIL_SENSEdivider are the only new passives.~~ Superseded 2026-08-17. As built there are nine 10 kΩ termination resistors plus the divider, and the 100 kΩ value would have been wrong on the TMC SPI lines — see Open Schematic Findings. Buffer packages are three (U1601,U1701,U1801);U_CandU_Dwere never placed and do not exist. The spare twelfth channel has its input tied to GND, but its output stops atR1805and does not reachDIG12. - It costs layout schedule — a 40-pin FFC, three buffer footprints, and 18 more routed nets on the board whose layout was already the long pole.
docs/interface-map.md,docs/instrument-strategy.md.- Event-line routing. Three lines on BNC, two dual jacks plus one. Rev 1.
- The pyControl port pinout, from the syringe pump. Cited, not bench-verified.
- Current sense, inherited: 0.15 Ω,
vsense = 1, ~~IRUN = 28~~IRUN = 17(0.421 A RMS), corrected 2026-09-08. 28 predates the motor being identified and is above its rating on either reading of 0.6 A/phase; 24-25 is the alternative if the thermal measurement supports the equal-heating reading. - Supply voltage: 36 V, using the existing
hv_input_36V_terminal_2pos_5p08_protectedblock. - TVS on
HV_IN, for the switch and the buck — not for the driver. A standard TVS cannot clamp under the TMC5130A's 49 V on a 36 V rail, but the switch (70 V) and the buck (65 V) are the parts exposed at hot-plug, and they are clampable.SMBJ40Apreferred, stockedSMF48Ausable now. The driver is protected by construction — bulk downstream of the soft-start switch, capped at tens of µF, tightVSloop — and verified at Gate B. As built, no TVS is populated (confirmed by netlist, 2026-08-17); it stays a Gate B measurement rather than a fabrication blocker. - The 36 V input block is clean. The six edits were verified by netlist review
on 2026-08-12 —
ENdivider,DIAG_EN,LATCH,ILIM= 40 kΩ,SNS= 249 Ω, and the~FAULTroute. The three defects below were fixed and verified in the block on 2026-08-16:R27005now pulls~{FAULT}up to+3.3V_MCU, andHV_INandSNSare hierarchical. Kept for the record only — historically,~FAULTwas pulled down toGNDinstead of up to+3V3_MCU, andHV_INandSNSare local labels rather than hierarchical, so the board can reach neither. Plus two cheap concerns —C27002's 50 V rating on a 36 V rail, and the misleadingVAAnet name. The+3V3_MCUnaming concern was withdrawn; naming the rail by its domain is correct. All indocs/power.md; fix before the block's PCB layout is finished. - Multi-source logic power, superseded 2026-08-13. Now two sources only —
USB
VBUSand pyControl pin 6 — ORed at+5V_MCU, upstream of the MCU domain's own LDO. The onboard buck is not one of them; it lives on the switched domain. The two 5 V rails are separate nets, which is what keeps+5V_SWin spec for the TMC'sVCC.
Blocking Items
Two remain, and only the second can actually invalidate the board. Ranked.
- 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 thetmc5130block's bulk-capacitance question, restated here so it survives a clone: the block ships 200 µF onVM(C1213+C1214, 2× 100 µF 50 V,OW-PASS-EEEHAH101UAP, D8×10.2 mm) sitting on switched+36V_SW, wheredocs/power.mdbounds bulk at "tens of µF, not hundreds". 200 µF at 36 V is 130 mJ perHV_ENturn-on into an HTSSOP-14. Unresolved. Options: depopulateC1214(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:
- DAC8563
VIH— deferred with the DAC to rev 1. - Where
+3.3V_MCUcomes from — USB or pyControl only, two diodes, no always-on converter onHV_IN. - The TMC's switched
VCC_IO— it is on+3.3V_SWfrom the second LDO, so all three TMC supplies fall together and the pin-34 ordering holds by construction. D3, the pyControl 5 V branch — populated. It is one of only two sources that power the board, so it is no longer conditional.EXT_FAULT_INonGP27— rev 1, since Rev 0 has noFAULToutput to cross-wire.- The regulator feed point — post-switch, deliberately.
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
-
TMC_DIAG1crossed the isolation barrier onto a hard ground. Therp2040_gpio_isolation_sn74cb3q3245block cross-mapped its A and B labels on the two middle switches. Fixed inkicad-libsand re-placed. All 32 channels were then audited: the pairing on the RGY-20 package isA_n= pin (n+1),B_n= pin (19−n), every used pair matches, unused A pins are open and unused B pins are tied to GND. -
Netclass patterns matched none of this board's rails. 146 of 156 nets fell to
Default(0.2 mm / 0.2 mm), including/+36V_SW, both 5 V rails, both 3.3 V rails, all four motor phases and theBRA/BRBsense returns. The inherited patterns target+24V,VMand/tmc5130_spi/OA*, none of which exist here, and*/HV_INwas silently matchingPWR_HV_LOCALat 0.25 mm — worse than falling through. Added:
| 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.
-
DIAG_ENmoved from a fixed+3.3V_MCUpull-up toGP14. This is the subtlest defect found in the whole review anddocs/power.mdhad analysedDIAG_ENat length without catching it. HoldingDIAG_ENhigh keeps the TPS1HTC100's OFF-state open-load detection permanently enabled, which switches in an internal 150 kΩ pull-up fromVStoVOUT(ROL_OFF110–166 kΩ, datasheet §7.3.4.2). At 36 V that injects ~240 µA into+36V_SW, which carries ~208 µF, so the rail creeps at ~1.2 V/s and crosses the TPSM365R6's 3.6 V minimumVINin about 3 s. The buck then starts, collapses, and hiccups — so the switched domain never cleanly powers off, the "HV_ENpower-cycles every resettable chip" property is broken, andSW_RAIL_SENSEreads a fluctuating value.DIAG_ENhas an internal pull-down, so withGP14high-Z at reset diagnostics are off and+36V_SWsits at true zero. Current limit and thermal shutdown are unaffected (Table 7-1); only reporting is gated, so firmware must raiseGP14before it can read current sense or faults. A 10–15 kΩ bleeder on+36V_SWwas the considered alternative. Note this interacts with Blocking Item 1: halving the bulk by depopulatingC1214would double the creep rate, not remove it. -
Nine board-level termination resistors added. All in board-authored sheets (
mcu,motor_encoder_limit,instrument_port), so a design-block re-place cannot lose them.
| 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:
- Pulls that must read HIGH reference
+3.3V_SW, never+3.3V_MCU. Referencing the always-on rail back-powers+3.3V_SWto roughly 1.3 V through the TMC's protection diodes — defeating theHV_ENpower-cycle and corruptingSW_RAIL_SENSE, and parking the buffer inputs at ~2.0 V, exactly theSN74LVC125A'sVIH. This was built the wrong way once and corrected. +3.3V_SWis held at 0 V when off byR401/R402(2 kΩ to GND), which is what makes a pull-up to that rail a valid LOW in the off state. If theSW_RAIL_SENSEdivider is ever removed, four of these resistors stop defining their nets.- Do not use 100 kΩ pull-downs on the TMC SPI lines, which is what
docs/interface-map.mdused to prescribe. The TMC'sCSN/SCK/SDI/SDOare(tpu)withRPU= 132/166/200 kΩ; 100 kΩ against that parks the node at ~1.9 V, straddling the buffer's 2.0 VVIHand leaving chip select ambiguous. 10 kΩ gives 0.28 V, a clean LOW. ~{TMC_DRV_EN}neededR1103even thoughDRV_ENNhas an internal pull-up. Once firmware enables the bus switch, the RP2040's reset-state internal pull-down (RPD50–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 onVS.R1103makes the worst case 2.75 V against a 2.31 VV_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) |
-
AGNDwas a floating four-pin island —J1501pins 31/34/37/40 connected to nothing, and theinstrument_portsheet had noAGNDpin so it never left the sub-block. Now tied toGND. -
PMC_UART_RX_BUF/PMC_UART_TX_BUFtypo fixed toPYC_. -
GP1pull-up fitted (R403). Note the reason is idle level, not floating: every RP2040 GPIO resets with its internal pull-down enabled, which on a UART RX line reads as a permanent break, and the net leaves the board through the RJ-45 where nothing drives it when unplugged. That is also why the bus-switch B side needs no termination at all — the MCU's own pull-downs define it.
Verified correct — do not re-derive
- Power architecture.
+5V_MCUis ORed from USB (D301) and pyControl (D1901) only;hv_to_5v_tpsm365r6takes+36V_SW; four distinct rails, one source each, and the two 5 V rails never meet. All three diodes verified oriented correctly (symbol pin 1 is the cathode):HV_RAW→HV_IN,VBUS→+5V_MCU,PYC_5V_FUSED→+5V_MCU. - The pyControl crossover.
PYC_DIO_A→PYC_UART_RX→GP1,PYC_DIO_B→PYC_UART_TX→GP0, withTXanOutputandRXanInputon themcusheet.PYC_DIO_A/_Barebidirectionalin both the library block and the placed copy. - InstrumentPort mapping — all eleven Rev 0 Strands land where
docs/interface-map.mdsays. - TMC5130A strapping, every pin checked against the datasheet:
TST_MODE,SD_MODEandSWSELto GND;SPI_MODEleft open on its internal pull-up;ENCNtied to GND throughR120110 kΩ exactly as the datasheet requires when unused;N.C.pins 6/31/36 to GND as required;DNCpins open;VCP,CPI/CPO,BRA/BRBand5VOUTcapacitors all per the pin table.R1203/R1204are 0.15 Ω. R207= 249 Ω onHV_SNSis correct and needs no series resistor.ISNSFLTis a 5.2–6.4 mA current source, so a fault reads 1.3–1.6 V — safely under the RP2040'sVPINabsolute maximum ofIOVDD+ 0.5 = 3.8 V, and clearly distinct from the ≤0.37 V maximum normal-current reading.VSNSFHis 3.3/3.95/4.4 V atVDIAG_EN= 3.3 V, so the 1 kΩ thatdocs/power.mdoriginally specified would have drivenGP28past its absolute maximum. Safe upper bound is about 594 Ω.- Capacitor voltage ratings, all ≤80%. Two notes:
OW-CAP-100N-0402-X5R-16Vdeclares onlyReqMinVoltage = 16Vbut resolves to LCSC C162178, a 100 V X5R 0402, so the TMC'sVSbypass capsC1209/C1210are rated by vendor mapping rather than by requirement — worth switching to a part whose requirement states the rail voltage.C1213/C1214are 100 µF 50 V on 36 V (72%), which with no TVS fitted is a bench-measurement item. R6017.87 kΩ pulls~{BUS_SW_EN}high to+3.3V_MCU, so the bus switches default to isolated at reset.R1601/R1701/R180110 kΩ pull~{INSTR_BUF_OE}high, implementing exactly what theSN74LVC125Adatasheet requires: "to ensure the high-impedance state during power up or power down, OE should be tied to VCC through a pull-up resistor".- 13 of the 14 placed design-block copies are byte-identical to their library
sources. The exception is
tmc5130, and the board is the correct one — see "Carry into layout" below.
Checked and explicitly not defects — do not chase these
X1201pin 1 (Tri-State) left open. The ECS-2520S pad table states "High or NC → Active". Datasheet-sanctioned. An earlier review called this a defect; it was wrong.RUN,SWCLKandSWDunconnected. RP2040RUNhas an internal 50–80 kΩ pull-up and the datasheet permits leaving it open or tying it toIOVDD, so this is an access limitation, not an electrical defect. Deferred to a future board revision by explicit decision, 2026-08-17. The cost is no debugger during bring-up and BOOTSEL recovery requiring both USB and pyControl unplugged.~{TMC_DRV_EN}relies onDRV_ENN's internal pull-up for its fail-safe direction — "the power stage becomes switched off when this pin becomes driven to a high level".R1103now backs that up in hardware.U901BOOT/SWunconnected (bootstrap capacitor is internal);U1201SPI_MODEfloating (internal pull-up = SPI, matchingSD_MODE=0 andSWSEL=0);U1201AIN_IREFfloating (GCONF.I_scale_analogresets to 0);R1805.2dangling (spare 12th buffer channel, input grounded);J1102pin 2 (reserved index, NC-flagged);J301SBU1/SBU2;J1901pins 3/5/7/8; the TMCDNCpins; spare RP2040 GPIOs; the LDONRpins.- No MCU-side termination on the bus switch. Every RP2040 GPIO resets with its internal pull-down enabled, and the switch's A/B ports are FET pass gates with no logic input, so nothing floats there.
~OEis not a substitute for terminating a buffer input.ΔICCis 500 µA typ / 5 mA max per partially biased input, independent ofOE, against 40 µA max for the whole device with valid levels. This was proposed and rejected.
Carry into layout — decisions, not blockers
- [ ]
U603andU604are entirely unused;U602uses 1 of 8 channels. Three QFN-20 packages plus three decoupling capacitors of board area for seven signals. The cheapest moment to drop them is before placement, by movingTMC_DIAG1onto one ofU601's two free channels. Note the counter-argument no longer holds: the unused channels have their MCU-side pins tied to GND, so the reserved rev-1 DAC channels are not pre-wired and rev 1 must edit the block regardless. - [ ] Re-placing
tmc5130reverts the shunt — reset them by hand afterwards. Rewritten 2026-09-08; the previous instruction ("fix the library down to 0.15 Ω") is reversed and must not be reinstated. The board hasR1203/R1204= 0.15 Ω andkicad-libsnow ships 0.12 Ω deliberately, as a per-motor placeholder — see that repo's plan →tmc5130shunt policy. So a re-place will silently revert them, and the fix is on this side: set them back to 0.15 Ω (OW-PASS-RLH12FTCR150) and confirmvsense= 1. The old "25% over the intended 0.679 A RMS" figure is wrong twice over: the overshoot is +21.4% and 0.679 A is a superseded driver setting, not the motor's rating. Correct reference point is 0.421 A RMS at IRUN 17. - [ ] Placement intent lives in the library blocks'
.kicad_pcb, not in ours.C1201isX1201's bypass (1.64 mm pad-to-pad) andC1204is the TMC'sVCC_IObypass (3.04 mm). "Update PCB from Schematic" will not bring that across. Also keepBRA/BRBshort to their sense resistors, andVCP/CPI/CPOtight, per the TMC pin table. - [ ]
R1903is a polyfuse position fitted with a jumper. The block's own note: "R29003 IS A POLYFUSE POSITION, NOT PROTECTION... Populate a 1206 PPTC, 250-500 mA hold." Port 5 V is a shared 1.0 A that also feeds the pyControl controller's own logic regulator with no per-port fuse, so a fault here can reset the experiment controller. Either source the PPTC or accept the jumper and record it. There are no test points either side, so a tripped PPTC could not be diagnosed in circuit. - [ ] No test points exist anywhere on the board, against roughly a dozen that
docs/interface-map.mdcalls for. Decide whether they are schematic symbols (add before layout so the netlist carries them) or layout-only pads. TheSWSELpad it asks for is moot —SWSELis tied directly to GND with no net. - [ ] The bus switches run from
+3.3V_MCU, whereas theSN74CB3Q3245part note inkicad-libssays to "power from the switched target-domain 3.3-V rail so Ioff provides isolation at VCC=0". As built, isolation depends on the MCU holding~BUS_SW_ENhigh, whichR601makes the reset default. Deliberate divergence; reconcile the note or the rail. - [ ] Two unrecorded TMC deviations to sign off.
R1202= 10 Ω sits between+5V_SWand5VOUT, where the datasheet describes 2.2–3.3 Ω between5VOUTandVCC; andVSAis on+5V_SWwhere the datasheet says "normally tied to VS". Both are benign —VCCis supplied externally so the internal regulator idles, and the sequencing rule "VCC comes up before or in parallel to 5VOUT or VCC_IO" is satisfied — but neither is recorded as a decision. - [ ] The
SN74LVC125Ahas noIoffand TI does not specify its leakage atVCC= 0.VIabsolute maximum is −0.5 to 6.5 V absolute, not VCC-referenced, andIIKis specified only forVI< 0, so there is no input clamp to VCC and gross back-power is unlikely — but it is unspecified rather than guaranteed. Measure+3.3V_MCUwith 36 V applied and both USB and pyControl unplugged at Gate B. - [ ] The encoder chain has no ESD device, contrary to the "filtering and ESD"
description in
docs/design-block-map.md. The 4.7 kΩ series and 470 pF give some protection and theSN74LVC2G17has internal clamps. Decide and record.
Milestones
- ~~Scaffold the repository and write the design record.~~ Done 2026-08-12.
- Answer blocking items 1-5.
- Prove
hv_input_36V_terminal_2pos_5p08_protectedin a scratch project with ERC. It has never been placed on any board. - Compose the schematic from existing
kicad-libsblocks, in the order indocs/rev0-plan.md. - ~~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.
- Freeze the GPIO allocation and the instrument overlay against the schematic.
- Run ERC/DRC, generate fabrication outputs, order.
- Bench bring-up against Gates B through D. The 36 V transient measurement at
the TMC5130A
VSpins is the one that justifies or condemns the supply choice. - Native pyControl integration against Gate E, adapting
pump_controller.pyas the device-class template. - Record results in
docs/bringup-log.md, against the board serial.
Verification
Ready for active PCB work when:
- ~~the schematic is electrically sound~~ done 2026-08-17 — ERC clean at every severity, zero undefined cross-domain nets, netclass patterns cover every current-carrying net;
- the motor part is identified, and the encoder's CPR and maximum shaft RPM are
known well enough to confirm the
C802/C803filter value; - ~~the DAC logic-level question is settled~~ deferred to rev 1;
- the bulk-capacitance placement and snubber footprint are decided;
- ~~the
+3.3V_MCUsource and the switchedVCC_IOrail are decided~~ done 2026-08-13; - ~~the Guix pin, Makefile, and KiCad scaffold exist~~ done;
make validate-dbstill to be run once in the managed shell; - Gate A items in
docs/validation-plan.mdhave explicit status.
Progress Log
- 2026-09-17 (latest): schematic complete and ERC-clean; all fourteen v1.0 design blocks error-free; layout still not started.
- 7 root sheets, 241 nets, ERC zero errors. PCB at 57 footprints, 0 traces,
0 vias, 4 zones — Update PCB From Schematic has not been run since any of this
landed. Run ERC from
make kicad-edits: through the MCP server it reports ~240 violations that are alllib_symbol_issues/footprint_link_issuesbecauseORB_WEAVER_KICAD_LIBSdoes not resolve in that process. - The four schematic defects from 2026-09-16 are all closed. The rails now
cross the
mcuboundary (+3.3V_MCUone net of 44 nodes where it was 34 trapped plus a 1-node orphan;+3.3V_SWone net of 18) and the staleSW_RAIL_SENSEpin is gone;R1107's pull-up is connected so~{TMC_CS}has its termination;HV_LATCHis wiredGP24to the HV block'sLATCH; and 31 no-connect flags cover the unusedISOand rawGPIOpins, with ERC severities untouched. - The digital-50 DUT block is placed —
J1301plusU1301-U1304. All 26 assigned channels verified at pad level by cross-referencing the block's internalDIGn→buffer-pin map against the board netlist. Note thatget_sheet_inforeportscomponent_count: 0forinstrument_portbecause the block is a sub-sheet; that reads like "not placed" and is not. - The three phantom pyControl signals are retired, taking footnote 8's
offered choice:
DIG4andDIG5join the four spares as explicit ground ties, so six of 32 strands are tied and noLV541Ainput floats. ~{BUS_SW_EN}has a read-only monitor port on the combined MCU block and reachesDIG27. The net still has exactly its six nodes, so the port is a declaration not a second driver, and because it isoutputon both sides of the sheet boundary ERC catches any board that tries to drive it.docs/interface-map.mdinstrument-port section rewritten to one table. The 40-pin overlay and its dependent subsections are deleted rather than kept alongside the new map; the remaining pinout is 32DIGnrows with no ground rows, columnsStrand | Pin | Bank | Board net | Tap | State, one row per sheet pin.J1501is now the HiroseFH12-50S-0.5SVA(54). Net effect 140 insertions, 480 deletions. The#### mcu Sheet Pinstable is still stale and should be regenerated from the schematic.- Design rules fixed here too, committed as
ff081ff. Same root cause as../kicad-libs: an unconditional(constraint clearance (min 0.10mm))DRU rule was replacing every netclass clearance, so the effective value was 0.10 mm project-wide. Fab floors moved to Board Setup (min_clearanceandmin_track_width0.0 → 0.1 mm), both unconditional rules deleted, and netclass patterns 124 → 130 to cover*/+36V,*/+12V_FILTand the*/BMA1-style bridge names.rule_severities.clearancestayserrorhere — the advisory treatment applies only to the authoring project. Since the PCB has no traces, none of this has been exercised on this board yet. - Landmine:
../kicad-libs'stmc5130still carries 0.12 Ω shunts against this board's 0.15 Ω withvsense = 1. That block now passes its own checks, which gives no protection — the shunt is a value, not a rule. Fix upstream before placing, and then the "never re-placetmc5130" rule can be retired. - Two improvements proposed and not applied, both cheapest before layout:
regrouping four instrument-port pairs so no SPI clock sits beside its own data
and I2C is not split across a ground (hygiene, not a fix for an observed
problem — coupled amplitude is tens of millivolts); and landing
PG_5VandPG_12Von two of the six ground-tied strands now that all fourhv_to_*blocks exposePGOOD. The bank assignment is already correct and should not change. -
Smaller items found and left open:
instrument_portnames two sheet pinsDAQ_SCL/DAQ_SDAwhere the nets areDAC_*;ANGLE_OUT_MONis a one-node net with no destination;/motor_encoder_switch/tmc5130/SWSELis a one-node net where the docs claim a hard GND tie. -
2026-09-08, final: scope narrowed to v1.0-only, then the
RUNswitch put back on evidence. - Narrowing removed both remaining regression traps for free. Not
re-placing
tmc5130means the shunt can never revert; not re-placingrp2040_gpio_isolation_sn74cb3q3245means its 6 unconnected-copper errors never reach the board. Verified the isolation block already exposes all 30 channels asGPIO0-GPIO29/ISO0-ISO29port pairs, so~{BRAKE_FAULT}needs board wiring only — no block edit, no re-place. Zero traps left on this board. ~{BRAKE_FAULT}kept (user agreed): ~2 parts, and without it a brake current-ceiling event is invisible to both the MCU and the capture on first hardware.RUNswitch restored to Required, and the deciding fact was already in this repo: theSTATUShang of 2026-09-03 left the board unable to honour the 1200 baud reset touch, so recovery took BOOTSEL held through a power cycle with both cables out. It costs no new BOM line (SW501'sC7185056line just goes to quantity 2), no new footprint, no new symbol — and it is the only cut item that cannot be added later without a respin. The debug connector and SWD pads stay cut.- Paired firmware task opened in
../mouse-joystick-firm/.agents/PLANS.md: enable the RP2040 hardware watchdog. They are not substitutes — a watchdog reset does not enter BOOTSEL, so it makes the just-flashed-something-broken case harder to escape, which is exactly the caseRUN+ BOOTSEL covers. -
Correction to an earlier item this session: the claim that
../mouse-joystick-firm's AGENTS.md still carriesIRUN = 28was wrong. It was fixed 2026-09-02 and already readsIRUN <= 17. Only this repo'sdocs/rev0-plan.mdis stale, in five places. -
2026-09-08, latest: v1.0 costed against stock — buy nothing — and the on-board debug MCU rejected.
- Nothing is short. Whole-BOM model, not part by part: 302 placements over
81 lines, every line mapped, none at zero stock, tightest coverage 42
boards. Table and method in Purchases before layout, not after, which was
rewritten; four of its entries were stale in the safe direction, including
DAC80501ZDGSRat 98 in stock rather than 50 andUSBLC6-2P6at 1002 against an August warning that it capped the build at ten boards. - One real purchase remains — the 1206 PPTC — and it is blocked on sizing, not
on money. No fuse, polyfuse or PPTC exists in the catalog at all. Size the
hold current against v1.0's load first.
SMBJ40Aand the 3.3 V RRIO op amp are both gated on v0.3 measurements; ordering either now is buying ahead of the data. - If the build becomes 25 boards rather than 5, top up
ADS8689IPWR,INA821IDR,REF5010AIDR(all at exactly 50) andDAC80501ZDGSR(98) before layout — half-consumed with no rework margin, and the expensive lines. - No SWD probe on hand for either candidate footprint. Land the pads anyway
(Tag-Connect TC2030-style, zero BOM lines) and buy a probe separately — any
spare RP2040 running
debugprobeis a CMSIS-DAP probe, so this is ~$4 rather than a board decision. TheRUNswitch is now split from the connector so it is not held up. -
An on-board debug MCU, and a debug FFC port, are both rejected — new section Why an on-board debug MCU was rejected. Decisive point beyond the duplication with the InstrumentPort:
docs/instrument-strategy.mdmakes observe-only a deliberate safety property, and a permanently populated debug MCU is exactly the drive path that section forbids as a normal experiment arrangement. The surviving gap is HIL stimulus, which belongs in a separate bench-only FFC adapter board if it is ever wanted. -
2026-09-08, later: the
tmc5130shunt moved from a library fix to a board responsibility, and four stale items closed against measurements this repo already had. Plans only; no KiCad files and no docs touched. The companion entry in../kicad-libs/.agents/PLANS.mdcarries the shunt derivation. R1203/R1204stay 0.15 Ω withvsense= 1, and that is now this repo's job.kicad-libskeeps 0.12 Ω as a deliberate per-motor placeholder. Two places in this file said "fix the library down to 0.15 Ω"; both are rewritten, and the instruction is reversed rather than softened.- The old "25% over the intended 0.679 A RMS" was wrong twice. The overshoot is +21.4%, and 0.679 A is a superseded driver setting rather than a motor rating. The correct reference is 0.421 A RMS at IRUN 17.
- The dangerous stale number is in the docs, not the hardware.
IRUN = 28still sits indocs/rev0-plan.md(the firmware's AGENTS.md was already corrected 2026-09-02) — 0.679 A RMS on the fitted 0.15 Ω, which is 1.61× the conservative reading of the motor's 0.6 A/phase and 1.13× even the equal-heating reading.docs/bringup-log.mdflagged it on 2026-09-02 and the two docs were never updated. Now a Required task. - Both Blocking Items are answerable and were left open by bookkeeping. The motor has been identified since 2026-09-02 (Lin Engineering WO-208-13-01D) and the encoder is an E4T-1000 with 1000 CPR bench-confirmed. The purchase list still called both "still open and still blocking"; the milestone list still gated on them. What the motor number did not settle is whether 0.6 A is RMS or peak — Lin does not say — so that survives as the IRUN 17 versus 24-25 choice.
- The
C802/C803encoder filter is closed on paper, no scope needed. At 1000 CPR the 470 pF corner of 44.9 kHz is reached only at 2694 RPM at the motor shaft; bring-up ran at 6 RPM. The encoder's own 100 kHz / 6000 RPM ceiling binds first. 470 pF is correct. docs/power.md's 5 V budget still reads "Unknown — the encoder part is not identified". Fill in 42 mA worst case (the E4T'sCPR > 500branch: 34 mA typ / 42 mA max, output drive down to 5 mA).- A measurement was sitting behind the wrong gate. The coil-current and case-temperature reading that settles IRUN needs v0.2, not v0.3 — the motor and driver path are unchanged and two boards are in hand. Added to the bring-up list with the numbers that make it matter: 2.31 W of copper loss at 0.421 A, 4.68 W at 0.600 A, in an 82 g NEMA 8 with a 60 °C datasheet case maximum against a 50 °C ambient maximum. That 60 °C figure contradicts the Class B (130 °C) winding insulation on the same page.
-
v1.0 needs no new design blocks. All 36 exist,
hv_to_12v_tpsm365r6included. Two regression traps remain, not three. -
2026-09-08: v0.3 test plan and v1.0 change list opened (the two sections at the top). No KiCad files touched. Four
../kicad-libsdesign docs were updated; that repo's.agents/PLANS.mdhas the matching block-work list. What changed in the picture: - The brake's pass device is a 3 V-class part.
V_GS(th)= 2.4 / 3 / 4.0 V from../kicad-libs/tmp/BUK7D36-60E.pdf, so worst-corner gate margin on a 5 V rail was 90 mV with a −7.5 mV/°C tempco. Resolved by moving the brake's loop amplifier to 12 V, which the board is already adding ahv_to_12v_tpsm365r6for. The part stays; the margin did not. - Decided: no board-exiting analog signal may exceed 3.3 V, so a pyControl
MCU pin cannot be damaged by a direct connection. Implemented as a ÷2 divider
per output, in-block. The torque channel was believed exempt and is not —
verified from the block netlists, its ceiling is set by
U37005's 5 V rail, not by the ÷3 attenuator, so a buffer fault puts ~4.95 V on the BNC. Same for the angle buffer,BRAKE_ISENSE, and both monitor taps. TheTPD1E05U06on each output is a 5.5 V-working ESD part and does not bound a DC fault. - The pin budget is not the constraint it looked like. Nine GPIOs are free;
v1.0 needs six. The real squeeze was the ADC, and
~{HV_FAULT}is a digital input sitting onGP29/ADC3 — moving it frees an ADC forBRAKE_ISENSEat no cost. So the pyControl event lines can be dropped because the researcher does not want them, not under pin pressure. - A
RUNreset switch is in for v1.0, in therp2040block along with exposingSWCLK/SWD. Verified from the netlist that a warm reset is safe:HV_ENreleases intoR1904's 10 kΩ pull-down so+36V_SWdrops, and~{TMC_DRV_EN}pulls up to+3.3V_SWthroughR1103so the driver disables. - Hard-wired fault signalling is deferred, at Peter's call — test v1.0 and the new blocks first. The RJ-45 investigation that closed the "can it go on a spare pin" question is written up in the v1.0 section's Deferred subsection so it does not have to be repeated.
-
torque_bridge_0_3v3_ina821is not as finished as it looked: the block has no.kicad_pcband 114 unscrubbed vendor fields. Placing it today would bring the netlist and none of the layout. -
2026-08-31 (latest): Rev-2 brake block schematic drawn and netlist-verified in
kicad-libs— 47 parts, 26 named nets, no floating pins. Not laid out, not bench validated. Two consequences for this board: - The pass device is final at 60 V (
BUK7D36-60EX) with no alternate, so the brake zone needs one land pattern, not two. The 80 VBSC072N08NS5ATMA1was rejected on gate-drive headroom. - The
+36V_SWringing measurement no longer chooses the brake FET. This board's own TMC5130A limit is the binding one: 49 V absolute maximum with an inductive load is 80% of the brake FET's 60 V rating. What the measurement decides is whether to populate a TVS at the 36 V entry, which would protect the driver and the brake stage together. Recorded indocs/power.md. Brake-zone area is now known from the drawn sheet: 36 small passives, three SOT-23-5 amplifiers, a VSSOP-10 DAC, a DFN2020 pass device, two SOD-123FL diodes, an X1SON clamp, two 0603 ferrites, one 2-position 3.5 mm vertical terminal, and no electrolytic. Leave pads for an RC snubber from the pass-device drain to GND. Updateddocs/design-block-map.mdanddocs/power.md. - 2026-08-31: Rev-2 brake driver design settled in
kicad-libs, and the InstrumentPort allocation changed as a result. The block isbrake_current_sink_0_49ma_dac80501— renamed fromb5z_brake_current_driver— a DAC-programmed linear current sink. Full specification, values, BOM, and open items:../kicad-libs/docs/brake-current-sink-design.md. The schematic is not drawn and nothing is bench validated. What this board needs to know: DIG11becomesBRAKE_CS, notBRAKE_DIM. There is no PWM in the design, so aBRAKE_DIMnet does not exist in any revision. The DAC chip select is the truthful equivalent of a brake-command edge, and withDIG9/DIG10already carrying the shared DACSCK/COPI, one capture can now recover both the commanded angle and the commanded brake current.AIN1_P/Nresolved toBRAKE_ISENSE, 0-2.01 V through 249 Ω, needs a >=100 kΩ load, floor about 25 mV.AUX_ANALOG_OUTloses the slot; it was already unpopulated. The "must be low-side or ground-referenced" constraint is satisfied by construction — the sink is low-side, so no high-side node exists to put on the connector.~{BRAKE_FAULT}onDIG12is a continuous limiter output, not a latch, and it arrives already at 3.3 V logic from the block's own optionalSN74LVC2G17, so it needs a route but not anSN74LVC125Achannel.- Rev-2 GPIO cost dropped from about 6 to about 4. The brake side is 1 GPIO plus 1 ADC, because both DACs share one write-only 3-wire bus.
- The 36 V decision is vindicated and now quantified. The sink needs at
least about 31 V for 49 mA through a 605 Ω hot winding; 36 V −5% leaves 4.2 V
of compliance and 36 V +5% costs 0.71 W in the pass device. A 24 V rail could
not drive the brake at all. Propagated to
docs/relationship-to-twin-axis-board.md. - New load-budget rows: 48.8 mA DC on
+36V_SW, about 3 mA on+5V_SW, about 2 mA on+3.3V_SW. No electrolytic in the brake zone — the 150 µF 63 V can was sized for the droppedLM3404HVfallback. - One measurement this board owes rev 2:
+36V_SWmaximum and ringing at the future brake-stage location with the TMC5130A driving. The pass device sees the full rail whenever the brake is off, so this is what decides between a 60 VBUK7D36-60EXand the 80 VBSC072N08NS5ATMA1DNP alternate. Updateddocs/design-block-map.md,docs/interface-map.md,docs/power.md,docs/rev0-plan.md,docs/prior-art.md,docs/instrument-strategy.md, anddocs/relationship-to-twin-axis-board.md. - 2026-08-31: Bumped the opt-in local KiCad overlay
(
.guix/local-modules/orb-weaver/kicad-local.scm) from 10.0.5 to 10.0.6, matching the same bump inkicad-libs; the two overlay modules stay byte-identical. KiCad,kicad-doc, and the symbol, footprint, 3D model, and template libraries all move to the10.0.6tag, withkicad-templateskeeping its hash because that tag points at the same commit as10.0.5. The pinned Guix channel carries 10.0.5 (README and AGENTS still claimed 10.0.4), so the plain targets stay on 10.0.5 and only the*-localtargets see 10.0.6. Channel pins were deliberately left alone. Committed fab outputs underfab/were generated by 10.0.5 and are unchanged; a futuremake fab-localwill stamp 10.0.6 into the Gerber headers. -
2026-08-21: Generated the v0.2 JLCPCB fabrication release after the corrected Molex 43860-0001 RJ45 footprint was propagated through
kicad-libs, the pyControl design block, and this board. ERC and DRC both report zero violations and DRC reports zero unconnected pads. The release has all four copper layers (F.Cu,In1.Cu,In2.Cu,B.Cu); the drill report reflects the corrected RJ45 geometry with eight 0.89 mm plated signal holes and two 3.25 mm unplated locating holes. The strict JLCPCB BOM resolves all 52 grouped rows / 142 unique physical designators, exactly matching 142 unique placement rows with no missing or duplicate references. The Gerber-only upload archive contains 14 files and has SHA-256fc628d4e5b39820e8a8128eaa3fcecd050a2dda82fce49c02ae428a683cf1976. The channel-pinnedorb-weaver-kicad-fabwas not used for the final package because it emitted only the outer copper layers; the final release used the current helper from the siblingkicad-libscheckout, which derives all enabled copper layers from the board. -
2026-08-17: Third pre-layout review. The schematic is cleared for layout. ERC clean at every severity, 142 components, and — the check that actually mattered — zero undefined cross-domain nets, verified by walking the flattened netlist against the TMC5130A, RP2040, SN74LVC125A, SN74CB3Q3245, TPS1HTC100-Q1 and ECS-2520S datasheets. Both prior blockers are closed:
TMC_DIAG1now crosses on a matchedU602channel pair (all 32 audited), and netclass patterns went 108 → 124 so the 36 V rail, both 5 V rails, both 3.3 V rails, the four motor phases andBRA/BRBno longer route at 0.2 mm. Nine board-level termination resistors were added, and the reasoning behind which rail each references is the most transferable thing in this review — see the four numbered notes under "Closed this session". Three earlier findings were retired as wrong after reading the datasheets:X1201's openTri-Statepin is explicitly permitted,RUNfloating is safe on its internal pull-up, andHV_SNSneeds no series resistor becauseSNSis a current source. One genuinely new defect was found and fixed —DIAG_ENheld high keeps the TPS1HTC100's off-state open-load pull-up enabled, which stops the switched domain ever cleanly powering off; it now runs fromGP14.AGNDwas tied toGND, thePMC/PYCtypo fixed, and theGP1pull-up fitted. Decided, not fixed: no SWD header orRUNconnection in Rev 0. Remaining items are layout decisions, not schematic defects, and are listed under "Carry into layout". -
2026-08-17 (later): Re-reviewed the schematic after integrating the new encoder block. ERC is now clean at every severity — 0 violations, down from 9 errors and 129 warnings. 133 components, no duplicate refdes, every part footprinted. Ten of the twelve original findings are closed, including the encoder input, which is now conditioned by
quadrature_encoder_input_5v_sn74lvc2g17(SN74LVC2G17 dual Schmitt on+3.3V_SW, 4.7 kΩ series, 470 pF filters, 100 kΩ pull-downs on both the harness and output sides) withENCNtied to GND throughR1201. The rail-sense divider, sense resistors,SWSEL, shadow labels, off-grid endpoints,PWR_FLAG, and all 125 symbol mismatches are resolved. Two blockers remain and neither is visible to ERC — both were found by walking the flattened netlist and both will keep passing ERC after any edit. First,TMC_DIAG1never crosses the isolation barrier and is shorted to GND: its two halves land on different bus-switch channels because therp2040_gpio_isolation_sn74cb3q3245block inkicad-libscross-maps its A and B labels on the two middle switches. Second, netclass patterns still match none of this board's rails, so the 36 V motor rail would route at Default width. Then seven pre-layout items: theGP1pull-up, five floating buffer inputs on the switched domain,X1201's floating enable, thePMC/PYCtypo, no reset/SWD, bus-switch direction versus the docs, and buffer grouping. Full detail with refdes, pin numbers, and repro commands in Open Schematic Findings above — written self-contained becausehandoff.local.mddoes not travel between machines. Sharpened Blocking Item 2: the encoder part no longer gates the schematic, only theC802/C803filter value. -
2026-08-17: Reviewed the completed Rev 0 schematic. Ran
kicad-cli sch ercplus a flattened-netlist check againstdocs/interface-map.md. The InstrumentPort mapping is exactly right — all 11 Rev 0 Strands where the allocation says — and the power architecture verifies clean:+5V_MCUfed only by+5V_USBand+5V_PYC,hv_to_5v_tpsm365r6on+36V_SW, the MCU LDO on+5V_MCU, the two 5 V rails never meeting, and theDIO-A→RX /DIO-B→TX crossover the right way round. Annotation is per-instance distinct across the twice-placed LDO sheet, and there are no global power symbols, so no cross-domain name collision is possible. Twelve findings, five of them blockers, all recorded in.agents/handoff.local.mdwith reproduction steps: sense resistors still 0.12 Ω; TMCSWSELfloating; twopin_not_connectederrors caused by off-grid wire endpoints; noPWR_FLAGon+5V_MCU; and the encoder input bare withENC_Nunwired and a 5 V-supplied encoder feeding 3.3 V TMC inputs. Then:SW_RAIL_SENSEtied bare toGP26, the bus switch wired A/B inverted so the instrument taps sit on the switched side, no pull resistors anywhere yet,+3.3V/+5Vlocal labels shadowing the hierarchical names, aPMC/PYCtypo, a buffer grouping that differs from the documented one, and 127lib_symbol_mismatchwarnings. Corrected two design-record items that the review found the board disagreeing with, rather than leaving the docs lying — see Resolved. -
2026-08-16 (later): Cut the encoder and limit switch off the MCU, and retargeted the rail sense.
ENC_A/ENC_B/ENC_NandLIMIT_SWnow reach the TMC5130A and nothing else, in every revision — the driver owns them and firmware reads position and homing from its registers over SPI. That freesGP14,GP15,GP16, andGP18, taking Rev 0 to 17 of 30 and full scope to 25 of 30 with five spares. It deletes rather than defers the PIO encoder cross-check,ENCODER_MISMATCH, and theGP14-GP16contiguity constraint; the fault code is retired and its bit left permanently reserved. Isolated channels dropped from 8/16 to 7/12, which makes the four-package bus-switch block twice the size it needs rather than a bit over — still accepted on schedule grounds, now recorded as a larger waste.LIMIT_SWjoined the InstrumentPort onDIG11, a third swap slot alongsideENC_A/ENC_B, taking Rev 0 to 11 of 18 Strands and three fitted buffer packages. With no GPIO copies, those taps are the board's only independent witnesses to what the driver is being fed; they are also the last ones, since nothing replaces them at rev 2. Fourth 100 kΩ pull-down added forLIMIT_SW.GP26went fromV5_SENSEon divided+5V_SWtoSW_RAIL_SENSEon a ÷2 divider off+3V3_SW— same two resistors, end of the chain, so one reading confirms 36 V, 5 V, and 3.3 V. It is not redundant with the TMC answering SPI, which was the tempting argument: SPI reaches the driver through the bus switch the sense exists to gate, so that check is circular.~{HV_FAULT}does not cover it either — with no 36 V the switch is unpowered and its open-drain output reads as no fault.HV_SNSdoes cover most of it, and that is recorded as the fallback if the pin is ever wanted elsewhere. Rev 2 may now fit on RP2040 — five spares against roughly six needed for brake and torque on one axis, five ifTQ_DRDYis dropped. Recorded as an observation to check, not a commitment. (Narrowed 2026-08-17:GP14went toHV_DIAG_EN, so it is now four spares against those same six. Rev 2 no longer fits without dropping something — recheck before relying on it.) Updatedinterface-map,rev0-plan,power,uart-protocol,instrument-strategy,design-block-map,validation-plan,bringup-log,README,.agents/AGENTS.md, the handoff, and this file. -
2026-08-16: Moved InstrumentPort-40 into Rev 0 and re-allocated all 18 digital Strands. The port is now justified by firmware development rather than by experiments — both directions of the pyControl serial link, tapped at the RJ-45, plus the TMC5130A SPI bus in one capture. 10 Strands live at fabrication, 8 allocated and unrouted, 4 reserved for the rev 2 brake and torque sensor so those circuits inherit a pinout.
ENC_A/ENC_Bborrow two of the reserved slots —DIG0/DIG1, in the rev 2 buffer package so the eviction is as late as possible — because the driver'sX_ENCregister is the only encoder count Rev 0 has and the raw edges are the only way to check it decodes correctly.ENC_Nwent to a test pad. The new allocation also follows the v0.3 profile's own Preferred Digital Overlays table much more closely than the superseded one did. Decided the three tap points — the existingPYC_DIO_A/PYC_DIO_Bpins for the pyControl serial, A side of the bus switch for the TMC, driver side of the filter for the encoder — and the three 100 kΩ pull-downs they need, onTMC_SPI_CIPO,ENC_A, andENC_B. Proposed and then rejected a connector-side pyControl tap needing two new boundary pins: a digital channel cannot resolve 33 mV across the block's 100 Ω, and the tap would have sat on the unprotected side of the ESD clamp. Regrouped the five buffer packages so each revision populates whole packages and no CMOS input floats:U_A/U_B/U_Efitted,U_C/U_DDNP,U_E's two brake inputs GND-tied until rev 2.GP24went live with no pin moved. Corrected the old claim that six slots carried signals Rev 0 does not have — only the three DAC wires were genuinely absent. Updatedinterface-map,design-block-map,instrument-strategy,rev0-plan,validation-plan,bringup-log,README,.agents/AGENTS.md, the handoff, and this file. Then added aDircolumn to the GPIO table and a "Signal Direction And Sheet Pin Types" section giving everymcusheet pin its KiCad shape, what it mates with, and the far side's declared shape — surveyed from the blocks rather than assumed.kicad-libsneeds one small change:PYC_DIO_A/PYC_DIO_Bfrompassivetobidirectional. Not yet done: that, and every bit of the schematic and layout work itself. - 2026-08-14: Built three
kicad-libsblocks and closed the pin 6 budget question. Added the ORing diode tousb2exposing bothVBUSand+5V_USB; createdlimit_switch_debounced_inputandpycontrol_behavior_port_rj45_boundary. Traced the pyControl 5 V supply from the host side in~/Repositories/github/pycontrol-h7-breakout: a shared 1.0 A RECOMR-78E5.0-1.0feeding all ten ports, the terminal block, and the controller's ownAMS1117-3.3, with no per-port fuse — so blocking item 3 is answered, and its real consequence is a new polyfuse requirement on this board's pin 6 branch. Also corrected the pinout: pin 5 isDIO-Con eight of ten ports and aPOW-Csink driver on two, not "SPECIAL", and pins 3/7 are driver outputs, which kills the "usePOW-A/POW-Bfor event lines" fallback. Decided a hardware 10 kΩ pull-up onGP1rather than relying on firmware. Mirrored the pinout findings into../mouse-joysticks-pcb. Rev 0 was not finished and not ordered. A netlist audit found the schematic much less connected thanREADME.mdclaims — themcusheet has no sheet pins at root, so there are three separate GND nets, the isolation block is entirely unwired, and all 30 GPIOs are unwired. Full state and a ranked action list are in.agents/handoff.local.md, which did not exist before today despite being referenced here and in.agents/AGENTS.md. - 2026-08-13: Rescoped Rev 0 to the interfaces only and rewrote the design
record around it —
interface-map,rev0-plan,design-block-map,README, plus scope banners on the eight docs describing deferred subsystems. Rev 0 connects 17 of 30 GPIOs and reserves the rest at fixed numbers, so rev 1 adds the DAC, the PIO encoder tap, the event lines, and the InstrumentPort without (counts superseded 2026-08-16) moving a pin. DroppedTMC_REF_STIMpermanently to pay for the bus-switch enable onGP9. Confirmed the whole Rev 0 connector complement is stocked in one 3.5 mm terminal family, and flagged that it is screw, not locking. - 2026-08-13: Adopted the two-domain power architecture. The high-side switch
is a researcher-facing reset button, so the regulator chain is fed
post-switch and the MCU is fed from USB
VBUSor pyControl pin 6 through two ORing diodes, never from the 36 V chain. Exactly two5v_to_3v3_tps73733, one per domain. This reversed the pre-switch guidance inpower.mdand.agents/AGENTS.md, and dissolved three open items at once: the+3V3_MCUsource, the switchedVCC_IO, and whether the TMC toleratesVCC_IOwithoutVCC/VS. Confirmed pyControl pin 6 is 5 V from the syringe-pump netlist; its current budget stays unmeasured. - 2026-08-13: Rewrote the GPIO isolation rule to follow the far end's power domain rather than an ad-hoc list. The pyControl UART now bypasses the bus switch, so the board stays reachable with the motor rail off — the earlier write-up had it isolated, which defeated the purpose. 8 isolated channels in Rev 0, 16 at full scope. Accepted the oversized 4-package block for schedule.
- 2026-08-12: Created the repository. Seeded
.guix/,Makefile, agent shims, and an empty KiCad 10 project from../mouse-joysticks-pcb, renamed throughout. Wrote the design record:rev0-plan,interface-map,power,design-block-map,relationship-to-twin-axis-board,pycontrol-port-pinout,pycontrol-interface,uart-protocol,encoder-analog-output,instrument-strategy,validation-plan,bringup-log,prior-art. - 2026-08-12: Derived the RP2040 allocation — 27 of 30, 3 ADC-capable spares, nothing descoped — and the InstrumentPort-40 overlay, which fills all 18 digital slots. Gave the DAC SPI bus two slots so the analog angle output can be verified against the encoder from a single capture.
- 2026-08-12: Added
orb_weaver_smd:Texas_HTSSOP-14-1EP_..._ThermalVias_JLCPCBtokicad-libs. The stock footprint's thermal pad was already correct; the via array was missing, and KiCad's official_ThermalViasvariant fails this project's DRU with 15drill_out_of_rangeerrors (0.2 mm drills vs a 0.3 mmmin_through_hole_diameter). Enlarging only the drill would breach the 0.18 mm PTH annular rule, so the local variant uses 0.7 mm pads / 0.3 mm drills (0.20 mm annular) with the y-pitch tightened to 1.0 mm to keep the outer pads inside the 5 mm pad. DRC-verified clean, and assigned at source on Peter's instruction: threedb/source/CSVs plus the symbol default, thenmake build-db. Placed instances are copies and still carry the old footprint — Update Symbols from Library is needed in the two hv_input blocks, the mining example project, andtmc51x0-driver-dev-pcb, which places the 24 V barrel-jack block and will gain thermal vias on its next update. Itsfp-lib-tablealready carriesorb_weaver_smd. - 2026-08-12: Reversed the
VSA→VMrecommendation. The external 5 V onVSAexists to bypass the internal linear regulator, which the datasheet explicitly sanctions ("a different (lower) supply voltage can be used for VSA") and which matters thermally: the regulator can contribute up to 1 W at 46 V, and even withVCCexternal it would add a few hundred mW to a package already dissipating ~0.45 W of bridgeI²R. SoVSAstays on+5Vand+5Vis switched with+36V. Cheapest way to get every ordering right: feedhv_to_5v_tpsm365r6from the switched+36V, which reverses the earlier pre-switch recommendation for that buck. Consequence to plan for: the TMC'sVCC_IOcannot sit on the always-on MCU rail, because an always-onVCC_IOwith a switchedVCCis the order pin 34 forbids — so there are two 3.3 V domains,+3V3_MCUalways on and a switched 3.3 V forVCC_IOalone. That makes the+3V3_MCUname load-bearing rather than merely descriptive. New open item: where+3V3_MCUcomes from when only the 36 V supply is connected. - 2026-08-12: Settled the "do not power the TMC5130A without VM" question against
rev 1.21, and it lands on a different pin than expected.
VCCmust come up EARLY, not late — pin 34: "make sure, that VCC comes up before or in parallel to 5VOUT or VCC_IO, whichever comes up later". SoVCCalive withVS= 0 is the permitted direction, and the buck→LDO cascade guaranteesVCCbeforeVCC_IOby construction. The real violation isVSA: thetmc5130block tiesVSA(pin 39) to+5VwhileVSgoes toVM, and the absolute maximum isV_VSA≤V_VS+ 0.5 V. With the buck fed pre-switch,VSAsits 4.5 V over that limit every time the motor rail is off — the boot state and the safe state. Fix is to tieVSAtoVM, the datasheet's "normally tied to VS"; costs ~0.15 W in the internal regulator becauseVCCis fed externally. Rejected switching the 3.3 V rail: the MCU runs on it and drivesENABLE, so that is a deadlock. Also notedR4002is 10 Ω where the datasheet recommends 2.2-3.3 Ω betweenVCCand5VOUT. This is a shared change to thetmc5130block. - 2026-08-12: Withdrew the
+3V3_MCUrename recommendation. The pull-ups onDIAG_EN,LATCHand~FAULTmust reference the rail the MCU itself uses, so naming the rail by its domain is correct and more precise than+3.3V. Only consequence:5v_to_3v3_tps73733emits+3.3V, so the two names need reconciling at board level and a netclass pattern added. - 2026-08-12: Reviewed
hv_input_36V_terminal_2pos_5p08_protectedafter the first round of edits (refdes renumbered; a.kicad_pcbnow exists). Correct: theENdivider is now 1 kΩ series + 10 kΩ pulldown giving 3.0 V;DIAG_ENandLATCHare pulled to+3V3_MCUthrough 1 kΩRPROT;ILIM= 40 kΩ;SNS= 249 Ω; diode orientation, both capacitor placements, thermal pad toGND,NCleft open,PWR_FLAGonHV_IN, and the HTSSOP-14 PowerPAD footprint all check out. Three defects:R27005pulls~FAULTtoGNDinstead of+3V3_MCU, so the open-drain output idles low and never presents a valid high;HV_INis a local label, so the board cannot tap the unswitched node for the buck feed or the TVS; andSNSis a local label, so the current sense cannot reachGP28. Three concerns:C27002is 50 V on a 36 V rail whereC27001is 100 V, andVOUTis specified to −60 V during inductive turn-off;+3V3_MCUwill not connect to the board's+3.3V; andVAAnames a pin the part does not have. ERC is clean — 32 violations, all library-path or isolated-fragment artifacts. - 2026-08-12: Checked the six input-block edits against actual stock and the parts
database. Neither 470 Ω nor 24.9 kΩ is stocked, so the values changed again:
R27004→ 249 Ω (OW-PASS-CRCW0402249RFKED, fault level 1.29-1.59 V, even safer than 470 Ω would have been) andR27003→ 40 kΩ (OW-PASS-SCR0603F40K,I_CL~1.2 A), with 2 × 49.9 kΩ in parallel as the 25 kΩ / 1.96 A fallback. Every value now in use is stocked and already promoted, so the block edits carry no lead time. Confirmed the SNS amplitude loss is irrelevant becauseI_SNSaccuracy is ±25-30% below 60 mA anyway. - 2026-08-12: Netlist export of the
usb2block shows it has no ESD protection — only the receptacle and two 5.1 kΩCCpulldowns, withSHIELDunconnected. The 27 ΩD+/D-series resistors RP2040 needs are not missing; they areR10001/R10002inside therp2040block. Decided to add the stockedUSBLC6-2P6(already in the parts database asPower_Protection:USBLC6-2P6/Package_TO_SOT_SMD:SOT-666, 20 on hand) on the connector side of those resistors, rather than purchasingUSBLC6-2SC6, which is the same die in SOT-23-6 and would also need a new internal part. Buy the SC6 only if boards are hand-assembled. This matters more than usual becauseVBUSnow feeds the logic rail and the USBLC6 clampsVBUSas well as the data lines. - 2026-08-12: Resolved the four follow-up questions on the 36 V input block against
tps1htc100-q1.pdf.DIAG_ENandLATCHat 3.3 V withVSunpowered is within Recommended Operating Conditions, because those pins are specified −1 to 5.5 V with respect to GND and independently ofVS; this is unlike theDAC8563case, whose inputs are specified relative to its ownVDD.ILIMandSNSare each a single resistor to ground, so the earlier "2:1 divider" advice is withdrawn: changeR2700310 kΩ → 24.9 kΩ (limit 5.3 A typ → ~2 A) andR270041 kΩ → 470 Ω (fault level 3.3-4.4 V → 2.44-3.01 V, under the ADC limit). Both values were superseded later the same day by the stock check recorded in the entry above — 24.9 kΩ and 470 Ω are not stocked; the values in use are 40 kΩ and 249 Ω. Corrected the current-limit figure from 4.6 A to 5.3 A typ (4-6.3 A); the earlier number used the wrongK_CLrow instead of the datasheet's tabulatedI_CLvalues.LATCHfloating gives auto-retry every 2 ms, so tie it high for latch-off; clearing is by togglingEN, which firmware already owns onGP19.R27001→ 1 kΩ done. - 2026-08-12: Audited
hv_input_36V_terminal_2pos_5p08_protectedby exporting its netlist and checking it againsttps1htc100-q1.pdf. Confirmed the 220 nF is theVSbypass onHV_INand the 22 nF is the output bypass on+36V, both correctly placed. Found six things to change when the block is placed: theENdivider (10 k/10 k) gives only 1.65 V against a 1.5 VVIH;DIAG_ENis left unconnected with an internal pulldown, which disables theSNScurrent sense thatGP28was allocated to read;~FAULTandLATCHare unconnected;ILIMis set to ~4.6 A; and theSNSfault level of 3.3-4.4 V would overvoltage the RP2040 ADC. AllocatedGP29to~{HV_FAULT}and specified a 2:1 divider onSNS. Also bounded output bulk capacitance at tens of µF, since the switch dissipates ½CV² charging it and the datasheet requires the design not reach thermal shutdown while doing so. - 2026-08-12: Corrected the previous "no TVS" conclusion. It was right that no
standard TVS protects the TMC5130A at 36 V and wrong to conclude none was
needed: because the regulator chain taps pre-switch,
HV_INcarries the buck's input capacitance, so hot-plug rings against it while the switch is off and the driver is disconnected. The exposed parts at that moment are the 70 V switch and the 65 V buck, both clampable by a 40 V-standoff part. TVS goes onHV_IN. - 2026-08-12: Ran the TVS arithmetic against the actual stocked parts and
reversed the earlier "add a board-level TVS" recommendation. Standard SMBJ
parts clamp at ~1.6× standoff, so a 36 V rail needs 40 V standoff and clamps
near 64 V, against the TMC5130A's 49 V absolute maximum; the
SMBJ40Abreakdown spread of 44.4-49.1 V straddles that limit. TheTPS1HTC100-Q1(70 V / 81 V transient) andTPSM365R6V5(65 V / 70 V) need no clamp. Recorded that the stockedSMBJ28Aworks only on a 24 V rail, which is why the 24 V input block is passive-with-TVS while the 36 V block is active-with-soft-start — two coherent designs, not one with a missing part. Also recorded that the 36 V block runs happily from 24 V or 30 V as an escape hatch. - 2026-08-12: Added the multi-source logic rail — onboard buck, USB
VBUS, and optional pyControl 5 V, diode-ORed at theTPS73733input rather than at+5V, because ORing onto+5Vviolates the TMC5130A's 4.6 VVCCminimum and truncates the DAC's full-scale output. AllocatedGP26/ADC0to a switched-rail sense (retargeted from+5V_SWto+3V3_SWon 2026-08-16) so firmware can detect the MCU-alive/+5V-dead state that USB-only power creates, and recorded the input-clamp back-powering hazard it introduces. - 2026-08-12: Worked the 36 V consequences from
TMC5130A_datasheet_rev1.21.pdfchapter 29: 46 V operating, 49 V absolute with inductive load, and the datasheet's own warning about supply ringing from stray inductance. Recorded that the internal 5 V regulator's 1 W limit is reached at 32 mA whenVM = 36 V, soVCCmust be supplied externally, and that the 36 V input block populates no TVS. Also flagged the pre-/post-switch feed point as the one way to make an unbootable board out of correct blocks.
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
.1and prepare the actual v1.0 overlay, expected states, test criteria and evidence manifest withOBS-IF/OBS-FW(G0). - [ ] On actual unit arrival/readiness, perform
.2through the qualifiedOBS-HW-ADAPTERpath and record measured limits per serial (G1). - [ ] Coordinate
.3withOBS-FW/OBS-HOSTfor one bounded command, bus, encoder/limit and physical-response capture with uncertainty (G4). - [ ] Prepare
.4as a separate future-revision debug/trace-connector proposal withOBS-IF,OBS-FWandOBS-PROBE; review pin mux, actual probe UART capability, voltage, cable and power behavior before schematic work. Preserve current routing and read-only 32-signal Digital-50. Defer combined-60 and a permanently on-board debug MCU until demonstrated need. - [ ] Record direct researcher-to-DUT USB and separate external-probe USB, preventing VBUS tying and signal-pin back-powering. Two connectors are not evidence of isolation. Scope any future attached-monitoring session separately; no halt/reset/program/write authority follows from attachment.
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)
- Pinned guix-agent-tools to published commit
788874ea3af208c30d66bd4e415b18e35ec14c2b; other channel pins are unchanged. - Task manifests provide Make; the agent manifest also declares Guix so nested managed Make targets work without host PATH.
- Verified the final pure agent environment, basic Make entrypoints, and both published agent launchers using offline probes and temporary state.
- 5 task manifests passed pure runtime smoke checks.
- Validation logs:
/tmp/pure-rollout; cross-repository summary in guix-agent-tools/.agents/PLANS.md. Changes left uncommitted.