Modbus TCP¶
Der WattWächter Plus stellt einen integrierten Modbus TCP Server bereit. Die Messdaten deines Stromzählers sind damit direkt als Modbus-Register abrufbar — geeignet für Home Assistant, evcc, openHAB, ioBroker, SMA, Loxone, SCADA-Systeme und jeden anderen Modbus-TCP-Client.
SunSpec-kompatibel
Der Server implementiert die SunSpec-Modelle 1 (Common Block) und 203 (Three Phase Meter, Wye). Systeme mit SunSpec-Discovery (z.B. Home Assistant, evcc, openHAB, SMA Sunny Home Manager) erkennen den WattWächter dadurch automatisch als Dreiphasen-Zähler. Loxone hat keine SunSpec-Discovery — siehe Integrationen → Loxone.
Voraussetzungen¶
- WattWächter Plus eingerichtet und mit dem WLAN verbunden — falls noch nicht geschehen, folge zunächst der Anleitung unter Erste Schritte
- Firmware 1.1.0 oder neuer
- Modbus-TCP-Client bzw. SunSpec-Client im selben Netzwerk
- Port 502 (Standard) erreichbar zwischen Client und WattWächter
Modbus TCP aktivieren¶
Die Modbus-Schnittstelle ist ab Werk deaktiviert und muss einmalig eingeschaltet werden. Du hast zwei Möglichkeiten:
Über den API-Explorer (empfohlen) — öffne die Web-UI des WattWächters, klicke auf „API-Explorer", wähle den Endpunkt POST /api/v1/settings und sende folgenden Body:
{ "modbus": { "enable": true, "port": 502 } }
Per curl — falls du es lieber von der Kommandozeile erledigst:
curl -X POST http://wattwaechter-XXXXXXXXXXXX.local/api/v1/settings \
-H "Authorization: Bearer WRITE_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"modbus": {
"enable": true,
"port": 502
}
}'
Die Änderung wird sofort übernommen — ein Neustart ist nicht erforderlich. Du kannst den Port anpassen, falls 502 bereits belegt ist.
API-Authentifizierung deaktiviert?
Ist die API-Authentifizierung ausgeschaltet (Werkseinstellung), kannst du den Authorization-Header weglassen. Siehe REST-API.
Verbindungsdaten¶
| Parameter | Wert |
|---|---|
| Protokoll | Modbus TCP |
| Port | 502 (konfigurierbar) |
| Unit ID / Slave ID | 1 |
| Unterstützte Function Codes | 0x03 (Read Holding Registers), 0x04 (Read Input Registers) |
| Max. gleichzeitige Clients | 2 |
| Idle-Timeout | 5 Minuten |
| Byte-Order | Big-Endian (Modbus-Standard) |
| Register-Basis | 40000 (SunSpec-Standard) |
Als Host wird der mDNS-Name (wattwaechter-XXXXXXXXXXXX.local) oder die IP-Adresse des Geräts verwendet. Wie du die IP-Adresse findest, ist in den FAQs beschrieben.
SunSpec Register-Map¶
Der Adressraum startet bei 40000 und enthält drei Blöcke:
| Adresse | Inhalt | Größe |
|---|---|---|
| 40000–40001 | SunSpec-ID "SunS" (0x53756E53) |
2 Register |
| 40002–40069 | Model 1 — Common Block (Hersteller, Modell, Seriennummer, Firmware) | 68 Register |
| 40070–40176 | Model 203 — Three Phase Meter (Wye) | 107 Register |
| 40177–40178 | End-Marker (0xFFFF, 0x0000) | 2 Register |
Nicht befüllte Felder sind mit dem SunSpec-Sentinel 0x8000 (int16) bzw. 0xFFFF (uint16) als not implemented markiert.
Model 1 — Common Block¶
| Feld | Adresse | Typ | Inhalt |
|---|---|---|---|
| ID | 40002 | uint16 | 1 |
| L | 40003 | uint16 | 66 |
| Mn (Manufacturer) | 40004–40019 | string32 | SmartCircuits GmbH |
| Md (Model) | 40020–40035 | string32 | WattWaechter |
| Vr (Version) | 40044–40051 | string16 | Firmware-Version |
| SN (Serial Number) | 40052–40067 | string32 | MAC-Adresse (hex, 12 Zeichen) |
| DA (Device Address) | 40068 | uint16 | 1 |
Model 203 — Three Phase Meter (Wye)¶
Alle Messwerte werden per Skalierungsfaktor (_SF) in ganzzahlige Register umgerechnet. Der tatsächliche Wert ergibt sich aus:
Wert = Rohwert × 10^SF
| Adresse | Beschreibung | Feld | Typ | Einheit | SF | OBIS |
|---|---|---|---|---|---|---|
| 40070 | 203 |
ID | uint16 | — | — | — |
| 40071 | 105 |
L | uint16 | — | — | — |
| 40072 | Summenstrom (berechnet) | A | int16 | A | A_SF | — |
| 40073 | Strom L1 | AphA | int16 | A | A_SF | 1-0:31.7.0 |
| 40074 | Strom L2 | AphB | int16 | A | A_SF | 1-0:51.7.0 |
| 40075 | Strom L3 | AphC | int16 | A | A_SF | 1-0:71.7.0 |
| 40076 | Skalierung Strom (-2 → 0,01 A) |
A_SF | int16 | — | — | — |
| 40077 | Spannung LN (Mittelwert) | PhV | int16 | V | V_SF | — |
| 40078 | Spannung L1 | PhVphA | int16 | V | V_SF | 1-0:32.7.0 |
| 40079 | Spannung L2 | PhVphB | int16 | V | V_SF | 1-0:52.7.0 |
| 40080 | Spannung L3 | PhVphC | int16 | V | V_SF | 1-0:72.7.0 |
| 40085 | Skalierung Spannung (-1 → 0,1 V) |
V_SF | int16 | — | — | — |
| 40086 | Netzfrequenz | Hz | int16 | Hz | Hz_SF | 1-0:14.7.0 |
| 40087 | Skalierung Frequenz (-2 → 0,01 Hz) |
Hz_SF | int16 | — | — | — |
| 40088 | Gesamtwirkleistung | W | int16 | W | W_SF | 1-0:16.7.0 |
| 40089 | Leistung L1 | WphA | int16 | W | W_SF | 1-0:21.7.0 |
| 40090 | Leistung L2 | WphB | int16 | W | W_SF | 1-0:41.7.0 |
| 40091 | Leistung L3 | WphC | int16 | W | W_SF | 1-0:61.7.0 |
| 40092 | Skalierung Leistung (0 → 1 W) |
W_SF | int16 | — | — | — |
| 40108–40109 | Gesamte Einspeisung | TotWhExp | acc32 | Wh | TotWh_SF | 1-0:2.8.0 |
| 40116–40117 | Gesamter Bezug | TotWhImp | acc32 | Wh | TotWh_SF | 1-0:1.8.0 |
| 40124 | Skalierung Energie (0 → 1 Wh) |
TotWh_SF | int16 | — | — | — |
Datentypen
- int16 — 16-Bit signed, 1 Register. Negative Werte bei Einspeisung (Leistung).
- acc32 — 32-Bit unsigned Akkumulator, 2 Register (Highword zuerst).
Welche Register sind verfügbar?¶
Welche Felder tatsächlich befüllt werden, hängt von deinem Stromzähler ab. Liefert er z.B. nur den Summenverbrauch 1-0:1.8.0 und die Momentanleistung 1-0:16.7.0, bleiben die Phasen-Register auf not implemented (0x8000).
Welche Register aktuell gültige Werte enthalten, kannst du direkt über den Status-Endpunkt prüfen — ein GET auf /api/v1/modbus/status liefert für jedes Register ein valid-Flag:
# Nur ungültige (nicht vom Zähler gelieferte) Register anzeigen
curl -s http://wattwaechter-XXXXXXXXXXXX.local/api/v1/modbus/status \
| jq '.registers[] | select(.valid == false)'
Details siehe Status-Endpunkt weiter unten.
Beispielzugriff¶
Gesamtleistung lesen (modpoll)¶
# Register 40088 (W) lesen — Unit ID 1, 1 Register
modpoll -m tcp -a 1 -r 40088 -c 1 -t 3 wattwaechter-XXXXXXXXXXXX.local
Zählerstand Bezug lesen (Python / pymodbus)¶
from pymodbus.client import ModbusTcpClient
client = ModbusTcpClient("wattwaechter-XXXXXXXXXXXX.local", port=502)
client.connect()
# TotWhImp: 2 Register ab 40116 (acc32)
rr = client.read_holding_registers(address=40116, count=2, slave=1)
tot_wh_imp = (rr.registers[0] << 16) | rr.registers[1]
# Skalierungsfaktor (TotWh_SF) aus Register 40124
sf = client.read_holding_registers(address=40124, count=1, slave=1).registers[0]
sf = sf if sf < 0x8000 else sf - 0x10000 # int16
kwh = tot_wh_imp * (10 ** sf) / 1000
print(f"Bezug: {kwh:.3f} kWh")
client.close()
Status-Endpunkt¶
Zum Testen und Debuggen liefert die REST-API eine Live-Übersicht des Modbus-Zustands und der Registerbelegung:
curl http://wattwaechter-XXXXXXXXXXXX.local/api/v1/modbus/status \
-H "Authorization: Bearer READ_TOKEN"
Antwort (gekürzt):
{
"enabled": true,
"running": true,
"port": 502,
"active_connections": 1,
"registers": [
{ "register": 40073, "name": "AphA", "obis": "31.7.0",
"value": 1.23, "unit": "A", "valid": true },
{ "register": 40088, "name": "W", "obis": "16.7.0",
"value": -452.0, "unit": "W", "valid": true },
{ "register": 40116, "name": "TotWhImp", "obis": "1.8.0",
"value": 12345600.0, "unit": "Wh", "valid": true }
]
}
| Feld | Beschreibung |
|---|---|
enabled |
Modbus-Server in den Einstellungen aktiviert |
running |
Server-Task läuft aktuell und lauscht auf dem Port |
active_connections |
Anzahl der aktuell verbundenen Modbus-Clients (max. 2) |
registers[].valid |
true, wenn der Zähler diesen OBIS-Code liefert |
Siehe auch REST-API.
Fehlerbehebung¶
Verbindung wird abgelehnt / Timeout
- Ist Modbus in den Einstellungen aktiviert? Prüfe
/api/v1/modbus/status→enabled: true,running: true. - Verwendest du den richtigen Port? Standard ist 502.
- Ist der Client im selben Netzwerk/Subnetz? In Gast-WLANs ist Client-zu-Client-Kommunikation meist gesperrt.
- Es sind maximal 2 gleichzeitige Verbindungen möglich. Weitere Clients werden abgewiesen, bis eine Verbindung geschlossen wird oder nach 5 min Leerlauf.
Register liefern nur 0x8000 / -32768
Der Wert 0x8000 ist der SunSpec-Sentinel für not implemented. Das bedeutet: dein Zähler liefert diesen OBIS-Code nicht. Prüfe im Dashboard, welche OBIS-Codes tatsächlich ankommen — nur diese werden in die Modbus-Register geschrieben.
Werte erscheinen um Faktor 10 / 100 verschoben
Die SunSpec-Skalierungsfaktoren (A_SF, V_SF, W_SF, Hz_SF, TotWh_SF) müssen bei jeder Abfrage angewendet werden. Beispiel: Strom-Register AphA = 123 mit A_SF = -2 ergibt 1,23 A. Echte SunSpec-Clients (Home Assistant, evcc, openHAB modbus.sunspec, pymodbus mit SunSpec-Parser) rechnen das automatisch um — bei generischen Modbus-Clients (Loxone, modpoll) musst du es selbst tun.
Port 502 bereits belegt
Hast du bereits ein Gerät auf Port 502? Stelle in den Einstellungen einen alternativen Port ein, z.B. 1502:
curl -X POST http://wattwaechter-XXXXXXXXXXXX.local/api/v1/settings \
-H "Authorization: Bearer WRITE_TOKEN" \
-H "Content-Type: application/json" \
-d '{"modbus": {"port": 1502}}'