Skip to content

WritingYourFirstSysMod

Rob Dobson edited this page May 3, 2026 · 1 revision

Writing Your First SysMod

A walkthrough that takes you from raft new to a custom SysMod with its own JSON config, REST endpoint and publish data, with logs visible in the serial monitor.

Time required: about 30 minutes.

This tutorial assumes you have followed Quick Start and have the Raft CLI installed.

What we will build

A BlinkSysMod that:

  • Reads a blink rate (Hz) and pin number from the SysType JSON.
  • Toggles a GPIO pin in loop() at the configured rate.
  • Exposes a blink/rate/<hz> REST endpoint that updates the rate at runtime.
  • Publishes the current rate so a subscriber can be notified when it changes.

1. Scaffold a new app

raft new MyBlinker
cd MyBlinker

This creates a project with RaftCoreApp already wired up, a default SysType, and a CMakeLists. Build and flash to confirm:

raft build
raft flash
raft monitor

You should see the standard SysManager startup logs.

2. Create the SysMod source files

Under main/ add BlinkSysMod.h:

#pragma once

#include "RaftSysMod.h"

class APISourceInfo;
class RestAPIEndpointManager;

class BlinkSysMod : public RaftSysMod
{
public:
    BlinkSysMod(const char* pModuleName, RaftJsonIF& sysConfig)
        : RaftSysMod(pModuleName, sysConfig)
    {
    }

    static RaftSysMod* create(const char* pModuleName, RaftJsonIF& sysConfig)
    {
        return new BlinkSysMod(pModuleName, sysConfig);
    }

protected:
    virtual void setup() override final;
    virtual void loop() override final;
    virtual void addRestAPIEndpoints(RestAPIEndpointManager& endpointManager) override final;

private:
    int      _pin           = -1;
    float    _rateHz        = 1.0f;
    uint32_t _lastToggleMs  = 0;
    bool     _level         = false;

    RaftRetCode apiBlink(const String& reqStr, String& respStr, const APISourceInfo& sourceInfo);

    static constexpr const char* MODULE_PREFIX = "Blink";
};

…and BlinkSysMod.cpp:

#include "BlinkSysMod.h"
#include "Logger.h"
#include "RaftArduino.h"
#include "RaftUtils.h"
#include "RestAPIEndpointManager.h"

void BlinkSysMod::setup()
{
    _pin    = configGetLong("pin", -1);
    _rateHz = configGetDouble("rateHz", 1.0);
    if (_pin >= 0)
        pinMode(_pin, OUTPUT);
    LOG_I(MODULE_PREFIX, "setup pin %d rateHz %.2f", _pin, _rateHz);
}

void BlinkSysMod::loop()
{
    if (_pin < 0 || _rateHz <= 0.0f)
        return;
    uint32_t periodMs = (uint32_t)(1000.0f / (_rateHz * 2.0f));
    if (Raft::isTimeout(millis(), _lastToggleMs, periodMs))
    {
        _level = !_level;
        digitalWrite(_pin, _level ? HIGH : LOW);
        _lastToggleMs = millis();
    }
}

void BlinkSysMod::addRestAPIEndpoints(RestAPIEndpointManager& endpointManager)
{
    endpointManager.addEndpoint("blink",
        RestAPIEndpoint::ENDPOINT_CALLBACK, RestAPIEndpoint::ENDPOINT_GET,
        std::bind(&BlinkSysMod::apiBlink, this,
                  std::placeholders::_1, std::placeholders::_2, std::placeholders::_3),
        "blink/rate/<hz> - set blink rate in Hz");
}

RaftRetCode BlinkSysMod::apiBlink(const String& reqStr, String& respStr, const APISourceInfo&)
{
    String cmd = RestAPIEndpointManager::getNthArgStr(reqStr.c_str(), 1);
    if (cmd.equalsIgnoreCase("rate"))
    {
        String hzStr = RestAPIEndpointManager::getNthArgStr(reqStr.c_str(), 2);
        _rateHz = hzStr.toFloat();
        LOG_I(MODULE_PREFIX, "rate set to %.2f Hz", _rateHz);
    }
    Raft::setJsonResult(reqStr.c_str(), respStr, true, nullptr,
        ("\"rateHz\":" + String(_rateHz)).c_str());
    return RaftRetCode::RAFT_OK;
}

Add the file to main/CMakeLists.txt:

idf_component_register(
    SRCS "main.cpp" "BlinkSysMod.cpp"
    INCLUDE_DIRS "."
    ...)

3. Register the SysMod

Open main/main.cpp and register the SysMod with SysManager after RaftCoreApp is constructed:

#include "RaftCoreApp.h"
#include "RegisterSysMods.h"
#include "BlinkSysMod.h"

extern "C" void app_main(void)
{
    RaftCoreApp raftApp;

    // Register built-in SysMods (NetworkManager, BLEManager, etc.)
    RegisterSysMods::registerSysMods(raftApp.getSysManager());

    // Register your SysMod
    raftApp.getSysManager().registerSysMod("Blink", BlinkSysMod::create);

    raftApp.setup();
    while (true)
        raftApp.loop();
}

4. Add the SysMod to your SysType

Open the active SysType JSON (e.g. systypes/Common/SysType.json) and add a Blink entry to the SysMods array:

{
    "SysMods": [
        // ... existing built-ins ...
        {
            "name": "Blink",
            "pin": 2,
            "rateHz": 2.0
        }
    ]
}

The name value must match what you registered ("Blink"). Any other keys are read by your SysMod via configGet*.

5. Build, flash, test

raft build
raft flash
raft monitor

In the serial monitor you should see:

Blink: setup pin 2 rateHz 2.00

…and the LED on GPIO 2 will toggle at 2 Hz. From a host on the same network, change the rate:

curl http://<device-ip>/api/blink/rate/5

Response:

{"req":"blink/rate/5","rslt":"ok","rateHz":5.00}

6. (Optional) Make the rate publishable

Add a getDebugJSON() override or, better, register the SysMod as a data source so subscribers can be notified on rate changes. See Registering as a Data Source.

What's next

Clone this wiki locally