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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fa78c21010ebda236a882830e0f7de6d79cae8255aa6296df4e95bbfdd2feb06
|
|
| MD5 |
597c1e51c43710885e8191022f3e4352
|
|
| BLAKE2b-256 |
733215ecea17a1b65360a6b8cc70f6a93a5612d55a07a84272860e34f4cc9507
|
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
295272b1f66d05e013b1df39f17b7212bc68ea9dfaeccf089a1503dc3a1b6777
|
|
| MD5 |
25c9e14a0f4d8c4c68bd29c5f218a447
|
|
| BLAKE2b-256 |
824f20c6396f3b22b17d8978569c2241a773a63ece4f79a51b144a902a7f6c6c
|