What is CANStepper?
CANStepper is a compact closed-loop stepper motor controller that mounts directly on a NEMA17. Each board carries:
- ESP32-C3 microcontroller
- TMC2209 silent stepper driver (UART-configured)
- MT6701 14-bit magnetic encoder (16384 counts/rev) on the motor shaft
- TCAN3413 CAN transceiver
Up to 31 boards daisy-chain on a single 1 Mbps CAN bus. A host computer connects to the USB port of any board on the chain — that board bridges serial to CAN, so one cable controls the whole machine.
Buy the board: CANStepper Adapter Board on the Grafito shop
The stack
| Layer | What it is |
|---|---|
| Firmware | GrafitoCANStepper_C3 — motion, closed loop, homing, safety on the board |
| Protocol | GCSP v1 — compact CAN frames, documented wire format (reference) |
| Python library | grafito-canstepper — from raw frames to whole-machine configs |
| Simulator | the full protocol in software, for tests and development without hardware |
Why closed loop?
Every board plans a trapezoidal move (firmware ≥1.2), reads its encoder 200 times per second, and tracks the plan with velocity feedforward + PID. That means:
- Lost steps are corrected, not silently accumulated.
- Stalls are detected — a blocked axis faults instead of grinding.
- Position telemetry is truth, measured at the shaft, not assumed.
Closed loop is on by default and can be disabled per node
(closed_loop = 0) for classic open-loop stepping.
Design principles
- No firmware clamps. Motion limits (speed, acceleration, closed-loop ceilings) are parameters with conservative defaults, persisted on the board and applied at power-on. Your machine, your limits.
- Safety is layered. Emergency stop works per node, per group, and bus-wide with a single broadcast frame. E-stop latches until explicitly re-enabled.
- Real-world units. The Python library speaks millimetres via
rotation_distance, and machines are described declaratively inmachine.toml.
Measured speed (characterization motor)
On a PR42HS40-1204AF-02 NEMA 17 (1.2 A, 3.2 mH) at 24 V, firmware 1.2:
| Mode | Max measured |
|---|---|
Open-loop continuous run() | ~1200 RPM |
| Closed-loop position cruise | ~1000 RPM max / 800 RPM production |
| 10 min continuous closed-loop soak @ 800 RPM | 100% settle success |
Full matrix and soak details: Closed-loop speed tuning.
Where to go next
- Buy a board — CANStepper Adapter Board in the Grafito shop.
- Quickstart — from unboxing to a moving motor.
- Closed-loop speed tuning — trap + v_ff, RPM limits, production tune.
- Hardware guide — wiring, termination, the IO8 boot caveat.
- Python library — the full API tour.
- Protocol reference — implement your own client.