Skip to main content

Python client library for the Open Robot Control Protocol (ORCP)

Project description

orcp

Python client library for the Open Robot Control Protocol (ORCP).

Communicate with ORCP-compliant motor controllers over USB serial or WiFi using a clean, Pythonic API.

Installation

pip install orcp

Quick start

from orcp import ORCP

with ORCP('socket://192.168.4.1:3333') as robot:
    robot.ping()
    robot.cmd_vel(v=0.2, w=0.0)   # 0.2 m/s forward
    import time; time.sleep(2)
    robot.stop()

Connection options

ORCP('/dev/cu.usbmodemXXXX')                  # USB serial (auto baud)
ORCP('/dev/cu.usbmodemXXXX', baudrate=115200)  # Explicit baud rate
ORCP('socket://192.168.4.1:3333')              # WiFi bridge

Teleop demo (orcp-drive)

orcp-drive is a keyboard tele-operation app — drive any ORCP controller around with the WASD/arrow keys. It's both a handy test tool and a worked example of the library: it uses CMD_VEL, the safety presets, the enable gate, a background heartbeat, and live telemetry streaming all together.

Install it (the teleop extra just adds curses support on Windows — macOS and Linux need nothing extra):

pip install "orcp[teleop]"

Run it against hardware, a WiFi bridge, or — with no hardware at all — the reference simulator:

orcp-drive /dev/cu.usbmodemXXXX        # USB (macOS; /dev/ttyACM0 Linux, COM3 Windows)
orcp-drive socket://192.168.4.1:3333    # WiFi / TCP

# No robot handy? Drive the simulator instead:
pip install orcp-sim
orcp-sim --link /tmp/orcp &             # terminal 1: a virtual ORCP controller
orcp-drive /tmp/orcp                    # terminal 2: drive it

Controls

Key Action
W / Forward
S / Reverse
A / Spin left
D / Spin right
Q / E Arc forward-left / forward-right
Space Stop
+ / - Increase / decrease speed
M Toggle SLOW ↔ NORMAL (NORMAL auto-enables and starts a background heartbeat)
R Re-enable the motors after a fault is cleared (sends ENABLE ON)
Esc Quit (always returns to a safe, stopped state)

Faults & recovery

The teleop shows the controller's active fault under the battery line (e.g. FAULT: ENCODER_STALL — press [R] to re-enable). Fault behaviour follows the ORCP safety model (spec §5): when the controller trips a fault it stops the motors and latches the fault — it does not silently auto-recover when the cause goes away. That's deliberate: a machine must not become ready-to-move again on its own.

Faults you may see on real hardware:

