Skip to content

DeviceManager

Rob Dobson edited this page Oct 23, 2026 · 12 revisions

DeviceManager SysMod

Overview

The DeviceManager SysMod is responsible for managing devices and buses. It sets up and monitors the status of devices, handles bus communication, and provides an interface for interacting with devices via REST API endpoints. The DeviceManager interacts with the system manager (SysManager) to register devices and manage data sources. Two distinct kinds of devices are managed:

  • Dynamic Devices these are generally attached to a physical, (perhaps wireless, or even notional) bus and detected through some mechanism such that they can dynamically appear and may go offline again.
  • Static Devices are generally hardware attached in other ways (SPI, GPIO, etc) and are not detected automatically but require the device code itself to instantiate and support them

Features

  • Device Setup and Management: Initializes and manages devices based on configuration.
  • Bus Management: Monitors and handles bus operations and element statuses.
  • REST API Interface: Provides REST endpoints for interacting with devices.
  • Device Data Change Callbacks: Supports registering callbacks for device data changes.
  • Status Monitoring: Continuously monitors the status of devices and buses.

Lifecycle and runtime behavior

  • setup(): Initializes buses from the Buses configuration section and instantiates configured devices from the Devices list.
  • postSetup(): Loads the device type file and the saved device settings, registers data sources for publishing (devjson, devbin), calls postSetup() on each device, and registers device data/status callbacks. Buses start scanning straight away, but no device is identified until the type file has been loaded (or typeFileTimeoutMs has passed), so the file's records and disabled types apply from the first identification.
  • loop(): Services the bus system and then calls loop() on each online device.

Device discovery and instantiation

DeviceManager supports two paths for device creation:

  • Statically configured devices: Devices listed under Devices are created using the DeviceFactory, assigned a direct-connection device ID, then setup() and postSetup() are called. If a device provides a device type record, it is added to the device type records and assigned a type index.
  • Dynamically detected bus devices: As buses report element status changes, newly identified devices are created as RaftBusDevice instances, assigned their bus ID, and brought online. The manager updates online/offline state and registers for device data notifications when a device is first identified.

Device factory and classes

DeviceManager relies on the DeviceFactory to create devices from the class field in each device configuration entry. Device classes register themselves with the factory at startup. When a configuration entry is processed, DeviceManager looks up the class name in the factory and invokes the registered create function.

Device type records

If a device provides a type record, DeviceManager registers it with the device type records and assigns a type index. The type information is used by the REST API (/devman/typeinfo) and by published device data. Two static devices with the same type share one record if their records are identical; if they differ, the second device's record is rejected (an error is logged) and that device has no type index. The number and size of these records are limited by the typeTable settings.

Device names, roles and display names

DeviceManager maintains two per-device maps that are independent of the device type record:

  • Name map — associates a friendly name (e.g. IMU1, FuelGauge) with a RaftDeviceID. Names can be configured via the name field on a Devices entry, via a top-level DeviceNames map, or assigned at runtime through /devman/setname.
  • Role map — associates a role string (default normal, or e.g. system) with a RaftDeviceID. Roles allow callers to distinguish system-infrastructure devices (fuel gauges, RTCs, chargers) from standard published measurement devices. Roles can be configured via the role field on a Devices entry or assigned at runtime through /devman/setrole. The current role is reported by per-device /devman/typeinfo requests and by /devman/listdevs.

Names, roles, devconfig settings and stopped polling set over the REST API last until the next restart, or are kept over restarts with save=1 (see saved device settings). Saved settings are keyed by device ID.

The display name is what to show for a device: its device name if it has one, otherwise its type display name (devInfoJson.name, or the device type key) and its device ID, e.g. Accelerometer & Gyroscope 1_68. getDeviceDisplayName(deviceID) returns it, and devman/listdevs and per-device devman/typeinfo report it as displayName. Consumers (clients, RaftROS, the data logger) should use it rather than forming their own.

A Devices entry that omits the class field is treated as a bus-tagging entry — it does not instantiate a device but instead assigns a name and/or role to a bus-discovered device identified by its bus+addr. See DeviceManager Settings for the full schema.

Status, events, and named values

  • Named values: getNamedValue() and getNamedString() use the form DeviceName.paramName to query values on a specific device; setNamedValue() and setNamedString() route writes the same way.
  • JSON command routing: receiveCmdJSON() expects a JSON body with a device field and forwards the command to that device.
  • Device events: Device status/event callbacks are registered and forwarded to SysManager as sysevent messages.
  • Device status changes: registerForDeviceStatusChange() listeners get a BusAddrStatus for each bus device that comes online, goes offline or changes identity - see identity changes (identEvent: Identified, TypeChanged, Lost). Subscriptions made with registerForDeviceData() by device ID last through re-identification and absences, so a consumer should subscribe through DeviceManager rather than on the bus.

Consuming device data in a SysMod

