A Victron Venus OS plugin that bridges a Monarch BMS over Modbus TCP to the Victron DBus battery interface. The battery appears as a managed battery in the GX UI and supports DVCC (charge control).
| Item | Value |
|---|---|
| DBus service | com.victronenergy.battery.monarch |
| Product ID | 0xB090 |
| Protocol | Modbus TCP (read-only) |
| Update interval | 2 seconds |
| Package name | VenusOS-Monarch-BMS |
The service runs as a runit daemon, polls the BMS via Modbus TCP, and publishes values to DBus. Venus treats it like a CAN-bus battery. Settings (IP, port, unit ID) are stored in com.victronenergy.settings and survive reboots.
┌─────────────────┐ Modbus TCP ┌──────────────┐ DBus ┌─────────────────┐
│ Monarch BMS │ ◄────────────────── │ Service │ ────────────► │ Venus GX UI │
│ (Modbus slave) │ read input regs │ (Python) │ battery.* │ DVCC, gauges │
└─────────────────┘ └──────────────┘ └─────────────────┘
│
▼
┌─────────────────┐
│ Settings │
│ (IP, port, ID) │
└─────────────────┘
- Read-only: No Modbus writes; only input registers are read.
- Chunked reads: Registers are read in small chunks (10 at a time) with a fresh connection per chunk. The Monarch BMS may close the connection after each response; chunked reads with reconnect can succeed where a full block read fails.
- Per-field mapping: Each DBus path has its own Modbus register(s).
- Plausibility checks: Voltage, current, SOC, temperature are validated before publishing.
| DBus Path | Modbus Reg (1-based) | Type | Registers | Description |
|---|---|---|---|---|
/Serial |
1 | uint32 | 2 | BMS serial number |
/HardwareVersion |
5 | uint32 | 2 | Hardware version |
/FirmwareVersion |
7 | uint16 | 1 | Firmware version |
/System/NrOfCellsPerBattery |
9 | uint16 | 1 | Number of cells |
/Dc/0/Voltage |
13 | float32 | 2 | Pack voltage (V) |
/Dc/0/Current |
15 | float32 | 2 | Current (A, + charge, − discharge) |
/Soc |
17 | float32 | 2 | State of charge (%) |
/Info/MaxChargeCurrent |
19 | float32 | 2 | Charge current limit (A) |
/Info/MaxDischargeCurrent |
21 | float32 | 2 | Discharge current limit (A) |
/Info/MaxChargeVoltage |
23 | float32 | 2 | Max charge voltage (V) |
/Info/BatteryLowVoltage |
25 | float32 | 2 | Low voltage cutoff (V) |
/Info/ChargeRequest |
27 | uint16 | 1 | Charge request (0/1) |
/Dc/0/Temperature |
29 | float32 | 2 | Pack temperature (°C) |
/TimeToGo |
31 | float32 | 2 | Time to empty/full (min) |
/Alarms/LowVoltage |
33 | uint16 | 1 | 0=OK, 1=Warning, 2=Alarm |
/Alarms/HighVoltage |
34 | uint16 | 1 | 0=OK, 1=Warning, 2=Alarm |
/Alarms/LowSoc |
35 | uint16 | 1 | 0=OK, 1=Warning, 2=Alarm |
/Alarms/HighTemperature |
36 | uint16 | 1 | 0=OK, 1=Warning, 2=Alarm |
/Alarms/LowTemperature |
37 | uint16 | 1 | 0=OK, 1=Warning, 2=Alarm |
/System/Switch |
38 | uint16 | 1 | System switch (0=off, 1=on) |
/Soh |
39 | float32 | 2 | State of health (%) |
/Info/InstalledCapacity |
41 | float32 | 2 | Installed capacity (Ah) |
/Info/AvailableCapacity |
43 | float32 | 2 | Available capacity (Ah) |
/Alarms/LowChargeTemperature |
45 | uint16 | 1 | 0=OK, 1=Warning, 2=Alarm |
/Alarms/HighChargeTemperature |
46 | uint16 | 1 | 0=OK, 1=Warning, 2=Alarm |
/Alarms/CellImbalance |
47 | uint16 | 1 | 0=OK, 1=Warning, 2=Alarm |
/Alarms/InternalFailure |
48 | uint16 | 1 | 0=OK, 1=Warning, 2=Alarm |
- Function: Read Input Registers (FC 0x04)
- Byte order: Big-endian for float32 and uint32
- Register numbering: 1-based (e.g. reg 1 = first input register)
- Float32: Two consecutive 16-bit registers, high word first
Address adjustment: Edit REGISTER_MAP in venusos_monarch_bms_service.py to match your Monarch BMS protocol. Registers 38–48 (system switch, SOH, capacities, extra alarms) may need adjustment per your BMS documentation.
| DBus Path | Source |
|---|---|
/Dc/0/Power |
Calculated: Voltage × Current |
/Capacity |
From /Info/AvailableCapacity or /Info/InstalledCapacity |
/Alarms/State |
Max of all alarm levels |
/Alarms/Active |
Comma-separated list of active alarm names |
/Connected |
1 when Modbus read succeeds |
/Status |
Service state: "Running", "Disabled", "Connection failed", etc. |
/Status/LastError |
Last error message |
/Status/LastUpdateTs |
Unix timestamp of last successful update |
/Io/AllowToCharge |
From /Info/ChargeRequest |
/Io/AllowToDischarge |
Always 1 when connected |
| Path | Type | Default | Description |
|---|---|---|---|
com.victronenergy.settings/Settings/VenusOsMonarchBms/IpAddress |
string | 192.168.0.20 |
BMS IP address |
com.victronenergy.settings/Settings/VenusOsMonarchBms/Port |
int | 502 | Modbus TCP port |
com.victronenergy.settings/Settings/VenusOsMonarchBms/UnitId |
int | 154 | Modbus slave/unit ID |
com.victronenergy.settings/Settings/VenusOsMonarchBms/Enabled |
int | 1 | 0=off, 1=on |
com.victronenergy.settings/Settings/VenusOsMonarchBms/DeviceInstance |
int | 41 | Venus device instance |
- Victron Cerbo GX or compatible Venus device
- SetupHelper installed (Package Manager)
- Monarch BMS on the same network with Modbus TCP enabled
- Settings → Package Manager → Inactive packages → New
- Enter:
- Package name:
VenusOS-Monarch-BMS - GitHub user:
kyros32(or your fork) - Branch/tag:
main
- Package name:
- Save → Download → Install
rm -rf /data/VenusOS-Monarch-BMS
mkdir -p /data/VenusOS-Monarch-BMS
wget -O - https://github.com/kyros32/VenusOS-Monarch-BMS/archive/refs/heads/main.tar.gz | tar -xzf - -C /data/VenusOS-Monarch-BMS --strip-components=1
chmod +x /data/VenusOS-Monarch-BMS/setup
chmod +x /data/VenusOS-Monarch-BMS/service/run
bash -x /data/VenusOS-Monarch-BMS/setup install- Settings → Monarch BMS (or via Settings menu)
- Set IP to the BMS IP address
- Set Port (default 502)
- Set Unit ID (default 154)
- Enable the service
VenusOS-Monarch-BMS/
├── README.md
├── setup
├── version
├── venusos_monarch_bms_service.py
├── service/
│ └── run
└── qml/
└── PageMonarchBms.qml
| File | Purpose |
|---|---|
setup |
SetupHelper install/uninstall script; patches QML menu |
venusos_monarch_bms_service.py |
Main daemon; contains REGISTER_MAP |
service/run |
Runit entrypoint |
qml/PageMonarchBms.qml |
Settings and status page |
Settings → Monarch BMS (PageMonarchBms): Monarch-specific configuration only.
- Enabled, IP address, Port, Unit ID
- Status, Connected, Last Error, Connection (Modbus TCP IP:port unit)
Devices → Monarch BMS (standard Victron battery UI): When you tap the Monarch battery in Devices, Venus uses the standard battery page with:
- Switch (Off/Standby/On from System/Switch), State (Running/Error)
- Battery row (V, A, W), SOC, SOH, Temperature, Time-to-go
- Parameters (CVL, CCL, DCL), Details (BatteryDetails: installed/available capacity, connection info; cell voltages/temps if available)
- Alarms (MbItemAlarm), Device (Serial, HW, FW)
The service publishes Victron-compatible paths (/Mode, /State, /Mgmt/Connection, BatteryDetails paths) so the standard UI works.
- Check runit:
sv status dbus-monarch-bms(orVenusOS-Monarch-BMSdepending on SetupHelper) - Logs:
tail -f /var/log/dbus-monarch-bms/currentor/service/dbus-monarch-bMS/log/main/current
- Venus (einstein) does not provide
MbItemEditorMbItemNumeric. IP usesMbEditBoxIp, Port and Unit ID useMbSpinBox(all editable).
- Verify BMS IP, port, and unit ID
- Check network:
ping <BMS_IP> - Run manually:
cd /data/VenusOS-Monarch-BMS && /usr/bin/python3 venusos_monarch_bms_service.py - The service uses chunked reads (10 registers per request) with reconnect between chunks; it tries input registers first, then holding registers. If it still fails, try reducing
REGISTER_CHUNK_SIZEinvenusos_monarch_bms_service.py(e.g. to 5).
- Adjust
REGISTER_MAPinvenusos_monarch_bms_service.pyto match your Monarch BMS protocol. Registers 38–48 (system switch, SOH, capacities, extra alarms) may need adjustment.
dbus-spyto inspectcom.victronenergy.battery.monarch- Ensure service is enabled and BMS is reachable
cd /data/VenusOS-Monarch-BMS
/usr/bin/python3 venusos_monarch_bms_service.pyStop with Ctrl+C.
- v1.2.0 – Nr. of cells, installed/available capacity, SOH, system switch; units (V, A, W); alarms: Low/High charge temp, Cell imbalance, Internal failure; extended register map (48 registers)
- v1.1.3 – Block read for all Modbus registers; fixes "Connection unexpectedly closed" when BMS closes after each request
- v1.1.2 – Editable IP (MbEditBoxIp), Port and Unit ID (MbSpinBox)
- v1.1.0 – Full register map (temp, alarms, TimeToGo, cells), compact QML, detailed README