working in production - not entirely validated. parallel mode working.
This commit is contained in:
@@ -544,7 +544,7 @@ FAULT_NAMES = {
|
||||
# GS field indices (see powermon/protocols/pi18.py :: QUERY_COMMANDS["GS"])
|
||||
GS_AC_OUTPUT_V = 2
|
||||
GS_AC_OUTPUT_HZ = 3
|
||||
GS_PARALLEL_VALID = 27
|
||||
GS_PARALLEL_INDEX = 27 # 0 = master, 1+ = slaves; one unit per cluster reports 0
|
||||
|
||||
|
||||
async def _read_raw_parts(port: USBPort, code: str, retries: int = 3) -> list[str]:
|
||||
@@ -574,7 +574,7 @@ async def _snapshot_sync(path: str) -> dict[str, Any]:
|
||||
finally:
|
||||
await port.disconnect()
|
||||
return {
|
||||
"parallel_valid": gs[GS_PARALLEL_VALID] == "1",
|
||||
"parallel_index": int(gs[GS_PARALLEL_INDEX]),
|
||||
"ac_output_v": _tenths_to_v(gs[GS_AC_OUTPUT_V]),
|
||||
"ac_output_hz": int(gs[GS_AC_OUTPUT_HZ]) / 10.0,
|
||||
"fault_code": fws[0],
|
||||
@@ -593,9 +593,10 @@ async def cmd_sync_check(args) -> int:
|
||||
b = await _snapshot_sync(path_b)
|
||||
|
||||
def _row(label: str, s: dict) -> str:
|
||||
valid = "valid" if s["parallel_valid"] else "NOT VALID"
|
||||
idx = s["parallel_index"]
|
||||
role = f"instance {idx}{' (master)' if idx == 0 else ''}"
|
||||
return (f"{label}: fw={s['main_cpu']}/{s['slave_cpu']} mode={s['mode']} "
|
||||
f"parallel={valid} fault={s['fault_name']} "
|
||||
f"parallel={role} fault={s['fault_name']} "
|
||||
f"vac={s['ac_output_v']}V fac={s['ac_output_hz']}Hz")
|
||||
|
||||
print(_row(path_a, a))
|
||||
@@ -604,10 +605,10 @@ async def cmd_sync_check(args) -> int:
|
||||
issues: list[str] = []
|
||||
if a["main_cpu"] != b["main_cpu"] or a["slave_cpu"] != b["slave_cpu"]:
|
||||
issues.append(f"firmware mismatch: {a['main_cpu']}/{a['slave_cpu']} vs {b['main_cpu']}/{b['slave_cpu']} — parallel requires matching firmware on both units")
|
||||
if not a["parallel_valid"]:
|
||||
issues.append(f"{path_a}: GS parallel_instance_number = Not valid")
|
||||
if not b["parallel_valid"]:
|
||||
issues.append(f"{path_b}: GS parallel_instance_number = Not valid")
|
||||
if a["parallel_index"] == b["parallel_index"]:
|
||||
issues.append(f"both units report the same parallel instance index ({a['parallel_index']}); should differ — cluster handshake incomplete")
|
||||
if 0 not in (a["parallel_index"], b["parallel_index"]):
|
||||
issues.append(f"neither unit reports instance 0 — cluster has no elected master (got {a['parallel_index']}, {b['parallel_index']})")
|
||||
if a["fault_code"] != "00":
|
||||
issues.append(f"{path_a}: active fault {a['fault_code']} ({a['fault_name']})")
|
||||
if b["fault_code"] != "00":
|
||||
|
||||
59
LVX6048/lvx-flash/profiles/eg4-lp4-v2.yaml
Normal file
59
LVX6048/lvx-flash/profiles/eg4-lp4-v2.yaml
Normal file
@@ -0,0 +1,59 @@
|
||||
# LVX6048 settings profile — EG4 LifePower4 v2 LiFePO4 bank, open-loop.
|
||||
#
|
||||
# Designed for: 3× EG4 LP4 v2 100 Ah in parallel (300 Ah, ~15.4 kWh).
|
||||
# Apply identically to both inverters in a parallel pair:
|
||||
# sudo systemctl stop powermon.service powermon2.service
|
||||
# ./flash.py apply --device /dev/lvx6048-1 --profile profiles/eg4-lp4-v2.yaml --confirm
|
||||
# ./flash.py apply --device /dev/lvx6048-2 --profile profiles/eg4-lp4-v2.yaml --confirm
|
||||
# sudo systemctl start powermon.service powermon2.service
|
||||
# ./flash.py compare --device-a /dev/lvx6048-1 --device-b /dev/lvx6048-2
|
||||
# ./flash.py sync-check --device-a /dev/lvx6048-1 --device-b /dev/lvx6048-2
|
||||
#
|
||||
# Voltage rationale (16S LFP, 3.2 V/cell nominal = 51.2 V pack):
|
||||
# bulk 56.4 V = 3.525 V/cell — long-life sweet spot for LFP
|
||||
# float 54.0 V = 3.375 V/cell — rest near mid-knee, avoid sitting on the top
|
||||
# stop_charge 54.0 V — grid charges only to ~mid-knee; solar handles the bulk top-off
|
||||
# stop_dis 48.0 V = 3.000 V/cell — soft "switch to grid" point
|
||||
# cutoff 48.0 V = 3.000 V/cell — inverter hard shutdown floor (BMS still protects below)
|
||||
# Conservative off-grid policy: keep grid as a soft top-up, let solar do the bulk work.
|
||||
#
|
||||
# Current rationale (300 Ah bank):
|
||||
# 60 A/unit × 2 units = 120 A combined ≈ 0.4 C — well under bank's continuous spec
|
||||
# 30 A/unit MUCHGC keeps grid-charging conservative (60 A combined)
|
||||
|
||||
# enum: AGM | FLOODED | USER
|
||||
# USER required to enable the per-cell custom voltages below.
|
||||
battery_type: USER
|
||||
|
||||
# V 40.0..48.0 — hard shutdown; must be < stop_discharge_voltage.
|
||||
cutoff_voltage: 48.0
|
||||
|
||||
# V 44.0..51.0 — switch battery→grid below this (pair: stop_charge_voltage).
|
||||
stop_discharge_voltage: 48.0
|
||||
|
||||
# V 0 (=Full) or 48.0..58.0 — switch grid→battery above this. 0 = let bulk define.
|
||||
stop_charge_voltage: 54.0
|
||||
|
||||
# V 48.0..58.4 — CC→CV transition (pair: float_voltage; must be >= float_voltage).
|
||||
bulk_voltage: 56.4
|
||||
|
||||
# V 48.0..58.4 — held while on grid (pair: bulk_voltage; must be <= bulk_voltage).
|
||||
float_voltage: 54.0
|
||||
|
||||
# A 10,20,30,40,50,60,70,80 — combined solar+AC cap, per unit.
|
||||
max_charging_current: 60
|
||||
|
||||
# A 2,10,20,30,40,50,60,70,80 — grid-side cap, per unit.
|
||||
max_utility_charging_current: 30
|
||||
|
||||
# enum: solar_utility_battery | solar_battery_utility
|
||||
output_source_priority: solar_battery_utility
|
||||
|
||||
# enum: solar_first | solar_and_utility | solar_only
|
||||
charger_priority: solar_first
|
||||
|
||||
# enum: battery_load_utility_ac | load_battery_utility
|
||||
solar_power_priority: battery_load_utility_ac
|
||||
|
||||
# enum: enabled | disabled (PEI/PDI)
|
||||
grid_tie: disabled
|
||||
Reference in New Issue
Block a user