Command-line tool
pipx install openbricks installs one console script, openbricks,
which mirrors the pybricksdev workflow: flash firmware over USB, then
run / upload / stop programs and pull logs over BLE. With the [sim]
extra installed, openbricks sim … forwards to the
MuJoCo-backed simulator.
A typical session:
$ openbricks flash --name RobotA # port, chip and newest firmware auto-detected
$ openbricks list # hubs in BLE range
$ openbricks run -n RobotA main.py # push + stream output
$ openbricks upload -n RobotA main.py # stage; start it with the hub button
$ openbricks stop -n RobotA # Ctrl-C a running program
$ openbricks log -n RobotA # dump the most recent run log
$ openbricks docs hardware # open this manual offline in your browser
Firmware versions and provenance
openbricks flash first reports the firmware already on the chip —
version plus an (official) / (customized) suffix — before it
looks up the newest release. Flashing the same version again, or
an older one, asks for confirmation first; pass --yes to skip
the prompt in scripts.
The default output is step-level (probe, download, erase, write,
hub name, marker, reboot); pass --verbose / -v to also echo
every underlying mpremote / esptool command line and the
firmware cache paths — useful when reporting a flash problem.
Every firmware image published by CI is signed (Ed25519), and the
CLI ships the matching public key. An image whose .bin.sig
verifies is labeled (official); anything else — a self-built
image, a missing or wrong signature — is (customized). Customized
firmware flashes normally: the suffix is provenance, not a gate.
After each flash the verdict is stored on the hub, which is how the
next openbricks flash labels the current firmware.
The suffix follows the version everywhere it reaches you: the
firmware 1.79.0 (official) banner at the top of every
openbricks run, the started: header line in every run log
(openbricks log), and the flash preflight above. On the hub,
openbricks.firmware_label() returns the same string.
Programs are compiled on the host
Since 1.92.0, openbricks run and openbricks upload cross-compile
your script with mpy-cross before connecting, and stage
compiled bytecode instead of source (like Pybricks). Three things
get better:
syntax errors surface in milliseconds, on your terminal, naming your file and line and quoting the offending source line — no BLE scan, no connect, no upload round-trip;
programs start faster: the hub loads bytecode directly and skips its on-device parse/compile step;
tracebacks name your real file and line (
File "square.py", line 12) instead ofFile "<string>".
No flags, nothing to configure. Firmware older than 1.92.0 can’t run
compiled programs, so the CLI probes the hub’s version in-session and
sends plain source instead — announced on stderr, never silently.
upload --path (custom boot flows) always stages your file verbatim,
uncompiled, at the path you give.
run is an upload-then-run (since 2.7.0, deliberately
different from Pybricks): it stages your script at the button’s
/program.mpy before executing it, so even a run that fails midway
leaves the program on the hub — press the start button to rerun it,
no BLE round trip needed. The flip side: running a calibration or a
one-shot diagnostic replaces the button’s program too, so re-upload
your mission after such tools (upload alone stages without
running).
flash --with-qtr-init additionally stores a starter QTR
line-sensor calibration at /qtr.cal (recorded on the reference
bench, default pins 1-10), so the line-follow examples work on a
fresh hub out of the box. Heights, mats and lighting differ — run
examples/qtr_calibrate.py once for a calibration measured on your
own rig.
Reference
The reference below is generated from the CLI’s own argument parser, so it always matches the installed version.
Host-side CLI for flashing and running code on openbricks hubs, plus a MuJoCo-backed simulator (openbricks sim …).
usage: openbricks [-h] [--version] COMMAND ...
Positional Arguments
- COMMAND
Possible choices: flash, run, upload, stop, list, log, servo-id, paste-probe, docs, doc, sim
Named Arguments
- --version
Print the openbricks package version and exit.
Sub-commands
flash
Flash a firmware image onto a hub (via esptool) and write the hub’s BLE advertising name into NVS (via mpremote). –name is mandatory so every hub gets a unique identifier — two hubs with the same name can’t be individually addressed over BLE.
openbricks flash [-h] --name NAME [--port PORT] [--firmware FIRMWARE]
[--chip CHIP] [--baud BAUD] [--with-qtr-init] [--skip-erase]
[--yes] [--verbose]
Named Arguments
- --name
Hub identifier for BLE (required, <=20 chars recommended).
- --port
Serial port (/dev/ttyUSB0, /dev/cu.usbserial-XXXX, COM5 …). Omit to auto-detect — works when exactly ONE ESP device is connected (Espressif native USB or a CP210x/CH340/FTDI bridge).
- --firmware
Path to firmware.bin produced by scripts/build_firmware.sh or downloaded from the Releases page. Omit to download the newest release automatically for the detected chip (cached under ~/.cache/openbricks/firmware).
- --chip
esptool –chip value (esp32, esp32s3, auto). Default: auto.
Default:
'auto'- --baud
esptool flash baud rate. Default: 460800.
Default:
'460800'- --with-qtr-init
After flashing, store a starter QTR line-sensor calibration at /qtr.cal (recorded on the reference bench, default pins 1-10) so line-follow examples work out of the box. Re-run examples/qtr_calibrate.py for a calibration measured on your own mat and lighting.
Default:
False- --skip-erase
Skip erase_flash (faster dev loop; leaves stale NVS keys).
Default:
False- --yes
Skip the confirmation prompt when the target firmware is the same version as (or older than) the current one.
Default:
False- --verbose, -v
Echo every subprocess command line (mpremote/esptool) and cache paths. Default output is step-level only.
Default:
False
run
Connect to the named hub over BLE, push SCRIPT to its REPL (via paste mode), and stream stdout/stderr back to this terminal until the script finishes. Ctrl-C interrupts the remote program.
openbricks run [-h] -n NAME [-c CODE] [--scan-timeout SCAN_TIMEOUT] [--debug]
[SCRIPT]
Positional Arguments
- SCRIPT
Path to the local Python script to run on the hub. Mutually exclusive with -c.
Named Arguments
- -n, --name
Hub name baked in at flash time (
openbricks flash --name).- -c, --code
Inline Python code to run on the hub (analogous to
python -c CODE). Useful for quick diagnostics — e.g.openbricks run -n ls -c 'import openbricks; print(openbricks.__version__)'. Mutually exclusive with the SCRIPT positional.- --scan-timeout
How long to scan for the named hub before giving up. Default: 5.0 s.
Default:
5.0- --debug
Print every BLE notify packet (timestamp + hex + ascii) to stderr as it arrives. Use to diagnose ‘timed out reading from hub’ errors — tells you whether the hub is sending anything at all.
Default:
False
upload
Upload SCRIPT to the hub’s filesystem (default path /program.py). The uploaded code does NOT run automatically — the hub’s frozen main.py watches the hub button and exec’s the staged script on each short press. Second short-press stops a running program. (Pybricks calls this same operation download from the hub’s perspective; we name by direction-of-data-travel — bytes flow up to the hub.)
openbricks upload [-h] -n NAME [--path PATH] [--scan-timeout SCAN_TIMEOUT]
SCRIPT
Positional Arguments
- SCRIPT
Path to the local Python script to stage.
Named Arguments
- -n, --name
Hub name baked in at flash time.
- --path
Destination path on the hub’s filesystem; the file is staged VERBATIM there (no compilation) for custom boot flows. Default: compile with mpy-cross and stage /program.mpy (which the frozen launcher runs; older firmware gets the source at /program.py).
- --scan-timeout
BLE scan timeout. Default: 5.0 s.
Default:
5.0
stop
Connect to the named hub over BLE and send a single Ctrl-C, which MicroPython surfaces as KeyboardInterrupt. Use when a long-running openbricks run has already ended and you just want the hub to idle again.
openbricks stop [-h] -n NAME [--scan-timeout SCAN_TIMEOUT]
Named Arguments
- -n, --name
Hub name.
- --scan-timeout
BLE scan timeout. Default: 5.0 s.
Default:
5.0
list
Run a BLE scan and print every device found, sorted by RSSI (strongest first). Unnamed devices are shown with a placeholder so you can still spot a hub whose name wasn’t flashed.
openbricks list [-h] [--timeout TIMEOUT] [--all]
Named Arguments
- --timeout
Scan duration in seconds. Default: 5.0.
Default:
5.0- --all
Show every BLE device, not just those with names. Useful when debugging a hub that came up without a flashed name.
Default:
False
log
Every program executed via the launcher (button press OR openbricks run) gets its stdout / stderr tee’d to a flash file under /openbricks_logs/. Ten rotating slots are kept. With no flags this prints the most recent run; --list shows the index; --run N selects a specific slot. Useful for post-mortem on an untethered run where no live console was attached.
openbricks log [-h] -n NAME [--list] [--run RUN] [--scan-timeout SCAN_TIMEOUT]
Named Arguments
- -n, --name
Hub name baked in at flash time.
- --list
List the available run indices + their on-flash size, instead of dumping a run’s contents.
Default:
False- --run
Specific run index to dump. Defaults to the most recent.
- --scan-timeout
BLE scan timeout. Default: 5.0 s.
Default:
5.0
servo-id
Scans the bus (IDs 0..253), rewrites the servo’s EEPROM ID register, and verifies the result. With several servos attached, –old-id is required so the tool never guesses which one to re-ID. Wire the servo to a USB half-duplex adapter (e.g. the URT-2 board) — this talks directly to the adapter’s serial port, no hub involved.
openbricks servo-id [-h] [-p PORT] [-n NAME] [--tx TX] [--rx RX]
[--scan-timeout SCAN_TIMEOUT] [--scan] [--old-id OLD_ID]
[--baudrate BAUDRATE] [--timeout TIMEOUT]
[new_id]
Positional Arguments
- new_id
Bus ID to assign (0..253). Omit with –scan.
Named Arguments
- -p, --port
Serial port of the USB adapter, e.g. /dev/cu.usbmodem123. Omitted (and no -n): auto-detected when exactly one USB serial device is connected.
- -n, --name
Hub name: run the scan/re-ID THROUGH THE HUB over BLE instead of a USB adapter — the servo stays wired to the robot. Mutually exclusive with -p.
- --tx
Hub path only: servo-bus TX pin (default 14).
Default:
14- --rx
Hub path only: servo-bus RX pin (default 41).
Default:
41- --scan-timeout
Hub path only: BLE scan timeout. Default: 5.0 s.
Default:
5.0- --scan
Just list the IDs that answer on the bus; change nothing.
Default:
False- --old-id
Current ID of the servo to re-ID. Required when more than one servo is attached; otherwise auto-detected.
- --baudrate
Bus baudrate. Default: 1000000 (Feetech factory).
Default:
1000000- --timeout
Per-ping serial read timeout in seconds. Default: 0.02 (a full 254-ID scan takes ~5 s).
Default:
0.02
paste-probe
Pastes padded no-op programs of increasing size through the real raw-paste path and reports the largest that completes, plus how each failure presents (truncated vs hung). Use before changing the firmware’s MICROPY_REPL_STDIN_BUFFER_MAX: two windows may be in flight at once, so that setting is only safe at or below the measured limit.
openbricks paste-probe [-h] -n NAME [--scan-timeout SCAN_TIMEOUT] [--max MAX]
[--timeout TIMEOUT]
Named Arguments
- -n, --name
Hub BLE name.
- --scan-timeout
BLE scan timeout in seconds. Default: 5.
Default:
5.0- --max
Largest size to try, bytes. Default: 8192.
Default:
8192- --timeout
Per-size timeout in seconds. Default: 15.
Default:
15.0
docs (doc)
Opens the full manual in your browser — the same Sphinx build as docs.openbricks.dev, API reference included, bundled and served from disk so no internet is needed. Pass a topic to jump straight to that page.
openbricks docs [-h] [topic]
Positional Arguments
- topic
Page to open (e.g. install, hardware, robotics). Guides and API pages both work. Omit for the index.
sim
Forwards all remaining arguments to the MuJoCo-backed simulator’s CLI. Use openbricks sim --help to see the sim’s own subcommand list.
openbricks sim