|
|
hace 1 mes | |
|---|---|---|
| docs | hace 1 mes | |
| tools | hace 1 mes | |
| .gitignore | hace 1 mes | |
| README.md | hace 1 mes | |
| must_pv1800_monitor.py | hace 1 mes |
Real-time monitor for the MUST PV1800 (2024) solar inverter/charger via Modbus RTU.
The PV1800 firmware is non-compliant — it ignores the count field and
often dumps a 39-register block in response to any read. This script
handles that by reading the whole block in one go and extracting the
registers we care about, which is the only way to unambiguously identify
which response belongs to which request.
1a86:7523), shows up as
/dev/ttyUSB0 on the remote host.192.168.44.94, user root. The
CH340 is plugged into this host directly.| File | Purpose |
|---|---|
must_pv1800_monitor.py |
The monitor script. Self-contained — embeds the pyserial-based TCP proxy it runs on the remote host, plus the full PV1800 register map. |
docs/register-map.md |
The PV1800 register map, derived from taHC81/MUST-ESPhome/PH1800 PV1800 EP1800 PV3500 EP3500 RS485 Modbus RTU communication Protocol 1.4.15.xlsx. |
docs/quirks.md |
Things I learned the hard way about the PV1800 firmware and the CH340 driver. Read this before debugging "why isn't it working". |
tools/test_proxy.py |
A standalone copy of the TCP<->serial proxy, useful for ad-hoc debugging with nc or socat. |
# Live updates every 3 s (default)
./must_pv1800_monitor.py
# One snapshot, for scripting
./must_pv1800_monitor.py --once
# JSON output, one line per snapshot
./must_pv1800_monitor.py --json
# If you're on the LXC with the USB cable attached directly
./must_pv1800_monitor.py --serial
# If you already have a socat/ser2net bridge running on a remote host
./must_pv1800_monitor.py --tcp --tcp-host 192.168.44.94 --tcp-port 8502
┌─────────────────┐ SSH ┌────────────────────┐ pyserial ┌──────────────┐
│ this script │ ─────────► │ remote host LXC │ ────────────► │ /dev/ttyUSB0 │
│ (your laptop) │ ◄───────── │ 192.168.44.94 │ ◄──────────── │ (CH340) │
└─────────────────┘ └────────────────────┘ └──────┬───────┘
│ RS-485
▼
┌──────────────┐
│ MUST PV1800 │
│ slave ID 4 │
└──────────────┘
/tmp/.must_pv1800_proxy.py)
over SFTP and runs it as a detached background process.docs/quirks.md), then opens /dev/ttyUSB0 and bridges it to
127.0.0.1:9500 on the remote.--interval seconds, and loops.Local: pyserial, paramiko. Install with:
pip install --break-system-packages pyserial paramiko
Remote: python3-serial (apt package). The script auto-installs it if
missing — just needs apt-get available.
Example snapshot (panels disconnected, inverter in ByPass mode feeding a ~350 W load from grid):
=== 2026-09-10 17:13:36 ===
---------------
CHARGER
---------------
--- State ---
Charger workstate = SelfTest mode
MPPT state = Stop
Charging state = Stop
--- Live readings ---
PV voltage = 0.0V
Battery voltage (charger) = 27.9V
Charger current = 0.0A
Charger power = 0.0W
--- Sensors ---
Charger radiator temp = 26.0°C
External temp = 0.0°C
--- Relays ---
Battery Relay (charger) = Open
PV Relay (charger) = Open
--- Configuration ---
Battery voltage grade = 24V system
Charger rated current = 60.0A
--- Energy totals ---
Acc PV energy (total) = 0.1kWh
--- Runtime ---
Acc runtime days = 0.0d
Acc runtime hours = 0.0h
Acc runtime minutes = 0.0m
---------------
INVERTER
---------------
--- State ---
Inverter Work state = ByPass
--- Configuration ---
AC voltage grade = 120V variant
Rated power = 2000.0VA
Battery voltage grade = 24V system
--- Voltages ---
Battery voltage (inverter) = 28.2V
Inverter voltage = 116.0V
Grid voltage = 117.5V
BUS voltage = 254.9V
--- Currents ---
Control current = 0.0A
Inverter current = 0.0A
Grid current = 3.3A
Load current = 3.2A
--- Real power (P) ---
PInverter = -14W
PGrid = -363W
PLoad = 352W
Load percent = 18.0%
--- Apparent power (S) ---
SInverter = 0VA
SGrid = 395VA
SLoad = 352VA
--- Reactive power (Q) ---
QInverter = 0var
QGrid = 152var
QLoad = 127var
--- Frequencies ---
Inverter frequency = 60.0Hz
Grid frequency = 60.0Hz
--- Temperatures ---
AC radiator temp = 38.0°C
Transformer temp = 60.0°C
DC radiator temp = 0.0°C
--- Relays ---
Inverter relay = Open
Grid relay = Closed
Load relay = Closed
N_Line relay = Open
DC relay = Open
Earth relay = Open
--- Energy totals ---
Acc charger (total) = 0.0kWh
Acc discharger (total) = 0.1kWh
Acc grid-buy (total) = 17.0kWh
Acc grid-sell (total) = 0.0kWh
Acc load (total) = 16.0kWh
Acc self-use (total) = 0.1kWh
Acc PV-sell (total) = 0.0kWh
Acc grid-charge (total) = 1.0kWh
--- Battery telemetry ---
Battery power = 16W
Battery current = -3A
---------------
SETTINGS
---------------
Float voltage = 27.2V
Absorb voltage = 28.2V
Battery stop discharging voltage = 23.0V
Battery stop charging voltage = 26.4V
Battery low voltage = 20.4V
Battery high voltage = 30.0V
Charger current = 60.0A
PV18-output-example.log shows the
register map in action.PH1800 PV1800 EP1800 PV3500 EP3500 RS485 Modbus RTU communication
Protocol 1.4.15.xlsx (in the same repo) — the authoritative register map
for the inverter family. Parsed copy in docs/register-map.md.[poll error: ...] to stderr.
The next cycle almost always succeeds.Ctrl-C doesn't clean it up, run on the remote:
pkill -9 -f must_pv1800_proxy.