A portable embedded command shell. The same module code — compass, sonar, IR,
locomotion, displays — runs across an 8-bit Arduino Uno, the Arduino R4 WiFi, the
Raspberry Pi Pico W / Pico 2 W, the ESP32-S3, the STM32 "Bluepill", and the
dual-brain Arduino Uno Q.
Write a module once against a small C HAL; cmdr composes it into a project and it
builds for any target.
It's for experimenting and prototyping — one framework that scales from an AVR up to a Linux-hosted board, without forking per platform.
Left to right: Arduino Uno, UNO R4 WiFi, UNO Q, Raspberry Pi Pico, Pico W,
Pico 2, Pico 2 W, ESP32-S3, STM32 "Bluepill" — the same module code runs on all
of them. The pico and pico2 targets build for the wireless variants; the
non-W boards run the same firmware without WiFi or Telnet.
A shell on the board. Modules register commands; you talk to them over serial or telnet while the firmware runs:
$ cmdr init pico myrobot && cd myrobot
$ cmdr module enable wifi
$ cmdr module enable sonar
$ cmdr module enable i2c
$ cmdr enable ota # a feature, not a module — adds `ota`
$ ./bum # build + upload + monitor
> help
reset -- reboot the firmware
bootloader -- enter USB bootloader
help -- list all commands
version -- firmware name, build number, commit
i2c -- I2C bus diagnostics - 'i2c' for usage
ping -- measure distance (cm / in)
wifi -- WiFi status/control - 'wifi status|off|on'
ota -- flash firmware from URL (http)
> wifi status
wifi: connected ssid=my-network ip=192.168.1.71 rssi=-36 dBmA real session on a Pico W, and every line above the prompt put something in it.
ping, i2c and wifi are there because of the three cmdr module enable
lines; ota because of cmdr enable ota. No code was written to register any
of them, and what you didn't enable isn't compiled at all. Only four come free:
help and version from the always-on system module, reset and
bootloader from the Pico runner.
Arduino sketches are the fastest way to start, and if one board and
Serial.println debugging cover your needs, stay there. Commander is for when
you want to interrogate a running device rather than re-flash it to change a
constant — and for when the same sensor code has to run on an AVR and a Pico
and an ESP32 without three forks of it.
MicroPython / CircuitPython give you a REPL, which is the closest thing to this, and a faster edit loop. You trade away native SDK access, hard real-time behaviour, and the small tiers — an Uno isn't a realistic target. Commander keeps you in C++ on the vendor SDK and still gives you the interactive loop.
Zephyr's shell is genuinely portable and more capable than this one, but it's Zephyr: a much larger commitment, and it won't run on an ATmega328. Commander uses Zephyr as one backend (the Uno Q) rather than requiring it.
ESPHome is excellent at what it targets — declarative devices on ESP chips, usually pointed at Home Assistant. Commander is imperative, C++, and multi-vendor; different shape of problem.
Your own serial command parser is the honest comparison, because that's what most people write. This is that, plus a HAL so modules move between boards, plus a tool that composes them, plus transports (telnet, the Uno Q channel bus) the parser would have grown eventually.
core/ → hal/<platform>/ → modules/ → transport/ → platform/<board>/main.cpp
core/ is pure C++ with zero platform deps. Modules include only core/ and the C
HAL (hal/hal.h). Platform specifics are confined to hal/, transport/, and
platform/. One HAL .cpp per platform is compiled; the build excludes the rest.
| Board | Build | Status |
|---|---|---|
| Arduino Uno (AVR) | PlatformIO | ✅ shell over serial |
| Arduino R4 WiFi | PlatformIO | ✅ shell + WiFi + Telnet + mDNS |
| Raspberry Pi Pico W (RP2040) | CMake + Pico SDK | ✅ shell over USB CDC + WiFi + Telnet |
| Raspberry Pi Pico 2 W (RP2350) | CMake + Pico SDK | ✅ shell + WiFi + Telnet; SMP/M33 |
| ESP32-S3 | ESP-IDF v5 | ✅ shell over native USB + WiFi + Telnet |
| STM32 Bluepill (F103) | PlatformIO (CMSIS) | ✅ shell over USART/USB CDC; USB-DFU upload (I2C pending) |
| Arduino Uno Q (Debian + M33) | Zephyr (west) + Debian services | ✅ shell + channel bus + IR — see the IR-speaker walkthrough |
Real devices, not demos — each is a separate repo that pulls commander in as a dependency and provides two functions.
| Project | Board | What it demonstrates |
|---|---|---|
| cmdr-ipstube | ESP32 | Six SPI displays on three CS lines, filesystem assets, OTA |
| cmdr-robot | Pico 2 W | Bluetooth pad → I2C actuation; WiFi and BT on one radio |
| cmdr-oi-bridge | Uno R4 WiFi | Commander as an I2C peripheral — the robot's other half |
| cmdr-solar-monitor | ESP32-S3 | The smallest useful consumer: two modules and a host script |
| cmdr-pico-breadboard-kit | Pico 2 W | A whole dev board as modules: touch panel, stick, buttons, LEDs, buzzer |
| cmdr-unoq-ir-speaker | Uno Q | Dual-brain: hard real-time on the MCU, neural TTS beside it — with no custom firmware |
Between them they cover all four build flavours (ESP-IDF, Pico SDK, PlatformIO, Zephyr/west). docs/projects.md has the full write-ups — modules enabled, pins, what each one proved on hardware.
# install the project manager
pip install "git+https://github.com/gbryant/commander.git#subdirectory=tools/cmdr"
# scaffold a project, add a module, build+upload+monitor
cmdr init pico myrobot
cd myrobot
cmdr module enable sonar
./bumFull setup — host tools, SDK bootstrap, the env-var contract, and a per-board prerequisite matrix — is in docs/getting-started.md.
cmdr(tools/cmdr/) scaffolds projects and composes modules. A project fetches commander as a CMake / PlatformIO dependency rather than vendoring it;cmdr pulladopts framework updates.- Modules (
cmdr module enable <name>) are composed, not hand-wired: enabling one records its config incmdr.tomland regenerates the registration glue, so disabled modules aren't compiled. Cross-platform modules use the same code on every target; some are platform-gated (e.g. displays on ESP32, Bluetooth controllers on Pico). - The HAL is a C interface (I2C, GPIO, UART, time). Porting to a new board is
one HAL
.cppplus aplatform/<board>/main.
- docs/getting-started.md — install, SDKs, first project.
- docs/modules.md — what every stock module does, per board.
- docs/writing-a-module.md — write your own module.
- docs/cmdr.md — the
cmdrtool, command by command. - docs/projects.md — real devices built with commander, as worked examples.
- CLAUDE.md — deep per-board build details, modules, conventions.
- PLAN.md — roadmap and status.
- Arduino Uno Q — docs/unoq-ir-speaker.md (start here: an end-to-end walkthrough, remote button → spoken name, no code), docs/getting-started-unoq.md (bring up a stock board), docs/unoq-access.md (board access), and dev/unoq/README.md (Debian-side bridge/broker). Generic board tooling (bring-up wizard, TTS, BT audio) lives in the companion unoq-tools repo.
- Channel bus (multi-consumer Linux-hosted commander) — docs/commander-channels-design.md.
A solo project, built for my own hardware and shared because it may be useful. It works — all seven boards are hardware-confirmed running the shell, and the projects listed above are real devices in daily use, not demos. But calibrate accordingly: there is one maintainer, and no support promise.
- Releases are
vMAJOR.MINOR; the major number moves only when a release breaks existing consumers. Scaffolded projects pin a release tag, so an upstream mistake can't reach a project generated last month. - Testing is a local tiered suite —
tests/run.sh(host C++ units +cmdrcodegen golden files) andtests/build-matrix.sh(compile smoke across boards). GitHub Actions was deliberately declined for a solo project; see docs/testing.md. - Known gaps are tracked honestly rather than glossed. Bluepill I2C is
stubbed, so the I2C modules aren't offered there. And two IR backends —
ESP32 (RMT) and Bluepill (EXTI/DWT) — are written and compile but have never
been run on hardware;
cmdr module enable iroffers them, so treat those two as untested rather than working. PLAN.md marks state per area. - Issues and PRs are welcome.
MIT — see LICENSE. Embed it, ship it, sell it; just keep the notice.
Third-party code in this repository and the SDKs you supply at build time are listed
in THIRD_PARTY.md, including the two dependencies with non-obvious
terms: BTstack (behind the optional controller module) and the optional STM32 DFU
bootloader.
