Serial and DeviceControl
Two control surfaces share the USB connection
Section titled “Two control surfaces share the USB connection”A development LilBub exposes two related interfaces over USB serial:
- The serial console is for a person. It prints boot and network logs and
accepts short commands such as
help,wifi status, andpair status. - DeviceControl is for tools. It accepts structured requests and returns structured results over serial, authenticated TCP, BLE, or an internal firmware call.
Use the console when you need to watch a boot or inspect one setting by hand. Use DeviceControl when the result must be repeatable, parsed, asserted, or captured as an artifact. The Python harness handles framing and log noise; do not build automation by scraping human console text.
Set up the harness
Section titled “Set up the harness”From lil_buddy/harness, create an isolated Python environment and install the
device tooling:
python -m venv .venv& .\.venv\Scripts\python.exe -m pip install -e ".[test,e2e]"& .\.venv\Scripts\lilbub.exe --helpThe harness controls the separate waveshare_dev firmware image. Installing
that image changes the device and is not part of an ordinary read-only
connection. Build it only when you are intentionally working on firmware:
platformio run -e waveshare_devThe first development image must be installed over USB. The bootstrap
workflow performs that upload, verifies the exact build, and enrolls the host.
Treat it as a deliberate firmware operation, not a discovery command:
& .\.venv\Scripts\lilbub.exe bootstrap --port COM4The host credential is stored in the operating-system user configuration directory, outside the repository.
Find the device
Section titled “Find the device”On Windows, the device normally appears as a COM port. Start with discovery
and diagnostics:
& .\.venv\Scripts\lilbub.exe discover& .\.venv\Scripts\lilbub.exe doctorWhen more than one serial device is connected, pass the expected port and device identity rather than relying on discovery order. A read-only identity call is a useful first check:
& .\.venv\Scripts\lilbub.exe call system.info ` --serial COM6Confirm that the returned device and build are the ones you intended to inspect before running a control or destructive method.
Use the interactive console
Section titled “Use the interactive console”The console runs at 115200 baud. Open it with the repository serial helper or a
serial monitor, press Enter, and run help to discover the commands compiled
into that build.
Safe starting commands include:
helpwifi statusbackend statuspair statusota statusThe console also carries firmware logs. Those logs are useful for a human following Wi-Fi, HTTPS, MQTT, OTA, or recovery state, but their wording is not a stable API. Close the monitor before giving the same port to the harness; serial ports normally have one owner at a time.
Never paste credentials into a transcript that will be committed or attached to a public report. Wi-Fi provisioning stores passwords on the device, while DeviceControl intentionally returns profile metadata without returning those passwords.
Make structured calls
Section titled “Make structured calls”DeviceControl exposes named methods. The call command accepts a method,
optional JSON parameters, and a transport:
& .\.venv\Scripts\lilbub.exe call system.health ` --serial COM6
& .\.venv\Scripts\lilbub.exe call display.brightness ` --serial COM6 ` --params '{"brightness":48}'Ask the device for its compiled method inventory instead of assuming that two firmware builds expose the same surface:
& .\.venv\Scripts\lilbub.exe call control.capabilities ` --serial COM6The response identifies the DeviceControl protocol version and, for each method, its name, description, required scope, and transport mask.
Understand transports and scopes
Section titled “Understand transports and scopes”DeviceControl applies two independent checks:
| Dimension | Values | Why it matters |
|---|---|---|
| Transport | serial, authenticated TCP, BLE, internal | A recovery or enrollment method may be intentionally unavailable over a remote transport |
| Scope | read, control, destructive | A transport can be connected without being authorized for every side effect |
Serial is the physical recovery anchor. Authenticated TCP is convenient after the development host is enrolled. BLE carries the nearby companion protocol. Internal calls let trusted firmware subsystems use the same typed operation without inventing another control path.
Do not treat method availability as permission to run it. Before a destructive call, record the intended device, current build, expected state change, and recovery path.
Provision Wi-Fi
Section titled “Provision Wi-Fi”The firmware stores up to four device-local Wi-Fi profiles. Copy the ignored example file, add the networks needed for the device, and provision the exact target:
& .\.venv\Scripts\lilbub.exe wifi provision ` --env .wifi.env ` --serial COM6 ` --expect-device lilbub-abcdef
& .\.venv\Scripts\lilbub.exe wifi list ` --serial COM6Lower priority numbers are tried first. Adding a profile does not intentionally disconnect the active network. Listing profiles returns SSIDs and priorities, not passwords.
Backend targets are a development concern separate from Wi-Fi. Inspect the active target before changing it, and never publish a private host or network address in documentation or test artifacts.
Drive the interface semantically
Section titled “Drive the interface semantically”UI automation uses stable scene and element identifiers instead of screen coordinates:
& .\.venv\Scripts\lilbub.exe call ui.elements --serial COM6& .\.venv\Scripts\lilbub.exe call ui.activate ` --serial COM6 ` --params '{"id":"settings.wifi"}'& .\.venv\Scripts\lilbub.exe call ui.swipe ` --serial COM6 ` --params '{"direction":"left","duration_ms":320}'& .\.venv\Scripts\lilbub.exe screenshot artifacts/settings.png --serial COM6A screenshot produces the circular raw image, an annotated image, and a JSON sidecar describing the captured state. This makes the artifact useful for visual review and for diagnosing semantic metadata without guessing at touch coordinates.
For repeatable product flows, use the pytest UI fixture. It waits for stable postconditions, audits the active semantic registry, and records a timeline:
& .\.venv\Scripts\python.exe -m pytest ` -m device ` --serial COM6 ` -qDiagnose before changing state
Section titled “Diagnose before changing state”Start with the narrowest read-only evidence:
system.infoconfirms device and build identity.system.healthshows boot and health state.wifi.infoandwifi.profilesseparate association failures from missing configuration.backend_target.infoidentifies the active API target.pairing.infoandota.infoshow lifecycle state without returning keys.diagnostics.snapshotgathers a structured cross-section when individual checks are not enough.
If the harness cannot open the port, close other serial monitors and confirm the port has not changed after a reboot. If structured calls time out while logs continue, confirm that the running image is the development build and that no second harness process owns the device lock.
Recovery over serial
Section titled “Recovery over serial”Development safe mode keeps a deliberately small recovery plane available: serial control, Wi-Fi management, authenticated TCP, development OTA, display diagnostics, and restricted signed OTA handling. Inspect recovery state before clearing or rebooting it:
& .\.venv\Scripts\lilbub.exe call recovery.info ` --serial COM6Clearing safe mode, replacing persisted face data, rebooting, or flashing can
change recoverable device state. Follow the harness recovery workflow and keep
USB available as the physical fallback. Ordinary inspection commands such as
doctor, call, and screenshot do not implicitly upload firmware.
