Metadata-Version: 2.5
Name: canbus-stepper
Version: 0.1.0
Summary: Python client for the CANBUS Stepper Motor Controller (thingsbyjosh.com), over USB serial or WiFi
Project-URL: Homepage, https://thingsbyjosh.com
Author: Josh
License-Expression: MIT
License-File: LICENSE
Keywords: canbus,esp32,motor,serial,stepper,tmc2209,websocket
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: System :: Hardware
Requires-Python: >=3.8
Requires-Dist: pyserial>=3.5
Requires-Dist: websocket-client>=1.6
Description-Content-Type: text/markdown

# canbus-stepper

Python client library for the [thingsbyjosh.com](https://thingsbyjosh.com) CANBUS Stepper Motor Controller
(ESP32-S3 + TMC2209 + MT6701). Talk to a node over USB serial or over WiFi
(WebSocket) through the exact same high-level API -- only how you construct
the `CANBus` differs.

## Install

```bash
pip install canbus-stepper
```

## Usage

```python
from canbus_stepper import CANBus

# USB serial
with CANBus.serial("COM3") as bus:
    node1 = bus.node(1)
    node1.set_acceleration(720.0)
    node1.set_deceleration(720.0)
    node1.set_position_speed(360.0)
    node1.enable()
    node1.set_position(180.0)

# WiFi -- the node must already be joined to your network via the Web
# GUI's "Configure WiFi" dialog over USB first; WIFI_SET is USB-only.
with CANBus.websocket("canbus-stepper-1.local") as bus:
    node1 = bus.node(1)
    node1.set_position(180.0)

    angle = node1.get_angle()
    voltage = node1.get_voltage()

    # Broadcast to every node on the bus
    bus.broadcast.estop()
```

Connection failures (bad port/hostname, node offline, nothing listening,
etc.) raise `canbus_stepper.CANBusConnectionError` with a plain-English
message instead of a raw socket/serial traceback -- and no try/except is
needed to get that clean behavior. `CANBusConnectionError` is a `SystemExit`
subclass, so if you leave it uncaught, Python just prints its message and
exits (the same as `sys.exit(message)`), not a traceback:

```python
from canbus_stepper import CANBus

with CANBus.websocket("canbus-stepper-1.local") as bus:   # exits cleanly if this fails
    ...
```

Catch it yourself only if you want different behavior, e.g. a retry:

```python
from canbus_stepper import CANBus, CANBusConnectionError

try:
    bus = CANBus.websocket("canbus-stepper-1.local")
except CANBusConnectionError as e:
    print(f"Could not connect: {e}")
```

See `MsgType`, `StandstillMode`, and `AuxFunction` in `canbus_stepper` for
the full set of protocol constants, and the "Control Interface Examples"
folder for complete runnable examples of both transports.

## Protocol

- 11-bit CAN Standard ID encodes `NodeID` (5 bits) and `MsgType` (6 bits):
  `CAN_ID = (NodeID << 6) | MsgType`
- Line protocol: `"<CAN_ID_hex> <RTR> <payload_16hex_chars>"`
- All multi-byte values are little-endian
- `NodeID` 0 = broadcast to all nodes

See `CAN Protocol.md` in the project for the full protocol reference.

## Publishing (maintainer notes)

```bash
pip install build twine
python -m build
twine upload dist/*
```
