Architecture Overview
RADR uses a two-tier registry system:| Tier | Description | Example |
|---|---|---|
| Hardcoded | Devices registered directly in code with custom factories | OSSM |
| Dynamic | Devices loaded from registry.json using the ButtplugIO factory | Lovense, Satisfyer, Kiiroo |
initRegistry().
Registry Structure
The registry is a global map that associates service UUIDs with factory functions:DeviceFactory is defined as:
Key Functions
| Function | Purpose |
|---|---|
initRegistry() | Populates the registry with hardcoded and dynamic devices |
getDeviceFactory() | Looks up a factory by service UUID |
Hardcoded Devices
Hardcoded devices are registered directly ininitRegistry() with a lambda factory. This approach is used for devices with custom protocols that require specialized handling.
OSSM Example
When to Use Hardcoded Registration
Use hardcoded registration when:- The device uses a custom protocol not covered by Buttplug.io
- You need specialized UI or control logic
- The device requires unique characteristic handling
Dynamic Devices (ButtplugIO)
Dynamic devices are loaded from thedata/registry.json file on the filesystem. This allows adding support for new devices without modifying code.
registry.json Structure
The registry maps service UUIDs to arrays of protocol spec files:Factory Flow
When a device with a registered service UUID is discovered:ButtplugIODeviceFactoryreadsregistry.jsonfrom LittleFS- Retrieves the list of spec files for the service UUID
- For each spec file:
- Loads and parses the JSON configuration
- Matches the advertised device name against
communication[0].btle.namespatterns - Extracts TX/RX characteristics for the service UUID
- Creates a
LovenseDeviceinstance with the matched configuration
Protocol Spec File Format
Spec files follow the Buttplug.io v4 format:| Field | Purpose |
|---|---|
defaults | Default device name and feature set |
configurations | Device-specific variants and identifiers |
communication[].btle.names | Device name patterns (supports * wildcard) |
communication[].btle.services | TX/RX characteristic UUIDs per service |
Device Discovery Flow
- Scan: RADR scans for BLE devices
- Extract UUID: Gets the advertised service UUID
- Registry Lookup:
getDeviceFactory(serviceUUID)finds the factory - Create Device: Factory instantiates the appropriate device class
Adding New Device Support
Option A: Dynamic (Buttplug.io Registry)
For devices compatible with the Buttplug.io protocol:Obtain or create the spec file
Get the Buttplug.io v4 protocol specification for the device. These are available in the Buttplug device config repository.
Option B: Hardcoded (Custom Protocol)
For devices requiring custom protocol handling:Implement required methods
Implement
getServiceUUID(), getName(), and any device-specific control logic.Development Workflow
Updating the Registry (Development)
When developing locally, use PlatformIO’s “Upload Filesystem” feature to flash thedata/ directory to LittleFS:
- Modify
data/registry.jsonor add spec files todata/protocols/ - In VS Code with PlatformIO, click Upload Filesystem (or run
pio run --target uploadfs) - The device will use the updated registry on next boot
Updating the Registry (Production)
OTA registry updates are a pending feature. Currently, registry updates require a firmware flash.
- Navigate to Settings > Look for updates on the device
- RADR checks for registry updates from the server
- Updated
registry.jsonand protocol files are downloaded OTA - New device support is available without a full firmware update
Key Source Files
| File | Purpose |
|---|---|
src/devices/registry.h | Registry interface and type definitions |
src/devices/registry.cpp | Registry implementation and initialization |
src/devices/buttplugio/buttplugIOFactory.cpp | Dynamic device factory |
src/devices/device.h | Base device class |
data/registry.json | Service UUID to spec file mappings |
data/protocols/*.json | Buttplug.io v4 device specifications |

