Skip to content

serial

tested on simulator

Tier Tier 1
Protocol line-based text over USB CDC serial, UART, RFC 2217, raw TCP (socket://)
Covers Arduino and other microcontroller sketches, bench instruments with ASCII command sets, sensor modules with text output, lab equipment with RS-232 ports
Hazard default medium
Conformance control
Tested with simulated pyserial port and a real pty opened with pyserial

Estimated reach. Tens of thousands of models in practice. The driver has no built-in command set. Any device whose serial protocol is one text command and one text reply per line can be described in a device file. That covers most hobby microcontroller sketches and a large share of RS-232 lab and industrial equipment. The real limit is how many devices have documented commands, not the driver.

The serial driver connects to any device with a line-based text protocol over a serial port, with pyserial 3.5, on USB CDC and UART paths, socket://host:port or rfc2217://host:port. Every query, reply pattern and command template comes from the device file, so one driver covers Arduino sketches, RS-232 instruments and sensor modules.

Supports

  • Discovery of serial ports with USB ids, opening none.
  • port: auto picks the one port matching match.usb and any match.serial_number.

Setup

  1. Install with uv tool install --compile-bytecode "halkit[serial] @ git+https://github.com/sohan-bhat/OpenHAL".
  2. Find the port with openhal discover or python -m serial.tools.list_ports -v.
  3. Give each read channel a map with query or listen, and each write channel and action a map with command.
  4. Validate with openhal file validate my-device.hal.yaml.
  5. Confirm the limits by hand, since high risk channels and actions stay read-only until then.
KeyDefaultMeaning
portrequiredDevice path, pyserial URL or auto
baud9600Baud rate
newlinelfLine ending, lf, crlf or cr
framinglineline, or stx-etx for packets from STX 0x02 to ETX 0x03 as New Era syringe pumps reply
encodingasciiascii, utf-8 or latin-1
timeout1 sReply deadline, 50 ms to 30 s
settle0 sDelay after opening for boards that reset, with bytes received meanwhile dropped
identifynoneRead-only query for identify(), such as ID? or *IDN?
boardnoneFree text for the identity
rtscts, dsrdtrfalseHardware flow control

Channel maps

KeyUsed byMeaning
queryreadLine to send. The next non-stream line is the reply
listenreadRegex for unsolicited lines. The latest match is the value
commandwrite, actionTemplate with {value} for writes or argument names for actions
getwriteReadback query. Without one, a read returns the last value sent, or null before the first write
parseread, writeReply regex. Group value, else group 1, else the whole match
expectwrite, actionRegex the reply must match. Without expect or error no reply is read
errorwrite, actionRegex for an error reply, reported as a failed command
scale, offsetread, writechannel value = raw * scale + offset, inverted for writes
unitread, writeDevice unit when different from the channel unit
on, offbool channels, actionsDevice text for true and false. Reads accept common words when unset
lookupenum channelsDevice text to enum value, inverted for writes

Templates accept only these placeholders, with an optional format spec such as {value:.1f}. A rendered command with a control character is blocked before sending, so a string value can't append a second command.

Example device file

openhal/drivers/serial/examples/arduino-fan-board.hal.yaml reads a temperature with TEMP? and sets a fan speed with FAN {value:.0f} on an Arduino Uno.

Known limitations

  • One command and one reply line at a time, so multi-line replies and binary protocols need a dedicated driver.
  • Replies match by order, so out-of-order replies or chatter that no listen pattern covers get matched wrongly. Add a listen pattern for the chatter.
  • Regexes have no per-match timeout, and lines are cut at 4096 characters.
  • Give every file a safe_state, and prefer firmware that falls back to a safe output when commands stop.

Hazard defaults

medium, since the driver can drive anything. Set hazard to what the device really is. safe_state comes from the device file only.

Sources