Changelog
Per-release notes for the RbAmp Arduino library. Library SemVer is
independent of the rbAmp protocol version — see the rbAmp protocol spec CHANGELOG_PROTOCOL.md (see API Reference)
for the wire-protocol history (the protocol bumped from 1.0 to 1.1 on
2026-05-24 — additive, fully backward-compatible).
v1.5.0 — Per-channel CT models + sensor-model registry
Adds per-channel CT-model assignment for every channel (including senior-SKU channels 3+) via the channel window (field 15), and moves the CT-model list off a hand-maintained enum onto the generated registry. Firmware: v1.4.18 (fw ver 0x0A), where window field 15 is signed on silicon.
Added
- Per-channel CT models on channels 3+ —
setCTModel(channel, code)andconfigureChannels()now bind any channel0..channels()-1. Channels 0-2 use the flatCMD_SET_CT_MODEL_CHnpath; channels 3+ bind through the window (field 15), which the firmware applies immediately (NF+GAIN preset). Every window write is confirmed by a read-back; a model the module does not accept is surfaced asRB_ERR_PARAMand the channel keeps its previous model. readCTModelCh()covers all channels — channels 3+ read the applied model from the window; the flat mirrors0x51-0x53still serve ch0-2.- Generated sensor-model registry —
RbAmpSensorModels.h(generated fromlibs/spec/sensor_models.yaml):RBAMP_CT_*code defines for both classes (SCT-013 + WIRED_CT 1..13), human descriptors, per-model status, andrbamp_sensor_model_lookup(). Use theRBAMP_CT_*defines to name a model. 10_PerChannelModelsexample.
Changed
- CT-model validation is registry-driven — the client no longer keeps a
per-class accept-list (it had drifted: WIRED_CT was absent entirely, and the
new clamp models 6-9 plus reserved 10-13 would have been rejected). The client
now only fast-fails codes absent from the registry; acceptance is runtime
truth — the module returns
RB_ERR_PARAMfor a code without a preset row. RbAmpCTModelenum is now a frozen backward-compat alias for the seven SCT-013 codes; its values are sourced from the generatedRBAMP_CT_SCT013_*defines so they cannot drift. New code (and all WIRED_CT models) uses the generated defines directly.- The
code > 7hard caps insetCTModel()/configureChannels()are gone — the valid range is whatever the registry defines. - Internal: the ch<3 / ch3+ routing for the RT metrics (I_RMS / I_PEAK / P_REAL
/ PF) is consolidated into a single
readChannelMetric()decision point.
Fixed
- CT model / sensor class now persist in production.
setCTModel(),configureChannels()andsetSensorClass()persisted viaCMD_SAVE_GAINS, which is factory-gated — on a production module the save was a silent no-op, so the binding applied to RAM but was lost on the next reboot. These registers areuser_config, so they now persist viaCMD_SAVE_USER_CONFIG(ungated). No API change; same ~700 ms settle.
v1.4.0 — Senior SKUs (UI5 / UI7)
Adds support for the 5- and 7-channel senior SKUs (UI5, UI7). Hardware-verified on silicon (UI7 board, bench, 4/4 PASS). Cross-platform-aligned with the STM32 HAL / ESP-IDF / Python bundle.
Added
- Senior SKU detection —
begin()now reads the channel count fromREG_TOPOLOGY(0x24, values 1/2/3/5/7) and the SKU + voltage presence fromREG_HW_VARIANT(0x55):UI5andUI7added toRbAmpVariant;Five/Sevenadded toRbAmpTopology. - Channel access window —
readCurrent()/readCurrentPeak()/readPower()/readPowerFactor()now acceptch = 0..channels()-1. Channels 0-2 use the flat block; channels 3+ are read transparently through the device's channel window (re-selected on every read). No new public read methods — the existing per-channel calls just extend. readCommitSeq()— reads the digest commit sequence byte; increments once per RT commit (~20 ms), for detecting that the device advanced a sample between two reads. Change flag only (there is no CRC in the digest).- Period energy now covers all channels:
readPeriodSnapshot()fillsavg_p[]for every channel (ch3+ via the window's latched period field) andenergy().wh(ch)accumulates per channel. 09_SeniorSkuexample.
Changed
RBAMP_MAX_CHANNELS(=7) now sizes the per-channel arrays inRbAmpSnapshot(current/current_peak/power/power_factor),RbAmpPeriodSnapshot.avg_p, and the Wh accumulator (was hardcoded 3). Warning: Source-recompile note: the snapshot structs grew — recompile any code that includes this library against the new headers. Wire protocol and the method surface for junior SKUs are unchanged.
v1.3.0 — Fleet + v1.3 protocol
Aligns the Arduino library with the v1.3 rbAmp wire contract and the cross-platform reference (STM32 HAL / ESP-IDF / Python). Hardware-validated on a heterogeneous fleet (UI1 + I2 + I3) through the public API with raw-register ground-truth (38/38 full-coverage suite, plus an extended app-WDT soak).
Added
RbAmpFleetmulti-module manager: busscan()(with collision detection), batchedpollAll(), fleet-widetotalPower()/totalEnergyWh()/pollErrors(), General-Call sync (enableGcAll()/gcLatch()/checkSync()),assignAddress(),checkConflict(), andprovision()for bringing a factory-fresh module onto the bus.- Identity / capability:
readVariant(),readCapability(),readProductId(),readUid(). - Event channel:
readEventFlags(),clearEventFlags(),hasError(),clearError(),readLastError(). - Per-channel CT configuration:
configureChannels()(batched, one flash save) andreadCTModelCh()(applied-model mirror read). - Fleet primitives on the device:
enableGc(),setGroupId(),readGroupId(),readGcTick(),readFleetConfig(),readLabel(),writeLabel(),saveUserConfig(), staticbroadcastLatchGroup(). 08_FleetSyncexample (scan -> GC sync -> aggregate).RbAmpSnapshot.implausibleper-field mask: a field that fails the physical sanity filter is setNaNand flagged, leaving the rest of the snapshot usable (only a transport failure fails the whole read).
Changed
- Energy integrates over the master's wall-clock, never the module's
diagnostic
latch_ms(the module timer under-counts ~25-30%). On a stale period the integration anchor is held so the next valid window is not under-counted. - CT model codes follow the v1.3 per-class accepted set —
Sct013accepts{005, 010, 030, 050, 020};SCT-013-100and-060are recognised SKUs but uncharacterised and rejected client-side.REG_CT_MODELis pure staging: binding is via the per-channel command, so multi-channel binds are order-independent and never clobber channel 0. - Address change is a production-OK two-phase magic commit — no factory mode required.
- Variant detection reads
REG_HW_VARIANT; the constructor topology hint is now a fallback for pre-v1.3 firmware. The default hint isSingle(safe — no spurious polls of absent channels). - I2C writes now share the same NACK-retry discipline as reads (a silently dropped config write on a contended bus is otherwise invisible).
setSensorClass()rejects out-of-enum classes client-side (the firmware silently accepts a bad class, so the client guard is the only defense).
Notes
- AVR:
doubleis 32-bit (==float) on the classic AVR toolchain, so the Wh accumulator loses precision on long soak logging — reset periodically or prefer a 32/64-bit-doublecore (ESP32, SAMD, STM32, RP2040). - Marginal bus / ESP32: Arduino-ESP32's
Wirewraps a driver that can spin on a held bus below the library. Use proper external ~4.7 k pull-ups, avoid a debugger/NRST in production, and arm an app-level task watchdog on the polling task as the recovery path.
v1.0.0 — 2026-05-25 (initial release)
First public release of the RbAmp Arduino library. Implements
the canonical API surface from the rbAmp protocol spec §12 for protocol v1.0.
Features
RbAmpclass — one instance per slave device, public methods for every metering operation:- Lifecycle:
begin(),probe(),waitReady(),firmwareVersion(),topology(),channels(),hasVoltageHw(),address(),rawTopology() - Real-time reads (200 ms refresh):
readVoltage(),readVoltagePeak(),readCurrent(ch),readCurrentPeak(ch),readPower(ch),readPowerFactor(ch),readFrequency(),readAll(&snapshot) - Period metering:
latchPeriod(),isPeriodValid(),readPeriodAvgPower(ch),readPeriodMaxPower(),readPeriodLatchMs(),readPeriodSnapshot(&snap, settle_ms, skip_latch) - Configuration:
setCTModel(code),saveGains(),prepareAddressChange(addr),commitAddressChange(),factoryReset(),reset() - Multi-module:
RbAmp::broadcastLatch(bus)(returnsfalseon v1 firmware per SPEC §9 — General-Call disabled) -
Diagnostics:
lastError(),errorString(code),setLogStream(&Serial),retryExhaustionCount(),sanityRejectCount(),resetCounters() -
RbAmpEnergyper-channel Wh accumulator — owned by eachRbAmpinstance, exposed viadev.energy(). Updated automatically byreadPeriodSnapshot(). Signed (negative = net export). Opt-out viadev.energy().disable(). -
RbAmpSnapshot/RbAmpPeriodSnapshotPOD structs — readAll() and period snapshot result containers. -
RbAmpTopologyenum —Single/SplitPhase/ThreePhasefor the variant hint. -
SPEC §B.5 retry+sanity discipline built in:
- Per-byte retry on ESP32 targets (default 3 attempts × 5 ms gap)
- Configurable via
#define RBAMP_NACK_RETRY_ATTEMPTS / _GAP_MSbefore#include <RbAmp.h> - Loose sanity filter on float reads (
!isfinite(x) || fabsf(x) > 10000) - Diagnostic counters (
retryExhaustionCount()/sanityRejectCount()) -
Auto-disabled on non-ESP32 platforms (single attempt default)
-
v1.1 forward-readiness:
dev.rawTopology()readsREG_TOPOLOGY(0x24) directly. Returns1/2/3on v1.1 firmware,0x00on v1.0 (unmapped),0xFFon I2C failure.-
dev.setCTModel(code)automatically benefits from v1.1 firmware's preset NF + GAIN auto-load — no API change needed. -
Two-step address-change protocol (SPEC §10) —
prepareAddressChange()arms,commitAddressChange()must follow within 5 s. Library validatesREG_MODE == factoryand the 5 s window. Internal handle address updates automatically on successful commit.
Supported platforms
- Arduino AVR (Uno / Mega / Nano) — 32-bit
floatenergy accumulator - arduino-esp32 v3.x (ESP32 / S2 / S3 / C3) —
RBAMP_NACK_RETRY_ATTEMPTS=3default - arduino-esp8266 — single attempt default, no NACK pattern
- STM32duino (F1 / F4 / G4) — single attempt default
- SAMD / RP2040 (arduino-pico) — lightly tested, expected to work
Examples (7 bundled sketches)
01_QuickRead— smoke test: U/I/P/PF per second02_PeriodEnergyOLED— 60-second Wh meter on SSD1306 OLED03_MultiModuleBroadcast— 3 modules, per-device sequential LATCH04_UI3PerChannelMQTT— UI3 + MQTT per-channel publish05_AddressChange— two-step I2C address reassignment06_BidirectionalEnergy— master-side consume / export split07_DeepSleepLogger— ESP32 deep-sleep with RTC-memory Wh
Plus ../examples/SoakMonitor (12-hour autonomous bench monitor for
v1.1 firmware validation).
Documentation
11 reference documents under docs/ covering installation,
hardware, tiers, sensor selection, examples, DIY + cloud integrations,
API reference, troubleshooting, and changelog.
Known limitations
These are deliberate omissions from v1.0 — tracked for v1.x or v2:
broadcastLatch()always returnsfalse(v1 firmware has General-Call disabled — SPEC §9). v2 firmware will enable it; this library will returntrueautomatically once the wire transaction succeeds.- No explicit
setNoiseFloor()/setGain()helpers — these registers fall under the calibration namespace (out of scope for v1 client libraries). For bench tuning use rawWirewrites per Sensor Selection. - No reactive-power read — RESERVED for v2 (STANDARD / PRO tier feature, not exposed in protocol v1).
- No dimmer control (
REG_DIM0_*0x10..0x18 untouched). Out of scope for v1 — see futureRbAmpDimmercompanion library.
Wire-protocol compatibility matrix
| Library v | Firmware v | Behaviour |
|---|---|---|
| 1.0 | 1.0 | Library uses constructor topology hint. Fully functional. |
| 1.0 | 1.1+ | Library uses constructor hint; REG_TOPOLOGY ignored. CT_MODEL preset auto-load works via existing setCTModel() call. |
| 1.3 | 1.0 | Library reads REG_TOPOLOGY = 0x00, falls back to constructor hint. Identical to 1.0/1.0. |
| 1.3 | 1.3+ | Library reads REG_TOPOLOGY as authoritative. Fleet, events, per-channel CT config, GC sync all available. |
| 1.4 | 1.3+ | Senior SKU detection (UI5/UI7); channel window extends per-channel reads to ch 3+. |
| 1.5 | 1.4.18+ | Per-channel CT models on all channels via window field 15; sensor-model registry; CT/class persist fix. |
Bench validation
Phase 1 closed 2026-05-24 (root commit 4b16cbb) — reference master for
the v1.0 firmware validation. Calibration baseline: NF=6, GAIN=2.1094
on the reference SCT-013-5A bench. Final acceptance #4 (100-paired-sample
capture via CalibSampler @ 4 Hz):
| Quantity | DUT | METER | Ratio | Spec | Status |
|---|---|---|---|---|---|
| U (V) | 225.30 | 225.24 | 1.0003 | ±2% | ✓ |
| I (A) | 0.793 | 0.786 | 1.0090 | ±2% | ✓ |
| P (W) | 111.05 | 114.17 | 0.9727 | ±2% | ⚠ borderline -2.7% |
| PF | 0.620 | 0.643 | 0.964 | ±2% | ⚠ -3.6% |
P/PF marginally outside ±2% due to SMPS load flicker (mean-of-100 converges to ~1 %; a stable resistive reference would close the gap). See the rbAmp protocol spec PHASE1_LESSONS_LEARNED (see API Reference) §2.5.
Additional 12-hour SoakMonitor validation (ard-amp-log.csv, 145 cycles):
100 % period_valid, 1 retry exhaust, 0 sanity reject.
Roadmap
v2.0.0 (planned — firmware v2)
- Broadcast LATCH via I2C General-Call.
- Reactive-power register exposed:
readReactivePower(ch). - Dimmer control exposed:
setDimmer(level),getDimmer().
Reporting issues + contributing
Open issues at github.com/rbamp/rbamp-arduino/issues with the diagnostic info checklist from Troubleshooting.
Pull requests welcome — the library is in pure standard-Arduino C++,
no platform-specific dependencies. Run the conformance test in
extras/conformance/ against a real DUT before submitting (PR template
will guide).
License
MIT — see LICENSE.
Related — main rbAmp documentation
- API Reference — formal I²C register / command / error spec the library wraps
- Arduino Examples (raw I²C) — same scenarios without the library, useful for porting
- Period Metering — atomic latch concept and master-side energy formula
- Hardware Connection — pinout, wiring, CT installation
- Troubleshooting — module-side issues (NACK, calibration drift, bus noise)
Source & issues: rb-amp/rbamp-arduino · this page in the repo: docs/11_changelog.md