Skip to Content

The Whole Panel on One ESP32 — Real Per-Circuit Energy in Home Assistant

Mains plus every branch over a two-wire I²C bus — add a circuit for the price of a module
July 29, 2026 by
The Whole Panel on One ESP32 — Real Per-Circuit Energy in Home Assistant
Administrator

The problem this solves

Your utility meter says the house is pulling about 3.9 kW right now. It won't tell you that the water heater is ~2.2 kW of it, the AC compressor another ~1 kW, and the rest is sockets and lights. To see per circuit you need a meter per circuit — and the usual DIY meter, a PZEM on a UART, is one device per serial port. Two of them already means bit-banged serials fighting the ESP's WiFi; a whole panel is out of the question. The commercial per-circuit boxes (Emporia and friends) solve the wiring but tie you to their cloud.

rbAmp scales the other way. A multi-channel BASIC module measures voltage and current on each of its channels and computes real active power and energy per channel on the module itself — and it talks I²C, a bus, so one ESP32 reads a stack of them over the same two signal wires. One ESP32, real numbers for every circuit, in your own Home Assistant. This is the build B1 promised — the whole panel, per circuit.

What you'll have

Per-circuit energy and the mains total in the Home Assistant Energy Dashboard One ESP32, one bus: the mains total plus every branch, broken out per circuit — real energy, not an estimate.

How it scales: UI modules on one bus

The architecture is the whole point, so it's worth thirty seconds up front:

  • Each module measures voltage and current — so it reports real power. A UI-series module samples V and its currents together and computes mean(u·i) per channel on-chip: real active power (W) and energy (Wh), power factor included, no host-side guessing.
  • Up to 3 circuits per module. UI1 / UI2 / UI3 = one voltage reference + 1 / 2 / 3 current channels. A UI3 meters three circuits; two UI3 modules meter six. (5- and 7-channel modules are on the roadmap.)
  • It's a bus, not a serial port. Every module shares the same SDA/SCL. Add circuits by adding modules — no new ESP32, no new UART, no SoftwareSerial.
  • Synchronized across modules. Run the fleet in one group and a single I²C General-Call (GC) broadcast latches every module's period on the same instant, so the per-circuit energies reconcile against the mains feed.

Real active power, measured per circuit — not disaggregated

This is where a monitoring build has to be honest, so here it is plainly. (Per circuit means a CT on every branch — a real measurement — not one whole-home sensor disaggregating the total into per-appliance guesses the way single-point NILM monitors do.)

  • The power and energy are real, computed on the module. Because each channel has both voltage and current, the module computes true active power (P = mean(u·i), power factor and waveform distortion included) and accumulates real watt-hours per channel. There's no "current × assumed voltage" apparent-power estimate and no Riemann-sum guesswork in Home Assistant — HA receives finished, per-circuit Wh.
  • What Basic doesn't do: a Basic module measures consumption (import), single measurement range. If you need to separate import from export (solar feed-in) or a wider dynamic range, that's the Standard series — it adds bidirectional metering and multi-range accuracy on top of the same per-channel real power.
  • Not a revenue meter. Monitoring-grade and calibrated, but not certified for billing.

