Project: 500Ah LiFePO4 Bank Monitor Upgrade — From Voltage Logging to Coulomb Counting
TL;DR: After 130+ days and 712k samples on a Shelly Plus Uni, the 500Ah mixed-brand parallel bank study hit the limit of voltage-only monitoring. The new THT V2 carrier — XIAO ESP32-C3 + INA228 + DROK 200A/75mV low-side shunt — is commissioned and live as of July 16 2026. First anchored full charge: 115.39 Ah / 1586 Wh, 16.8 min absorption. It turns the bank from a voltage trend into a true energy instrument.
Full commissioning report and open data: https://github.com/wkcollis1-eng/Lifepo4-Battery-Banks
What the Shelly Plus Uni Did Right
The Shelly was perfect for Phase 1. It ran for 130+ days straight:
- 397 Ah validated (99.3% of nameplate @80% DoD) — architectural immunity holds
- MA-60s noise reduction validated: 42-50% (712k high-freq samples @ ∼3s)
- Temperature coefficient: +1.0 mV/°F, stasis drift -0.575 mV/day OLS
- Self-discharge: ∼0%/month confirmed, all loss from parasitic loads
- BMS balancing signature: ∼80-90 sec cycles above 14.4V
For $15 and 5 wires, it proved the 500Ah parallel topology doesn't diverge.
Shelly Limits on a 500Ah Flat Curve
This is exactly what you hit in the UPS build too:
- Voltage-only, 10 mV quantization. In the LiFePO4 plateau, 6.0 mV = 1% SOC for this bank. Shelly can't resolve SOC.
- No current. Parasitic draw had to be inferred from voltage drift (12.5 mA = Drok ∼10mA + Shelly ∼2-6mA). No efficiency, no SOC, no self-discharge direct measurement.
- Sense-tap IR error. Shelly read 24-34 mV low, load-dependent, because it shares a different IR path than the power bus. It tracked a 300 mV sag faithfully, but as a cross-check, not a reference.
- Flatlined idle. Anything < ∼0.05A reported as 0.0 W. Idle self-draw invisible.
- No local display, no independent protection. All alarms depended on Wi-Fi + HA.
That was fine for stasis monitoring. It fails for efficiency, SOC, and Ri trending.

The Upgrade: Bank-Monitor-THT V2
Same carrier philosophy as the UPS-MONITOR-THT V1 you shipped to OSH Park:
- Sensor: Adafruit INA228 breakout, on-board 15 mΩ shunt removed, 20-bit, ADCRANGE 0 = ±163.84 mV, 4120 µs x 128 averaging, 2s poll, Kelvin taps to DROK 200A / 75mV manganin shunt (375 µΩ) in negative leg, low-side
- Voltage: Fused VBUS lead from positive busbar — 195.3125 µV LSB survives end-to-end to HA CSV export, σ ≈ 0.10 mV (half an LSB)
- Brain: Seeed XIAO ESP32-C3, ESPHome
- Temp: DS18B20 on pack case, isolated mount
- UI: SSD1306 OLED STEMMA + wake button + status LED (slow-flash proof-of-life / solid active / fast FAULT)
- Power: Pololu D24V7F3 3.3v regulator
Firmware V1.21 at commissioning: 2s integration (stale-bus watchdog 10s, 8-16V and ≥350A plausibility guards), coulomb-counted SOC anchored at confirmed full-charge (≥14.20V sustained 60s + charger-stop edge), true signed mW power.
Independent hardware alerts written to INA228 at boot and latched on GPIO5: BUVL 12.20V, BOVL 14.80V, SOVL +250A, SUVL -250A, POL 1500W — decoded to alert_reason with operator acknowledge, independent of firmware.

What V2 Can Do That Shelly Can't
Capability | Shelly Plus Uni | V2 INA228 + DROK |
|---|---|---|
Current / Power | No — inferred from drift | Direct ±250A, mW resolution. Idle -0.08W now visible |
SOC | Impossible in plateau | Coulomb-counted, anchored at full charge. First anchor: 2026-07-16 19:50 UTC, SOC 100.0%, 1 cycle |
Energy Accounting | No | Ah/Wh integrators agree with independent CSV integration to 0.03-0.15h |
Internal Resistance | ∼4.9 mΩ system baseline (inferred) | DC-IR 3.73 mΩ @ 69°F ∼80% SOC, ohmic R0 2.63 mΩ. Trend metric with 45s dwell + ±15% stability gate, expected 3.3-3.5 mΩ |
Efficiency Ladder | Inverter 90.3% @440W avg only | Voltaic 95.8% (13.17V dis / 13.745V chg), Round-trip ∼94.8%, Charger AC→DC 95.7% ±2.5% (1586 Wh DC / 1657 Wh AC), Inverter 87-94% provisional + 127W self-consumption @1kW measured |
Voltage Sag | 300 mV tracked, but offset | 3.5% sag at nameplate (∼130A), Vmin 12.851V quantified |
BMS Balancing | Detected as ∼80-90s oscillation | Countable metric: 10.7 min above 14.4V, 183 mV pk-pk, 8 reversals, baseline absorption 16.82 min |
OCV Metrology | No | OCV slope 6.0 mV/%SOC, τ63 ≈7 min, 95% ≈34 min relaxation fit, thermal +2.7°F @0.157C charge |
Protection | HA-dependent | Hardware BUVL/BOVL/SOVL/SUVL/POL + firmware 12.40/12.20/11.80/14.80V — live-validated with genuine over-power alert and double inverter-overload trip during commissioning |


Commissioning Highlights — July 13-16
Deliberate discharge ladder 41W → 70W → ∼1kW → nameplate, then full LiTime 12V 14.6V 80A charge:
- CC 80 min at 78.6A (98.3% nameplate) → CV entry 14.20V → Vmax 14.584V (-16mV of spec) → taper 66.7→6.48A (≈C/77) → stop. 115.39 Ah in 98 min @0.157C.
- Ledger closes: 110.71 Ah discharged, 115.39 Ah charged = 4.68 Ah excess = ≤1.1 Ah true CE loss + balancing + ∼3.5 Ah pre-commissioning deficit (bank rested since July 5 full charge, 5x BMS standby 1.3-2.6 Ah + lamp session).
- First-cycle CE 95.78% is an accounting floor, not a measurement. True CE ≥98.5% expected cycle 2+.
- All 8 commissioning defects (3 hardware, 5 firmware) closed without carrier respin — e.g. F3 idle flatline, F4 outage duration desync, F5 Ri phase scatter from 5s dwell at 20 mV/s.
Why This Matters for the Study
The Shelly answered: "Does a mixed-brand 500Ah parallel bank diverge?" No.
V2 answers the question "Highest-value next step: Direct 24-72h bus-current measurement collapses SOC/endurance uncertainty in the flat-OCV region."
Now it's done:
- Self-discharge is no longer inferred — it's measured.
- Storage endurance (∼11+ months to 80% SOC at 12.5 mA) is now closed-loop.
- Ri, CE, and absorption time (16.8 min baseline #1) become long-term health trends for InfluxDB.
Old role for Shelly: keep it as independent voltage watchdog on a different sense tap.
Next: definitive inverter efficiency run with synchronized Kill-A-Watt window, shunt calibration vs. calibrated clamp at ∼115A to bring DROK ±1% to ∼0.1%, and Cycle 2 CE.
Design files, V1.21 YAML, and CSVs with LSB-faithful 2s cadence are in the repo.
Bill Collis