A SysMod can subscribe to a device's decoded data directly (in addition to, or instead of, the published devjson/devbin topics) using DeviceManager::registerForDeviceData(). The callback fires whenever fresh data arrives for the target device, with the device type index (for decoding), the raw poll bytes, and any registered callback info:

using RaftDeviceDataChangeCB = std::function<void(uint32_t deviceTypeIdx,
                                                  std::vector<uint8_t> data,
                                                  const void* pCallbackInfo)>;

The target device can be identified three ways:

  • By device ID / address — RaftDeviceID, or a device-id string such as "I2CA_0x6a@3" / "1_6a" (bus name or number, hex address, optional @slot for a multiplexer slot).
  • By device name — e.g. "IMU1" (a name set in the configuration or with setDeviceName()).
  • By device type index — DeviceTypeIndexType.
  • By device type key — e.g. "LSM6DS3".

Registration is deferred: it is safe to call during setup() before the device has been identified — the request is stored and wired up when a matching device comes online (see the bus element status handling). The const char* overload accepts either a device-id string or a device type key (it first tries to resolve the string as a bus_addr identifier, then as a device name, then as a device type key), so callers targeting a specific bus address and callers targeting a type both work.

// e.g. from a SysMod setup(), register for the accelerometer at I2CA 0x15
getSysManager()->getDeviceManager()->registerForDeviceData("I2CA_0x15@0",
    [this](uint32_t devTypeIdx, std::vector<uint8_t> data, const void* /*cbInfo*/) {
        // decode `data` using devTypeIdx and use the values
    },
    50 /* min ms between reports */);

Managing device types and devices at runtime

Every DeviceManager REST endpoint has a C++ equivalent, for SysMods that manage devices themselves:

C++ REST Notes
requestReIdentify(BusReIdentRequest, busNum) devman/reident Scopes: all, unidentified, one address, devices of a type, devices a type now outranks. An identified device keeps its type (and is polled) until a different result is known
setTypePolicy(deviceTypeKey, policy) devman/typepolicy Enable or disable a type at runtime and identify the affected devices again. Not saved - the type file is
markTypeFileChanged() devman/typefile restartRequired Call after changing the device type file, so clients know a restart (devman/typefile/apply) is needed
setDeviceName(), setDeviceRole(), getDeviceDisplayName() devman/setname, setrole, listdevs
bus getBusDevicesIF()->requestReInit(address, doneFn) devman/reinit Runs the init steps again on the bus task; devconfig settings are then applied again
bus getBusDevicesIF()->setDevicePollingEnabled(address, enabled) devman/devenable The REST endpoint also keeps the setting while the device is offline
bus getBusDevicesIF()->getIdentInfoJson(address) devman/identinfo
bus getBusDevicesIF()->getIdentState(address, state, attempts) - The state (BusIdentState) and the consecutive attempts with that outcome, without building JSON: cheap enough to call for every device several times a second (e.g. status LEDs). False if the bus doesn't support it (RaftCore 1.62.0, RaftI2C 1.24.0)

A change to the device type records themselves (added, replaced or removed records) is applied by a restart: edit the type file, call markTypeFileChanged(), and restart when convenient. A type's policy can be changed at once.

Device Data Publishing

DeviceManager automatically publishes device data through the StatePublisher system using two topics:

  • devjson: Human-readable JSON format for device data
  • devbin: Compact binary format for efficient transmission

For detailed information about the message formats, subscription methods, and integration details, see Device Data Publishing.

Device data publishing uses a state-hash mechanism that combines bus timestamps and per-device state hashes, so publish updates only occur when device data changes.

Supported Devices

DeviceManager supports various device types. Some devices have dedicated documentation:

Leaving DeviceManager out

DeviceManager is part of every RaftCoreApp build unless the project sets set(RAFT_DEVICE_MANAGER OFF) in its features.cmake (see SysTypes). Without it SysManager::getDeviceManager() returns nullptr, so code that uses DeviceManager must check for that (RaftCore, RaftSysMods, RaftROS and the Robotical libraries already do - RaftROS then logs that auto-publishing is disabled).

DeviceManager Settings

When constructed normally (in RaftCoreApp for instance) the DeviceManager is configured using the contents of the SysTypes key DevMan. The settings available to configure DeviceManager are described in Device Manager Settings

RaftI2C Devices

RaftI2C scans I2C buses to detect devices, including those connected through bus multiplexers. For configuration of the I2C bus itself (pins, frequency, multiplexers, task parameters, etc.) see I2C Bus.

Scanning uses priority-based address ordering for efficient detection and supports device hot-swapping. For detailed information about the scanning process, priority lists, multiplexer handling, and bus stuck recovery, see I2C Device Scanning.

After devices are detected through scanning, DeviceManager identifies device types, initializes devices, and polls them for data at configured intervals. Device type records define how to identify, initialize, and poll each supported device type.

For detailed information about device type detection, initialization sequences, polling configuration, and data collection, see I2C Device Identification and Polling.

DeviceManager REST API

See Device Manager REST API for full endpoint details and examples.

Clone this wiki locally