Parviz · Build docs

Assembly

Current assembly notes for the tracked desk-pi prototype. The task #28 insertion/torque-path audit (2026-07-07) verified a complete install order exists (see "Assembly order (verified)" below). The 2026-07-08 maintenance pass then killed the worst service traps: tilt motor is a rear-access cartridge (tilt_carrier), the worm wheel uses an accessible radial grub on a center-only axle flat, tracks close with a master link (no loop-flexing), the pan race got a BB cage, both joints got stall-homing hard stops, the microSD swaps through a plugged left-wall slot, the positively located side-panel joints, and power is a 12 V PD-trigger + dual-buck belly tray (firmware/WIRING.md). Grub-on-flat backlash remains a bench measurement covered by the coupon below.

Bill of materials

Owned quantities cross-checked against a personal parts inventory (2026-07-07; re-audited 2026-07-13 with bag-label photo reads: TT motors, M8, 608zz, F688 candidates, fuses all settled below). "Need" is per robot.

Electronics

Part Spec / where it goes Need Owned Buy
Raspberry Pi 5 2 GB, rides the display's own 58x49 standoffs 1 1 (Tray 1) ,
7" touchscreen official kit; 4 factory M3 mounts (126.2x65.65) 1 1 (Tray 1) ,
Camera Module 3 recessed forehead, 4x M2 at 21x12.5 1 1 (Tray 1) ,
45-65W USB-C PD brick must advertise 15V (mandatory at 45W per PD power rules; fixed 12V is optional on generic bricks); the robot bucks 15V down internally. The official 27W brick is a 12V FALLBACK only, with the strict firmware co-scheduling rules load-bearing again -- power review 2026-07-16, firmware/WIRING.md 1 0 (27W owned = fallback) 1
USB-C PD trigger board set to 15V (12V only for the 27W fallback); mounts on the rear-wall M2 pilots beside the USB slot 1 0 1
XL4015-class 5A buck, CC/CV variant Pi rail (trim to 5.25V, CC pot set to 5.0A -- the CV-only board has no current limit and does not satisfy the hard-limiting rule); 40x20 post grid on the belly-plate tray 1 0 1
MP1584-class mini buck motor rail 5V; zip anchors beside the main buck 1 0 1
Polyfuse 2A hold / ~4A trip (MF-R200 class) in series with the MX1588 VCC feed on the tray: a dual TT stall trips it and self-recovers instead of folding the shared brick (firmware/WIRING.md "Hard current limiting") 1 0 1
JST-XH kit + crimper every joint-crossing / board run is a keyed XH plug 1 0 1
18 AWG silicone pair + 5A blade fuse + inline holder Pi-rail run + inline fuse at the tray 1 m fuse: OWNED (ATC/ATO blade assortment, settled 2026-07-13); wire + holder: 0 1 m wire + 1 holder
28BYJ-48 stepper 5V, pan + tilt + 2x antenna drives 4 6 (Bag 14) ,
ULN2003 driver one per stepper 4 9 (3 Bag 14 + 6 Bag 5) ,
TT gearmotor 1:120 track drive, one per pod, shaft on X into the sprocket; +2 OPTIONAL for the twin-drive front stations 2 (+2 opt) 3 (Bag 5, re-audited 2026-07-13; the old "own 1" was stale) , (1 only if the optional 4th station is populated)
MX1588 dual H-bridge drives both TT motors, skid steer 1 5 (Bag 7) ,
WS2812 forehead segment 8 LEDs in the led_slot recess: 42 x 5 mm, 1.5 deep (model dot pitch 4.6). A standard 8x5050 stick is 53.3 x 10.2, it does NOT fit. Buy a narrow (4–5 mm wide) addressable strip (SK6805-2427 / WS2812-2020, ≥160 LED/m) and cut an 8-LED segment, or widen led_slot to ~54 x 11 for the common stick 1 seg 0 1 m narrow strip (also covers the front strip, next row)
Front white strip 7 dots at 5 mm pitch in a 36 x 2.5 lip (fled_*): either 7x 3 mm white LEDs or a second segment cut from the same narrow WS2812 strip 1 0 covered by the strip above (or 10x 3 mm white LED)
Amber indicator LEDs 2 corner lamps, 12 x 7 windows (lamp_*): 2 rectangular amber LEDs (2x5x7) or 5 mm amber behind a printed lens 2 0 2–5
HC-SR04 ultrasonic x4 since the 2026-07-10/11 sensor passes (this row used to say "optional x1", stale): forward + rear obstacle (grille rings, Ø16 barrel passes at 26 mm c-c, us_dx=±13) + 2 cliff (deck slopes). With the Arduino I/O plane (docs/AWARENESS.md) plain 5V HC-SR04 is fine; HC-SR04P (3.3V) only needed if wired straight to Pi GPIO 4 0 4
Rear pod audio rear Ø14 cylinder pod (rear_cyl_*): an owned Ø12 active buzzer fits for beeps. For real audio, buy a MAX98357A I2S amp and drive the owned 8Ω 0.5W mini speaker (Bag 15) from inside the chassis, the speaker is ~40–50 mm and can't live in the pod; the pod becomes the grille 1 buzzers: 5x active 5V (Bag 1) + 4x ~12 mm (Bag 16); speaker: 1 (Bag 15) 1x MAX98357A (only if speech/audio wanted)
Arm actuation placeholder arms, TBD pending the arm mechanism pass. If actuated: 9x 9g servos owned (5x T-8090, 3x SG90, 1x MG90S) TBD 9 servos nothing yet

Bearings, race, axles

