Building Hardware on OVOS¶
In a nutshell
You may want to build a physical device, such as a smart speaker, a robot, or a custom kiosk, that listens and talks using OVOS. Most of the software underneath does not care what board you run it on. The part that does care about hardware is a small layer called PHAL (Platform/Hardware Abstraction Layer). PHAL is also where you plug in your own LEDs, buttons, and sensors. This page maps what is generic and what is Raspberry-Pi-specific. It shows how to write a driver for your own LED ring or button panel, what resources your device needs, and what OVOS does and does not do for over-the-air updates and on-screen UI.
SBC-agnostic vs Raspberry-Pi-only¶
Most of the OVOS stack is just Python talking to ALSA (Linux's standard sound system) and the network. It runs the same way on a Raspberry Pi, an x86 mini-PC, an Orange Pi, or a laptop. A few pieces are tied to specific Raspberry-Pi/Mycroft hardware:
| Component | Portability | Why |
|---|---|---|
ovos-messagebus, ovos-core, ovos-audio, ovos-dinkum-listener, ovos-PHAL, skills |
Any Linux SBC / PC | Pure Python + ALSA + network; no board-specific code |
| Wake word / STT / TTS / VAD plugins | Any Linux SBC / PC | CPU (or GPU) inference; heavier models just need more RAM/CPU. For concrete sizing, use the Raspberry Pi tiers as a baseline: a Pi 3-class board handles wake word locally with cloud speech (or only the very smallest local models), Pi 4-class (4 GB) runs local ONNX STT/TTS comfortably, Pi 5-class adds headroom for Whisper-class models |
ovos-i2csound |
Raspberry Pi (i2c HATs) | Auto-detects and configures i2c sound HATs specific to the Pi's i2c bus layout |
VocalFusionDriver |
Raspberry Pi (SJ201/Mark 2 hardware) | Out-of-tree kernel module + device-tree overlays for the XMOS VocalFusion DSP mic array |
ovos-PHAL-plugin-mk1, ovos-PHAL-plugin-mk2-v6-fan-control |
Mycroft Mark 1 / Mark 2 only | Drive specific hardware that only exists on those boards |
| raspOVOS image | Raspberry Pi only | It's a Raspberry Pi OS image |
If you are designing new hardware, use a generic microphone or ALSA sound card and a plain
Linux install. The ovos-installer works the same way on a Pi or an
x86 box. Only reach for the Pi-specific pieces above if you reuse an i2c HAT or the SJ201
board design.
Writing your own hardware driver: AbstractLed / AbstractSwitches¶
ovos-hardware-helpers also ships an AbstractFan base (covered below) for
fan-speed and thermal-shutdown control.
Illustrative skeletons
The MyRingLed and switch examples below are skeletons, not complete, copy-pasteable
drivers. Real hardware needs the actual bus, SPI, or GPIO calls for your board filled in
where the example stops short.
Custom LEDs and buttons are the two things almost every maker adds. Instead of writing your
own bus-message plumbing, subclass the abstract base classes in
ovos-hardware-helpers. Wrap the
result in a PHAL plugin. See that page for the entry-point
and validator mechanics. This section covers the hardware interface itself.
LEDs: AbstractLed¶
from ovos_hardware_helpers.led import AbstractLed
class MyRingLed(AbstractLed):
"""Minimal LED ring driver — replace set_led/fill/show with real hardware calls."""
def __init__(self, num_leds: int = 12):
self._num_leds = num_leds
self._state = [(0, 0, 0)] * num_leds
@property
def num_leds(self) -> int:
return self._num_leds
@property
def capabilities(self) -> dict:
return {"num_leds": self._num_leds, "brightness_control": True}
def set_led(self, led_idx: int, color: tuple, immediate: bool = True):
self._state[led_idx] = color
if immediate:
self.show()
def fill(self, color: tuple):
self._state = [color] * self._num_leds
self.show()
def show(self):
# Push self._state to real hardware here (SPI/I2C/GPIO write).
pass
def shutdown(self):
self.fill((0, 0, 0))
For a full, real-hardware reference implementation, see
ovos-PHAL-plugin-dotstar. It wraps
a DotStar (APA102) LED strip's SPI driver in an AbstractLed subclass, plus the surrounding
PHAL plugin and validator boilerplate. It still imports AbstractLed from the deprecated
ovos_plugin_manager.hardware.led compat path (the same class, re-exported) rather than
ovos_hardware_helpers.led shown above — import from ovos_hardware_helpers.led in new
drivers to avoid the deprecation warning.
AbstractLed also ships a scale_brightness(color_val, bright_val) static helper for dimming.
The library's eval_color() helper (in ovos_hardware_helpers.led) turns a color name, hex
string, or RGB tuple into a normalized color object using
ovos-color-parser. This lets your plugin accept "Mycroft blue" as well
as (0, 168, 255).
Buttons: AbstractSwitches¶
from ovos_hardware_helpers.switches import AbstractSwitches
class MyButtonPanel(AbstractSwitches):
@property
def capabilities(self) -> dict:
return {"volume": True, "mute": True, "action": True}
def on_action(self):
pass # e.g. bus.emit(Message("mycroft.mic.listen"))
def on_vol_up(self):
pass
def on_vol_down(self):
pass
def on_mute(self):
pass
def on_unmute(self):
pass
def shutdown(self):
pass
Fans: AbstractFan¶
from ovos_hardware_helpers.fan import AbstractFan
class MyFanController(AbstractFan):
def set_fan_speed(self, percent: int):
pass # drive a PWM pin, 0-100
def get_fan_speed(self) -> int:
return 0 # last commanded 0-100 speed
def get_cpu_temp(self) -> float:
return -1.0 # celsius, or -1.0 if not available
def shutdown(self):
pass # set the fan to a reasonable speed before exit
Wire an instance of any of these classes up inside a PHALPlugin.__init__. Poll your GPIO or
i2c hardware on a background thread, or drive it from an interrupt callback, and call the
matching on_*, set_led, or set_fan_speed methods. See
PHAL: Writing PHAL Plugins for the full plugin lifecycle,
validator, and entry-point registration.
Resource footprint¶
There is no published, current benchmark of OVOS's CPU/RAM footprint across hardware targets. Treat any specific number you see elsewhere with suspicion. The real cost depends entirely on which STT, TTS, and wake-word plugins you pick. As a sizing anchor, the raspOVOS images run the full stack with cloud speech on a Pi 3 (barely), cloud STT with on-device TTS on a Pi 4, and fully on-device speech (local STT and TTS) on a Pi ⅘ with 4 GB+ RAM — those tiers are the closest thing to a documented reference deployment. A tiny wake-word model and a cloud STT server cost almost nothing locally. A local Whisper model needs real CPU or a GPU. Measure the footprint on your own target hardware instead of relying on an unverified number:
# Memory: sum RSS of every OVOS process
ps -u ovos -o rss,comm --sort=-rss | grep -E 'ovos|mycroft'
# Or per systemd unit, if using the units above
systemctl --user status ovos-core.service | grep Memory
# CPU, live, while you talk to it
top -b -n 1 -u ovos
Run the same commands idle and mid-conversation. The delta tells you what your wake word, STT, and TTS choice actually costs on your board. That number matters far more than any generic published figure.
Sizing by relative cost, not measured numbers¶
Without a published number to anchor on, reason about relative cost instead: which plugin
choices are light, medium, or heavy compared to each other. One fact stays fixed: the
always-on core (ovos-messagebus, ovos-core, ovos-audio, ovos-dinkum-listener,
ovos-PHAL, and whatever skills you load) runs on every device, regardless of which STT, TTS,
or wake-word models you pick. That core must fit on your board before you add any model on
top of it. Size the board for the core first, then budget headroom for the models.
On top of that fixed base, from lightest to heaviest:
- Lightest: an energy/noise-threshold VAD with no
model download, a small wake-word model such as
ovos-ww-plugin-precise-onnx, and cloud STT/TTS such as Azure, Polly, or Edge-TTS. Inference happens on someone else's server, so the device only needs enough CPU for the always-on core plus audio capture and playback. - Middle: local, on-device inference using a small or quantized model. Examples:
ovos-stt-plugin-onnx-asrwith a smallint8-quantized model,ovos-tts-plugin-phoonnx, andovos-vad-plugin-silero's small neural VAD model.int8weights consistently have a lower footprint thanfp32for the same model. - Heaviest: local Whisper-class or other large general-purpose models. See the
largevariants in the STT plugin reference. Several plugins in that table recommend a GPU oruse_cuda: truefor this reason. Running these well on a small board without a GPU is likely to disappoint.
Pick a tier that leaves headroom above the fixed core cost. Do not pick one that just barely fits the model alone.
Over-the-air updates¶
OVOS does not ship an OTA update system
There is no built-in over-the-air update mechanism, update server, or delta-update
protocol. Updating an OVOS device means running pip/uv against a
release channel's constraints file. This
is the same command whether you run it by hand over SSH or push it out with your own
device-management tooling, such as Ansible, a custom MDM agent, or a cron job. See
Staged Upgrades and Rollback
for the update-and-rollback pattern. If your product needs image-level OTA that swaps the
whole OS image, not just Python packages, bring your own solution, such as Mender,
SWUpdate, or RAUC. OVOS has no opinion on that layer.
Screens: where things stand today¶
If your device has a display, ovos-gui provides the protocol.
ovos-shell, or a Qt5/QML client speaking the same
GUI protocol, renders it, with a homescreen skill as the
default idle view. This works today on Linux desktops and the
GUI-capable installer path, but the legacy Qt5 stack is deprecated
pending the GUI rework. If you are starting a from-scratch hardware design, plan for a
voice-first experience with an optional screen, not the other way around. Treat the GUI
layer as an extra your device can degrade gracefully without, not a hard dependency for
launch.
Read next: i2c Sound & Audio Setup Related: PHAL · Mark 2 Hardware · Production Operations · Mark 1 Hardware