Skip to content

GPIO tools

GPIO write/read for tethered boards, the Uno Q bridge, the serial transport, and hardware context files.

GPIO tools let your agent drive and sense pins on a connected board through plain function calls. The two core tools — gpio_write and gpio_read — work the same way for every tethered board (Raspberry Pi Pico, Arduino Uno, ESP32, STM32 Nucleo) because they all speak the same Revka serial JSON protocol. This page covers those two tools, the serial transport that carries them, the hardware_capabilities discovery tool, the Arduino Uno Q Bridge variant that runs over a TCP socket, and the hardware context files that teach the agent about your specific board.

Use this page when you want the agent to flip an LED, read a button, or check which pins a board exposes. If Revka runs directly on a Raspberry Pi (not tethered over USB), use the native gpio_rpi_* tools instead — see Raspberry Pi (self-hosted). To connect and flash a board first, see Hardware quickstart and Flashing firmware.

gpio_write sets a GPIO pin HIGH or LOW on a registered device. The agent calls it as a function; you typically trigger it with natural language (“turn on pin 25”).

ParameterTypeRequiredMeaning
pinintegeryesGPIO pin number
valueintegeryes1 = HIGH (on), 0 = LOW (off)
devicestringnoDevice alias, e.g. pico0, arduino0. Auto-selected when exactly one GPIO-capable device is registered; required when multiple boards are present.

A typical agent invocation:

User: Turn on pin 25
Agent: gpio_write(pin=25, value=1)
# With more than one board connected:
Agent: gpio_write(device="pico0", pin=25, value=1)

Under the hood the tool sends one newline-delimited JSON line over the device transport and reads one line back:

// Host → Device
{"cmd":"gpio_write","params":{"pin":25,"value":1}}
// Device → Host
{"ok":true,"data":{"pin":25,"value":1,"state":"HIGH"}}

On success the tool returns a line like GPIO 25 set HIGH on pico0. Values other than 0 or 1 are rejected before any I/O, and a missing pin or value returns a parameter error.

gpio_read reads the current HIGH/LOW state of a pin. It uses the same transport and the same auto-selection rules as gpio_write.

ParameterTypeRequiredMeaning
pinintegeryesGPIO pin number to read
devicestringnoDevice alias; auto-selected if only one GPIO device is registered
Agent: gpio_read(pin=25)
# or, with multiple boards:
Agent: gpio_read(device="pico0", pin=25)

Wire exchange:

// Host → Device
{"cmd":"gpio_read","params":{"pin":25}}
// Device → Host
{"ok":true,"data":{"pin":25,"value":1,"state":"HIGH"}}

The tool output includes both the numeric value and the text state, for example:

GPIO 25 is HIGH (1) on pico0

gpio_write and gpio_read are the same tool names across very different transports. Revka substitutes the right implementation based on how the board is registered, so the agent never has to choose.

SetupToolsTransport
Tethered Pico / Arduino / ESP32 / Nucleo over USBgpio_write, gpio_readSerial JSON protocol (this page)
Revka running natively on a Raspberry Pigpio_rpi_write, gpio_rpi_read, gpio_rpi_blinkDirect /dev/gpiomem via rppal — see Raspberry Pi (self-hosted)
Arduino Uno Q with transport = "bridge"gpio_write, gpio_readBridge TCP socket on 127.0.0.1:9999 (below)
Total Phase Aardvark adaptergpio_aardvark8-pin bitmask over USB — see Aardvark I2C/SPI/GPIO & datasheets

All tethered boards reach their GPIO through the serial transport, which carries the Revka serial JSON protocol over a serial port. You configure it implicitly by setting transport = "serial" on a board entry.

[peripherals]
enabled = true
[[peripherals.boards]]
board = "nucleo-f401re"
transport = "serial"
path = "/dev/ttyACM0"
baud = 115200
FieldTypeDefaultMeaning
pathstringSerial port path (e.g. /dev/ttyACM0, /dev/cu.usbmodem101)
baudinteger115200Serial baud rate

