Architecture¶
The cyberhud project is a Go-based system for driving small SPI/I2C displays attached to a Raspberry Pi, providing real-time status dashboards, hardware monitoring, and interactive menus on connected panels.
Component Overview¶
graph TB
subgraph Binaries
CTL[cyberhudctl<br/>CLI Tool]
DAEMON[cyberhudd<br/>Daemon]
end
subgraph "display"
CATALOG[catalog/]
COORD[coordinator/]
MODES[modes/]
REGION[region/]
REGIONID[regionid/]
STYLE[style/]
SURFACE[surface/]
WIDGETS[widgets/]
end
subgraph "hardware"
DRIVER[driver/]
PANELS[panels/]
GPIO[gpio/]
INPUT[input/]
end
subgraph "runtime"
ACTION[action/]
CONSOLE[console/]
UI[ui/]
end
CTL -->|Unix socket| CONSOLE
DAEMON --> CONSOLE
DAEMON --> UI
DAEMON --> ACTION
UI --> MODES
UI --> SURFACE
UI --> CATALOG
UI --> COORD
MODES --> WIDGETS
MODES --> STYLE
PANELS --> SURFACE
INPUT --> UI
GPIO --> MODES
DRIVER --> PANELS
REGIONID --> COORD
The system consists of two main binaries and a set of libraries:
cmd/
├── cyberhudd/ ← Background daemon (display + hardware management)
└── cyberhudctl/ ← CLI tool (user-facing control interface)
display/
├── catalog/ ← Mode registry + command dispatcher
├── coordinator/ ← Per-region mode state manager (Set, Next, Prev)
├── modes/ ← Display mode implementations (30 packages)
│ ├── attract_bokeh/
│ ├── attract_geometric/
│ ├── attract_matrix/
│ ├── attract_particles/
│ ├── attract_plasma/
│ ├── attract_shapes/
│ ├── attract_starfield/
│ ├── attract_waveform/
│ ├── clock/
│ ├── cycle/
│ ├── dashboard/
│ ├── demo/
│ ├── gpio/
│ ├── gpio_control/
│ ├── image/
│ ├── menu/
│ ├── pager/
│ ├── serial/
│ ├── snapshottest/
│ ├── stemma/
│ ├── system/
│ ├── systemd/
│ ├── testfonts/
│ ├── testicons/
│ ├── testpattern/
│ ├── testwidgets/
│ ├── thermal/
│ ├── ticker/
│ ├── usb/
│ ├── wifi/
│ └── zmq/
├── region/ ← Display region allocation and management
├── regionid/ ← Region ID parsing (<surface>.<index> notation)
├── style/ ← Rendering framework (TextHints, LayoutBridge, StyleContext, ViewData)
├── surface/ ← Framebuffer, rendering surface, font catalog, text layout
└── widgets/ ← Reusable UI widget components
hardware/
├── driver/ ← Display driver abstraction (SPI, I2C interfaces)
├── gpio/ ← GPIO pin manager
├── input/ ← Button and joystick input handler
└── panels/ ← Panel hardware registry and drivers (ST7789, SSD1680, SH1106, etc.)
runtime/
├── action/ ← Action handling framework
├── console/ ← Unix socket console protocol server
└── ui/ ← UI runtime integration and render loop
tools/
├── docsnap/ ← Documentation snapshot tooling (collect, gallery)
├── fontgen/ ← Font code generation
├── gen-icons/ ← Material icon generation
└── modegen/ ← Mode scaffold generation
util/
└── cfgutil/ ← Configuration utility helpers
ghpages/ ← MkDocs documentation site (GitHub Pages)
website/ ← Astro marketing site (cyberhud.io)
debian/ ← Debian packaging files
systemd/ ← Systemd service files
examples/ ← Usage examples and sample config files
cyberhudd (Daemon)¶
The daemon is the long-running process that:
- Initializes hardware (SPI displays, I2C buses, GPIO pins)
- Scans for STEMMA QT / QWIIC devices on I2C buses
- Renders display modes to connected panels via the style system
- Manages per-region mode state through the coordinator
- Listens for commands on a Unix domain socket
- Handles button/joystick input for interactive navigation
On startup, the daemon selects a panel matching the attached hardware and configures the available display modes for that panel. It supports fallback panels if the primary one fails to initialize.
cyberhudctl (CLI Tool)¶
The CLI tool communicates with the running daemon over its Unix socket. It translates user-friendly commands into the daemon's line-oriented protocol and formats responses for terminal display.
# Check daemon status
cyberhudctl status
# Switch region main.0 to clock mode
cyberhudctl display set 0 clock
# List available display modes
cyberhudctl display modes
# List configured display regions
cyberhudctl display regions
# Cycle to next mode on region 0
cyberhudctl display next main.0
# Configure policy fields on the active mode
cyberhudctl display config main.0 speed=1.5
Communication Protocol¶
The daemon and CLI communicate via a Unix domain socket at /run/cyberhudd/console.sock. The protocol is line-oriented and text-based:
sequenceDiagram
participant Client as cyberhudctl
participant Socket as Unix Socket
participant Daemon as cyberhudd
Client->>Socket: connect
Daemon-->>Client: greeting line
Client->>Daemon: command (e.g. "display set main.0 clock")
Daemon-->>Client: OK + result data
Client->>Daemon: command (e.g. "display modes")
Daemon-->>Client: OK + mode list
Client->>Daemon: quit
Daemon-->>Client: connection closed
- The daemon writes a greeting line on connection
- The client sends one command per line
- The daemon responds with
OKorERRfollowed by result data - The client sends
quitto close the connection
This design allows any tool that can write to a Unix socket to interact with the daemon, not just cyberhudctl.
Catalog System¶
The catalog (display/catalog/) provides the mode registry and command dispatcher for the entire display system.
Mode Registry (Register, Describe)¶
Mode packages call catalog.Register() in their init() function to publish a Definition containing:
- ID — the mode's unique lowercase identifier (e.g.,
"attract_bokeh") - Title — human-readable name
- Scope — categorization scope (e.g.,
"display mode") - Summary — one-sentence description
- Order — priority for display listing
- Options — configurable policy fields (
OptionDefinitionwith Key, Type, Summary, Default, Allowed)
At runtime, the daemon calls catalog.Describe(mode) to retrieve metadata for a specific mode, or catalog.Definitions() to enumerate all registered modes in priority order.
Command Dispatcher (RegisterCommand, Command)¶
Mode packages register mode-specific console commands via catalog.RegisterCommand(), providing a CommandDefinition with:
- Verb — the command verb (e.g.,
"attract_bokeh") - Summary — one-line description
- Usage — usage pattern string
- Handle — the
CommandHandlerfunction that processes arguments
When the console receives a mode-specific command, it calls catalog.Command(verb) to look up the handler and dispatches the arguments. This allows each mode to implement its own console sub-commands without coupling the protocol layer to mode internals.
Coordinator¶
The coordinator (display/coordinator/) tracks per-region display mode state and provides remote-control operations. It depends on the catalog for enriching region metadata with mode definitions.
Operations¶
- Set(index, mode) — switches the specified region to the named mode. Returns an error if the region is not configured or the mode is not in the region's allowed list.
- Next(index) — advances to the next mode in the region's mode list, wrapping from the last mode back to the first.
- Prev(index) — moves to the previous mode, wrapping from the first mode to the last.
Region State¶
Each region tracks:
- A name and controller identifier
- An ordered list of available modes
- A current mode index (pointer into the modes list)
The coordinator is initialized with Region definitions specifying the index, name, controller, available modes, and default mode. It provides Status() and Definitions() methods for querying region snapshots enriched with catalog metadata.
Style System¶
The style system (display/style/) is the rendering framework that display modes use to produce visual output. It decouples mode logic from panel-specific rendering concerns through a layered set of types.
TextHints¶
TextHints (defined in display/surface/textlayout/) provides panel pixel and glyph metrics:
- Pixel dimensions —
PixelWidth,PixelHeight - Glyph metrics —
GlyphWidth,GlyphHeight,GlyphAdvance,RowHeight - Capability flags —
Capability(MonoSlow through ColorFast), scroll support flags, refresh preferences - Font catalog — tier-based font access via
Catalogfield - Panel identity —
PanelProduct,ScreenName,PPI
TextHints is screen-level metadata independent of chipset, describing what a panel can render.
LayoutBridge¶
LayoutBridge (defined in display/style/layout/) provides content positioning calculations from TextHints and padding configuration:
- Content origin and available dimensions
- Row positioning (
RowY,RowX,RowBottomY) - Text measurement (
TextPixelWidth,CenterX) - Vertical layout (
MaxVisibleRows,CenterBlockY,FitRows,BottomAnchorY) - Inline widget bounds
Styles construct a LayoutBridge via NewLayoutBridge(hints, cfg) with their own BridgeConfig specifying padding, title bar, and status bar preferences.
StyleContext¶
StyleContext is the per-render context passed to a Style's Build method. It combines:
- A pre-built
LayoutBridgefor spatial queries - The raw
TextHintsfor styles that need custom layout - A font
Catalogfor tier-based font lookups Capabilityflags for hardware-aware rendering decisions
ViewData¶
ViewData is the rendering output produced by a Style's Build method. It carries:
- Title — display title string
- Items — rendered text lines
- Colors — per-item color values
- Hint — status hint text
- Tiers — per-row font tier intent
- FontID/FontIDs — resolved font identifiers
- LineOffsets — per-line horizontal pixel offset (marquee)
- OffsetY — vertical pixel offset for centering
- Cursor/TopRow — scroll state
- Static — whether the view requires no animation ticks
- Sprites — positioned widget sprites
- StyleReport — metadata about which style was resolved
Display Modes¶
graph LR
subgraph "Mode Packages"
CLK[clock]
DASH[dashboard]
GPIO[gpio]
MENU[menu]
SYS[system]
MORE[...]
end
subgraph "Catalog"
DEF[Mode Definitions]
CMD[Command Dispatch]
end
subgraph "Coordinator"
STATE[Region State]
end
CLK & DASH & GPIO & MENU & SYS & MORE -->|Register| DEF
CLK & DASH & GPIO & MENU & SYS & MORE -->|RegisterCommand| CMD
STATE -->|Set / Next / Prev| DEF
Display modes are self-contained packages under display/modes/. Each mode:
- Implements the mode interface (ID, action handler, item count, view builder, data signature)
- Registers itself in the catalog via
catalog.Register()with metadata - Optionally registers mode-specific console commands via
catalog.RegisterCommand()
The catalog pattern allows the daemon to look up modes by ID at runtime without hardcoding dependencies between the UI loop and individual mode packages.
See Display Modes for a full list of available modes.
Panel Drivers¶
Panel drivers live under hardware/panels/ with each hardware product in its own sub-package. A panel driver:
- Calls
panels.Register(...)in itsinit()function to self-register - Defines display dimensions, controller type, SPI configuration, and GPIO pin assignments
- Specifies which input pins (buttons, joystick) are available on the hardware
The hardware/panels/all package imports every built-in panel driver so the daemon picks them up automatically at startup.
Supported panel controllers include:
| Controller | Example Hardware |
|---|---|
| ST7789 | Waveshare 1.3" LCD HAT, Waveshare 2.2" SPI |
| ST7735S | Waveshare 1.44" LCD HAT |
| SH1106 | Waveshare 1.3" OLED HAT |
| SSD1680 | Adafruit 2.13" e-ink bonnet |
| IS31FL3731 | Adafruit 15×7 CharliePlex LED |
Startup Flow¶
flowchart TD
A[Parse CLI flags + JSON config] --> B[Resolve display panel + fallbacks]
B --> C[Initialize periph.io host drivers]
C --> D[Start STEMMA QT / QWIIC I2C scanner]
D --> E[Start GPIO manager]
E --> F[Open console Unix socket]
F --> G{Display enabled?}
G -->|Yes| H[Initialize display hardware]
G -->|No| K[Wait for signal]
H --> I{Input enabled?}
I -->|Yes| J[Start input handler]
I -->|No| L[Enter UI render loop<br/>passive mode]
J --> M[Enter UI render loop<br/>interactive mode]
L --> K
M --> K
K --> N[SIGINT / SIGTERM received]
N --> O[Clean shutdown]
- Parse CLI flags and optional JSON config file
- Resolve the display panel (primary + fallbacks)
- Initialize periph.io host drivers
- Start the STEMMA QT / QWIIC I2C scanner
- Start the GPIO manager
- Open the console Unix socket
- Initialize the display hardware and input handler
- Enter the UI render loop
The daemon runs until it receives SIGINT or SIGTERM, then performs a clean shutdown of all subsystems.