Part Spec Need Owned Buy
695-2RS 5x13x4, tilt-axle cheeks 2 30 (Bag 13) ,
F688ZZ flanged 8x16x5, flange Ø18; END idlers (both loop ends are free idlers since the 2026-07-11 mid-drive), 2 per wheel x 4 wheels (one pressed at each face, Ø15.95 through-seat + Ø18.5x1.0 flange recess both sides). The Bag 13 "Miniature Ball Bearings" were checked as a candidate 2026-07-13: label reads 10pcs MR105 ZZ (5x10x4, unflanged) -- wrong part 8 0 8
6 mm airsoft BBs pan race, Ø80 circle, pan_race_n=18. Printed-race acceptance test: assembly step 9 (reject -> commercial ring below) 18 0 smallest bag (100+)
Metric O-ring 6.0 x 1.0 antenna gland and positive park grooves; NBR or silicone, silicone preferred for low breakaway; VERIFY_ON_BENCH 2 0 2
Commercial thin-section lazy-Susan / 4-point turntable bearing OPTIONAL print-3 upgrade for the printed PLA race (buy only if step 9 acceptance fails). Pitch circle as close to Ø80 as available (typical cheap: 70 mm or 3" Al/steel lazy-Susan rings, or a thin-section slewing ring with ID >= 68 so the cable pass and 16T pinion hub stay clear). Height budget = current printed stack seat floor -> platform underside (_pan_stack in src/pan.py): pan_race_ring_t 5.0 + air gap (ball_d - 2 * pan_groove_engage) 2.4 = 7.4 mm (absolute: seat_floor = base_h - pan_plate_t - 7.4, plate_bot = base_h - pan_plate_t; with base_h 66 / pan_plate_t 7.6 that is z 51.0..58.4). Dims VERIFY_ON_ARRIVAL; fitting needs re-cut deck seat + platform underside (not modeled now; see step 9 upgrade path) 1 0 only if printed race fails acceptance
Ø5 SOLID rod tilt axle, 209 mm silver steel (NOT tube: a 1.0 flat on a Ø5/Ø2.5 tube leaves a 0.25 wall). File one 9.5 mm long x 1.0 ±0.1 mm deep center-only flat, axle x +1.5..+11.0. Its center is 98.25 mm from the +X rod end. Finished across-flats is 4.0 mm: a 4.1 GO slot must pass and a 3.9 NO-GO slot must not. Both 695 journals at x 20..24 and both head-clamp lands remain fully round 1
M8x70 bolts + NYLOC nuts END BOLT-AXLES: head = outboard hubcap, smooth journal through both F688s, thread begins at x=78 and crosses the tower clamp stack. M8x60 is too short to reach the NYLOC insert. 4+4 0 4x M8x70 + 4 NYLOC
M8 JAM NUTS + washers Required and modeled: one AF13 jam nut bears on each tower's outboard face; the washer and NYLOC bear inboard. This closes the previous 17.4 mm air gap, clamps the tension slot/tower, and leaves the idler free on the smooth journal. The printed export likewise contains 8 total M8 nuts. 4+4 washers 0 4x M8 jam nut + 4 washers
12x28x1.0 steel strip Front tension-slot nut bearing insert, cut square from mild-steel stock. Presses flush into the x62 face recess; captured ends spread sustained clamp load into the tower shoulders. This mitigates PLA creep but is not a positive axle lock 2 0 2x
608zz not used in the current design. SETTLED 2026-07-13: the bag label reads "10pcs-608ZZ" -- real 608ZZ bearings under translucent shrink wrap, not plastic rings. Still don't design around them 0 ~10-30 (608ZZ, confirmed by label) ,

Fasteners and pins

Part Spec Need Owned Buy
M3 screws + hex nuts captive-nut joints everywhere; incl. M3x35 x8 bezel↔back and 6x M3x10 + 6 M3 nuts for the pan retainer. 2026-07-15 fastening campaign: ~+58 M3 hex nuts as failed thread-form pilots became captive traps. Screw length changes: y=26 seam M3x12 → M3x20, neck→platform M3x12 → M3x14 lots 540pc M3 stainless kit + 175pc M3 30–50 mm kit + 600pc M2-M5 kit + 1263pc M2-M4 kit (all Tray 1) , (kits cover it)
M3×10 countersunk screws + M3 hex nuts track hold-down shoes, 2 per shoe × 4. Heads recess flush in the z 10.4 running face; nuts slide up into the wheel beam at z 18.0. M3x10 (not x12): the beam clearance bore is capped at z 21.0 to keep 0.8 of wall under the sprocket journal. The front spr_y2 pair is fittings-ready and only required when twin drive is populated 8+8 (4+4 for rear-only drive) covered by M3 kits ,
M3x3 cup-point grub tilt-worm forward axial retention; seats through the O2.5 radial hub pilot onto a 28BYJ shaft flat 1 0 (screw kits do not list grubs) 1
M3x4 cup-point grub worm-wheel drive; radial O2.5 hub pilot, cup tip preloaded on the center-only axle flat, head flush with the Ø11 hub OD 1 0 (buy with the M3x3 grub above) 1
M2/M3 brass heat-set inserts 16 total, and a soldering-iron insert tip. Used ONLY where a captive nut was probed and measurably does not fit: 4x M2 track master-link keepers (below), 8x M2 cliff HC-SR04 (a 3.8 mm skin at 33.7 deg has no room behind), 2x M2 PD-trigger (edge-on into wall layers = the weakest thread orientation), 2x M3 rear panel L-feet (boxed on all four axes by the BME board, the tail pad, the TT gearbox and the glacis: 5.70 window vs 6.80 needed). Everywhere else the campaign uses a real captive nut -- probing DISPROVED the audit's insert calls twice (tilt_carrier, pedestal ears). All VERIFY_ON_ARRIVAL: vendor OD/length varies, re-key keeper_insert_* / foot_insert_* before printing 12x M2 + 2x M3 (+2 spare) 0 16 inserts + 1 insert tip
M2 brass heat-set inserts (detail) track master-link keepers (2/master x2 = 4). The captive M2 nut does NOT fit: the pocket is boxed between the jaw slot's tension wall and the neighbour A-knuckle = 5.50 mm usable vs 5.82 needed across-corners (measured, tracks agent). Inserts need 5.40. This is the one repeatedly-serviced M2-in-PLA joint, so self-tapping was never going to survive the service cycles 4 none 4x M2 brass heat-set + a soldering-iron insert tip. VERIFY_ON_ARRIVAL: vendor OD/length varies, re-key keeper_insert_{d,l} before printing masters
M5 penny washers tilt worm thrust seat (OD 15, ID 5.3, t 1.0): closes 2.0 mm of worm float (≈3° tilt slop) to 0.1. A direct shoulder is impossible because any ID under the Ø10.55 crest blocks cartridge extraction 2 check the owned washer assortment 2x M5 penny washer (OD ~15)
Ø4 dowels seam/joint registration so parts self-hold while screwing (the audit's "nothing holds it" class). +3 head (2 bezel↔back split plane, 1 Ø4x14 flange), +2 neck→platform printed pins, + existing y=26 pair. Tail seam uses a 6x12x4 tongue instead (no room beside the bore in a 10.2 pad; more shear area anyway) ~8 printed stand-ins in stl/hardware (Ø3.9) Ø4x12 metal dowels x4 (or keep printed)
M2 screws camera board (2 screwed + 2 locating pads) + cam_cover (2) + track master-link keepers (2/pod, M2×8 pan head, sunk in the tab counterbores) + PD-trigger mount (2) 8+ in the 600pc M2-M5 and 1263pc M2-M4 kits , (CLAUDE.md's "buy M2" is stale)
M3 nylon standoffs ULN2003 / driver mounts few 380pc kit (Tray 1) ,
Track hinge pins Ø1.75 filament, Ø2.2 boundary bores only (2026-07-12 print-in-place strips: the 59 in-strip joints/side ride INTEGRAL printed Ø2.0 pins). Per pod: 3 strip-to-strip + 2 at the master (its far-end pin + the jaw closure pin) = 10 pins x ~46 mm ≈ 0.5 m, cut from an owned spool (the black CR-PETG is tougher than PLA for pins) 10 spooled (Tray 1) ,
Ø4 dowel pins body-to-pod join (2 per side), Ø4x12: modeled (wall slip holes + rail press sockets) 4 0 4
HC-SR04 ultrasonic x4: forward + REAR obstacle (front/rear walls, inside the twin grille rings) + front/rear cliff (flush in the deck slopes, boards behind the 5-thick skin). Inventory has ZERO (checked 2026-07-10) 4 0 4
M4x40 + nuts road-wheel bolt-axles (head = outer hubcap, shank in Ø4.2 wheel bore, nut captive in the beam slide-up slot). HARD SHANK SPEC (2026-07-16 K1): under-head length 40 mm; plain journal 34.0..35.5 mm (covers full 30.0 wheel hub + 1.0 beam-to-wheel gap + beam path to the nut outboard face at 33.95 under-head; thread must NOT enter the wheel bore; threaded tail must still engage the M4 nut with ≥2 turns of M4×0.7 = 1.4 mm). REJECT full-thread M4x40 kits (thread chews the Ø4.2 bore or clamps the wheel). REJECT stock DIN 931 / ISO 4014 M4x40 (nominal plain ~18..20 mm starts thread inside the wheel). REJECT Ø5 shoulder screws (wheel bore Ø4.2 and beam bore Ø4.4 cannot accept Ø5). ACCEPT: partially-threaded M4x40 with measured plain shank in 34.0..35.5 mm; or the printed hw_m4_bolt stand-in (JOURNAL_L=34.7 / THREAD_L=5.3) until metal arrives. M4 nuts OWNED 10+10 nuts: OWNED (the 600pc M2-M5 kit lists M4 nuts x40, settled 2026-07-13); bolts: 0 10x M4x40 per hard shank spec

Printed parts (watertight; tank base + split head)

Materials: print head_back (all four pieces), screen_tray and head_door in PETG, not PLA. The Pi 5 lives in the closed head and sustains 70-80 C bursts under brain load (measured, see CLAUDE.md thermal notes); PLA creeps from ~60 C, so a PLA tray would slowly sag under the screen+Pi module and a PLA back wall would relax its snap tongues and tray pilots. The head_door shares the same hot bay, so it goes PETG too (its snap tongues also live longer in PETG). Everything else on the robot -- chassis, tracks, bezel, neck, pan parts, cosmetics -- can stay PLA. The black CR-PETG spool (Tray 1) covers the PETG set.

Plastic hardware stand-ins (interim, plate "Hardware stand-ins")

Until the metal order lands, every buy-list hardware row above has a PRINTED stand-in (src/standins/ -> stl/hardware/), so the whole robot dry-assembles in plastic. The BOM is unchanged; swap each stand-in 1:1 for metal on arrival.

REWORKED 2026-07-16 ("as functional and as close to reality as possible"). The M4 and M8 pairs now carry real ISO threads (src/threads.py) and screw together for real. More importantly, the rework caught three v1 parts that were physically unbuildable -- each had inherited nominal metal dimensions with no print compensation, which looks perfect in CAD:

RULE: a stand-in mates PRINTED-TO-PRINTED or PRINTED-TO-STEEL, never nominal-to-nominal. Budget 0.1-0.2 mm per printed surface, put the compliance on the printed part (crush ribs beat a tight window), and probe the real mating geometry -- never copy the metal part's numbers or the CAD placeholder's.

Stand-in Qty Replaces Notes
hw_m4_bolt 10 M4x40 bolt-axles real SHOULDER bolt: plain Ø3.9 journal 34.7 mm + M4x1.0 threaded tail 5.3 mm (verified 2026-07-16 K1 against the live stack: wheel 30.0 + gap 1.0 + beam-to-nut-outboard 33.95 under-head; purchase window plain 34.0..35.5). The shoulder is the axial stop, so the wheel stays free however hard it is done up. Ø10.4 thumb head. Stock DIN 931 M4x40 is REJECTED (~20 plain starts thread inside the wheel); full-thread kits and Ø5 shoulder screws also rejected (see BOM row)
hw_m4_nut 10 (owned steel M4 nuts) AF7 hex, real M4x1.0 internal thread + lead-in per face. AF and 3.2 thickness are the SLOT's (it is cut for an AF7 hex ACROSS CORNERS -- that is what centres it on the bore)
hw_m8_bolt 4 M8x70 end bolt-axles knurled Ø22 thumb head, smooth Ø8.0 journal under the bushings AND through the tension slot, M8x1.25 thread only where the nut runs
hw_m8_nut 8 4 inner M8 nuts + 4 outer jam nuts AF13, real M8x1.25 thread, 6.0 tall + countersinks. The pair clamps each tower; no NYLOC analogue exists in PLA, so re-snug after creep
hw_m8_washer 4 Ø14.4 washers flatted to AF13: a round Ø14.4 disc overlaps the tower nut cage by 5.2 mm³ (its old seat was deleted by running-gear v2). A printed wave washer was rejected -- it creeps by the mechanism it would compensate
hw_f688_bushing 8 F688ZZ bearings v1 could not spin (all three fits closed nominal-to-nominal). Now bore Ø8.6, body Ø15.2 + its own crush ribs at Ø16.0, flange Ø17.9, 3 axial grease grooves. Grease is required -- it is the service life, not a nicety
hw_pan_ring 20 18x Ø6 BBs v1 could not move -- the torus slid at ~96 mNm vs the pan's ~15-17 mNm. Now 18 barrel rollers + 2 spares: Ø5.9 sphere, flats on the SPIN POLES, printed axis-up / installed axis-radial. pan_cage is used again. Print at 0.1 mm layers
hw_tilt_axle 1 Ø5 silver-steel rod v1 could not be assembled (Ø5.000 into a Ø5.000 STEEL 695 bore = +0.000). Now Ø4.8 print-compensated with a center-only flat; both bearing journals stay round
hw_seam_dowel 5 Ø4x12 dowels Ø3.9 (not Ø4.0 -- a printed dowel is the other half of the tolerance stack) + lead-in chamfers
hw_foot_pin 2 Ø3x8 trim_neckfoot pins Ø3x8, not x6 -- the 2026-07-15 socket deepening (5.0 socket + 3.0 collar = flush)

Limits to respect on plastic (full rationale in src/standins/__init__.py):

Verify on arrival (caliper before printing)

The CAD models several bought parts from datasheet/typical dims, not measurements. The rule for every row: caliper the real part -> update the named param(s) -> make build -> make check + make fits -> only then print the dependent parts. Clone boards vary: barrel/jack spacings tend to be stable across clones, mounting-hole patterns are not. Printing a seat before its part has arrived is how reprints happen.

Arriving part Caliper this CAD that depends on it (do not print first)
XL4015 5A buck board LxW, mounting-hole pattern (40x20 assumed) + hole Ø, tallest component height belly power tray posts (Ø6x6, Ø2.5 pilots, build_belly_plate in src/chassis.py); board must clear the z14 ballast ribs
MP1584 mini buck board LxW only (zip-mounted, no holes) zip-anchor pair spacing (x 20/34, y -58) on the belly plate
12V PD trigger board LxW, jack position + height above board, mounting-hole spread rear-wall USB-C slot (14x8 at x -38, z0+24) + the 2x Ø1.7 M2 pilots at x -38±9
HC-SR04 x4 barrel c-c (26.0 assumed -- stable), barrel Ø (16.0/16.6 bores), board LxW, mounting-hole positions (vary by clone!) front/rear grille recesses + fascia pilots; both cliff recesses in the deck slopes (1.2 skin-back recess + 4x Ø1.6 M2 pilots each, sensor_cliff*)
Gooseneck mic windscreen foam Ø (17 assumed) + gooseneck stem Ø ear grommet Ø15 compress-fit bores + Ø19/Ø15 trim rings in the head_back side walls (ear_* params)
LD2410 / SW-420 / TTP223 board LxW + hole pattern each chassis sensor seats (being added parametrized -- treat every one as VERIFY_ON_ARRIVAL, exact modules not yet chosen)
Sense HAT Rev2 (ordered 2026-07-14) 65x56.5 outline, M2.5 holes on the 58x49 pattern, component heights replaces the IMU posts + BME bosses on chassis_base with a 4-standoff HAT seat (next base iteration; the HAT covers IMU + temp/humidity/pressure, BME688 now optional-for-gas)
Joy-IT RPI5-HEATSINK5 (ordered 2026-07-14) true installed envelope incl. fan 65x45x15 EXCEEDS the verified official-cooler keep-out 63.5x42.5x13.7 -- re-run tools/probe_cooler.py with measured dims BEFORE head install
AI Camera IMX500 (ordered 2026-07-14) module depth + lens barrel vs CM3 forehead cam pod is CM3-sized; re-fit pass needed before swapping the eye
M8x70 + jam nut + NYLOC nut across-flats (13.0 nom), nut height, washer OD, and usable thread span panel-tower clamp stack + Ø14.4 flatted washer seat
M8 shank / Ø8 journals actual shank Ø tower Ø8.4 through holes + F688 8 mm bores (a fat zinc bolt binds)
F688ZZ flange Ø (18) + width (5) + OD (16) idler Ø15.95 press seats + Ø18.5x1.0 flange recesses
Narrow LED strip strip width + dot pitch 42x5x1.5 led_slot + the 36x2.5 front lip (widen led_slot to ~54x11 only if forced onto 8x5050 sticks)
Ø5 rod actual Ø (silver steel is -0/-0.01; generic rod varies) 695-2RS 5 mm bore slip fit, head clamp bores, and the 4.0 across-flats filed datum
XH connectors crimped-head width 16x8 platform obround + neck channel passes (sized for a 5-pos XH head)

Assembly order (verified)

Task #28 insertion-path + torque-path audit (2026-07-07): a full install order EXISTS end-to-end. Every step below was checked with swept-cylinder driver-line / extraction probes on the neutral-pose mesh; the numbers in parentheses are the worst measured clearance. Read the order constraints and nasty steps at the bottom before you start: several joints are only reachable at ONE point in the sequence.

Tools you need: a 1.5 mm hex key (head-clamp grubs), a standard M3/M2 driver, tweezers, and thread-lock or CA glue (cosmetics). (The old ~95 mm slim driver is retired: the screen tray killed the 88.5 mm blind channels, 2026-07-08.)

Sub-assemblies to build on the bench FIRST (they become unreachable once seated)

Chassis + drive (fixed frame)

ACTION BEFORE ORDERING: Measure all 3 Bag-5 TT 1:120 shaft tips now. Record whether each has the Ø2 axial hole. Order M2x25 screws and Ø9 washers only for motors that actually have it. Many TT variants omit this hole.

  1. Print + prep the chassis. Confirm the deck pan-seat, the pedestal, and both TT motor pockets are clean.
  2. Fit the four structural side panels. The old separate pod_rail_L/R parts and their dowel joint are retired. Each panel carries its wheel beam and end tower as one print. Seat the front/rear splice tongue and locating pads, install its two M3 screws into captive nuts, then bolt the panel feet to the lower tub. The tongue and separated feet hold alignment before tightening. Before fitting the track, slide two M3 nuts into each shoe trap and press the matching track_shoe_{L,R}_{rear,front} upward onto its two locator pins at y=-68 or +90. Drive two M3×10 countersunk screws from below. The shoe is 10.6 mm wide in X, 14.76 mm along Y and 4.6 mm thick, with its running face at z=10.4. Fit all four shoes for a twin drive; the +90 front pair may be omitted with the optional motors.
  3. TT motors. Set each TT gearmotor: shaft +X into the sprocket hub's double-D socket, front tab into the rear-wall pocket, nub into the wall pocket, 2× M3 through the gearbox + wall. Push each upper nut past the slot-mouth crush nibs until it clicks into the captive seat; it stays retained while the motor is positioned. The M2x25 plus Ø9 washer is the preferred axial retainer only when the measured shaft has the Ø2 tip hole. Otherwise use the positive cross-pin fallback:

  4. Slide the sprocket fully onto the shaft and mark the shaft through the vertical Ø2.1 hub bore.

  5. Pull the sprocket off and file an approximately 1 mm deep notch across the round shaft arc at the mark. Do not cut through the plastic shaft.
  6. Reassemble and drop a straight Ø2 filament pin through the upper hub wall, the filed shaft notch, and the lower hub wall.
  7. Trim both ends flush with the Ø12 hub. The pin must never stand proud of the hub OD because the hub slides through the side-panel journal.
  8. Secure the upper pin end against vibration walk-out with a small drop of CA in the bore mouth, or briefly heat-mushroom it with the iron tip, still sub-flush. The bore is open-top in service, so an unsecured pin can migrate up and out.

The pin positively locks axial motion and torque. The socket crush ribs are only a handling aid. Service the pin from above with the track open. 4. Track running gear. Press an F688ZZ into EACH face of all four end idlers (Ø15.95 seat + Ø18.5 flange recess both sides). Each end wheel: M8 bolt from outboard through the bearings, add the OUTBOARD jam nut against the tower, then the flatted washer + NYLOC nut inboard, started ON THE BENCH with the deck upside down. Orient each nut HEX FLATS FORE-AFT: as the deck drops onto the tub the nut descends into its prow-cheek NUT CHANNEL (y-walls 13.8 apart), which grips the flats -- all torquing happens from the outboard head, no inboard tool ever needed. Before fitting each front NYLOC, deburr and press one 12x28x1.0 steel strip fully flush into the tower's x62 bearing-face recess. Its square ends must seat against both printed end shoulders. Rear pair: snug. Front pair: leave loose, set tension AFTER threading the tracks, then tighten from the head -- the channel holds the nut, the nut clamps the slot. Fit the sprockets on the TT shafts (they mesh the ground run under the hull; the robot's weight seats them). Push an M4 nut up each wheel-beam slot until it clicks past the two crush nibs. The nuts remain retained while the panel is handled; inserting them on the bench is convenient but not required. Then bolt each road wheel with its M4x40 from outboard -- snug, then back off 1/8 turn so the wheel spins free.

Each side-panel beam now closes around the Ø12 sprocket hub over the absolute |X|=70.2..74.5 band with a Ø12.5 full-circle printed journal. Apply a thin film of plastic-safe grease to the hub before insertion and renew it when the track is open. The remaining |X|=74.5..81.2 crossing is open on top for cross-pin access. The owned MR105 bearing cannot replace this land: its 5x10x4 bore is too small for the Ø12 hub, while the hub itself contains a Ø6 free bore, so there is no compatible race seat. 5. Join the strips + close with the master (print-in-place chain, 2026-07-12). Each pod is 4 PRINT-IN-PLACE strips (16+16+16+15 links, hinges already free off the printer -- flex every joint once to crack any sag bonds) + 1 separate master. On the bench: pin the strips end-to-end with 3 Ø1.75 filament pins (each boundary = one strip's Ø2.2 open A-bores interleaving the previous strip's Ø2.2 far bores), then pin the MASTER's far end to strip 1's first link (4th filament pin). Wrap the open chain around the pod with the front idler retracted. Seat the last filament pin (5th) in strip 4's final-link far bores, swing the master's open jaws down onto it, slide the two track_keeper bars into the jaw slot from the side faces, and lock each with its M2 into the side-face pilot. Tension the idler -- run the chain TENSIONED: the PIP joints carry 0.35 radial slop (~8 mm of loop slack vs the old filament chain) that otherwise lands as top-run sag; if the front tension slot runs out of travel on the physical chain, that is the expected first fix (more slot travel), not a link redesign. Track removal forever after: 2 M2s out, slide the keepers, lift the master off its pin.

Pan joint

  1. Pan motor + drivers. Fit the printed 32T pan_gears onto the motor's D-flats, then drop the pan 28BYJ into the pedestal can pocket (fast-pan 2026-07-12: the shaft sits OFF-axis at (-19.2, 0), gear up; ears run along X), clamp the 2 ears with M3 into the pedestal pilots from ABOVE (deck open). Mount ULN #1 and the 2nd ULN/MX1588 board on their standoffs; wiring box leads exit the pedestal +Y relief.
  2. Power tray, ballast, then belly plate. Screw the PD trigger to the rear-wall M2 pilots (jack aligned with the USB slot), the 5A buck to the belly plate's 40×20 post grid, zip the mini buck beside it, and wire per firmware/WIRING.md (leave 60 mm slack on every tray run; zip the incoming wall cable to the floor anchors as strain relief). Fit chassis_pedestal to the loose belly plate with its two printed pins near mid-slot and start the four M3x12 csk screws into the captive pedestal nuts. Leave them loose enough for X adjustment. The swept countersinks keep every screw flush anywhere in the slot instead of pulling the pedestal back to nominal. Backlash setting: mesh the 32T motor gear against the platform's 16T pinion with a 0.10 to 0.15 mm paper feeler between the flanks. Snug all four pedestal screws, remove the feeler, then rotate the platform one full turn by hand and check for tight spots. Re-set if it binds. The target is perceptible but small lash at the teeth. Load the low ballast into the rear bay + the belly-plate pockets from BELOW, then bolt on belly_plate (6× M3 csk, flush at z=7). Ballast must go in before the plate closes the floor.
  3. Pan race. Grease the pan_race lower groove, seat it on the deck floor, lay the pan_cage ring over it, and drop the 18× 6 mm BBs through the cage pockets into the groove with tweezers. The cage keeps them spaced; any later turret lift leaves all 18 sitting evenly in the groove instead of bunching and rolling out. Load all six retainer nuts through the inward-opening seat-wall slots before seating the race, since the platform and retainer close that access.
  4. Lower sub-assembly A (neck+platform) onto the BBs so the platform's upper groove captures them and its integral 16T pinion drops into mesh with the motor's 32T gear (fast-pan 2026-07-12: fit the 32T on the motor D-flats BEFORE this step). Screw drop pan_retainer straight down over the seated platform. Its 90.8 mm lip ID clears the platform's 90.0 mm top-band OD throughout the insertion path. Seat all six lobes in their deck pockets and drive 6x M3x10 into the captive nuts. The continuous lip reaches over the platform rim rebate to hold the top-heavy head down. Check the platform spins free.

Printed race acceptance (do this after the head is fitted, step 12+, so the race sees the real top-heavy load). Bench physics, not CAD: ~8 N/ball Hertz on PLA cold-flows and abrades; the 0.2 mm groove clearance sits inside FDM scatter, so a given print may rattle or pinch and will worsen with wear. The post-gear-up torque budget is ~15 mNm at the platform vs ~7 mNm assumed race friction (little headroom).

Tilt joint + head (all on the pan group)

  1. Bearings + axle cartridge. Press a 695-2RS into each neck cheek from the clevis gap (seats open flush to the inner face; a light lead-in helps start the press). Insert the prepped axle (B) through one cheek bearing, thread the worm_wheel and its two spacer tubes onto the leading end inside the gap (wheel meshes the worm), then drive the axle on into the far bearing. The round wheel bore and tubes pass freely over the round journals. With the head not yet hung, use the open rear bay to center the wheel, clock the filed flat to the -Y hub pilot, and seat the M3x4 grub flush. Loosen this grub before pulling the axle.
  2. Insert the tilt cartridge. Slide sub-assembly C (carrier + motor + worm) in from the open rear bay: the worm passes the bracket plate's Ø12.2 bore, its tail lands in the open-top cradle groove, the can registers in the Ø29 pocket, the motor's ear bar rides the neck's ear-bar channel to 0.2 behind the pocket-front wall, and the carrier's 4 bosses land on the plate/column rear faces. Drive 4× M3×16 from the rear into the captive nuts in the neck blocks -- this also captures the ear bar between the carrier's D-posts and the neck wall (hold the motor against its pins until the first screws bite). (Extraction reverses this and is UNCONDITIONAL since the 2026-07-12 3-start worm: the mesh back-drives, so with a dead motor just hand-nod the head while pulling and the worm screws itself out. The old rule -- drive the head fully UP first because the single-start pull needed ~46° of nod against ~34° of stop travel -- is retired, 2026-07-13.) Route the tilt ULN wiring on the column back standoffs, board centered at z 93, below the carrier (motor + driver both ride the pan group, so no leads cross a joint).
  3. Hang the head on the axle. Before lowering head_back, push each M3 clamp nut through its downward-facing slot mouth until it clicks past the crush nibs and seats on the cross-bolt axis. The front-down bench pose is still convenient, but retention no longer depends on it. Lower the head so its side hubs take the axle ends, then drive the two M3 pinch-clamp bolts from the rear bay. The axle now turns with the head; the worm holds tilt with the driver off ONLY marginally (fast-tilt 2026-07-12: the 3-start worm back-drives; hold is 28BYJ detent+gear friction through 4:1 -- firmware energize-hold or park at the balance point; see CLAUDE.md fast pan/tilt pass).
  4. Screen + Pi module (tray). On the BENCH: bolt screen_tray to the combined touchscreen+Pi with 4× M3×10 pan heads straight into the display's factory 126.2×65.65 bosses (the pillars are z-offset from the bores, so the driver line is open). Power-test the module on the desk if you like -- it is now a self-contained unit. Then drop the loaded tray into head_back from the open front (the pillars pass z-clear of the clamp tubes) and drive 4× M3×10 from OUTSIDE the back wall into the pillar-end pilots: short, visible screws in the fixed strip between the door outline and the hatch-frame opening. The bezel pocket locates the glass when the head closes (step 15).
  5. Camera + forehead LED. Mount the CM3 front-face-in on the 4× M2 pier bosses, trap it with cam_cover (2× M2 + ribbon pinch), drop the CSI ribbon into the pocket. Seat the WS2812 forehead segment in its recess and route the 3 wires through the wire pass to the Pi. Route the Pi power pair per the cable step below.
  6. Close the head. Bezel to back: 8× M3×35 through the perimeter posts into the captive nuts in the back bosses (screws from the front). Fit the rear service head_door (the stepped rear pod): engage the two top hook tabs first, swing the door in until both leg snap tongues CLICK behind the wall band beside the void (tool-free; replaced the 2× M3 csk 2026-07-10). To open: firm pull on the pod's bottom edge (35 mm proud, that IS the grip); the barbs' back ramps cam the tongues inboard and release.

    SERVICE (door / dead head): the tilt drivetrain occupies the pod cavity at the ±33.8° stalls by design (hollow clearance, not a closed-door clash). tools/probe_door_stall.py (2026-07-16) swept the tool-free open path (hook pivot 0..24°, then 15 mm lift along swung -Y) at tilt -33.8 / 0 / +33.8 against tilt_carrier, motor_tilt, tilt_worm, worm_wheel, and neck_clevis: all three poses path-clear on the baseline cavity (no enlargement). Stall-open is therefore geometrically possible, but the margin is millimeter-scale (closed-pose drivetrain-to-door clearance is ~2-3 mm at the stalls, and the swept path was checked at 2 deg / 1 mm resolution); neutral tilt remains the comfortable service pose (more free volume, easier hands, print tolerance and an un-held nodding head both eat into that margin). For a dead-motor or dead-Pi head: the 3-start worm back-drives, so hand-nod the head to roughly neutral first, then either hold it there with a second hand or wedge a soft prop (folded cloth / foam block) between the head chin and the chassis deck before pulling the door. Why prop: without energize-hold the head can slowly nod under its own imbalance while you work, and a hard-down park is awkward even though the open path does not collide.

15b. Antenna drives. Push one metric O-ring 6.0 x 1.0 into each modeled top-wall gland, then drop each mast in from above (rack facing the pinion slot), then hang ant_bracket on the back wall (spine + locating shoulders and 4x M3). Insert each printed ant_output_* half-shaft/pinion through its Ø4.2 bushings, fit ant_idler_axle_* and the compound ant_idler_gear_*, press ant_motor_gear_* onto the motor double-D, and bolt each 28BYJ nose-through its face plate (2x M3 into the vertical-ear pilots, shaft inboard). Each mast has its OWN motor + ULN2003 (independent control); wire both to the Pi in the head. Homing: leave park at the lowest step rate for maximum torque, then ramp. BUY: 2x metric O-ring 6.0 x 1.0, NBR or silicone; silicone is preferred for low breakaway. All 30T/12T/27T m0.8 gears, axles, shafts, and rack teeth are exported printable parts.

**BENCH BREAKAWAY PROTOCOL:** assemble one mast and ring with NO gears fitted. Measure
axial breakaway with a 0-1 N spring gauge, or hang calibrated 10/20/30 g weights from
the mast tip. Sliding breakaway target: <=0.15 N (~15 g). Park-groove breakaway target:
0.2-0.3 N (20-30 g). If outside the window, silicone grease lowers sliding friction;
a 6.0 x 0.8 ring gives a lighter section/squeeze variant; or reprint the cheap vertical
mast with `ant_park_groove_depth` adjusted by +/-0.1 mm. Groove depth is the park-force
tuning knob. Final friction and breakaway are bench validation. CAD fixes only the
geometry and the acceptable window.

Cables (per docs/CABLE-CHECK.md + firmware/WIRING.md)

  1. Wall PD brick → rear PD trigger (12 V) → belly tray bucks. The Pi-rail pair (18 AWG, from the 5A buck) coils a 2-turn service loop (~600 mm) at r≈48, z≈38–45 in the cavity → 16×8 deck pass → platform slot → neck channel → the column top-left exit window → through the bottom-rear head slot to the Pi's GPIO 5V/GND pins with ~60 mm of free head lead for the tilt drape. Set usb_max_current_enable=1 + EEPROM PSU_MAX_CURRENT=5000 (GPIO power skips PD negotiation). Software-limit pan to ±90° (hard stops at ±93.3) so the loop never over-winds.

Cosmetics LAST (glue + locating pins)

  1. Press-and-glue the pin-located cosmetic parts: trim_rail_L/R, trim_hatch_frame (after the screen, its band overlaps the 4 driver-channel mouths, up to 2.3 mm), camera_pod, and the chassis trim_fascia / trim_rear / sensor_rear grille cap. Fit fascia electronics if used: HC-SR04P barrels through the Ø16 passes, amber corner lamps, front LED strip, rear buzzer/speaker.
  2. Final check + homing. Power on; firmware stall-homes pan against its ±93.3° deck stops and tilt against its ±33.8° fin stops, backs off, and zeroes. Sweep pan ±90 and tilt ±30 and confirm the screen/Pi stack, worm, cheeks, axle, cables and bottom head edge stay clear (matches the make check-sweep gate).

Order constraints (do NOT reorder)

Nasty-but-possible steps (measured)

(2026-07-08 passes retired the old tilt-motor-ear reach, the inaccessible wheel grub, the last-track-pin loop flex, and the 4× 88.5 mm blind screen-standoff screws -- the worst step in the build is now four short bench screws plus four visible wall screws.)

Squaring up: datum + shim procedure (first build)

The tilt axis and the screen optical plane sit at the end of a long printed stack (chassis lower tub → z46 deck seam → pan race → pan platform → neck clevis → cheek 695 bearings → axle → head). CAD gates (make check / fits / joints) check part PAIRS at nominal, not accumulated tolerance. A first physical build needs this procedure to square itself up without reprinting anything.

Params cited below: clevis_half=22 (cheeks at x ±22), tilt_axis_y/tilt_axis_z = -18 / 153, scr_mount_pts factory pattern 126.2 × 65.65, base_h=66 (deck pan-mount plane), neck→platform 3× M3 on r16.5 clocked 270/30/150 about (0, neck_y=-17), head-clamp grubs at x ±30, pan race 18 BBs on Ø80 (pan_race_n / pan_race_circle_d).

Master datum

Master datum plane = the pan platform's top face as seated on the BB race. The deck's pan seat defines it (base_h=66). Nothing below the race is adjustable: the stack under it is edge-bearing (side panels prop the deck), so its errors tilt the whole turret together and the pan sweep averages them out. Errors that matter to the eye are platform-to-screen. All squaring happens between the platform and the head.

What to measure

Bench method: calipers + a machinist square. No dial indicator assumed. Do (a) after step 12 (head hung), (b)/(c) after step 13 (screen tray in), (d) anytime the race is seated.

a. Tilt-axis parallelism to the platform. Hold the platform still. Measure from the platform top face up to each exposed axle end (or the head clamp bosses at x ±30). Difference over the ~60 mm span (clamp-to-clamp, or cheek-to-cheek at clevis_half ±22) is the axis tilt. Target ≤ 0.3 mm (~0.3°).

b. Screen upright / lean. Stall-home tilt, command zero. Stand a square on the deck (or platform) and measure the gap to the glass at the top and bottom bezel edges. Difference over the ~66 mm vertical mount span (scr_mount_pts 65.65) is lean.

c. Screen twist (rotation about Y). Same square, gap to glass at the left and right bezel edges at one height. Difference over the 126.2 mm horizontal mount span is twist.

d. Pan wobble sanity. Pan slowly ±90 and watch the head top edge against a fixed reference. Cyclic rise/fall means race/platform debris or an unseated BB, not a shim case. Re-seat the 18 balls on the Ø80 groove (step 8) and recheck.

Where shims go

Symptom Correction Notes
Tilt-axis tilt (a) Shim washers under one or two of the three neck-clevis feet (3× M3, r16.5, clocked 270/30/150 about (0, neck_y)) between clevis base and platform Only place to correct axis parallelism. 695 rib seats self-center -- do not shim bearings.
Tilt zero offset (screen looks up/down at commanded zero) NOT a shim case. Loosen the two x ±30 head-clamp grubs and rotate the head on the axle (continuous trim, step 12), or fix in firmware after stall homing Grubs are the intended trim; see Nasty-but-possible above.
Screen lean / twist relative to the head shell (b, c) Shim between the screen_tray pillar ends and the head_back wall at the 4 tray screws (step 13) 0.1 mm there ≈ 0.09° over the 65.65 mm vertical spread, ≈ 0.05° over the 126.2 mm horizontal.
Any single point needs > 0.5 mm of shim STOP. Warped or mis-seated print. Find it (usually the z46 deck seam or a panel foot) Do not bury a bad stack in shims.

Clevis-foot clocking (which foot raises which side of the axis):

Shim the low side. Equal shims under 30°+150° raise the front of the clevis without rolling the axis; rear alone does the opposite pitch.

Shim stock + order

The CAD gates verify pair fits at nominal; this procedure is where the accumulated real-print tolerance gets taken out, and it is expected to be needed on a first build.

Track coupon protocol (Track coupon plate -- print BEFORE any strip plate)

The Track coupon plate carries two 5-link print-in-place strips + 1 loose master link + both keeper bars (~1 h sliced in PETG):

Unit (exporter name) PIP far-bore Radial gap on Ø2.0 pin Role
track_coupon_strip_d2.7 Ø2.7 (track_bore_pip_d) 0.35 mm Production geometry (matches the 64-link loop)
track_coupon_strip_d2.56 Ø2.56 (coupon-only) 0.28 mm Tighter-gap A/B wear candidate

Each strip is open-A first link, 3 integral-pin mids, open-far last, keels on. Production loop geometry, tools/probe_track_pip.py, and the long strip plates stay on Ø2.7 only; the Ø2.56 strip exists solely on this coupon plate (stl/base/track_coupon_tight.stl, written under EXPORT=1).

Static checks (both strips)

  1. Every PIP hinge frees after break-in flexing -- no fused knuckles.
  2. Hinge radial slop: pull the 5-link strip taut and measure total stretch vs 40.0 nominal; that gives real per-joint slack. Scale ×59 and check it still fits the 6.5 mm front idler outboard tension travel via delta_L / 1.84 (dL/d(idler_y) on the raised loop). Accept if scaled stretch ≤ 6.5 × 1.84 ≈ 12.0 mm of full-loop elongation budget; reject if a single 5-link strip already exceeds ~1.0 mm total stretch after static break-in (that scales past the tension budget).
  3. Ø2.0 integral pins unbroken after 20 full ±35 deg articulations.
  4. Keel faces clean (no sag scars), grousers flat.
  5. Ø2.2 boundary bores accept Ø1.75 filament.
  6. Master jaw drops onto the end pin; keepers slide and seat; M2 insert pilots hold.

Any static fail: adjust track_bore_pip_d / track_pin_print_d and reprint the COUPON, not a strip. Prefer the Ø2.56 strip only if it frees cleanly and beats the Ø2.7 strip on the powered stage below; otherwise keep production at 2.7.

Powered coupon (deciding bench test for mid-drive reliability)

After both strips pass the static checks, run a powered coupon on the production (Ø2.7) strip first; optionally repeat on the Ø2.56 strip for A/B wear data.

Setup (concrete):

  1. Mount a real mid-drive sprocket (printed drivewheels_* sprocket) on a TT gearmotor shaft, fixed to a bench block so the sprocket axis is horizontal.
  2. Pin the 5-link coupon into a short loop segment against the sprocket ground-run mesh (use Ø1.75 filament at the open ends, or clamp the strip as a weighted flat run over the sprocket with ≥200 g hanging on the free end so pins stay seated in the conjugate pockets). A hold-down shoe or a wooden block with a 0.9 mm gap over the link crowns is enough to keep the mesh from lifting.
  3. Drive the TT through the chassis MX1588 (or a bench H-bridge) at the firmware 80% PWM cap (never 100%: 27 W budget rule). Forward 60 s, reverse 60 s, then continuous forward for the break-in block.

Break-in: 30 minutes continuous at 80% PWM, or 5000 sprocket revolutions, whichever comes first. (At ~120:1 TT gearing and ~200 RPM motor free speed under light load, 30 min is on the order of several thousand pin engagements; count revs with a mark on the sprocket if you can.)

Re-measure after break-in (same gauges as static):

Metric Accept Reject
Per-joint bore ovalization (major − minor of each PIP B bore) ≤ 0.10 mm > 0.15 mm on any joint (pins are hammering the bore oval)
5-link strip free length stretch vs 40.0 nominal ≤ 0.8 mm total > 1.0 mm total (scales ×59 → > ~12 mm loop elongation, past the 6.5 mm outboard tension travel via /1.84)
Skip / lift under reverse at mesh no tooth skip any skip (erodes the 2.14 mm rigid-chain skip barrier measured by tools/probe_track_pip.py; shoes only leave 0.9 mm of lift room)
Integral pin integrity no cracks any pin crack or break (a broken pin scraps a whole 16-link production strip)

This powered coupon is the deciding bench test for mid-drive reliability under representative load and reverse mesh. Do not commit the 6.8 h strip plates until the Ø2.7 strip passes. If Ø2.7 fails ovalization/elongation but Ø2.56 passes, consider adopting 2.56 into production (update track_bore_pip_d, re-run probe + coupon) only after a second powered coupon confirms it.

Mid-drive engagement is quantified, not assumed: the conjugate 14T mesh has contact ratio CR 1.48 and the rigid-chain pitch-ratchet barrier from tools/probe_track_pip.py is 2.14 mm. Fixed road-wheel bolt-axles cap lift to about 0.1 mm at their stations; each hold-down shoe caps the sprocket mesh window to 0.9 mm.

PIP hinges are fatigue/wear items: integral Ø2.0 pins hammer Ø2.7 bores oval, designed slack grows past the ~8 mm tension budget, the 2.14 mm skip barrier erodes, and a broken integral pin scraps a whole 16-link strip. Expected wear life is unknown until the powered coupon above has real numbers (log hours and km of desk travel when you have them).

Replacement procedure:

  1. Open the master link: remove the 2 keeper M2 screws, slide the keeper bars out, swing the C-jaw off the closure pin.
  2. Count links from the master along the loop to the damaged strip segment (production layout per side: strips of 16+16+16+15 + 1 master).
  3. Drop out the worn strip at its two filament boundary pins (or cut a fused pin if a mid-strip pin failed), seat the spare strip, re-pin the boundaries with Ø1.75 filament (press tip into the Ø1.6 step).
  4. Re-close the master, refit keepers + M2s, re-tension the front M8 until the mesh bites without top-run flogging.

Print one spare production strip per side with the first batch (same PETG spool as the loop; the 16-link size covers the three long segments; also keep one 15-link if you can spare the plate). Masters and keepers are also consumables on the same service path (2 M2s + jaw wear).

Bearing-seat coupon protocol (print BEFORE neck_clevis and any wheel plate)

Print hw_coupon_695 and hw_coupon_f688 in the supplied orientations. Their five steps are marked by 1..5 tick notches and ascend through 0.075 / 0.100 / 0.125 / 0.150 / 0.175 mm rib crowns. Press the REAL bearing into the steps in ascending order. The correct step is the LOOSEST one where all of these are true:

  1. Seating needs a firm thumb or arbor push, not a tap-in drop.
  2. The seated outer race cannot be turned by finger torque.
  3. The bearing cannot be pushed back out by thumb from behind.
  4. There is no whitening or cracking at the rib roots.
  5. For the 695 only, the bearing still spins freely after seating, proving its shields were not crushed.

One step looser will creep. If two adjacent steps pass, choose the looser one. Re-key brg695_rib_proud or idler_rib_proud to the winning crown, run make build and make check, and only then print neck_clevis or the wheel plates. Repeat this protocol for every filament brand or material change. Results do not transfer between spools.

Axle flat + wheel grub coupon

Print the worm_wheel, or a short hub coupon with the same Ø11 outside diameter, round Ø5.2 bore and radial Ø2.5 pilot. Also print a simple fork gauge with two open slots: 4.1 mm GO and 3.9 mm NO-GO. This gauge is documented rather than exported as a stand-in because adding a new export, plate entry and invariant is more than the roughly 40-line trivial-part threshold.

  1. File a 9.5 mm long flat to 1.0 ±0.1 mm depth. The 4.1 GO slot passes over the across-flats dimension; the 3.9 NO-GO slot does not.
  2. Slide the round hub over an untouched round journal and center its pilot over the flat.
  3. Seat an M3x4 cup-point grub flush with the hub OD. Confirm its cup lands on the flat.
  4. Hold the axle and reverse wheel torque by hand. Confirm no hub rock and record backlash feel.
  5. Loosen the grub and confirm the hub again slides over both round journal sections.

Grub-on-flat backlash versus the retired chordal D-key is bench-only. If the coupon rocks or slips, adjust wheel_grub_pilot_d or axle_flat_depth, then reprint the coupon.

Tilt torque + holding bench coupon

Run this protocol on the complete physical head before choosing the production worm pair:

  1. Measure residual imbalance about the axle with a spring scale at a measured lever arm at tilt 0, +15, -15, +30 and -30 deg. Record the worst value in mNm.
  2. De-energize at +30, -30 and 0 deg. Watch each position for 10 minutes and record any nod or drift.
  3. Slew under the real head load at full speed in both directions through the full -30 to +30 deg working range. Record stalls, missed steps or hesitation.
  4. Energize-hold off balance for 10 minutes. Record coil current and motor temperature.

Decision rule: if worst imbalance exceeds about 50 mNm, any de-energized nod is not acceptable to firmware parking, or the loaded slew stalls, swap to the committed single-start fallback pair. It is a drop-in, see docs/WORM.md. A spring-ball neutral detent on the wheel hub remains the print-3 option and is deliberately not modeled, per the 2026-07-13 decision. Prove the need on the physical head first.

Recommendations (bigger than this pass)

Wiring

See firmware/WIRING.md (2026-07-08; power review 2026-07-16) for the full architecture: 15 V PD-trigger input from a 45-65 W brick (12 V = 27 W fallback), dual-buck belly tray (5.1 V Pi rail + 5 V motor rail), hard current limiting (CC pot + TT-branch polyfuse), the brownout test protocol, what crosses each joint, Pi 5 config flags, connector/labeling rules, and the buy-list delta. Short version: only the Pi-rail pair crosses tilt; the pan loop carries that pair plus the thin motor-rail/signal bundle; DSI and CSI ribbons never leave the head.

Order now (by lead-time importance)

  1. 8x F688ZZ flanged bearings (8x16x5, flange Ø18): most specific part, slowest to source; the end-idler seats are modeled around them (2 per wheel x 4 since the mid-drive). 1b. 10x M4x40 road-wheel bolt-axles to the hard shank spec: under-head 40 mm, plain journal 34.0..35.5 mm measured with calipers before accepting the bag. REJECT full-thread kits and stock DIN 931 / ISO 4014 M4x40 (~20 mm plain). REJECT Ø5 shoulder screws (bores are Ø4.2 / Ø4.4). M4 nuts are owned. Until metal arrives, print hw_m4_bolt (JOURNAL_L=34.7).
    • 4x M8x70 + 4 jam nuts + 4 NYLOC nuts + 4 washers (end bolt-axles; Bag 13 has no M8).
  2. 4x HC-SR04 (forward + rear obstacle + 2 cliff; zero owned; plain 5V is fine on the Arduino I/O plane). TT gearmotors are COVERED (own 3); buy 1 more only for the optional twin-drive 4th station.
  3. Power electronics (firmware/WIRING.md, re-specced 2026-07-16): a 45-65W USB-C PD brick that advertises 15V (the official 27W is a 12V fallback with firmware co-scheduling load-bearing), 15V PD trigger, XL4015-class 5A buck (CC/CV variant), MP1584 mini buck, 2A-hold polyfuse (MF-R200 class) for the TT branch, JST-XH kit + crimper, 1 m 18 AWG silicone pair plus 24-26 AWG high-flex silicone for the pan-loop runs, inline blade-fuse holder (the 5A blade fuse itself is owned).
  4. 1 m narrow addressable LED strip (4–5 mm wide, SK6805-2427 / WS2812-2020, ≥160 LED/m), one purchase covers the forehead 8-LED segment and the front 7-dot strip. (Alternative: widen led_slot to ~54x11 and buy two common 8x5050 sticks.)
  5. Ø5 SOLID rod ~100 mm (tilt axle; NOT tube) + Ø4x12 dowels for the remaining registered shell/seam joints. The retired body-to-pod rail joint no longer consumes four.
  6. 6 mm airsoft BBs (bag of 100+; need 18): cheap, everywhere.
  7. Optional: MAX98357A I2S amp x1 (pairs with the owned 8Ω speaker), amber LEDs x2–5.

Every board/hardware item above lands in the "Verify on arrival" table -- caliper before printing its seat.