Docs: reflect this session's findings across the repo

- top-level README.md (new): system overview, subsystem map, skills pointer,
  notable findings.
- eg4battery README/NOTES: 3 -> 6 packs (pack 6 oddball 0x01/115200); SoC drift
  + calibration section; closed-loop comms evaluated and rejected (loses per-pack
  telemetry, no native protocol, doesn't fix drift); how to force a grid charge
  via output-priority SUB.
- LVX6048 README: closed-loop pending item -> resolved decision; new "SoC
  calibration & known firmware quirks" section (POP single-digit/POP01, MCHGC
  charge-lock, re_discharge=re-discharge can't exceed float, PIRI lag, powermon
  adhoc wedge); skills pointer.
- lvx-control README: POP encoding fix, POP crc-but-applies quirk, verify-by-
  behavior, grid-charge-via-SUB usage.
- troubleshoot-inverter skill: corrected the stale "dead string per inverter"
  claim — both strings healthy; low PV is tilt/heat/shade/curtailment.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-25 21:46:02 -04:00
parent 4bfa021719
commit 8dafce7dfe
6 changed files with 168 additions and 23 deletions

View File

@@ -196,9 +196,42 @@ then restart the three services.
PF) are implemented in `lvx-flash/flash.py` for offline use, but aren't
exposed as HA button/select entities. Deferred until monitoring has been
stable for at least a week.
- **Closed-loop BMS comms.** Currently open-loop — inverters estimate SoC
from voltage, batteries don't push real-time SoC / charge limits to the
inverter. Closed-loop would give better SoC accuracy and dynamic CC
tapering near full. Path is the dedicated CAN port on the master pack
inverter BMS port (separate cable from the inter-pack daisy-chain).
Deferred.
- **Closed-loop BMS comms — evaluated 2026-06, NOT pursuing.** Stays open-loop.
Closed-loop would need the LP4V2 to emulate Pylontech-CAN (no native EG4
support on the LVX BMS port) and would require the inter-pack daisy-chain that
silences slave packs' RS485 ports — i.e. trade away the per-pack/per-cell
telemetry that's our best diagnostic. And it wouldn't fix SoC accuracy: it just
forwards the BMS's own drifted SoC. The cure for drift is a periodic full charge
(see below), done open-loop. See `../eg4battery/NOTES.md` §"Closed-loop BMS comms".
## SoC calibration & known firmware quirks
**SoC drift fix.** The EG4 pack SoC counters drift because the conservative profile
rarely drives a true full charge. A periodic **full charge to absorption** re-anchors
every pack to 100%. Automated by the `calibration-charge` skill —
[`../.claude/skills/calibration-charge/`](../.claude/skills/calibration-charge/):
solar-only on a clear day, or grid-assisted via **output-priority SUB** on a cloudy
day (`../.claude/skills/lib/grid-cal-monitor`, which safety-monitors and auto-reverts).
**LVX6048 firmware quirks** (learned the hard way; baked into the tooling):
- **PI18 `POP` is single-digit.** `solar_battery_utility` encodes as `POP1`, NOT
`POP01` — the inverter *silently rejects* the malformed `POP01` (no error on the
result topic). Fixed in `lvx-control` + `lvx-flash/flash.py` `POP_MAP`. A `POP` set
returns "crc check fails" on the result topic but still applies; `PCP` returns "Succeeded".
- **`MCHGC`/`MUCHGC` are locked while charging** — a charge-current change NAKs whenever
`mppt1_charger_status=charging` (even though `device_mode` reads `Battery`). Set them
only in a pre-charge idle window.
- **`stop_charge_voltage` is really `battery_re_discharge_voltage`** and can't exceed
`float`; firmware NAKs `0`/"Full". Don't use it to force a grid charge — use POP=SUB.
- **PIRI readback lags ~5 min**, so verify a setter by *behavior*
(`line_power_direction`, `device_mode`), not the readback entity.
- **powermon's adhoc queue can wedge** (commands stop landing on the result topic);
`sudo systemctl restart powermon.service powermon2.service` clears it.
## Monitoring & troubleshooting skills
Agent-runnable skills for this install live in
[`../.claude/skills/`](../.claude/skills/) (see `REFERENCE.md` there for the system
map): `solar-health-check`, `troubleshoot-inverter`, `troubleshoot-battery`,
`power-usage`, and `calibration-charge`, plus helpers `lib/solar-snapshot` (live MQTT)
and `lib/ha-history` (HA recorder lookback).

View File

@@ -41,18 +41,35 @@ Risky setters (battery thresholds, type, output mode, factory reset) are
intentionally **not** exposed here — those should go through
`lvx-flash/flash.py apply` with an explicit profile and confirmation.
### Known limitation
### Known limitations & quirks
`max_charging_current` (MCHGC) and `max_utility_charging_current` (MUCHGC)
return `Failed` via PI18 when the inverter is actively charging (mode 06)
— the firmware appears to lock these setters during charge cycles. Other
setters (POP / PCP / PSP / PEI / PDI) work in all observed modes. If you
need to reliably change the charge-current caps, either:
return `Failed` whenever the inverter is **actively charging** (charger_status
= `charging`, even while `device_mode` reads `Battery`) — the firmware locks
these charge-current setters during charge. Set them only in a pre-charge idle
window, or via the LCD (Programs 02 / 11), or `lvx-flash/flash.py apply`.
- wait for the inverter to settle into Standby (mode 01) and retry, or
- change via the LCD (Programs 02 / 11), or
- use `lvx-flash/flash.py apply` (it stops the powermon services first,
giving exclusive USB access).
Other quirks (confirmed 2026-06-25):
- **`POP` is single-digit.** `output_priority=solar_battery_utility` must encode
as `POP1`, NOT `POP01` — the inverter *silently rejects* the malformed `POP01`
(nothing on the `result` topic, no effect). Fixed in `POP_MAP` here and in
`flash.py`. If reinstalling, make sure the live `/usr/local/bin/lvx-control`
has the fix.
- A **`POP` set returns "crc check fails"** on the `result` topic but still
applies; `PCP` returns a clean "Succeeded". So verify a POP change by
**behavior** (`device_mode` / `line_power_direction`), not the result string.
- **PIRI readback lags ~5 min** — don't trust the `*_output_source_priority`
sensor to confirm a just-issued change; watch the behavior instead.
- If commands stop landing on the `result` topic entirely, powermon's adhoc
queue has wedged → `sudo systemctl restart powermon.service powermon2.service`.
### Forcing a full grid charge (calibration)
To grid-charge the bank to full (e.g. SoC calibration on a cloudy day), set
`output_priority``solar_utility_battery` (SUB) so the inverter runs loads off
grid and charges the battery to full, plus `charger_priority``solar_and_utility`.
Revert to `solar_battery_utility` + `solar_first` when done. Automated and
safety-monitored by `../../.claude/skills/lib/grid-cal-monitor`.
Track the `result` topic to see the actual outcome of each command.