Skip to content

openhal-firmware

tested on simulator

Tier Tier 1
Protocol OpenHAL firmware protocol 1 over USB serial or UART
Covers Arduino boards, ESP32 boards, maker projects that run the OpenHAL library
Hazard default low
Conformance full
Tested with protocol simulator, and the real Arduino library built for the host computer

Estimated reach. Any board the Arduino toolchain supports, once it runs the library. The library is plain C++ with no heap and no board-specific code, so it builds wherever the Arduino core provides Stream and millis. It is compile-checked for arduino:avr:uno and esp32:esp32. Coverage grows with every maker who adds it to a sketch, so there is no fixed model count.

The openhal-firmware driver talks to boards running the OpenHAL Arduino library (firmware/arduino/OpenHAL). The board describes its readings, outputs and actions, enforces its own limits and falls to its safe state when heartbeats stop, and the protocol is in spec/FIRMWARE-PROTOCOL.md.

Device file

This device file is the plant waterer from examples/plant-waterer/, for a real Uno.

device: Arduino plant waterer
id: plant-waterer
match:
  firmware: { product: plant-waterer }
connect: { openhal-firmware: auto, boot_s: 2 }
read:
  soil_moisture: { unit: "%", every: 1 s, range: [0, 100] }
  tank: { type: enum, values: [empty, ok], every: 1 s }
actions:
  water:
    description: Run the pump for a number of seconds.
    args:
      duration: { unit: s, min: 1, max: 30 }
    require:
      - condition: "tank == ok"
        message: "pump can't run while tank is empty"
    max_runs: { count: 3, per: 1 h }
  stop:
    description: Turn the pump off now.
safe_state: { action: stop }
watchdog: { heartbeat: 1 s, on_loss: safe_state }
hazard: low
ParameterDefaultMeaning
portautoA serial device path, a pyserial URL, auto (the first known Arduino or ESP32 USB id) or sim:<name> for the built-in simulator
baud115200Serial speed
timeout_s1.0How long to wait for each reply
boot_s0Wait after opening the port, for boards that reset when the port opens (set 2 for an Uno)

Install the optional dependency with uv pip install "openhal-hub[openhal-firmware]" (pyserial).

Behavior

  • Connect sends HELLO, checks the protocol version, reads DESC, and starts a heartbeat at a third of the board's timeout.
  • Reads use a streamed EVT value when the value is less than 5 s old, and send READ otherwise.
  • Channel changes send WRITE and return the value the board applied, and actions send ACT with every argument in the board's unit. A board refusal (ERR limit, ERR interlock) becomes a DriverError, and the kernel marks what the command touched unknown until a readback.
  • safe_state_support is native, so a stop sends SAFE and the kernel also applies the device file's safe state.
  • describe() turns DESC into device file sections with every limit marked suggested, so the file stays read-only until a person confirms the file.
  • Discovery only probes ports whose USB vendor id is Arduino, WCH CH340, Silicon Labs CP210x, Espressif, Adafruit or FTDI, and only sends HELLO.

Quirks

  • Because many boards reset when the port opens, set boot_s, or the first HELLO times out.
  • A sketch that registers channels wrongly (misconfigured()) returns ERR busy misconfigured for every WRITE and ACT, but reads still work.
  • On an ESP32 the USB CDC port may enumerate under Espressif (303a) or a bridge chip id, so discovery probes both.

Tests

tests/drivers/test_openhal_firmware.py covers the codec, the simulator and the driver through the kernel. tests/firmware/test_arduino_host.py builds the real library and every example for the host computer and runs the conformance suite against it. Hardware steps are in docs/hardware-tests/openhal-firmware.md.