Grafito CANStepper

Why

Code that configures twelve nodes by hand is write-once, debug-forever. A machine.toml names every node and axis, applies (and verifies) every parameter at startup, and gives operations one file to review.

Example

[bus]
transport = "serial"
port = "/dev/ttyACM0"

[node.z_left]
id = 2
run_current = 40
hold_current = 15
microsteps = 16
closed_loop = 1

[node.z_right]
id = 3
run_current = 40

[node.feeder]
id = 6
max_speed = 1440.0

[axis.z]
type = "dual_motor"
primary = "z_left"
secondary = "z_right"
invert_secondary = true
rotation_distance = 8.0
max_speed = 15.0
require_homing = true

[axis.z.homing]
method = "stallguard"
direction = -1
speed = 5.0
current_percent = 25
backoff = 2.0

[axis.feed]
type = "single"
node = "feeder"
rotation_distance = 30.0

Any key in a [node.*] table besides id must be a firmware parameter name — unknown keys fail loudly with ConfigError instead of being silently ignored.

Using it

from canstepper import Machine

with Machine.from_config("machine.toml") as m:
    m.enable_all()
    m.home("z")                       # uses [axis.z.homing]
    m.axes["z"].move_to(50.0, blocking=True)
    m.axes["feed"].run(12.0)
    m.estop_all()                     # broadcast, everything halts
  • Parameters are applied volatile on load; call m.save_all() to persist them to the boards' flash.
  • m.check() reads back every parameter from every node — snapshot it in CI or before a production run to catch drift.
  • Axis types: single, dual_motor, corexy.

On Python 3.9/3.10 reading TOML needs pip install tomli (3.11+ uses the standard library). Passing a dict works everywhere.

On this page