Hardware API
Load hardware node configurations from horus.toml and use them in your Python nodes.
# simplified
import horus
entries = horus.hardware.load()
for name, obj in entries:
print(f"Loaded: {name}")
Loading Hardware
hardware.load()
Reads the [hardware] section from horus.toml (searches current directory and up to 10 parents). Returns a list of (name, obj) tuples.
If a registered Python class matches the use field, obj is an instance of that class. Otherwise, obj is a NodeParams dict-like with the config values.
# simplified
entries = horus.hardware.load()
hardware.load_from(path)
Load from a specific config file. Useful for testing.
# simplified
entries = horus.hardware.load_from("tests/test_hardware.toml")
NodeParams
Typed access to config values from a [hardware.NAME] table.
| Method | Returns | Description |
|---|---|---|
params.get(key) | value | Required param — raises KeyError if missing |
params.get_or(key, default) | value | Optional param with fallback |
params.has(key) | bool | Whether key exists |
params.keys() | list[str] | All param names |
len(params) | int | Number of params |
params[key] | value | Dict-like access |
key in params | bool | Dict-like contains |
TOML types are auto-converted to Python: str, int, float, bool, list.
# simplified
entries = horus.hardware.load()
for name, obj in entries:
if isinstance(obj, horus.NodeParams):
port = obj.get_or("port", "/dev/ttyUSB0")
baud = obj.get_or("baudrate", 115200)
print(f"{name}: {port} @ {baud}")
Registering Python Drivers
Register a Python class so hardware.load() instantiates it automatically when the use field matches:
# simplified
import horus
class ConveyorDriver(horus.Node):
def __init__(self, params):
super().__init__(
name="conveyor",
pubs=["conveyor.velocity"],
rate=params.get_or("rate", 50),
)
self.port = params.get_or("port", "/dev/ttyACM0")
self.speed = params.get_or("speed", 1.0)
def tick(self):
self.send("conveyor.velocity", horus.CmdVel(self.speed, 0.0))
# Register the class
horus.hardware.register_driver("ConveyorDriver", ConveyorDriver)
Then in horus.toml:
[hardware.conveyor]
use = "ConveyorDriver"
port = "/dev/ttyACM0"
speed = 0.5
And in your main script:
# simplified
entries = horus.hardware.load()
nodes = [obj for _, obj in entries if isinstance(obj, horus.Node)]
horus.run(*nodes)
Complete Example
# horus.toml
[hardware.imu]
use = "Bno055Driver"
bus = 1
address = 104
[hardware.motors]
use = "MotorDriver"
port = "/dev/ttyUSB0"
baudrate = 115200
# simplified
import horus
import smbus2
import serial
class Bno055Driver(horus.Node):
def __init__(self, params):
super().__init__(name="imu", pubs=["imu"], rate=100)
bus_num = params.get_or("bus", 1)
self.addr = params.get_or("address", 0x68)
self.i2c = smbus2.SMBus(bus_num)
def tick(self):
data = self.i2c.read_i2c_block_data(self.addr, 0x08, 6)
self.send("imu", horus.Imu(linear_acceleration=parse_accel(data)))
class MotorDriver(horus.Node):
def __init__(self, params):
super().__init__(name="motors", subs=["cmd_vel"], rate=50)
port = params.get("port")
baud = params.get_or("baudrate", 115200)
self.ser = serial.Serial(port, baud)
def tick(self):
if self.has_msg("cmd_vel"):
cmd = self.recv("cmd_vel")
self.ser.write(encode_motor_cmd(cmd))
horus.hardware.register_driver("Bno055Driver", Bno055Driver)
horus.hardware.register_driver("MotorDriver", MotorDriver)
entries = horus.hardware.load()
nodes = [obj for _, obj in entries if isinstance(obj, horus.Node)]
horus.run(*nodes)
Simulation Override
Mark hardware entries with sim = true to swap them for stubs in simulation mode:
[hardware.imu]
use = "Bno055Driver"
bus = 1
sim = true
horus run # real hardware
horus run --sim # sim mode — stub nodes, simulator publishes to same topics
Error Handling
# simplified
try:
entries = horus.hardware.load()
except Exception as e:
print(f"No hardware config: {e}")
entries = []
for name, obj in entries:
if isinstance(obj, horus.NodeParams):
print(f" {name}: no registered class (params only)")
Legacy Support
The [drivers] section name and old source keys (terra, node, package) are still parsed. Migrate to [hardware] with the use field:
# Old
[drivers.imu]
terra = "mpu6050"
bus = 1
# New
[hardware.imu]
use = "mpu6050"
bus = 1
sim = true
Use horus.hardware to access hardware drivers.
See Also
- Rust Hardware API — Rust equivalent
- Hardware Drivers Tutorial (Python) — Step-by-step guide
- Real Hardware Recipe — Complete I2C + serial examples
- Configuration Reference —
[hardware]syntax in horus.toml