RumPi 1.0
A self-assembling C++ sensor & actuator library for the Raspberry Pi 5

A self-assembling C++ sensor & actuator library for the Raspberry Pi 5.

RumPi turns a JSON description of a robot's hardware into a running, self-managing system. Instead of hand-wiring every sensor and actuator in code, you describe the hardware in a manifest — and RumPi constructs it, wires the analog sensors to their ADCs, polls everything on a fixed cadence, and hands you a clean snapshot of the readings. It ships with roughly thirty component types behind one uniform interface, plus a companion wxWidgets dashboard that renders the live data as a condition-reactive scene.

Why RumPi

  • Declarative hardware. A JSON manifest says what is connected; a factory assembles it at runtime. Adding a sensor is a manifest entry, not a code change.
  • One contract for everything. A $2 thermistor and a serial air-quality sensor implement the same BaseComponent lifecycle (TurnOnProcessRawValuesTurnOffToString), so the poll loop, the dashboard, and the serializer treat every device identically.
  • Resolution-independent analog. Analog sensors read through an ADC abstraction and scale by the converter's own bit-depth and reference voltage, so the same sensor code is correct on an 8-bit PCF8591 or a 10-bit MCP3008.
  • Built to run unattended. A thread-safe alert bus feeds an asynchronous logger; the poll loop isolates a misbehaving component instead of taking down the robot; disconnected analog inputs fail safe rather than reporting a plausible-looking value.
  • Cleanly split in two. A headless RumPiLibrary that runs on the robot, and a RumPiLinuxClient GUI that visualizes it — joined by a value-typed snapshot, never shared state.

Architecture at a glance

manifest.json "what hardware is wired"
|
v
ComponentFactory builds each component, resolves ADC references by name
|
v
ComponentManager owns the components; polls them every tick
|
v ProcessRawValues() on every component
SelectedSensorReadings a curated, value-typed snapshot (-1 = "no reading")
|
+--> RumPiLinuxClient live wxWidgets dashboard (Home scene, tiles, logger)
+--> JSON over a socket for a remote viewer

The manifest is the single source of truth. ComponentFactory reads it, constructs each component, and resolves references — for example, an analog wind sensor names the ADC it reads through and the factory hands it the shared ADC instance. ComponentManager owns the assembled components and polls them on a fixed cadence; each poll refreshes the components' cached readings, and GetSelectedSensorReadings() curates them into a SelectedSensorReadings snapshot for display or transmission. The RumPi facade ties it together behind Initialize() / Start() / Stop().

The component model

Everything derives from BaseComponent and honors a four-method lifecycle:

Method Responsibility
TurnOn() Acquire the hardware — open the file descriptor, set pin modes, start soft-PWM.
ProcessRawValues() Sample once and refresh the cached reading (called every poll).
TurnOff() Release and de-energize the hardware.
ToString() Serialize the current reading for the raw data view.

Components fall into three families:

  • Sensors — environmental (BMP280, DHT11, TSL2591, PMS5003, PTQS1005, the QSFS01 anemometer, the HCSR501 motion detector), gas (MQ2MQ9 and MQ135), and input (RotaryEncoder, SoundSensor, Photoresistor, RainSensor).
  • ActuatorsLED / RGBLED / DualLED, Motor and Vehicle, Relay, the LCD1602 display, and PiCamera.
  • ADCsPCF8591 (8-bit, I²C) and MCP3008 (10-bit, SPI), both behind IAnalogDigitalConverterComponent. An analog sensor never needs to know which one it is wired to.

A manifest

{
"components": [
{ "type": "PCF8591", "i2cAddress": "0x48", "basePin": 120, "adcVoltage": 5.0 },
{ "type": "QSFS01", "adc": "PCF8591", "adcChannel": 2 },
{ "type": "DHT11", "pin": 13, "isUsingPi5OrAbove": true },
{ "type": "TSL2591" },
{ "type": "HCSR501", "pin": 18 },
{ "type": "SystemHealthComponent" }
]
}

That is the entire wiring: an ADC on I²C, an anemometer reading channel 2 of it, a GPIO temperature/humidity sensor, an I²C light sensor, a motion detector, and the Pi's own health telemetry. There is no matching C++ to write — ComponentFactory turns this into live objects, and if no manifest file is present a sensible built-in default assembles the current robot.

Quick start

#include <RumPi/RumPi.h>
int main()
{
RumPi::Initialize(); // load the manifest, set up wiringPi, assemble the components
RumPi::Start(true); // run the poll loop on a background thread
// ...later, from anywhere:
// readings.GetTemperatureCelsius(), .GetWindSpeed(), .GetAirQuality(), ...
return 0;
}
A point-in-time copy of the SELECTED sensor readings the Home dashboard surfaces - a deliberately cur...
Definition: SelectedSensorReadings.h:18
void Stop()
Cleanly shuts down after a pull-based Start(false): wakes anything waiting, then tears down (turns co...
Definition: RumPi.cpp:190
SelectedSensorReadings GetSelectedSensorReadings()
Gets the curated set of current sensor readings for the Home dashboard, assembled by the ComponentMan...
Definition: RumPi.cpp:382
void Initialize()
Brings the library up: initializes the logger, loads and validates the configuration,...
Definition: RumPi.cpp:66
void Start(bool runAndUpdateOnSeparateThread)
Turns on every managed component.
Definition: RumPi.cpp:141

GetSelectedSensorReadings() returns a value-typed snapshot; a sensor that is absent or has not produced a reading yet reports a -1 sentinel, so a consumer can show "N/A" instead of a fabricated zero.

The dashboard

RumPiLinuxClient is a wxWidgets application that polls the library on a background thread and marshals each snapshot to the UI thread as a deep-copied event — no shared state crosses the boundary. It presents:

  • a Home scene that reacts to conditions — a day/night backdrop that clouds over, hazes, tints for temperature, and streaks with the wind;
  • Summary tiles for at-a-glance readings;
  • a Devices grid with one tile per component instance;
  • a live Logger fed by the same alert bus the library logs through.

Reliability

RumPi is written to run on an unattended robot:

  • Alerts & logging. AlertManager is a thread-safe publish/subscribe bus — components Report failures and Check readings against thresholds. Logger subscribes and writes asynchronously off the poll thread, and each subscriber dispatch is exception-isolated so one bad subscriber cannot break the others.
  • Fault isolation. The poll loop wraps every component's ProcessRawValues(), so a single throwing sensor is logged and skipped rather than crashing the process.
  • Fail-safe reads. Railed or disconnected analog readings are rejected instead of being reported as plausible values, and hardware that never initialized is never polled.

Building

The library targets the Raspberry Pi 5 (wiringPi plus the POSIX APIs) and is written in modern C++ in the RumPi namespace. The RumPiLinuxClient dashboard additionally depends on wxWidgets 3.x.

Navigating these docs

A good reading order is RumPi (the facade), then ComponentManager (the poll-and-curate core), then ComponentFactory (the self-assembly) — that trio is the spine of the design.


RumPi is written and maintained by Eddie O'Hagan.