← Compilation | Contents | Next: Application Structure →
1.2 Commissioning
Your modules are wired (Hardware Guide) and the firmware is flashed (Compilation). This guide binds the modules to roles and starts regulation.
A v2.0 system is a host MCU plus external smart modules — rbAmp (sensing) and DimmerLink
(dimmers) — on the I2C bus. The UI is external (the device serves GET / → 302), so you drive
commissioning either through the external web app pointed at the device's IP, or with the REST API
directly.
Profile note. The REST commissioning flow below is available on the ESP32 and C2-HTTP builds. C2-MQTT is headless — it has no REST API; commission it over MQTT (MQTT-bootstrap, see the MQTT Guide).
Steps 1–7 are done through the web app or REST, so bring the device onto the network first — connect WiFi, then open the app at the device's IP. (Step 0 is the exception: I2C bus pins can also be set over the serial console before WiFi.)
Key Principles
- Roles are per-channel, not per-module. A module is a device (an address); a role is attached to
an
(address, channel)pair. The registry (GET /api/modules) is the source of truth. - The
gridrole must be on a module that also measures voltage. Only a channel with a voltage reference produces signed power (import vs. export). A current-only (CT-only) channel cannot tell direction. You can tell which modules are voltage-capable fromhas_voltage: trueinGET /api/modules(also inGET /api/rbamp/modules). Minimum viable config = one voltage-capable rbAmp assigned asgrid. role = dimmerauto-binds a DimmerLink to dimmer output id 4 — you work with roles and addresses, never raw output ids.
Step 0 — I2C Bus (only if not on default pins)
Default pins per target: ESP32 SDA 21 / SCL 22, ESP32-C2 SDA 5 / SCL 6. To change them:
POST /api/hardware/config
{"i2c":{"bus0":{"sda":N,"scl":M}}}This is saved to NVS and requires a reboot — the bus reads its pins at boot. After the reboot the bus can see the modules.
Step 1 — Discover (rescan)
POST /api/modules/rescan → 202 {"scanning":true}The ~2.5 s scan runs in the background (a second rescan while busy → 409 {"error":"busy"}). Poll
GET /api/modules until "scanning": false — the list is then fresh. Each module reports its
family (rbAmp / DimmerLink / …), addr, channels, current roles[], and valid_roles[] (the only
roles allowed for that family — a UI offers exactly these).
Step 2 — Assign Roles (per channel)
POST /api/modules/role
{ "addr": "0x51", "channel": 0, "role": "grid" }role∈grid | solar | load | voltage | dimmer | relay | none- →
200 {"success":true,"message":"Role saved"}· no module →404· bad channel →400 - This is the recommended way to bind a module — the registry is the source of truth, and
role = dimmerauto-binds the DimmerLink to dimmer id 4.
Start with the mandatory grid channel (on a voltage-capable rbAmp), then add solar / load as
wired, and dimmer for the DimmerLink.
🔁 The first DimmerLink needs one reboot. The DimmerLink poll task is created at boot only when at least one dimmer is enabled. When you enable your first dimmer, the role persists immediately, but reboot once so the poll task starts and the dimmer begins driving the load. Subsequent dimmers and level changes apply live (no reboot).
Step 3 — Set the CT Model (per rbAmp channel)
Fetch the catalog with GET /api/rbamp/ct-models, then set the model that matches your physical CT:
POST /api/rbamp/modules/ct-model
{ "addr": "0x51", "ct_model": "sct013-030" } → 202 {"pending":true, …}ct_modelis a catalogid(e.g.sct013-030= a 30 A clamp).- ⚠️ Changing the CT model overwrites the module's per-unit factory gain calibration with the model's preset gain. Set it once for the correct clamp; re-selecting the same model is a no-op.
Step 4 — Resolve Address Conflicts (re-address)
If two modules share the same default address, give each a unique one:
POST /api/rbamp/modules/address
{ "addr": "0x51", "new_addr": "0x52" } → 202 {"pending":true, …}Two-phase commit; the role mapping migrates and persists. The module re-appears at the new address.
Step 5 — Name the Channels (optional)
POST /api/modules/name sets a human-readable per-channel name that shows up in GET /api/metrics
(e.g. dimmers[].name) and the UI.
Step 6 — Verify Sensing
Read GET /api/sensors (per-source live V/I/P/PF/frequency + role) or GET /api/metrics (merged
power_grid / power_solar / power_load).
🔴 Check the grid sign under a known load.
power_gridmust read + when importing and − when exporting. If the sign or direction is wrong, the grid channel isn't voltage-capable, or the CT model / clamp polarity is off.
Step 7 — Start a Mode
POST /api/mode
{ "mode": "auto" }Modes: off · auto · eco · offgrid · manual · boost · grid_limit. For grid_limit, set the current
cap: POST /api/config {"grid_current_limit": <A>} (default 16.0 A). See
Router Modes for what each mode does.
Your system is now regulating — in AUTO it will raise the dimmer whenever you'd otherwise export, so the surplus heats your load instead of feeding the grid.
Not in v2.0
If you followed a v1.x commissioning guide, these steps are gone (sensing moved to smart modules):
- ADC channel setup (
adc_channels) — ignored; sensing is external. - Sensor-driver selection —
/api/hardware/sensor-profiles,/sensor-types,/voltage-drivers,/current-driversare removed (smart modules self-calibrate). - Per-GPIO dimmer config (
/api/dimmers/{0-3}/…) — removed; the dimmer is driven by the router mode, not per channel.
ESP-NOW nodes. Wireless measurement nodes appear with
source: espnow, are keyed by MAC, and their dimmer outputs start at id 12+. Assign their roles the same way (by MAC).