Nothing answers on the serial port
- Is main Vin applied (5–24 V, typically 24 V)? USB alone does not power the ESP32 for programming or reliable CDC — apply Vin, then connect USB-C for serial.
- Is another program holding the port? (Arduino serial monitor, a browser tab with Web Serial, another script). Only one process can own it.
Tools → USB CDC On Boot: Enabledwas set when flashing?- Open a serial monitor at 115200 — do you see
#boot lines? If yes, the board is fine and the problem is on the host side (port name, permissions — on Linux add yourself to thedialoutgroup). - After very high run current continuous motion, USB CDC has been seen
to drop (
/dev/ttyACM0gone). Power-cycle USB / 24 V and use ~70% current for long duty.
Flash / upload fails or board not detected for programming
- Vin must be present (typically 24 V). USB power is not used to power the ESP32 for programming — data only over USB-C.
- Confirm USB CDC On Boot: Enabled and the correct
/dev/ttyACM*/COM*port after Vin + USB are connected.
discover() returns an empty dict
- Wrong port, or the port was opened but the node's telemetry is off
(
slow_rate_hz = 0and nothing answers the ping): power-cycle and watch for the boot banner. - If you're bridging through node A but expecting node B: is the CAN wiring between them intact and terminated?
Motor is silent and free-spinning
node.enable()was called? (enable_on_bootmay be 0.)- Main 5–24 V rail present (typically 24 V)? Motor torque needs Vin; USB is data only.
run_currentreasonable (30–50 % for light bring-up; ~70% for high-speed closed loop on a 1.2 A NEMA17).
Motor stalls or loses position at speed
- Closed-loop (fw ≥1.2) uses a trapezoid with cruise
cl_max_speedand accelcl_max_accel. Prefer SpreadCycle, 8 microsteps, and ~70% current for continuous duty:
node.configure_closed_loop_speed(4800.0, run_current=70, microsteps=8,
stealthchop=False, persist=True)- Measured on PR42HS40-1204AF-02 @ 24 V: open-loop ~1200 RPM, closed-loop cruise ~1000 RPM max / 800 RPM production. Full tables: Closed-loop speed tuning.
- 100% current can hit high speed in short bursts but has caused USB disconnects when hammered continuously — prefer 70% for long soaks.
- Check
no progressinget_pid_status()— blocked axis or wronginvert_dir(node.set_direction(True)so +command increases encoder). - StealthChop is quiet but weak at speed — use SpreadCycle
(
stealthchop=False).
Closed loop runs away or faults with no progress
- Commanded positive direction must increase encoder angle. Toggle
node.set_direction(True), retest slowly, thensave_config(). - Confirm firmware ≥1.2 for trap + feedforward closed loop.
- Check
cl_max_speed/max_speedare not stuck at tiny NVS values (e.g. 30 deg/s) after a bad restore — re-set andsave_config(). - Clear e-stop with
enable()afterestop_all(); stickyNO_PROGRESSoften clears on the next successful enable + move.
TMC overheating / OTPW / OT (firmware ≥1.4)
Is this because I have no heatsink?
Often yes. Sustained high current and high-speed closed-loop hops heat the TMC2209. With no heatsink (and little airflow), multi-node tests commonly show:
- Frequent OTPW (pre-warning) while motion still settles
- Occasional OT shutdown (
fault=4) on the hottest axis
That pattern matches thermal limit of the driver package, not necessarily a wiring or PID bug. Install a heatsink (and optionally a fan) before blaming firmware.
Read live driver flags:
d = node.get_driver_status()
print(d.otpw, d.over_temp_shutdown, d.thermal_warning, d.thermal_fault)
temp_c, _ = node.get_env() # ESP32 die temperature (°C), not TMC junction| Symptom | Meaning | What to do |
|---|---|---|
otpw=True | Pre-warning — TMC still driving | Add heatsink/airflow; lower run_current; reduce hop duty |
over_temp_shutdown=True or fault=4 (DRIVER_OT) | TMC OT shutdown — coils cut | Cool down, enable(), heatsink, lower continuous current/speed |
fault=5 (DRIVER_SHORT) | Bridge short | Check motor wiring / shorts to chassis |
MCU temp from get_env() high but OTPW false | ESP32 warm, driver OK | Separate from TMC thermal |
High cs_actual during motion | Driver using more current (normal under load) | Not a fault; more heat → more OTPW risk |
Note: get_env() is the MCU sensor. TMC OTPW/OT come only from
get_driver_status() on firmware ≥1.4. Flash all nodes on the chain so
every axis reports the same DRIVER layout.
See also Hardware → cooling / heatsink.
Closed loop slower than open-loop run()
Expected for position moves: they must accelerate, decelerate, and settle.
Open-loop velocity never settles. On long moves, closed-loop 1.2 still
cruises near the physical ceiling — raise cl_max_speed (see tuning page).
Encoder angle is noisy, jumps, or counts backwards
- Magnet centering and air gap (1–2 mm) over the MT6701.
- The boot banner prints an encoder self-check;
raw=0x000000or all-ones means a wiring/CS problem, not a tuning problem. STATUSbit 0x40 clear means the most recent SSI frame was invalid. The controller holds the last good sample through an isolated CRC error, but sustained feedback loss still latches encoder fault 1; fix the magnet or wiring before tuning PID.
Two boards, dead bus
- Termination: on-board 120 Ω path is enabled by default (0 Ω jumper). End nodes should keep termination; mid-chain nodes must remove the 0 Ω resistor so only the two bus ends terminate. Too many terminators (every board left stock on a long chain) or none at the ends both break the bus. Ends can also restore termination by shorting the termination pads if the 0 Ω part was removed. See Hardware → CAN.
node.get_can_health():tx_error_countnear 128 with zero received frames means nobody is ACKing — the other node is unpowered or CANH/CANL are swapped.- A node that sat alone in bus-off recovers automatically;
recovery_counttells you it happened.
Board won't boot with the endstop wired
IO8 is a strapping pin and was LOW at power-on. Use a normally-open switch to GND, or wire NC switches through a series diode. Details in the hardware guide.
Follower lags or drifts
get_follow_status()—syncedtrue? If not, the follower never saw a leader POSITION frame: check the leader'sfast_rate_hzis not 0.- Encoder-corrected mode (default) absorbs missed steps; open-loop follower mode accumulates them — prefer encoder-corrected unless you have a reason.
- After manually moving either shaft, call
follow_sync()to re-capture the offset.
Homing times out
homing_timeout_mstoo short for the travel distance at the homing speed.- StallGuard homing: threshold too low (never triggers) — raise
stall_threshold; or speed too low for load measurement — home at 15–30 deg/s. - Endstop homing: polarity (
endstop_active_high) wrong, so the firmware believes the switch is already pressed or never pressed.
NodeFault code 4 (DRIVER_OT)
TMC over-temperature shutdown. Cool the driver, reduce duty cycle or
run_current, add a pause between high-speed cycles. See
driver diagnostics (get_driver_status().over_temp_shutdown).
NodeFault code 5 (DRIVER_SHORT)
The TMC2209 latched a short flag (often low_side_short_A/B or
short_to_gnd_*) and the firmware aborted the move.
- Real short: motor wiring, damaged coil, wrong connector — fix hardware.
- False trip at extreme step rates: common when commanding very high
mm/s with high current / SpreadCycle under belt load. Mitigate:
- Cool between bursts; lower
run_currentslightly (e.g. 50–70%) - Prefer StealthChop for high-speed open-loop soaks on this board
- Shorter cycles; avoid continuous 100% current
- Cool between bursts; lower
d = node.get_driver_status()
print(d.low_side_short_a, d.low_side_short_b, d.drv_err, d.cs_actual)Belt high-speed notes: Belt axis.
Belt only moves one way / “walks” into a hard stop
Relative soaks like move_by(+50); move_by(-100); move_by(+100); … shift
the soft frame if a leg is clipped by a mechanical stop. The next “down”
then digs further into the lower limit.
Fix: soft-zero once at the median, then use absolute move_to(+50) /
move_to(-50) (see examples/belt_oscillate.py and
Belt axis).