Metadata-Version: 2.4
Name: grafito-canstepper
Version: 0.2.1
Summary: Host library for Grafito CANStepper boards (GCSP v1): multi-node CAN, Axis mm units, Cartesian/CoreXY G-code, homing, TMC diagnostics
Author-email: Grafito Innovations <grafito.innovations@gmail.com>
License: Apache-2.0
Project-URL: Documentation, https://docs.grafito.in
Project-URL: Homepage, https://docs.grafito.in
Project-URL: Source, https://github.com/Grafito-Innovations/Grafito-Edge-Services/tree/main/can_stepper
Project-URL: Issues, https://github.com/Grafito-Innovations/Grafito-Edge-Services/issues
Keywords: stepper,CAN,motion-control,esp32,tmc2209,encoder
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Manufacturing
Classifier: Intended Audience :: Science/Research
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: System :: Hardware :: Hardware Drivers
Requires-Python: >=3.9
Description-Content-Type: text/markdown
Requires-Dist: pyserial>=3.4
Provides-Extra: toml
Requires-Dist: tomli>=2.0; python_version < "3.11" and extra == "toml"
Provides-Extra: dev
Requires-Dist: pytest>=7; extra == "dev"
Requires-Dist: ruff; extra == "dev"
Requires-Dist: mypy; extra == "dev"
Requires-Dist: tomli>=2.0; python_version < "3.11" and extra == "dev"
Dynamic: requires-python

# 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 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.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** — `IndependentDualAxis` commands 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](docs/grafito_visualizer_integration.md).
- **Simulator included** — `canstepper.sim.SimNetwork` implements the whole
  protocol in software; the test suite and the examples run without hardware.

## Install & first spin

PyPI: **[grafito-canstepper](https://pypi.org/project/grafito-canstepper/)**  
Import: `canstepper` · Docs: **https://docs.grafito.in**  
Buy: **[CANStepper Adapter Board](https://grafito.in/shop/products/canstepper-adapter-board/)**

```bash
pip install grafito-canstepper

# Development (from this directory):
# pip install -U pip setuptools && pip install -e ".[dev]"
```

```python
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](docs/quickstart.md) — first motion
- [docs/closed_loop_tuning.md](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](docs/grafito_visualizer_integration.md)
  — encoder-true 3D visualizer, dual-H + J, absolute multi-axis cycles
- [docs/protocol.md](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):

```bash
# 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):

```bash
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):

```bash
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

```bash
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.

```bash
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.
