Grafito CANStepper

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 (GCSP). 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.

Current production firmware is GCSP 1.12 (S-curve closed loop, encoder LUT, low-power boot: motors off until the host enables them). For a PLC that imports an EDS, flash the separate CANopen 2.1 sketch instead — see CANopen / CiA 402. Do not mix the two protocols on one bus.

Buy the board: CANStepper Adapter Board on the Grafito shop

System block diagram

CANStepper system block diagram — ESP32-C3, TMC2209, MT6701 encoder, TCAN3413 CAN, power, and NEMA 17

Full-size download and more CAD assets: Mechanical CAD & drawings.

The stack

LayerWhat it is
FirmwareGrafitoCANStepper_C3 fw 1.12 (GCSP) or GrafitoCANStepper_C3_CANopen fw 2.1 (PLC)
ProtocolGCSP v1 (reference) or CiA 301/402 (CANopen) — not on the same bus
Python librarygrafito-canstepper — GCSP, from raw frames to whole-machine configs
SimulatorGCSP + CANopen slaves in software, for tests without hardware

Why closed loop?

Every board plans an S-curve move (firmware ≥1.11), reads its encoder 200 times per second, and tracks the plan with velocity + acceleration feedforward and 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 in machine.toml.

Measured speed (characterization motor)

On a PR42HS40-1204AF-02 NEMA 17 (1.2 A, 3.2 mH) at 24 V, firmware 1.2:

ModeMax measured
Open-loop continuous run()~1200 RPM
Closed-loop position cruise~1000 RPM max / 800 RPM production
10 min continuous closed-loop soak @ 800 RPM100% settle success

Full matrix and soak details: Closed-loop speed tuning.

Where to go next

On this page