Skip to content

Flashing firmware

Flash Pico (UF2), Arduino (arduino-cli), and Nucleo (probe-rs), plus device code read/write/exec.

This page covers how Revka puts firmware onto a microcontroller and how the agent reads, writes, and runs code on a board once it is online. Each board family uses a different flashing toolchain — the Raspberry Pi Pico flashes a UF2 over a mass-storage drive, the Arduino Uno uses arduino-cli, and the STM32 Nucleo uses probe-rs over its built-in ST-Link. The Arduino Uno Q is different again: it runs a Bridge app rather than taking a firmware flash.

All hardware features require the hardware Cargo feature at build time:

Terminal window
cargo build --release --features hardware

After you flash a board, register it so the GPIO tools can reach it — see GPIO tools and the supported boards reference. New to the hardware stack? Start with the hardware quickstart.

The Pico is flashed by the LLM-callable pico_flash tool. It copies the embedded Revka MicroPython firmware (a UF2 image) onto the Pico in BOOTSEL mode, deploys main.py over mpremote, waits for the serial port to reappear, and reconnects the device transport — so GPIO commands work immediately without restarting the daemon.

  • The hardware feature compiled in.

  • mpremote installed:

    Terminal window
    pip install mpremote
  1. Hold the BOOTSEL button while plugging in the Pico. The RPI-RP2 drive mounts (a volume on macOS/Linux, a drive letter on Windows).

  2. Tell the agent to flash the board, for example: “Flash my Pico.”

  3. The agent calls the tool with the safety gate set:

    pico_flash(confirm=true)
  4. The tool copies the UF2, waits up to 20 seconds for the serial port, deploys main.py, and reconnects the transport. On success it reports something like:

    Pico flashed and main.py deployed successfully. Firmware is online at
    /dev/cu.usbmodem101. pico0 is ready — you can use gpio_write immediately.

Parameters

ParameterTypeRequiredMeaning
confirmbooleanyesMust be true to proceed. Safety gate.

Once a MicroPython (or CircuitPython) device is online, three tools let the agent manage the program running on it. All three use mpremote, so pip install mpremote is required. The optional device parameter is auto-selected when exactly one device is registered.

Reads the current main.py from the device. Run this before modifying device behavior.

device_read_code()
device_read_code(device="pico0")

Internally it runs mpremote connect <port> cat :main.py and returns the file contents. If main.py does not exist, it returns a friendly “no program yet” message rather than an error.

ParameterTypeRequiredMeaning
devicestringnoDevice alias; auto-selected if only one device is registered.

Writes a complete Python program to the device as main.py, then resets the board.

device_write_code(
device="pico0",
code="import machine, time\nled = machine.Pin(25, machine.Pin.OUT)\nwhile True:\n led.toggle()\n time.sleep(0.5)\n"
)

Internally it copies the code to main.py over mpremote and resets the device, then waits up to 15 seconds for the serial port to reappear and reports whether reconnection succeeded.

ParameterTypeRequiredMeaning
devicestringnoDevice alias; auto-selected if only one device.
codestringyesComplete program to write as main.py. Empty values are rejected.

Runs a Python snippet on the device without modifying main.py. The device keeps running its current program after the snippet completes. Use this for sensor reads, quick tests, and blink loops.

device_exec(code="print(machine.Pin(25).value())")

Internally it runs mpremote connect <port> run <snippet> and returns captured output (including MicroPython stderr, such as exceptions). There is a 30-second timeout; an unresponsive device produces a descriptive timeout error. Empty code is rejected.

ParameterTypeRequiredMeaning
devicestringnoDevice alias; auto-selected if only one device.
codestringyesPython snippet to execute; output is captured and returned.

The Arduino uses arduino-cli for both the one-time Revka firmware flash and per-sketch uploads.

Flash Revka firmware (revka peripheral flash)

Section titled “Flash Revka firmware (revka peripheral flash)”

This CLI command flashes the embedded Revka firmware sketch to an Arduino Uno. It handles installing arduino-cli (via Homebrew on macOS), installing the AVR core, compiling the sketch, and uploading it. The firmware enables the capabilities, gpio_read, and gpio_write commands over serial.

Terminal window
# Explicit port
revka peripheral flash --port /dev/cu.usbmodem14301
# Or, if the board is already in config.toml
revka peripheral flash
FlagTypeRequiredMeaning
--portstringnoSerial port. Auto-resolved from config.toml if omitted.

