Grafito CANStepper
Closed-loop stepper motor control over CAN. Each board carries an ESP32-C3, a TMC2209 driver, an MT6701 14-bit magnetic encoder and a CAN transceiver; up to 31 boards daisy-chain on one 1 Mbps bus and are driven from Python through the USB port of any board on the chain.
can_stepper/
├── firmware/GrafitoCANStepper_C3/ # node firmware (GCSP v1, fw 1.9)
├── firmware/GrafitoCANStepper_C3_CANopen/ # optional CiA 301/402 SKU (fw 2.0)
├── canstepper/ # Python package (grafito-canstepper)
├── examples/ # runnable examples + machine.toml
├── tests/ # pytest suite (runs on a software sim)
├── model_assets/ # assembly render, PCB drawing PDF, STEP CAD
└── docs/
├── PRD.md # product requirements
├── protocol.md # GCSP v1 wire protocol reference
├── quickstart.md # unboxing -> moving motor
└── grafito_visualizer_integration.md # machine visualizer + dual-H/J achievement
Highlights
- Layered Python API — raw frames →
StepperNode→NodeGroup→Axis(mm units viarotation_distance) →DualMotorAxis/IndependentDualAxis/Cartesian/CoreXY/MotionGroup→ declarativemachine.toml. - Safety scopes —
node.estop(),group.estop(),bus.estop_all()(single broadcast frame); e-stop is latched until re-enabled. - Homing — physical endstop on IO8, sensorless (StallGuard), or set-zero; configurable current, backoff and timeout.
- Closed loop — firmware ≥1.2 plans a rest-to-rest trapezoidal trajectory with velocity feedforward and a light tracking PID on the MT6701 encoder (settle + stall watchdog). All motion limits are configurable defaults, never hard clamps.
- On-bus leader/follower — dual-motor gantries stay coupled with no
host in the loop (
DualMotorAxis). - Encoder-gated dual screws —
IndependentDualAxiscommands both motors independently (optional invert for mirrored SFU1204 frames) and blocks the next pass until both encoders are within mm tolerance of the target (closed-loop reverse/straight oscillate). - Grafito machine visualizer — live magnetic-encoder SSE into the open-loop 3D UI (dual-H frame + Clipper Vertical J, absolute cycles); see docs/grafito_visualizer_integration.md.
- Simulator included —
canstepper.sim.SimNetworkimplements the whole protocol in software; the test suite and the examples run without hardware.
Install & first spin
PyPI: grafito-canstepper
Import: canstepper · Docs: https://docs.grafito.in
Buy: CANStepper Adapter Board
pip install grafito-canstepper
# Development (from this directory):
# pip install -U pip setuptools && pip install -e ".[dev]"
from canstepper import CANStepperBus
with CANStepperBus.serial("/dev/ttyACM0") as bus:
print(bus.discover())
node = bus.node(1)
node.set_run_current(40).enable()
node.move_to(360.0, blocking=True)
Continue with:
- docs/quickstart.md — first motion
- docs/closed_loop_tuning.md — trapezoid + v_ff, hardware matrix + 10 min soak on PR42HS40-1204AF-02 (NEMA 17 1.2 A)
- docs/grafito_visualizer_integration.md — encoder-true 3D visualizer, dual-H + J, absolute multi-axis cycles
- docs/protocol.md — GCSP v1 wire protocol
- https://docs.grafito.in/docs/mechanical-cad — assembly image, PCB drawing PDF, STEP models (PCB 3D, casing mount, heat spreader, TMC2209 heatsink)
- https://docs.grafito.in/docs/gcode — G-code layer (Cartesian + CoreXY; G0/G1, G28, M112, …)
G-code examples (from this directory):
# Cartesian mechanism (independent X/Y[/Z] steppers)
PYTHONPATH=. python3 examples/gcode_cartesian.py /dev/ttyACM0 1 2
PYTHONPATH=. python3 examples/gcode_cartesian.py /dev/ttyACM0 1 2 3
# CoreXY belts
PYTHONPATH=. python3 examples/gcode_corexy.py /dev/ttyACM0 1 2
PYTHONPATH=. python3 examples/gcode_repl.py /dev/ttyACM0 1 2
PYTHONPATH=. python3 examples/gcode_from_file.py examples/gcode/square.gcode
Homing examples (IO8 endstop, StallGuard sensorless, set-zero):
PYTHONPATH=. python3 examples/endstop_monitor.py /dev/ttyACM0 1 30
PYTHONPATH=. python3 examples/home_endstop.py /dev/ttyACM0 1 -1
PYTHONPATH=. python3 examples/home_sensorless.py /dev/ttyACM0 1 -1 60
PYTHONPATH=. python3 examples/home_set_zero.py /dev/ttyACM0 1
PYTHONPATH=. python3 examples/home_axis_mm.py endstop /dev/ttyACM0 1 -1
Docs: https://docs.grafito.in/docs/homing
Belt axis (mm, high-speed soak — absolute ±50 about median):
PYTHONPATH=. python3 examples/belt_move_mm.py /dev/ttyACM0 1 50 77.2 150
PYTHONPATH=. python3 examples/belt_oscillate.py /dev/ttyACM0 1 200 10
PYTHONPATH=. python3 examples/belt_speed_ramp.py /dev/ttyACM0 1 150 30 600
Docs: https://docs.grafito.in/docs/belt-axis
Development
cd can_stepper
python3 -m pytest # simulator suite
python3 tools/board_e2e.py /dev/ttyACM0 1 # new-board E2E + CL tune (persists)
python3 tools/hw_validate.py /dev/ttyACM0 1 # full API suite (restores params)
python3 tools/cl_speed_validate.py /dev/ttyACM0 1 70 8 # hardware OL vs CL ladder
Current GCSP firmware is 1.9 (motors off at boot until enable()).
Optional CANopen 2.0 (EDS/DCF for PLC) lives in
firmware/GrafitoCANStepper_C3_CANopen/ — see
https://docs.grafito.in/docs/canopen. Do not mix GCSP and CANopen on one bus.
Firmware builds with Arduino IDE or arduino-cli (ESP32C3 Dev Module, USB
CDC On Boot = Enabled; libraries: FastAccelStepper, TMC2209 janelia-arduino).
Apply Vin (5–24 V, typically 24 V) before upload — USB-C is data only and
does not power the ESP32 for programming. CAN termination is on by default
(0 Ω jumper); remove that 0 Ω on mid-chain daisy-chain nodes.
arduino-cli compile --fqbn esp32:esp32:esp32c3:CDCOnBoot=cdc \
firmware/GrafitoCANStepper_C3
arduino-cli upload -p /dev/ttyACM0 --fqbn esp32:esp32:esp32c3:CDCOnBoot=cdc \
firmware/GrafitoCANStepper_C3
Note on older paths
CANStepperNode_C3/ and canbus.py are obsolete bring-up sketches. Use
firmware/GrafitoCANStepper_C3/ and the canstepper package (GCSP v1) for all
new work.
Metadata
Release files for grafito-canstepper 0.2.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| grafito_canstepper-0.2.1.tar.gz | 80.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| grafito_canstepper-0.2.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 152.5 kB
Release files / grafito_canstepper-0.2.1.tar.gz
| Download URL | grafito_canstepper-0.2.1.tar.gz |
|---|---|
| Size | 80.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
65bb344bce78ea40eb6f6281b5524c4654f214bde0d61d40f22b4dad25772765
|
|
BLAKE2b-256 checksum How to use checksums |
fb760b7db3112320e1da3720490258be1d3d8456fe524f14f817ce05393a92a1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.10.12
|
Release files / grafito_canstepper-0.2.1-py3-none-any.whl
| Download URL | grafito_canstepper-0.2.1-py3-none-any.whl |
|---|---|
| Size | 72.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
6b67472a44693fc8aa3db52a7d4baf70f48de44cc52cdf8ef50203db41d04d55
|
|
BLAKE2b-256 checksum How to use checksums |
a31f4bdb8ca2ab0b6e267c60e4d154e82d15b577a95e1217e4721bc215735eb7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.10.12
|