(Want cheap, real-time load control instead of accurate energy accounting — current limits, over-load alerts, shedding? That's a different job for the current-only I-modules — see real-time load control.)

Bill of materials

Item Why Where
2× rbAmp Basic UI3 (V + 3 currents each = 6 channels) real power + energy on every circuit UI-series wattmeter
6× SCT-013 CT (rating per circuit: 50 A mains/EV, 30 A appliances, 5 A lighting) one clamp per measured conductor SCT-013 current transformer
1× ESP32 dev board the single host for the whole panel
bus wiring (SDA/SCL/3V3/GND) one I²C bus to both modules

Prices: see each product page. Scale by adding UI modules on the same bus — no extra ESP. (No external pull-up resistors — the modules have them on board. See addressing below for multi-module buses.)

Get the hardware: rbAmp UI-series wattmeter (SCT-013), 1–7 channels

Safety first — this is live-panel work

⚠️ A panel is more dangerous than a single appliance, and this project puts several CTs inside one. Every CT clamps around an insulated conductor — you never cut or strip live wire — but opening the panel, routing CTs, and working among live busbars is qualified-electrician territory. Kill the main, verify dead, and if you're not certain you're competent in the panel, have an electrician place the CTs and bring the low-voltage leads out to where you wire the modules. Label every CT to its breaker before you close up.

Address each module (one-time)

Modules ship at 0x50. A fleet needs unique addresses, so the second module gets 0x51, using ESPHome's new_address: one-shot — no factory mode, no extra tools. Do it one module at a time, with the other disconnected so there's no address clash.

Per additional module: 1. Connect only that module to the bus. 2. Flash a scratch config with its current and target address: yaml rbamp: id: meter address: 0x50 # current new_address: 0x51 # target — DELETE this line after it succeeds 3. Watch the log for the re-probe success line, then remove new_address: and set address: 0x51. 4. Label the module + its CTs to their breakers. Repeat for 0x52… if you add more.

Pull-ups (multi-module buses). The modules carry I²C pull-ups on board — you never add external ones. But on a bus of several modules those on-board pull-ups sit in parallel and become too strong. Keep one set active (the first module) and disable the pull-ups on every other module (jumper / solder-cut per the hardware guide). For one or two modules you can leave them all as-is.

The one YAML

One file drives the whole panel: two UI3 modules at 0x50/0x51, six channels, each reporting real V/I/P/ Wh/PF. Both modules run in one fleet group so their periods latch together.

yaml
esphome:
  name: rbamp-panel-ui
esp32:
  board: esp32dev
  framework:
    type: arduino
wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
api:
ota:
  - platform: esphome
logger:
i2c:
  sda: GPIO21
  scl: GPIO22
  frequency: 50kHz   # ESP-IDF i2c_master driver + the module's I²C recovery window interact at
                     # 100 kHz → intermittent read NACKs on ESP32. 50 kHz is the robust setting.
                     # STM32 / RP2040 / Linux-SBC masters run this module at 100 kHz fine.
  scan: true
external_components:
  - source: github://rb-amp/[email protected]
    components: [rbamp]
# Two UI3 modules, one synchronized fleet.
rbamp:
  - id: mod_heavy
    address: 0x50
    update_interval: 10s
    ct_models: [SCT_013_050, SCT_013_030, SCT_013_030]   # ch0 mains · ch1 water heater · ch2 AC
    fleet_gc_enable: true
    group_id: 1
  - id: mod_light
    address: 0x51
    update_interval: 10s
    ct_models: [SCT_013_005, SCT_013_030, SCT_013_050]   # ch0 lighting · ch1 sockets · ch2 EV
    fleet_gc_enable: true
    group_id: 1
sensor:
  # ── Module A @ 0x50: mains + water heater + AC — one voltage sense, real P/Wh per channel ──
  - platform: rbamp
    rbamp_id: mod_heavy
    voltage:        { name: "Mains Voltage" }
    frequency:      { name: "Mains Frequency" }
    current:        { name: "Mains Current" }
    power:          { name: "Mains Power" }
    energy:         { name: "Mains Energy" }             # → Energy Dashboard "Grid consumption"
    power_factor:   { name: "Mains Power Factor" }
    current_1:      { name: "Water Heater Current" }
    power_1:        { name: "Water Heater Power", id: p_heater }
    energy_1:       { name: "Water Heater Energy" }      # → Energy Dashboard "Individual device"
    power_factor_1: { name: "Water Heater Power Factor" }
    current_2:      { name: "AC Current" }
    power_2:        { name: "AC Power", id: p_ac }
    energy_2:       { name: "AC Energy" }
    power_factor_2: { name: "AC Power Factor" }
  # ── Module B @ 0x51: lighting + sockets + EV (voltage marked internal — same mains rail) ──
  - platform: rbamp
    rbamp_id: mod_light
    voltage:        { name: "Mains Voltage (mod B ref)", internal: true }
    current:        { name: "Lighting Current" }
    power:          { name: "Lighting Power", id: p_lighting }
    energy:         { name: "Lighting Energy" }
    power_factor:   { name: "Lighting Power Factor" }
    current_1:      { name: "Sockets Current" }
    power_1:        { name: "Sockets Power", id: p_sockets }
    energy_1:       { name: "Sockets Energy" }
    power_factor_1: { name: "Sockets Power Factor" }
    current_2:      { name: "EV Current" }
    power_2:        { name: "EV Power", id: p_ev }
    energy_2:       { name: "EV Energy" }
    power_factor_2: { name: "EV Power Factor" }
  # ── Sum of branches vs mains — should track within a few % on a fully-metered panel ──
  - platform: template
    name: "Branches Power Sum"
    device_class: power
    state_class: measurement
    unit_of_measurement: W
    update_interval: 10s
    lambda: |-
      float s = 0;
      if (id(p_heater).has_state())   s += id(p_heater).state;
      if (id(p_ac).has_state())       s += id(p_ac).state;
      if (id(p_lighting).has_state()) s += id(p_lighting).state;
      if (id(p_sockets).has_state())  s += id(p_sockets).state;
      if (id(p_ev).has_state())       s += id(p_ev).state;
      return s;

See it in Home Assistant

After the flash, the boot log confirms both modules on the one bus, latching together as a fleet:

text
[i2c ] Found i2c device at address 0x50
[i2c ] Found i2c device at address 0x51
[rbamp] mod_heavy: variant=UI3, fleet_gc=on, group=1
[rbamp] mod_light: variant=UI3, fleet_gc=on, group=1
[rbamp] GC latch enabled fleet-wide: 2 modules (6 channels) in group 1
  1. Adopt the rbamp-panel-ui node (Settings → Devices & Services). You get six circuits, each with real current, power, energy and power factor.

Per-circuit power and energy entities in Home Assistant

  1. Build a per-circuit view — a glance card ranks your loads by real power, live.

A per-circuit power dashboard card

  1. Branch sums ≈ feed. With every module in one group_id, a single broadcast latches them on the same instant, so the Branches Power Sum tracks Mains Power to within a few percent — the gap is your un-metered small loads. Because these are real active-power figures, that reconciliation actually holds (it wouldn't with apparent-power estimates).

Add it to the Energy Dashboard

  • Grid consumption: point it at sensor.mains_energy — the real, measured total.
  • Individual devices: add each circuit's ..._energy sensor. These are real per-circuit watt-hours computed on the module — drop them straight into the Energy Dashboard's Individual devices panel for an honest per-circuit breakdown, no Riemann-sum artifacts.

What's next

  • Real-time load control. Want to cap a circuit, alert on over-consumption, or shed load before the main breaker trips? That's a job for the fast, cheap current-only I-modules and a few HA automations. (See real-time load control with rbAmp.)
  • Honest three-phase. One synchronized sample set across all three phases, under the Standard 3-phase release. (Coming in this series.)
  • Hunt the vampires. With every circuit metered, the standby floor becomes a sortable list. (Coming.)

Start here if you haven't got a single module running yet: rbAmp in the Home Assistant Energy Dashboard in 15 minutes. For the bus wiring and addressing detail, see the ESPHome hardware guide and the API reference.

[newsletter / early-adopter subscribe block — deploy session inserts the site's standard subscribe snippet]


Documentation & source code
📖 rbAmp ESPHome component reference · 💻 rb-amp/rbamp-esphome on GitHub — issues, examples, ⭐
Migrating from PZEM-004T to rbAmp — Keep All Your Home Assistant History
Same entity IDs, history intact, UART freed — a one-YAML migration