Add the board to ~/.revka/config.toml after flashing:

[peripherals]
enabled = true
[[peripherals.boards]]
board = "arduino-uno"
transport = "serial"
path = "/dev/cu.usbmodem14301"
baud = 115200

arduino_upload is an LLM-callable tool that compiles and uploads an agent-generated Arduino sketch — separate from the Revka firmware flash above. It is added automatically when a board named exactly arduino-uno is configured, and it uses that board’s configured path as the serial port.

The agent writes the full .ino sketch (both setup() and loop()) and passes it as code. Revka writes the sketch to a temp directory, then runs arduino-cli compile and arduino-cli upload against the arduino:avr:uno board.

arduino_upload(code="void setup() {\n pinMode(13, OUTPUT);\n}\nvoid loop() {\n digitalWrite(13, HIGH);\n delay(500);\n digitalWrite(13, LOW);\n delay(500);\n}\n")

A natural-language trigger such as “Upload a blink sketch to the Arduino” causes the agent to generate the sketch and call the tool.

ParameterTypeRequiredMeaning
codestringyesFull .ino sketch content (complete file). Empty values are rejected.

The Nucleo-F401RE is flashed with probe-rs over the ST-Link debugger built into the board — no separate debug probe is needed. The CLI command builds the Embassy Rust firmware and flashes it. After flashing, the board speaks the Revka serial JSON protocol on USART2 (PA2/PA3), which is bridged to the ST-Link virtual COM port.

Install the probe-rs CLI tools (note the crate is probe-rs-tools, not probe-rs):

Terminal window
cargo install probe-rs-tools --locked

Flash via Revka (revka peripheral flash-nucleo)

Section titled “Flash via Revka (revka peripheral flash-nucleo)”
Terminal window
revka peripheral flash-nucleo

This builds firmware/nucleo and runs probe-rs run --chip STM32F401RETx. The firmware runs immediately after flashing.

Terminal window
cd firmware/nucleo
cargo build --release --target thumbv7em-none-eabihf
probe-rs run --chip STM32F401RETx target/thumbv7em-none-eabihf/release/nucleo

Key constants: chip STM32F401RETx, target triple thumbv7em-none-eabihf.

After flashing, the Nucleo appears as a serial device:

/dev/cu.usbmodem* or /dev/tty.usbmodem* (for example /dev/cu.usbmodem101).

Add the board to ~/.revka/config.toml:

[peripherals]
enabled = true
[[peripherals.boards]]
board = "nucleo-f401re"
transport = "serial"
path = "/dev/cu.usbmodem101" # adjust to your port
baud = 115200

Pin 13 = PA5 = the User LED (LD2) on the Nucleo-F401RE.

You can read chip info and memory maps from a Nucleo over USB/SWD without any firmware on the target by building with the probe feature (cargo build --features hardware,probe) and running revka hardware info. See supported boards for details.

The Arduino Uno Q is not flashed in the usual sense. Its Linux side runs Revka, and GPIO control goes through a Bridge app — a Python + MCU socket server that listens on TCP port 9999 and exposes gpio_read / gpio_write from the Linux side to the MCU’s GPIO. The revka peripheral setup-uno-q command deploys the Bridge app via scp + arduino-app-cli.

Terminal window
# From your Mac (with the revka repo)
revka peripheral setup-uno-q --host 192.168.0.48
# From the Uno Q itself (SSH'd in) — defaults to localhost
revka peripheral setup-uno-q

This copies the Bridge app (from firmware/uno-q-bridge/) to ~/ArduinoApps/uno-q-bridge and starts it.

FlagTypeRequiredMeaning
--hoststringnoIP address of the Uno Q. Defaults to localhost.

After setup, register the board in config.toml with the bridge transport:

[peripherals]
enabled = true
[[peripherals.boards]]
board = "arduino-uno-q"
transport = "bridge"

With this configured, the standard gpio_read / gpio_write tools are transparently backed by the Bridge TCP socket (127.0.0.1:9999) instead of a serial port — the agent uses the same tool names. The Bridge must be running for these to work; setup-uno-q starts it. Connection timeout is 5 s and response timeout is 3 s; error responses from the Bridge are prefixed with error:.