One column on the TMC5240 dev kit. A NUCLEO-G474RE drives a TMC5240-BOB. The BOB drives one lead-screw column on a wooden jig (or the fully printed stand, step E2). A TBMA732 encoder sits on the motor's rear shaft. Use it for software bring-up, weight tests (including 1.4 vs 2.0 A run current), failure tests and encoder health. Parts and prices: parts list. 3D: jig model.
| Part | Qty | Note |
|---|---|---|
| Board | ||
| NUCLEO-G474RE | 1 | STLINK-V3E on board, USB Micro-B (CN1) |
| TMC5240-BOB | 2 | 1 + 1 spare. 4.5–36 V, max 2.12 A RMS, SPI only, no bulk capacitor fitted |
| Watterott 201835 heatsink + Orion OD4010-24HB 24 V fan | 2 + 2 | On the BOB above 1.4 A |
| StarTech USB2CUB1M, USB-C to Micro-B | 1 | Nucleo to Mac |
| SparkFun 12794 M-F jumpers (20 × 150 mm), Sullins jumper shunts | 1 + 2 | Logic: 8, female end cut off and soldered into the BOB. Encoder: 6 (step L). The shunts stay unused: the BOB has no pins, L7–L8 get a wire bridge |
| Adafruit CAN Pal 5708 | 2 | Later (CAN FD), not wired |
| Power and protection | ||
| Lab dual supply (lab's own) | 1 | CH2 = motor line, 24–30 V, limit 3 A. CH1 = fan, 24 V / 0.1 A. OUTPUT button = the stop |
| Fuse 3 A / 58 V MINI (0997003.WXN) + Keystone 3568 holder | 3 + 1 | In from the first power |
| Nichicon UHW1J222MHD, 2200 µF / 63 V | 2 | On the rail |
| Pololu #3777 shunt, 33 V | 1 | On the rail, on standoffs |
| Panasonic EEU-FR1H101, 100 µF / 50 V | 2 | At the BOB's R1/R2 + 1 spare |
| WAGO 221-415 (5-way) / 221-412 (2-way) | 2 / 4 | RAIL+ and RAIL− junctions / motor leads |
| Wire 1.5 mm² red + black, 0.5 mm² red + black, 4 mm banana plugs, 1.5 mm² ferrules, heat-shrink, cable ties | — | 1.5 mm² = supply to rail; 0.5 mm² = soldered to the BOB |
| Column | ||
| 17HS16-2004D motor | 2 | 1 + 1 spare. Black + green = coil A, red + blue = coil B |
| Tr8x12 lead screw, 200 mm, + brass nut | 2 | 1 + 1 spare. 12 mm per turn |
| Adafruit 1176 coupler 5→8 mm, Olimex KFL08 bearing | 2 + 2 | 1 + 1 spare each |
| Pololu #2266 bracket + 2 mm shim (local) | 2 + 1 | Shim behind the leg |
| DeoxIT L260 grease | 1 | For the brass nut |
| Printed: shelf_stop, nut_block, tower, platform, peg | — | docs/print/. Print now |
| Local: two planks, wood screws, M3 × 25 / M3 × 30 / M4 × 20 + nuts, glue, clamp | — | Jig stand and tower |
| No wood: printed stand_base, stand_tube, stand_carrier, stand_post | 1 each | Instead of the planks, shelf, nut block and tower (step E2). Same column parts; M3 × 6 (Pololu bag), M3 × 25 and M4 × 20 + nuts |
| Encoder | ||
| MPS TBMA732-Q-LT-00A | 4 | 1 + 3 spares. MA732, 3.3 V only, A/B/Z 1,024 ppr out of reset. CN1 / CN2 come fitted: two red 8-way SMD receptacles, wires soldered on their legs (step L) |
| Radial 9049 magnet, 6 × 2.5 mm N35SH | 5 | Diametric, 150 °C max |
| Printed: encoder_magnet_holder, encoder_mount, encoder_tray | 1 each | Holder: print now. Mount and tray: after measuring the rear shaft (REAR_SHAFT_MM) and the board thickness (pcb_t), step L |
| M2.5 × 5 nylon, × 8, × 6; 0.5 mm nylon washers; 28 AWG rainbow ribbon | — | 4 screws each, 10 washers. Encoder leads: 4 ribbon wires + 2 cut M-F jumpers (step L) |
| Tools and weights | ||
| Biltema dial gauge 19-1443 + magnetic stand | 1 | On a steel plate ≥ 80 × 80 × 5 mm (local) |
| Weight discs 2.5 kg / 1.25 kg, kitchen scale 5 kg | 1 / 2, 1 | Biltema |
| From the lab | — | Meter, scope, soldering iron, crimper, thermocouple or IR thermometer, foam pad, pin or M4 bolt |
RAIL+ and RAIL− are two 5-way WAGO 221-415 lever connectors, not lengths of wire. Nothing is cut or chained: each part sits across the two WAGOs, so everything is in parallel. Only the 3 A fuse is in series, in the CH2 + lead.
| Opening | RAIL+ | RAIL− |
|---|---|---|
| 1 | Lead from the 3 A fuse (CH2 +) | CH2 − lead |
| 2 | C1 + | C1 − (striped leg) |
| 3 | C2 + | C2 − (striped leg) |
| 4 | Shunt + (VIN) | Shunt − (GND) |
| 5 | 0.5 mm² red to BOB R1 +VM | 0.5 mm² black to BOB R2 GND |
| Part | How |
|---|---|
| 2200 µF caps | Legs straight into the WAGOs, not shorter than ≈ 10 mm. Stripe = minus. Reversed, a cap can burst. |
| 33 V shunt | Only + and −. It sits across the rail and clamps above 33 V. |
| WAGOs | 10–20 cm from the BOB. |
| 100 µF | Soldered directly on the BOB's R1 (+VM) and R2 (GND), stripe to R2, shortest legs, together with the two rail wires. |
| Motor | Short 0.5 mm² wires soldered to R3–R6. Each motor lead joins its wire in a WAGO 221-412: black R4 A1, green R3 A2, red R6 B1, blue R5 B2. Wrong direction: params --set invert_dir_mask=1 (column disabled). |
| Rules | Never plug or unplug the motor with power on. First power at 24 V with the CH2 limit at 0.5 A, then 30 V. Never above 30 V. |
The BOB has bare holes: every wire at the BOB is soldered S. Logic leads: an M-F jumper with its female end cut off, D at the Nucleo. Encoder VDD / GND: female end D on the Nucleo's morpho pin.
| # | From | To | How | Wire | Note |
|---|---|---|---|---|---|
| Motor line (CH2) | |||||
| P1 | CH2 + | Fuse holder | B→S | 1.5 mm² red, red banana plug | 3 A / 58 V MINI in a Keystone 3568: solder, heat-shrink both pins, tie down. Not a 32 V car fuse. |
| P2 | Fuse holder, other pin | RAIL+ (WAGO 221-415) | S→W | 1.5 mm² red, ferrule | |
| P3 | CH2 − | RAIL− (WAGO 221-415) | B→W | 1.5 mm² black, black banana plug, ferrule | Don't link CH1 − and CH2 − at the supply. |
| P4 | C1 +, C2 + / C1 −, C2 − | RAIL+ / RAIL− | ·→W | Cap legs straight in, ≥ 10 mm | 2 × 2200 µF / 63 V. Stripe = minus, to RAIL−. |
| P5 | Shunt VIN / GND | RAIL+ / RAIL− | S→W | 1.5 mm², short | Pololu #3777 on standoffs, away from caps and wood. |
| P6 | RAIL+ | BOB R1 +VM | W→S | 0.5 mm² red, soldered at the BOB | Never above 30 V (BOB max 36 V). |
| P7 | RAIL− | BOB R2 GND | W→S | 0.5 mm² black, soldered | Star point at the BOB. The only other ground is the Nucleo's, through L2. |
| P8 | 100 µF / 50 V | R1 (+) and R2 (−), at the pins | ·→S | Soldered on the pins, shortest legs | Stripe to R2, together with P6 and P7. The BOB has no bulk cap (C209 not fitted). |
| Motor (BOB right header) | |||||
| M1 | R4 A1 | Motor black | S→W | 0.5 mm² soldered at the BOB, WAGO 221-412 to the motor lead | Coil A |
| M2 | R3 A2 | Motor green | S→W | ||
| M3 | R6 B1 | Motor red | S→W | Coil B. Wrong direction: params --set invert_dir_mask=1, column disabled. | |
| M4 | R5 B2 | Motor blue | S→W | ||
| Logic (Nucleo → BOB left header) | |||||
| S1 | CN6-4 3V3 | L1 VCC_IO | D→S | M-F jumper, female end cut off: soldered into the BOB hole, male end into the Nucleo | |
| S2 | CN6-6 GND | L2 GND | D→S | Logic ground | |
| S3 | CN5-3 D10, PB6 | L3 CSN | D→S | Idle high | |
| S4 | CN5-6 D13, PA5 (SPI1) | L4 SCK | D→S | Also drives LED LD2: don't use LD2 | |
| S5 | CN5-4 D11, PA7 (SPI1) | L5 MOSI | D→S | ||
| S6 | CN5-5 D12, PA6 (SPI1) | L6 MISO | D→S | ||
| S7 | L8 CLK | L7 GND | S→S | Bridge wire (a cut capacitor leg), bottom side | CLK to GND = internal clock. Must not float. |
| S8 | CN5-1 D8, PA9 | L9 DRV_ENn | D→S | M-F jumper, female end cut off, soldered | BOB pull-up: driver off until the MCU drives it low |
| S9 | CN9-8 D7, PA8 | L10 SLEEPn | D→S | M-F jumper, female end cut off, soldered | Optional (BOB pull-up). Low resets every register. |
| — | A0 / PA0, DIAG0 PA10, DIAG1 PB5 | open | — | — | PA0 open reads 'pressed', hence --no-estop. DIAG: the firmware reads the flags over SPI. |
| — | L11 REFL, L12 REFR, R7 GND, R8 AIN, R9 OV | open | — | — | Keep free on the Nucleo: PC4/PC5, PB3, PA13/PA14 (SWD), PA11/PA12 (FDCAN1 later). |
| Encoder (TBMA732 → BOB), fitted in step L | |||||
| E1 | CN1.1 VDD (leg) | Nucleo CN7-16 3V3 | S→D | M-F jumper (red, black), male end cut off: soldered on the leg, female end on the Nucleo's morpho pin | Never 5 V: MA732 abs max 4.6 V. CN7-18, right beside CN7-16, is 5 V. Same net as L1: nothing is cut or soldered at the Nucleo or the BOB. |
| E2 | CN1.2 GND (leg) | Nucleo CN7-8 GND | S→D | Same net as L2 | |
| E3 | CN1.8 TEST (leg) | CN1.2 GND (leg) | S→S | 2 cm of white ribbon wire, insulated | MA732 datasheet: connect TEST to ground; the board leaves it open (user guide Fig. 9). Outside legs 4 and 6 |
| E4 | CN2.5 A (leg) | BOB R10 ENCA | S→S | 28 AWG ribbon wire (yellow, green, blue, brown), soldered on the leg and in the BOB hole. The four are twisted together, away from the motor leads | 1,024 ppr = 4,096 counts/rev out of reset |
| E5 | CN2.4 B (leg) | BOB R11 ENCB | S→S | ||
| E6 | CN2.6 Z (leg) | BOB R12 ENCN | S→S | Index, once per rev | |
| E7 | CN1.8 leg (GND through E3) | BOB R7 GND | S→S | Return path along A / B / Z. On leg 8 so that no leg takes more than two wires | |
| — | CN1.3–1.7 (–, CS, SCLK, MISO, MOSI); CN2.1–2.3 (SSCK, SSD, PWM); CN2.7 MGL, CN2.8 MGH | open | — | — | MGL / MGH: meter only, at gap setup (both low at the set gap). Beep check of every used leg: step L |
| Fan (CH1) and USB | |||||
| F1 | CH1 + (24.0 V, limit 0.1 A) | Fan red | T→· | Fan lead | Only for runs above 1.4 A. Never on the 30 V rail (fan max 26.4 V). |
| F2 | CH1 − | Fan black | T→· | Fan lead | |
| U1 | Nucleo CN1 (Micro-B) | Mac | U→U | StarTech USB-C to Micro-B | Powers the Nucleo, flashing, serial at 921,600 baud |
| Part | Setting or behaviour |
|---|---|
| Stop | The supply's OUTPUT button, within reach of where you stand at the jig. It must switch CH2; if the supply has one button for all outputs, use that. |
| Over-current | CH2 limit 3.0 A is the first fuse. The 3 A / 58 V fuse protects the wiring if the limit is set wrong. |
| Over-voltage | 2 × 2200 µF + 33 V shunt on the rail, 100 µF at the BOB. Supply OVP 35.0 V: above the shunt's 32.0–34.0 V, below the driver's 36 V. |
| Firmware | --no-estop on every command (PA0 not wired). STREAM mode: 200 ms without a frame sends the column to rest height. MANUAL mode (all bench tests): a running move finishes (≤ 120 mm, ≤ 0.6 m/s), then holds. There the button is the only stop. |
| After a stop | The caps hold R1 up ≈ 0.1 s (estimate). The 10 ms health poll then sees undervoltage, disables the column and drops its reference. To continue: CH2 on, then init, enable, home. |
| Power order | On: USB, then CH2. Off: disable, CH2 off, CH1 off, USB last. |
firmware/board/board_g474.hMCU STM32G474RE, 24 MHz HSE
SPI1 TMC5240 mode 3, 40-bit, 170 MHz / 32 = 5.3 MHz (/64 if errors)
SCK PA5, MISO PA6, MOSI PA7, CS PB6
DRV_ENn PA9, push-pull (BOB pull-up: off until low)
SLEEPn PA8, held high (low wipes every register)
DIAG0 / DIAG1 PA10 / PB5, internal pull-ups, active low (not used yet)
E-stop PA0, internal pull-up, high = stop; bench: not wired, --no-estop
Motion chip ramp in velocity mode follows the 1 ms planner;
positioning mode at rest; homing with SW_MODE.sg_stop
Microsteps MRES 64 + intpol = 12,800 per rev (AMAX limit at 256)
Encoder TBMA732 (MA732) A/B/Z, 1,024 ppr, no SPI or setup
boots at enc_counts_rev 4096: binary, ENC_CONST 0x00032000
(3.125); reversed: encoder_invert_mask=1 (0xFFFCE000)
Encoder lag MA732 8-10 us = about 2 counts at 50 rev/s; ABZ
hysteresis 0.52 deg = 6 counts (limit below: 41 counts)
Lost steps ENC_DEVIATION (0x3D) = 128 (2 full steps), ENC_STATUS read
every 1 ms -> event ENC_DEVIATION
GCONF.stop_enable = 0 (else ENCA high stops the motor)
Limits accel <= 7,857 mm/s^2, speed <= 600 mm/s
Current RREF 12k: max 2.12 A RMS; cap 1.4 A after every boot; watch otpw
Link to Mac LPUART1 PA2 / PA3 via ST-LINK VCP, 921,600 8N1, 0483:374E
Flash ST-LINK, app at 0x08000000, no bootloader
Keep free PA11 / PA12 (FDCAN1), PA13 / PA14 (SWD)
firmware/dist/firmware_nucleo_g474re_cube.bin (STM32Cube build; ping shows a build id ending in -cube). It has run on a bench: boot, the ST-LINK link, SPI-OK, init read-back and 120 mm strokes at 0.8 A. Every command carries --board nucleo-g474re --no-estop: it finds the ST-LINK port at 921,600 baud, and no E-stop is wired. bench = .venv/bin/bench from the repository root (or run source .venv/bin/activate once: then the blocks below paste as they are; it is python -m link, the code in software/link). Details: firmware/README.txt section 12. Ticks stay in this browser.You will: solder S the BOB wires, the 100 µF, the fuse holder, the banana plugs and the shunt leads; crimp ferrules and push into WAGOs W; plug the jumpers' male ends into the Nucleo D. Every wire at the BOB is soldered. Soldering first, plugging last. Beep-check in B before any power.
no solderingYou will: beep and measure with the meter. Everything still unpowered.
no solderingYou will: plug USB U, switch CH2 on, run commands.
no solderingYou will: run commands, motor on the table.
bench --board nucleo-g474re --no-estop ping bench --board nucleo-g474re --no-estop regs bench --board nucleo-g474re --no-estop vsupply bench --board nucleo-g474re --no-estop current 0.8 bench --board nucleo-g474re --no-estop enable bench --board nucleo-g474re --no-estop spin --revs 1 --rps 1 bench --board nucleo-g474re --no-estop disable
no solderingYou will: screw and glue the jig. To fit the motor, open its four WAGOs W and close them again after.
docs/print/ (PETG or PLA, 4 walls, 40–60 % infill, no supports). Power off and the motor WAGOs open while you mount it.no solderingYou will: print four parts and bolt the column into them, instead of E1–E7. The heights match the wooden jig, so the coupler, the bearing and step L's encoder parts sit exactly as there.
docs/print/stand_*.stl, made by make_parts.py, which checks every fit: the motor bolts, the coupler, the screw, the full stroke and the encoder under the motor. PLA or PETG, 0.4 mm nozzle, 0.2–0.28 mm layers, 3 walls, 20–30 % infill, no supports. Every part fits 180 × 180 × 180 mm (Prusa Mini).| Part | How to print | Mass | Hours, Mini / MK4 |
|---|---|---|---|
stand_base | Upright, feet on the bed. The motor plate and the top plate print as 43 mm bridges | ≈ 47 g | ≈ 4.3 / 2.1 |
stand_tube | Upright, foot plate on the bed (177 mm tall) | ≈ 50 g | ≈ 4.6 / 2.3 |
stand_carrier | Flat, nut traps down, 40 % infill | ≈ 11 g | ≈ 1.0 / 0.5 |
stand_post | Upside down, platform on the bed | ≈ 50 g | ≈ 4.6 / 2.3 |
| Total | Estimates; the slicer's figure counts | ≈ 160 g | ≈ 14.5 / 7 |
Hardware: the motor, coupler, KFL08, screw and brass nut from E; 4 × M3 × 6 from the Pololu bag (motor); 2 × M4 × 20 + nuts (the KFL08 bolts also clamp the tube to the base); 4 × M3 × 25 low-head + nuts (brass nut to the carrier). No wood, no glue.
How it works: the motor hangs under the base's motor plate, shaft up. The tube stands on the base. The KFL08 sits on the tube's foot plate, under the tube's floor, between two side walls. The square carrier holds the brass nut and slides in the square tube, so it can't turn. At home it sits on the tube's floor, which is the stall stop. The post is a square sleeve with the 100 × 100 mm platform on top. It rests loose on the carrier and goes up and down with it. Boxes go on the platform. The free space under the motor (37 mm, open underneath, the front side open) takes the encoder stack of step L for any rear shaft up to ≈ 20 mm.
no solderingYou will: run commands.
no solderingYou will: run commands.
sgtune, then params --set sgt_home=… (lower = more sensitive).bench --board nucleo-g474re --no-estop enable bench --board nucleo-g474re --no-estop setpos 60 bench --board nucleo-g474re --no-estop move 80 --speed 10 bench --board nucleo-g474re --no-estop move 40 --speed 10 bench --board nucleo-g474re --no-estop params --set tcool_speed_mm_s=15 bench --board nucleo-g474re --no-estop home bench --board nucleo-g474re --no-estop move 115 --speed 50 # raise slowly bench --board nucleo-g474re --no-estop move 0 --speed 10 bench --board nucleo-g474re --no-estop disable
no solderingYou will: change the supply, measure, press OUTPUT.
bench --board nucleo-g474re --no-estop init # CH2 was off: driver re-initialised bench --board nucleo-g474re --no-estop vsupply bench --board nucleo-g474re --no-estop enable bench --board nucleo-g474re --no-estop home bench --board nucleo-g474re --no-estop move 115 --speed 100 bench --board nucleo-g474re --no-estop move 5 --speed 400 # press OUTPUT mid-move # after the stop: CH2 on, then bench --board nucleo-g474re --no-estop init bench --board nucleo-g474re --no-estop enable bench --board nucleo-g474re --no-estop home
no solderingYou will: assemble the tower, set up the dial gauge, weigh.
| Load | Total moving mass | Roughly |
|---|---|---|
| 2.5 N | 0.32 kg | bolts or a water bottle |
| 10 N | 1.08 kg | tower + light load |
| 25 N | 2.61 kg | 2 × 1.25 kg discs + tower |
| 40 N | 4.14 kg | 2.5 + 1.25 kg discs + tower |
no solderingYou will: stick on the heatsink; clamp the fan leads under CH1's terminals T; run tests.
Predictions from reports/BENCH_V1/PREDICTIONS.md (archived study: studies/ARCHIVE.md, tag pre-cleanup), made for the TMC2240; the TMC5240 has the same power stage. Cruise ≤ 0.6 m/s, ramp 4 m/s².
| Test | How | Prediction | Wrong if |
|---|---|---|---|
| Light loads | 2.5 N and 10 N: 20 × (lift 0→120, drop, step 60→70) at 0.4 m/s | No lost steps | Any stall flag, or gauge > 0.5 mm off |
| Timing | 10 N: full lift and 10 mm step; firmware times + 240 fps video of the ruler | Lift ≈ 0.4 s, step ≈ 0.1 s | Lift > 0.5 s or step > 150 ms |
| Speed vs load | 25 N and 40 N, cruise 0.1 → 0.6 m/s, 20 clean cycles per speed. Once at 1.4 A, once at 2.0 A (below). | 2.0 A: 25 N clean to ≈ 0.4 m/s, 40 N to ≈ 0.3 m/s. About half the 40 N lost steps of 1.4 A. | 2.0 A no better than 1.4 A at 40 N |
| Motor heat | 10 min of cycles at 40 N at each current; motor case temperature at the end | Copper loss ≈ 4.3 W at 1.4 A, ≈ 8.8 W at 2.0 A | Case > 80 °C at 2.0 A |
| Stall detection | 20 provoked stalls at 0.4 m/s, then 20 clean cycles at 10 N | ≤ 2 of 20 missed, no false flags | More missed, or any false flag (re-tune first) |
| Return accuracy | Dial gauge after each block of 20 cycles | Within 0.5 mm | > 0.5 mm (lost steps or slipping coupler) |
| Friction (optional) | Slide test, cardboard box on the woven PTFE fabric, ≈ 5 kg and ≈ 10 kg | As measured at 0–575 g: static 0.23, kinetic 0.16 | Friction clearly rises with load |
bench --board nucleo-g474re --no-estop a1 light --mass-kg 0.32 --cycles 20 # 2.5 N bench --board nucleo-g474re --no-estop a1 light --mass-kg 1.08 --cycles 20 # 10 N bench --board nucleo-g474re --no-estop a1 timing --mass-kg 1.08 bench --board nucleo-g474re --no-estop a1 speedload --mass-kg 2.61 # 25 N, 0.1 -> 0.6 m/s bench --board nucleo-g474re --no-estop a1 speedload --mass-kg 4.14 # 40 N bench --board nucleo-g474re --no-estop a1 stall --mass-kg 1.08 --speed 400 bench --board nucleo-g474re --no-estop a1 return --mass-kg 1.08 --blocks 3
Log moving mass, speed, stall flags, gauge and the scope's rail peak (the tool asks). Keep falls short: a fast-falling load drives the motor as a generator into the shunt. A move runs to its target: OUTPUT is the only stop.
Decides the production preset: 2.0 A only if it clearly loses fewer steps at 40 N and the case stays ≤ 80 °C. The firmware caps the current at 1.4 A after every boot.
# 1.4 A bench --board nucleo-g474re --no-estop current 1.4 --hold 0.8 bench --board nucleo-g474re --no-estop a1 speedload --mass-kg 2.61 --notes "1.4 A" bench --board nucleo-g474re --no-estop a1 speedload --mass-kg 4.14 --notes "1.4 A" bench --board nucleo-g474re --no-estop a1 light --mass-kg 4.14 --speed V --cycles N --yes --notes "1.4 A heat" bench --board nucleo-g474re --no-estop vsupply # V = top clean 40 N speed at 1.4 A (100 if none); N = cycles for 10 min (time 20 first) # 2.0 A (heatsink and fan on) bench --board nucleo-g474re --no-estop params --set current_cap_ma=2000 bench --board nucleo-g474re --no-estop current 2.0 --hold 0.8 bench --board nucleo-g474re --no-estop a1 speedload --mass-kg 2.61 --notes "2.0 A" bench --board nucleo-g474re --no-estop a1 speedload --mass-kg 4.14 --notes "2.0 A" bench --board nucleo-g474re --no-estop a1 light --mass-kg 4.14 --speed V --cycles N --yes --notes "2.0 A heat" bench --board nucleo-g474re --no-estop status # back to the default (current first: the cap can't go below it) bench --board nucleo-g474re --no-estop current 1.4 --hold 0.8 bench --board nucleo-g474re --no-estop params --set current_cap_ma=1400
no solderingYou will: open and close the four motor WAGOs W; run tests.
bench --board nucleo-g474re --no-estop move 60 --speed 50 bench --board nucleo-g474re --no-estop disable # A and B # C: CH2 off instead; afterwards CH2 on, then bench --board nucleo-g474re --no-estop init bench --board nucleo-g474re --no-estop enable bench --board nucleo-g474re --no-estop home
You will: solder S six wires and the TEST bridge onto the legs of the TBMA732's two red connectors, before the board goes into its tray, then beep every used leg against the MA732 before any power. VDD and GND: M-F jumpers, female end D onto the Nucleo's CN7-16 and CN7-8. A, B, Z and the spare GND: 28 AWG ribbon wires, soldered S into the BOB holes R10, R11, R12 and R7.
The TBMA732 on the rear shaft feeds the BOB's encoder inputs (rows E1–E7). The chip compares it with its own position every 1 ms and raises ENC_DEVIATION at 2 full steps. 3D, step by step: jig model, tab "Encoder". The model still shows bare pads and the old leads; the legs sit on those same pads.
encoder_magnet_holder (it doesn't depend on the shaft length).encoder_mount and encoder_tray (34 × 84.6 × 6 mm, flat, pocket up). Measure the rear shaft (REAR_SHAFT_MM) and the board thickness (pcb_t, 1.6 mm assumed). Set both in print/make_parts.py and run it; it checks the clearances.make_parts.py checks this). Keep 34 mm clear in front of the motor for the connector end and its wires.S soldered: on the legs, the TEST bridge, and in the BOB holes R7, R10–R12 · D female end onto the Nucleo's CN7-16 / CN7-8, no solder
Use the meter on continuity, with nothing plugged in. Touch each used leg, then the MA732 pin it must reach. The QFN pins are 0.5 mm apart at the chip's edge, so touch the solder fillet at the side with a sharp probe, or a needle held against it. For VDD and GND, the two ends of C1 are easier. The check is meter-only and can't harm the board. If any leg beeps to a different pin than listed, stop and don't power it: the legs are numbered differently from the user guide.
| Leg | Wire | Must beep to | Must stay silent to |
|---|---|---|---|
| CN1.1 VDD | red jumper (E1) | MA732 pin 13, one end of C1 | CN1.2: a short chirp is C1 charging, a steady beep is a short |
| CN1.2 GND | black jumper (E2), bridge | MA732 pin 8, the other end of C1 | CN1.1 |
| CN1.8 TEST | bridge (E3), brown (E7) | MA732 pin 10; CN1.2 once bridged | CN1.1 |
| CN1.4 CS, CN1.6 MISO | none | — | CN1.2 and CN1.8 (the bridge stays clear of them) |
| CN2.5 A | yellow (E4) | MA732 pin 2 | CN1.1, CN1.2 |
| CN2.4 B | green (E5) | MA732 pin 6 | CN1.1, CN1.2 |
| CN2.6 Z | blue (E6) | MA732 pin 3 | CN1.1, CN1.2 |
| CN2.7 MGL, CN2.8 MGH | none (meter points) | MA732 pins 11 and 16 (optional) | — |
| Each wire's far end | its own leg | its neighbours in the twist |
Würth's datasheet for the WR-MM male IDC 690157000872 (8-way, 28 AWG ribbon at 1.27 mm) lists 690367280876 as a mate. Digi-Key 732-1990-5-ND costs $0.90, with a 15-week lead time when checked on 2026-10-17. Digi-Key offers TE 215083-8 (Micro-MaTch male IDC, 28 AWG) as its substitute, with 813 shown in stock the same day. It uses the same Micro-MaTch interface, but no maker's table pairs it with this socket, so dry-fit it unpowered first. One plug per connector, pressed in a vise onto 8 conductors of the same ribbon, conductor 1 on pin 1. TEST to GND then joins conductors 8 and 2 within a few cm of the CN1 plug. Run the same beep check through the plug.
bench --board nucleo-g474re --no-estop regs bench --board nucleo-g474re --no-estop disable bench --board nucleo-g474re --no-estop params --set encoder_mask=1 bench --board nucleo-g474re --no-estop enable bench --board nucleo-g474re --no-estop home # encoder error grows with travel: disable, params --set encoder_invert_mask=1, enable, home bench --board nucleo-g474re --no-estop testmove stroke --speed 50 --reps 2 bench --board nucleo-g474re --no-estop health-run --db jig.sqlite --encoder deck-health report --db jig.sqlite
Before Order 2, with what is on the bench: one NUCLEO-G474RE, both CAN Pals from Order 1, no PC adapter. FDCAN1 talks to FDCAN2 over a short real bus, in both directions, with the normal bench image. The BOB stays wired and nothing moves. Command and output: firmware/README.txt.
| Part | Qty | Status |
|---|---|---|
| Adafruit CAN Pal 5708 (TJA1051T/3, 2 × 60 Ω terminator with a switch) | 2 | have (Order 1) |
| 120 Ω terminators | — | not needed: on the CAN Pals (switch ON) |
| Twisted pair + a ground wire, ≈ 30 cm | 1 | have: twist 24 AWG solid wire (Order 2) by hand |
| Jumpers: 2 M-F, 6 F-F | 8 | have (Order 1 M-F, Order 2 F-F) |
| # | From | To | How | Wire | Note |
|---|---|---|---|---|---|
| CAN Pal A on FDCAN1 | |||||
| C1 | Nucleo CN6-2 IOREF (3.3 V) | A Vcc | D→D | M-F jumper, male in the Nucleo socket | IOREF = 3V3 (SB39 on) |
| C2 | Nucleo CN6-7 GND | A GND | D→D | ||
| C3 | Nucleo CN10-12 PA12 (FDCAN1 TX) | A TX | D→D | F-F jumper | MCU TX → transceiver TXD |
| C4 | Nucleo CN10-14 PA11 (FDCAN1 RX) | A RX | D→D | Transceiver RXD → MCU RX | |
| CAN Pal B on FDCAN2 | |||||
| C5 | Nucleo CN7-12 IOREF (3.3 V) | B Vcc | D→D | F-F jumper | |
| C6 | Nucleo CN10-20 GND | B GND | D→D | ||
| C7 | Nucleo CN10-30 PB13 (FDCAN2 TX) | B TX | D→D | Free on the bench | |
| C8 | Nucleo CN10-16 PB12 (FDCAN2 RX) | B RX | D→D | Free on the bench | |
| The bus | |||||
| C9 | A terminal H | B terminal H | T→T | Twisted pair | |
| C10 | A terminal L | B terminal L | T→T | ||
| C11 | A terminal GND (middle) | B terminal GND | T→T | Wire along the pair | Common ground |
| — | Both SLNT | open | — | — | High = silent. Both TERM switches ON |
You will: solder the two CAN Pal headers S, plug jumpers D, clamp the bus T, run two commands. No image swap, nothing unplugged at the BOB.
likely cause:; the table below says what to check. -v adds the counts per direction.| The test says | Check |
|---|---|
likely cause: no ACK | Termination (both switches ON, 60 Ω), 3.3 V on each Vcc, SLNT open |
likely cause: bus-off | CAN_H / CAN_L swapped at one CAN Pal, or TX / RX swapped (PA12 / PB13 → TX, PA11 / PB12 ← RX) |
errors in the data phase only | Timing or the cable at 4 Mbit/s: a clean --classic pass confirms the wiring |
errors in the arbitration phase | Wiring: CAN_H / CAN_L, a loose contact, ground, termination |
| Anything else | 3.3 V at each Vcc; GND C2, C6, C11; 60 Ω; SLNT not pulled high. |
bench --board nucleo-g474re ping # the new build
bench --board nucleo-g474re canbus-test # 1000/1000 ok both ways
bench --board nucleo-g474re canbus-test --classic # 500k classic only, if FD fails
# --frames N: a longer or shorter run
# no --no-estop and no disable here: the motor supply can stay off, and --no-estop would
# refuse ("driver not initialised") because it re-arms the driver, which needs motor power
PC-side CAN (talking to the Mac) needs a USB CAN FD adapter: PEAK PCAN-USB FD, or a cheaper candleLight / slcan adapter (being evaluated). Not needed for the single-board pilot, which uses the debug serial port.
After the CAN FD test passed, with the same wiring (the BOB, the encoder, both CAN Pals C1–C11). A separate image runs 20 columns: the real motor is column 0, the other 19 are simulated inside the Nucleo, but their SPI traffic really goes out on the buses. The Mac's heights travel over the real CAN bus (FDCAN2 → FDCAN1) among a whole row's traffic, as on the deck. A recorded simulator run drives it. Nothing to wire. Details: firmware/README.txt section 17.
--scale 0.9. Motor supply 29 V, run current 0.8 A first. Keep a hand on the supply's OUTPUT button for every run.You will: flash the HIL image, run 2 minutes, then 30 minutes, then flash the bench image back. Each run ends with HIL PASS or HIL FAIL.
hil_replay.py record, about 75 s).| PASS needs | Limit |
|---|---|
| Planner tick (without the simulated chips' own work) | no deadline miss, p99 ≤ 600 µs, max < 1 ms |
| CAN targets | 0 lost, 0 duplicates, no bus-off |
| Real column vs. the firmware's command | ≤ 0.12 mm (2 full steps), encoder the same |
| Watchdogs, SPI | no trip, no timeout |
.venv/bin/python sim/tools/hil_replay.py run --virtual --duration 120 # dry run, no hardware cp -X firmware/dist/firmware_nucleo_g474re_hil_cube.bin /Volumes/NOD_G474RE/ bench --board nucleo-g474re-hil --no-estop ping # -cube-hil, columns 20/20 bench --board nucleo-g474re-hil --no-estop regs --col 0 # SPI-OK .venv/bin/python sim/tools/hil_replay.py run --no-estop --current 0.8 --duration 120 --json runs/hil/bench_120s.json .venv/bin/python sim/tools/hil_replay.py run --no-estop --current 0.8 --duration 1800 --json runs/hil/bench_1800s.json bench --board nucleo-g474re-hil --no-estop hil # the report again, any time cp -X firmware/dist/firmware_nucleo_g474re_cube.bin /Volumes/NOD_G474RE/ # the bench image back # encoder fitted and wired on the BOB: add --real-encoder; the jig (only after a free-shaft PASS): --scale 0.9
If it fails, the report says where: the tick figures, the CAN counters (check as in the CAN table above), or the real column's error. Keep the --json file and bench ... events.
firmware/dist/firmware_nucleo_g474re_3x3_cube.bin (STM32Cube build). Tested in simulation only (its 1-column twin has run on a bench). Use --board nucleo-g474re-3x3, without --no-estop. Columns 0–8 = A1 A2 A3 B1 B2 B3 C1 C2 C3. It boots with every DRV_ENn high. Every pin below is checked against firmware/board/board_g474.h and UM2505 Table 16.STAR+ and STAR− are two linked WAGO 221-415 each. The caps and the shunt sit across them in parallel. Only F0, K1 and the row fuses are in series.
| Opening | STAR+ a | STAR+ b | STAR− a | STAR− b |
|---|---|---|---|---|
| 1 | From K1 terminal 8 (XP) | Link from a5 | Supply − (P2) | Link from a5 |
| 2 | Cap 1 + | Row A fuse (P6) | Cap 1 − (stripe) | Row A − WAGO (P8) |
| 3 | Cap 2 + | Row B fuse (P6) | Cap 2 − (stripe) | Row B − WAGO (P8) |
| 4 | Shunt VIN | Row C fuse (P6) | Shunt GND | Row C − WAGO (P8) |
| 5 | Link to b1 | spare | Link to b1 | spare |
Each row has two more WAGO 221-415: row + takes the fuse lead (1) and R1 of BOB 1–3 (2–4); row − takes the STAR− lead (1) and R2 of BOB 1–3 (2–4). Opening 5 is spare. The 24 V side has two more: 24 V+ and 24 V−.
| # | From | To | How | Wire | Note |
|---|---|---|---|---|---|
| 30 V power | |||||
| P1 | Supply + | F0 holder | B→S | 1.5 mm² red, banana plug | 10 A / 58 V MINI in a Keystone 3568, soldered and heat-shrunk, tied down |
| XP | F0 out | K1 2; K1 4 → K1 6 (link); K1 8 → STAR+ a1 | S→R | 1.5 mm², 6.3 mm blue receptacles | Both relay contacts in series |
| P2 | Supply − | STAR− a1 | B→W | 1.5 mm² black, banana plug, ferrule | |
| P3 | STAR± a5 | STAR± b1 | W→W | 1.5 mm², short | Links the two WAGOs of each star |
| P4 | Cap 1, cap 2 (2200 µF) | STAR+ / STAR− a2, a3 | ·→W | Legs straight in, ≥ 10 mm | Stripe to STAR− |
| P5 | Shunt VIN / GND | STAR+ / STAR− a4 | S→W | 1.5 mm², short | Pololu #3777 on standoffs, away from the caps |
| P6 | STAR+ b2, b3, b4 | Row A, B, C fuse IN | W→S | 1.5 mm² red, soldered to the holder pins | 5 A MINI, on the row board's 30 V zone |
| P7 | Row fuse OUT | Row + WAGO 1 | S→W | 1.5 mm² red, soldered to the holder pins | Never through perfboard copper |
| P8 | STAR− b2, b3, b4 | Row − WAGO 1 | W→W | 1.5 mm² black, ferrule | |
| P9 | Row + WAGO 2–4 | R1 +VM of BOB 1–3 | W→S | 0.5 mm² red, soldered at the BOB | 9 wires in all |
| P10 | Row − WAGO 2–4 | R2 GND of BOB 1–3 | W→S | 0.5 mm² black, soldered | 9 wires in all |
| P11 | 100 µF / 50 V | R1 (+) and R2 (−) of each BOB | ·→S | On the pins, shortest legs | Stripe to R2, with P9 and P10 |
| E-stop, 24 V, fans | |||||
| X0 | 24 V adapter + / − | 24 V+ / 24 V− WAGO | T→W | 0.5 mm², barrel-jack terminal | |
| X1 | 24 V+ WAGO | E-stop NC1 | W→T | E-stop cable, core 1 | 4 × 22 AWG cable to the E-stop box |
| X2 | E-stop NC1, other side | K1 coil + (terminal 1) | T→R | E-stop cable, core 2; red 6.3 mm receptacle | The coil has no polarity: call terminal 1 the + side |
| X3 | K1 coil − (terminal 0) | 24 V− WAGO | R→W | 0.5 mm², red 6.3 mm receptacle | |
| XD | 1N4007 band on coil + (1) | anode on coil − (0) | R→R | Leads crimped in with the coil wires | Coil + is always +24 V |
| X4 | E-stop NC2 | Carrier X4: PA0 (CN7-28) and GND | T→T | E-stop cable, cores 3 + 4 | Dry contact. 10k PA0 to 3V3 is on the carrier |
| F1 | 24 V+ WAGO | Fan red, × 3 | W→· | Fan lead | One Orion OD4010-24HB per row, only above 1.4 A |
| F2 | 24 V− WAGO | Fan black, × 3 | W→· | Fan lead | Never on the 30 V line (fan max 26.4 V) |
| U1 | Nucleo CN1 (Micro-B) | Mac | U→U | USB-C to Micro-B | Powers the Nucleo and all 3.3 V logic |
Ribbon conductor n = IDC pin n at both ends (red stripe = pin 1). On the carrier each Row column is one wire: morpho pin to IDC pin. All buses at 170 MHz / 32 = 5.31 MHz.
| # | Signal | Row A · SPI1 | Row B · SPI2 | Row C · SPI3 | Row board → BOB |
|---|---|---|---|---|---|
| S1 | SCK | PA5 · CN10-11 | PB13 · CN10-30 | PC10 · CN7-1 | L4 of all three |
| S3 | MISO | PA6 · CN10-13 | PB14 · CN10-28 | PC11 · CN7-2 | L6 of all three |
| S5 | MOSI | PA7 · CN10-15 | PB15 · CN10-26 | PC12 · CN7-3 | L5 of all three |
| S7 | CS1 | PB6 · CN10-17 | PB12 · CN10-16 | PD2 · CN7-4 | BOB 1 L3 |
| S8 | CS2 | PB10 · CN10-25 | PB11 · CN10-18 | PA15 · CN7-17 | BOB 2 L3 |
| S9 | CS3 | PB4 · CN10-27 | PB2 · CN10-22 | PB1 · CN10-24 | BOB 3 L3 |
| S10 | EN1 (DRV_ENn) | PA9 · CN10-21 | PB0 · CN7-34 | PC7 · CN10-19 | BOB 1 L9 |
| S11 | EN2 | PC2 · CN7-35 | PB7 · CN7-21 | PC8 · CN10-2 | BOB 2 L9 |
| S12 | EN3 | PC3 · CN7-37 | PC6 · CN10-4 | PC9 · CN10-1 | BOB 3 L9 |
| S13 | SLEEPn | PA8 · CN10-23, to all three headers | L10 of all three | ||
| S14 | DIAG0 | PA10 · CN10-33, to all three headers (optional) | J203-1 of all three (optional) | ||
| S15 | 3V3 | CN7-16, to all three headers | L1 + ENC 3V3 of all three | ||
| S2 S4 S6 S16 | GND | CN7-8 to IDC C, CN10-9 to IDC A, CN10-20 to IDC B (all common on the Nucleo); pins 2-4-6 bridged, 16 wired, on every header | L2 + ENC GND of all three | ||
| — | E-stop | PA0 · CN7-28 to X4, 10k to 3V3; X4 second pole to GND (CN7-20) | — | ||
A1 keeps the bench's pins. Keep free: PA2/PA3 (VCP), PA11/PA12 (FDCAN), PA13/PA14 (SWD), PB3, PB8, PC4/PC5, PC13.
| # | From | To | How | Wire | Note |
|---|---|---|---|---|---|
| Row board → each BOB's left-header holes (× 9), soldered at both ends | |||||
| D1 | Lead L1 (3V3) | L1 VCC_IO | S→S | F-F jumper half, housing cut off: soldered in the row board and in the BOB hole | |
| D2 | Lead L2 (GND) | L2 GND | S→S | ||
| D3 | Lead L3 (CS1/2/3) | L3 CSN | S→S | Idle high | |
| D4 | Lead L4 (SCK) | L4 SCK | S→S | ||
| D5 | Lead L5 (MOSI) | L5 MOSI | S→S | ||
| D6 | Lead L6 (MISO) | L6 MISO | S→S | ||
| D7 | Lead L9 (EN1/2/3) | L9 DRV_ENn | S→S | BOB pull-up: off until driven low | |
| D8 | Lead L10 (SLEEPn) | L10 SLEEPn | S→S | Held high | |
| D9 | Lead DG (DIAG0) | J203-1 DIAG0 | S→S | Optional; open drain, wire-OR | |
| — | L8 CLK | L7 GND | S→S | Bridge wire (a cut capacitor leg), bottom side | Internal clock, on every BOB |
| Motor (× 9) | |||||
| M1 | R4 A1 | Motor black | S→· | Motor lead shortened, soldered into the BOB hole, heat-shrink | Coil A |
| M2 | R3 A2 | Motor green | S→· | ||
| M3 | R6 B1 | Motor red | S→· | Coil B. Wrong direction: invert_dir_mask | |
| M4 | R5 B2 | Motor blue | S→· | ||
| Encoder, TBMA732 (× 9): wires on the connector legs, beep check as in step L | |||||
| E1 | CN1.1 VDD (leg) | Row board ENC 3V3 of its BOB | S→D | F-F jumper half (300 mm): cut end on the leg, female end on the row board's ENC pin | Never 5 V (MA732 max 4.6 V) |
| E2 | CN1.2 GND (leg) | Row board ENC GND | S→D | Same nets as L1 / L2 | |
| E3 | CN1.8 TEST (leg) | CN1.2 GND (leg) | S→S | Short insulated bridge | Outside legs 4 and 6 (CS, MISO) |
| E4 | CN2.5 A (leg) | BOB R10 ENCA | S→S | F-F jumper half, housing cut off: soldered on the leg and in the BOB hole; the four twisted together, away from the motor leads | 4,096 counts/rev out of reset |
| E5 | CN2.4 B (leg) | BOB R11 ENCB | S→S | ||
| E6 | CN2.6 Z (leg) | BOB R12 ENCN | S→S | Index | |
| E7 | CN1.8 leg (GND through E3) | BOB R7 GND | S→S | Return along A / B / Z | |
| Supply | 30 V / ≥ 10 A lab supply (borrow): limit ≈ 8 A, OVP 35.0 V. ≈ 1.8 A at rest at 1.4 A run current, up to ≈ 7 A with all nine moving (estimate). |
| E-stop | NC1 feeds the relay coil: pressed or a broken wire drops K1 and cuts 30 V to all nine BOBs. NC2 pulls PA0 to GND: the firmware latches and drives every DRV_ENn high. Releasing restarts nothing. |
| Why wired | 9 pinch points at 48 mm pitch, up to ≈ 77 N per column at 1.4 A (estimate); a borrowed supply's button may be out of reach. |
| Logic | Nucleo 3V3 over USB, ≈ 210 mA for 9 encoders, 9 × VCC_IO and the MCU (estimate). |
| Cooling | Watterott heatsink on each BOB, as in step R1. Fans only above 1.4 A. |
| Ribbon | The pre-made 16-way cables in hand are 457 mm. Fold the extra length flat. SCK, MISO and MOSI each run next to a ground. |
| Relay | Omron lists only AC ratings for the G7L (25 A at 220 V AC). Both contacts in series share the 30 V DC break. |
| Encoders | The boards are 76 mm long: the 8 outer ones point outward, the centre one ≈ 27° off a row axis (≈ 4 mm clearance, estimate; arm_deg 27 for its tray). Joerg checks it on the frame. |
Two designs: the carrier on the Nucleo, and the row board, built three times. Chip Quik plated boards, true 2.54 mm grid. Holes are named by row letter and column number, e.g. F12. On the carrier the two female headers hang underneath, so their pins are soldered on top; every other joint on both boards is underneath. Wire: 24 AWG solid, insulated. Leads to the BOBs: the 300 mm F-F jumpers cut in half, housing cut off, soldered at both ends (the BOB has bare holes). No crimping.
Each drawing opens on group 1. Wire it, beep it, then Next. "Top side" shows what you see from above; "Underside" is the board turned over.
filled pad = a pin or wire end is soldered here empty ring = unused hole orange ring = hop hole big dot = wires join here
Work side: underside (the view switches there). On top: only the short hop wires, morpho pin → hop hole.
Work side: underside (the view switches there). On top: only the short hop wires, morpho pin → hop hole.
Work side: underside (the view switches there). On top: only the short hop wires, morpho pin → hop hole.
Work side: underside (the view switches there). On top: only the short hop wires, morpho pin → hop hole.
Work side: underside (the view switches there). On top: only the short hop wires, morpho pin → hop hole.
All 46 underside wires and 35 hops at once. Use Back to go group by group.
filled pad = a pin or wire end is soldered here empty ring = unused hole big dot = wires join here
Work side: underside (the view switches there). Every joint on this board is underneath.
Work side: underside (the view switches there). Every joint on this board is underneath.
Work side: underside (the view switches there). Every joint on this board is underneath.
Work side: underside (the view switches there). Every joint on this board is underneath.
Work side: underside.
All 51 wire pieces at once. Use Back to go group by group.
bench --board nucleo-g474re-3x3 --virtual ping # dry run, no hardware bench --board nucleo-g474re-3x3 ping # columns 9/9 bench --board nucleo-g474re-3x3 regs --col 0 # ... to --col 8 bench --board nucleo-g474re-3x3 params --set encoder_mask=511 # all nine bench --board nucleo-g474re-3x3 current 0.8 bench --board nucleo-g474re-3x3 enable --col 0 # no --col = all nine bench --board nucleo-g474re-3x3 home --col 0 bench --board nucleo-g474re-3x3 move 40 --col 0 --speed 100 bench --board nucleo-g474re-3x3 health-run --col 8 --encoder --db rig.sqlite # 9 columns: keep sample_period_us >= 10000
Next, stage 3: a 10 × 16 validation strip on 8 Tile-20 rev A pilot boards. See the electronics plan, path to production ↗