Fault Cause Recover by
ESTOP Emergency-stop loop opened Close the loop, then press R
ENCODER_STALL Driven against an obstacle (wheels commanded but can't turn) — this is motor protection, not a bug Back the robot off, then press R
OVERCURRENT_* Per-side current limit exceeded Reduce the load, then press R
LOWBATT Battery below the critical threshold Recharge, then press R
HEARTBEAT / TIMEOUT Host link / command flow lost in NORMAL Press R

To recover: clear the cause, then press R. That sends ENABLE ON, which clears recoverable faults and re-enables the motors — matching the ORCP spec (§5.3): "after the emergency stop is released the fault MUST persist until the host explicitly sends ENABLE ON; motors MUST NOT restart automatically." The same explicit-re-enable rule applies to every latched fault, not just e-stop. If R is rejected (e.g. the e-stop is still open), the status line tells you — fix the cause and try again.

The source — src/orcp/teleop.py — is a good read for how the pieces fit together. It's ~200 lines and uses only the public API documented below.

API reference

System

robot.ping()      # → True or raises ConnectionError
robot.info()      # → InfoResponse(fw, hw, proto, level, vendor, model, extra)
robot.status()    # → StatusResponse(preset, mode, enabled, fault, estop, vl, vr, vbat, battery, ...)

Motion

robot.cmd_vel(v=0.5, w=0.0)   # Unicycle: linear (m/s), angular (rad/s)
robot.wheel(l=5.0, r=5.0)     # Direct wheel velocities (rad/s)
robot.stop()                   # Immediate stop, never raises

Safety

robot.preset('SLOW')    # 30% power, auto-enabled, 1 s watchdog
robot.preset('NORMAL')  # 100% power, requires ENABLE ON + heartbeat
robot.enable()
robot.disable()
robot.heartbeat()                    # Single HB
robot.start_heartbeat(interval=0.1)  # Background thread, 100 ms interval
robot.stop_heartbeat()

Streaming telemetry

def on_telemetry(data):
    print(f"Left: {data.vl:.2f} rad/s, Right: {data.vr:.2f} rad/s")

robot.stream_on(rate=10, callback=on_telemetry)
robot.stream_off()

Configuration

kp = robot.get('pid.kp')         # single parameter
cfg = robot.get_all()            # → {name: value} for every parameter
robot.set('pid.kp', 0.08)
robot.save()      # Persist to flash
robot.load()      # Load from flash
robot.defaults()  # Factory reset

Push events

robot.on_fault(lambda e: print("FAULT:", e.code))          # ! FAULT <code>
robot.on_warn(lambda e: print("WARN:", e.type, e.fields))  # ! WARN <type> ...

Cached properties

robot.battery_voltage   # float | None  (volts)
robot.battery           # str | None    (band label "OK"/"LOW"/… or "80%")
robot.is_enabled        # bool | None
robot.fault             # str | None    (updated by STATUS and ! FAULT pushes)

Testing without hardware

Use MockTransport to test your code without a physical robot:

from orcp import ORCP
from orcp.transport import MockTransport

transport = MockTransport()
transport.queue_response('OK PING')
transport.queue_response('OK CMD_VEL')

with ORCP('mock://', _transport=transport) as robot:
    robot.ping()
    robot.cmd_vel(v=0.5, w=0.0)

Error handling

from orcp import CommandError, ConnectionError, TimeoutError

try:
    robot.cmd_vel(v=0.5, w=0.0)
except CommandError as e:
    print(f"Controller error: {e.code}{e.message}")
except TimeoutError:
    print("No response from controller")
except ConnectionError:
    print("Lost connection")

Development

git clone https://github.com/orcp-protocol/orcp-python
cd orcp-python
pip install -e ".[dev]"

pytest tests/
mypy src/orcp/
black src/ tests/
ruff check src/ tests/

Protocol

ORCP uses ASCII line-based messages:

Direction Format
Command COMMAND param=value\n
Success OK COMMAND field=value\n
Error ERR code=CODE msg="description"\n
Push ! TYPE field=value\n

See the ORCP specification for full details.

License

MIT — see LICENSE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

orcp-0.1.0.tar.gz (23.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

orcp-0.1.0-py3-none-any.whl (18.3 kB view details)

Uploaded Python 3

File details

Details for the file orcp-0.1.0.tar.gz.

File metadata

  • Download URL: orcp-0.1.0.tar.gz
  • Upload date:
  • Size: 23.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.4

File hashes

Hashes for orcp-0.1.0.tar.gz
Algorithm Hash digest
SHA256 fa78c21010ebda236a882830e0f7de6d79cae8255aa6296df4e95bbfdd2feb06
MD5 597c1e51c43710885e8191022f3e4352
BLAKE2b-256 733215ecea17a1b65360a6b8cc70f6a93a5612d55a07a84272860e34f4cc9507

See more details on using hashes here.

File details

Details for the file orcp-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: orcp-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 18.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.4

File hashes

Hashes for orcp-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 295272b1f66d05e013b1df39f17b7212bc68ea9dfaeccf089a1503dc3a1b6777
MD5 25c9e14a0f4d8c4c68bd29c5f218a447
BLAKE2b-256 824f20c6396f3b22b17d8978569c2241a773a63ece4f79a51b144a902a7f6c6c

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page