Skip to content

Configuration

Rob Dobson edited this page May 3, 2026 · 6 revisions

Configuration

Raft applications are configured using JSON, in three layered tiers.

Every Raft application has a single hierarchical JSON document that all SysMods read their settings from. Practically that document is assembled at runtime from up to three sources, with later sources overriding earlier ones.

This page describes those layers, how a SysMod fetches its slice, how runtime updates are persisted, and how to react to configuration changes. The underlying parser is documented in JSON / RaftJson, and advanced features in Advanced RaftJson.


The three layers

Layer Source Owned by Mutability Typical contents
1. Compiled defaults A const char* JSON literal compiled into flash (in RaftCoreApp.cpp) Framework Read-only Last-resort fallback values: SystemName, SysManager defaults, RICSerial framing bytes, ...
2. SysType The currently-selected SysType JSON, generated from systypes/ at build time and bound at startup App / build system Effectively read-only at runtime Pin assignments, default WiFi/BLE/Web settings, the DevMan.Devices list, feature toggles
3. NV-mutable A document persisted to ESP-IDF Non-Volatile Storage (NVS) in the namespace "sys" App at runtime Mutable via REST / SysMod APIs Per-deployment overrides: WiFi credentials, friendly names, anything the user changes after the fact

The wiring lives in RaftCoreApp and uses three RaftJson* objects:

RaftJsonNVS _systemConfig;          // namespace "sys"     — layer 3
RaftJson    _sysTypeConfig;         //                     — layer 2
RaftJson    _defaultSystemConfig;   // compiled-in JSON    — layer 1

// In the constructor:
_sysTypeConfig.setChainedRaftJson(&_defaultSystemConfig);

The "chain" mechanism is RaftJson's per-lookup fallback: when a path is not present in _sysTypeConfig, it is automatically retried in _defaultSystemConfig. See chaining for the precise semantics (in particular, the chain is followed at path-not-found boundaries — split layered config at object boundaries to avoid surprises).

_systemConfig (the NV layer, an instance of RaftJsonNVS) is what most SysMods are passed — it persists user-driven changes back to NVS automatically. The SysType layer is consulted for the build-time defaults of every section that has not been explicitly overridden in NVS.


How a SysMod accesses its config

Each RaftSysMod is constructed with a name and a reference to one of these top-level configurations. Internally the SysManager creates a RaftJsonPrefixed wrapper so the SysMod sees only the subtree under its own name.

For a SysMod called "NetworkManager", given:

{
    "NetworkManager": {
        "WiFiSSID":  "home",
        "RetryMs":   5000
    }
}

the SysMod simply asks for unprefixed paths:

String ssid = configGetString("WiFiSSID", "");
long  retry = configGetLong  ("RetryMs",  1000);

The prefixing is automatic. Within your SysMod use the configGet* helpers (which forward to _modConfig); only reach for the global config object when you genuinely need to read another module's section.

The numeric and string accessors are documented in JSON / RaftJson. For arrays, sub-objects, key enumeration and type queries see JSON / RaftJson — other helpful functions.


Worked example

// systype.json (layer 2)
{
    "SystemName": "WeatherStation",
    "NetworkManager": { "WiFiAPMode": false },
    "BLEManager":     { "AdvertisingName": "WX-default" },
    "ExampleSysMod":  { "ExampleGpioPin": 4, "Period": 1000 }
}
// In your SysMod (named "ExampleSysMod")
void setup() override
{
    int  pin    = configGetInt  ("ExampleGpioPin", -1);
    long period = configGetLong ("Period",         500);
    if (pin < 0) {
        LOG_W(MODULE_PREFIX, "No pin configured");
        return;
    }
    pinMode(pin, OUTPUT);
    _periodMs = period;
}

If the same key is later set in the NV layer (e.g. via REST), it shadows the SysType value the next time it is read.


Updating configuration at runtime

Most SysMods expose getstatus / getsettings / setsettings REST endpoints that read and write their slice of the NV-mutable layer. A typical pattern from inside a SysMod:

// Persist the entire mutable slice for this module
String json = R"({"WiFiSSID":")" + newSsid + R"("})";
_sysManager.setMutableConfigForMod(modName(), json);

The SysManager merges the new JSON into the appropriate spot in _systemConfig, calls setJsonDoc() on the underlying RaftJsonNVS (which writes through to NVS atomically), and then fires registered change callbacks.

Important properties:

  • Whole-document writes. The NV layer is replaced wholesale on each persist; there is no in-place patch operation at the persistence layer. Read-modify-write happens above it.
  • No restart required for most settings. Listeners receive a callback and re-read the keys they care about (see below).
  • Some settings need a restart. Pin assignments and other build-time-baked values typically come from the SysType and cannot be changed via the NV layer. Affected SysMods will indicate this.

For BLE / WebSocket clients, runtime overrides are usually delivered via the same setsettings REST endpoint — see RaftCoreAPI and Built-in REST endpoint list.


Reacting to changes

A SysMod can register a callback that fires after the underlying NV document is replaced:

void setup() override
{
    configRegisterChangeCallback([this]() {
        // Re-read whatever you cached
        _periodMs = configGetLong("Period", _periodMs);
        LOG_I(MODULE_PREFIX, "Config changed, period now %ldms", _periodMs);
    });
}

Callbacks are not given any payload — they are a "something has changed, please re-read" signal. They run on the SysManager / NVS write context; keep them short and avoid blocking calls. Register multiple callbacks if you want logically separate handlers; there is no de-registration API so callback lifetime should match the SysMod's. See change callbacks for the underlying mechanism.


Special configuration locations

Location Purpose
Compiled-in defaults in RaftCoreApp.cpp The lowest fallback layer. Modify only if your derived RaftCoreApp needs different framework-level defaults.
systypes/ Build-time configuration tree for each SysType. The active SysType becomes the layer-2 document at runtime.
NVS namespace "sys" Layer-3 persisted JSON. Inspect with RaftJsonNVS::debugShowNVSInfo(true).
Per-SysMod NVS namespaces Some SysMods use additional RaftJsonNVS instances with their own namespaces for state that is logically separate from system config.

To wipe all NV settings (factory reset), the standard mechanism is the reset REST endpoint or by erasing NVS at flash time. See RaftCoreAPI for the endpoints involved.


See also

Clone this wiki locally