Zum Inhalt

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/statusenabled: 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}}'