Two behaviors are worth knowing:

  • Lazy open. The port is opened on the first command, not at daemon startup. The daemon starts cleanly even if the board is unplugged at boot — plug it in and the first gpio_write opens the connection.
  • Firmware ping handshake. On first connection the transport sends a ping and expects a Revka firmware response within 300 ms. Boards that do not answer with Revka firmware are not treated as GPIO devices.

The wire contract is the newline-delimited ZcCommand / ZcResponse pair shown throughout this page: a host command (cmd + params) for each request, and a device response (ok + data, plus error when ok is false). Both firmware and host must agree on this format, so the Pico, Arduino, ESP32, and Nucleo all implement it identically.

Discover available pins (hardware_capabilities)

Section titled “Discover available pins (hardware_capabilities)”

Before writing to a pin, the agent can ask each serial board which pins it exposes and which pin drives the onboard LED. The hardware_capabilities tool queries the firmware’s capabilities command and reports the result.

ParameterTypeRequiredMeaning
boardstringnoFilter to a single board name. Omit to query every configured serial board.
Agent: hardware_capabilities()
Output: arduino0: gpio [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13], led_pin 13

This tool is only registered when the hardware feature is built and serial boards are configured. The firmware must implement the capabilities command in its serial JSON handler; the Revka Arduino firmware enables it alongside gpio_read and gpio_write. If no board matches the filter, the tool reports that capabilities are not supported.

The Arduino Uno Q is a Linux board with a companion MCU. Instead of a serial port, GPIO goes through a small Bridge app (Python + MCU socket server) that listens on TCP port 9999 and exposes the MCU’s pins to the Linux side. When you configure the board with transport = "bridge", Revka transparently swaps the standard gpio_read / gpio_write tools for socket-backed implementations — the agent uses the same tool names.

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

Calling the tools is identical to the serial case:

User: Turn on pin 13 on the Arduino
Agent: gpio_write(pin=13, value=1)

Internally the Bridge tools open a TCP connection to 127.0.0.1:9999 and send a space-delimited, newline-terminated line — for example gpio_write 13 1\n for a write or gpio_read 13\n for a read — then read the reply.

TimeoutValue
Connection5 s
Response3 s

GPIO tools work on raw pin numbers, but the agent reasons better when it knows what each pin does on your board. Revka loads markdown from ~/.revka/hardware/ at boot and injects it into the LLM system prompt, so you can teach the agent about your hardware without touching code. Three kinds of files are supported:

PathPurpose
~/.revka/hardware/HARDWARE.mdGlobal hardware notes shared across every device
~/.revka/hardware/devices/<alias>.mdPer-device profile, named for the session alias (e.g. pico0.md)
~/.revka/hardware/skills/*.mdHardware skills, loaded in alphabetical order by filename
  1. Write global notes. Capture board-agnostic rules and pin conventions.

    Terminal window
    cat > ~/.revka/hardware/HARDWARE.md << 'EOF'
    # Hardware Notes
    - Pin 25 is the onboard LED on Pico
    - Use device_exec for blink loops
    EOF
  2. Add a device profile. Name the file after the device alias so it loads only when that board is present.

    Terminal window
    mkdir -p ~/.revka/hardware/devices
    cat > ~/.revka/hardware/devices/pico0.md << 'EOF'
    # pico0 — Raspberry Pi Pico
    Port: /dev/cu.usbmodem1101
    Use pin 25 for the onboard LED.
    EOF
  3. Restart the daemon so the boot subsystem re-reads the directory and injects the new context.

How loading works:

  • Files are plain markdown — no schema or frontmatter required.
  • Sections are joined with double newlines when injected into the system prompt; missing files are silently skipped, and empty files are ignored.
  • Device profiles load only for aliases that are actually registered in the session.