Skip to main content

Grafito CANStepper

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.

├── canstepper/                              # Python package (grafito-canstepper)
├── firmware/GrafitoCANStepper_C3/           # node firmware (GCSP v1, fw 1.12)
├── firmware/GrafitoCANStepper_C3_CANopen/   # optional CiA 301/402 SKU (fw 2.1)
├── examples/                                # runnable examples + machine.toml
├── tests/                                   # pytest suite (runs on a software sim)
├── model_assets/                            # assembly render, PCB drawing, STEP CAD
└── docs/                                    # protocol, quickstart, CAD index

CAD and brochure files in this repo (model_assets/):

File Description
CANStepper.png Product photo (also at repo root)
CANStepper_pinout.png Connector / pinout diagram
CANStepper_multiaxis_can.png Multi-axis CAN bus demonstration
CANStepper_V1_assembly.mp4 V1 board assembly video (historical)
CANStepper_V1_assembly_preview.jpg Preview frame for the V1 video
CANStepper_test_station.png Board Test Station (after USB connect)
CANStepper_block_diagram.png System block diagram
CANStepper_V2_Assembly.webp V2 assembly render
Drawing_CANStepper_V2.pdf V2 PCB mechanical drawing
CANStepperV2_3D_PCB.step V2 PCB STEP
CASING_MOUNT.step Casing / mount STEP
Heat_Spreader.step Heat spreader STEP
TMC2209 UART Stepper Driver - Heat_Sink.step TMC2209 heatsink STEP
canstepper-brochure-v2.pdf Product brochure
CANStepper pinout CAN bus multi-axis demonstration

V1 board assembly (click the preview to play the video):

V1 board assembly

This clip is version 1 of the same board. V1 used three different connector types, which made cabling and crimping difficult. The current production board is V2, with JST 2.0 connectors throughout for easy crimping.

Important — encoder magnet: Fit a diametrical magnet on the motor shaft. The magnet polarity must be radial, not axial. An axial magnet will not produce a valid MT6701 angle and closed-loop control will fail.

After USB and Vin are connected, test the board in the CANStepper Test Station — set the node ID, enable the driver, jog, and check encoder / TMC / CAN telemetry.

CANStepper Test Station

Highlights

  • Layered Python API — raw frames → StepperNode → NodeGroup → Axis (mm units via rotation_distance) → DualMotorAxis / IndependentDualAxis / Cartesian / CoreXY / MotionGroup → declarative machine.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.12 plans a rest-to-rest S-curve trajectory with velocity + acceleration feedforward and a light tracking PID on the MT6701 encoder (optional encoder LUT). 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 — IndependentDualAxis commands both motors independently and blocks the next pass until both encoders are within mm tolerance of the target.
  • Simulator included — canstepper.sim.SimNetwork implements the whole protocol in software; the test suite and the examples run without hardware.

Install & first spin

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:

G-code examples (from this directory):

PYTHONPATH=. python3 examples/gcode_cartesian.py /dev/ttyACM0 1 2
PYTHONPATH=. python3 examples/gcode_corexy.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/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

Belt axis (mm):

PYTHONPATH=. python3 examples/belt_move_mm.py /dev/ttyACM0 1 50 77.2 150

Development

python3 -m pytest

Current GCSP firmware is 1.12 (S-curve + Ka + LUT; motors off at boot until enable()). Optional CANopen 2.1 lives in firmware/GrafitoCANStepper_C3_CANopen/ with GrafitoCANStepper.eds and GrafitoCANStepper_Node1.dcf / GrafitoCANStepper_Node2.dcf (FileRevision 2, identity 0x00020100). Discard any older 2.0 EDS — it will not import this board. Node 2 must be flashed with -DCO_FACTORY_NODE_ID=2. 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

Metadata

Release files for grafito-canstepper 0.2.3

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for grafito-canstepper 0.2.3
File Size Uploaded
grafito_canstepper-0.2.3.tar.gz 89.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for grafito-canstepper 0.2.3
File Interpreter ABI Platform
grafito_canstepper-0.2.3-py3-none-any.whl Python 3 none any Details

Total release size: 171.0 kB

Release files / grafito_canstepper-0.2.3.tar.gz

Download URL grafito_canstepper-0.2.3.tar.gz
Size 89.9 kB
Tags Source
SHA-256 checksum
How to use checksums
51355e7e230c9dfafce1b9e8bf203efb0d303f48e8cefdaa1238f2b2cccc0751
BLAKE2b-256 checksum
How to use checksums
8fb02eb527a8d19c22b7f39a5f338bef586e0a3023c970a63ee7a58616840f37
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.3-py3-none-any.whl

Download URL grafito_canstepper-0.2.3-py3-none-any.whl
Size 81.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
4401ca694da384fffa9f5238a06851b8a76a2f392efb2bb126a10f584391f7a0
BLAKE2b-256 checksum
How to use checksums
3d3c2f108ebf24eb2b976134dfaa594949c92cf0c87d1ef5afecbc5c8ef711d2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.10.12

Release history Release notifications | RSS feed

This release

0.2.3 This release

2 release files

0.2.2

2 release files

0.2.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page