Skip to content

DeviceFactoryAndClasses

Rob Dobson edited this page Feb 5, 2026 · 1 revision

Device Factory and Classes

The device factory manages device class registration and instantiation.

Device Classes

RaftBusDevice:

  • Automatically created for devices detected on buses
  • Uses device type records for all configuration
  • Handles generic I2C devices

Custom Device Classes:

  • Application-specific device implementations
  • Can override detection, initialization, polling
  • May provide custom data processing

Device Class Registration

// In device class implementation file
static RaftDevice* create(const char* pClassName, const char* pDevConfigJson)
{
    return new MyCustomDevice(pClassName, pDevConfigJson);
}

// Registration (typically in device class static initializer)
class MyCustomDeviceRegistrar
{
public:
    MyCustomDeviceRegistrar()
    {
        deviceFactory.registerDevice("MyCustomDevice", create);
    }
};
static MyCustomDeviceRegistrar registrar;

Static vs Dynamic Devices

Static Devices (Configured):

  • Defined in SysType configuration
  • Created during DeviceManager setup
  • Always present (may be offline)
  • Example: Built-in displays, dedicated sensors

Dynamic Devices (Auto-discovered):

  • Created when detected on bus
  • RaftBusDevice instances
  • Hot-swap capable
  • Example: Pluggable I2C modules

Configuration

Device Type Record Configuration

Device types are primarily defined in generated code, but can be extended:

{
    "DevMan": {
        "Devices": [
            {
                "name": "CustomSensor",
                "class": "CustomSensorClass",
                "enable": true
            }
        ]
    }
}

Buzzer Device Example

Here is a more concrete example of a buzzer device which is implemented using the following code snippets:

// DeviceBuzzer.h
#pragma once

#include "RaftCore.h"
#include "driver/ledc.h"

class RestAPIEndpointManager;
class CommsCoreIF;
class APISourceInfo;

class DeviceBuzzer : public RaftDevice
{
public:
    DeviceBuzzer(const char* pClassName, const char *pDevConfigJson)
        : RaftDevice(pClassName, pDevConfigJson) {}

    virtual ~DeviceBuzzer() {}

    // Create function
    static RaftDevice* create(const char* pClassName, const char* pDevConfigJson)
    {
        return new DeviceBuzzer(pClassName, pDevConfigJson);
    }

    /// @brief Setup the device
    virtual void setup() override final
    {
        // Buzzer pin
        _buzzerPin = deviceConfig.getInt("pin", -1);

        // LEDC config
        _ledcChannel = (ledc_channel_t)deviceConfig.getInt("ledcChannel", LEDC_CHANNEL_0);
        _ledcTimer = (ledc_timer_t)deviceConfig.getInt("ledcTimer", LEDC_TIMER_1);

        // ... setup ledc here using ESP IDF standard code
    }

    /// @brief Main loop for the device (called frequently)
    virtual void loop() override final {}

    // Add REST API endpoints
    virtual void addRestAPIEndpoints(RestAPIEndpointManager& endpointManager) override final 
    {
    // Audio
        endpointManager.addEndpoint("buzzer",       RestAPIEndpoint::ENDPOINT_CALLBACK, RestAPIEndpoint::ENDPOINT_GET,
                    std::bind(&DeviceBuzzer::apiControl, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3),
                    "buzzer/note/<freq>");
    }

private:
    // Buzzer pin
    int _buzzerPin = -1;

    // LEDC config
    ledc_channel_t _ledcChannel = LEDC_CHANNEL_0;
    ledc_timer_t _ledcTimer = LEDC_TIMER_1;

    // API
    RaftRetCode apiControl(const String &reqStr, String &respStr, const APISourceInfo& sourceInfo)
    {
        RaftJson cmdJson = RaftJson::getJSONFromRESTRequest(reqStr);
        if (cmdJson.getString("path[1]","").equalsIgnoreCase("note"))
        {
            playFreq(int(cmdJson.getString("path[2]", "440")));
        }
    }

    // Play a frequency
    void playFreq(uint32_t freqHz)
    {
        // ... LEDC play note
    }
};

In the startup process (e.g. in main.cpp) the device must be registered with the device factory using code like this - note that the name "Buzzer" must match the section in the SysType JSON (see below) otherwise the device will not be instantiated.

    #include "DeviceFactory.h"
    #include "DeviceBuzzer.h"

    extern "C" void app_main(void)
    {
        /// ... Other setup code

        // Register DeviceBuzzer
        deviceFactory.registerDevice("Buzzer", DeviceBuzzer::create);

    }

And the device is automatically configured when it is initialized by the device manager through the SysType mechanism under the DevMan\Devices key (which is an array of device settings).

    "DevMan": {
        "Devices":
        [
            {
                "enable": 1,
                "class": "Buzzer",
                "name": "Buzzer",
                "pin": 5,
                "ledcTimer": 1,
                "ledcChannel": 0,
                "volume": 20
            },
            {
                ...
            }
        ]
    }

Clone this wiki locally