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 Loxone, ioBroker, OpenHAB, SMA, 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). Dadurch wird der WattWächter von SunSpec-fähigen Systemen (z.B. Loxone Miniserver, SMA Sunny Home Manager) automatisch als Dreiphasen-Zähler erkannt.

Voraussetzungen

  • WattWächter Plus eingerichtet und mit dem WLAN verbunden — falls noch nicht geschehen, folge zunächst der Anleitung unter Erste Schritte
  • Firmware 1.0.9 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 API-Referenz.


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()

Loxone Miniserver

Schritt 1 — Modbus-Server anlegen

In Loxone Config:

  1. Peripheriegerät hinzufügenModbus Server
  2. IP-Adresse des WattWächters und Port 502 eintragen
  3. Slave-ID auf 1 setzen
  4. Abfrageintervall nach Bedarf (z.B. 5–10 s)

Schritt 2 — Register manuell anlegen

Lege für jeden gewünschten Wert einen Modbus-Sensor unter dem Modbus-Server an. Die wichtigsten Register:

Wert Adresse Anzahl Datentyp Skalierung (SF-Register)
Gesamtwirkleistung 40088 1 int16 (signed!) 40092 (W_SF)
Wirkleistung L1 / L2 / L3 40089 / 40090 / 40091 1 int16 40092 (W_SF)
Strom L1 / L2 / L3 40073 / 40074 / 40075 1 int16 40076 (A_SF)
Spannung L1 / L2 / L3 40078 / 40079 / 40080 1 int16 40085 (V_SF)
Frequenz 40086 1 int16 40087 (Hz_SF)
Bezug gesamt 40116 2 uint32 (acc32) 40124 (TotWh_SF)
Einspeisung gesamt 40108 2 uint32 (acc32) 40124 (TotWh_SF)

Für jeden Wert in Loxone:

  • Skalierungsfaktor als eigenen Sensor einlesen und per Formel-/Status-Baustein anwenden: Wert = Rohwert × 10^SF. Die SF-Register sind in der Praxis stabil — du kannst sie auch einmalig auslesen und als Konstante hardcoden, um eine Berechnung zu sparen.
  • Sentinel-Filter: Werte ≤ −32000 sind das SunSpec-0x8000 (not implemented). Filtere sie per Status-Baustein heraus, sonst zeigt Loxone falsche Werte für Felder, die dein Zähler nicht liefert. Welche Felder valide sind, siehst du am Status-Endpunkt.

Schritt 3 — Bezug und Einspeisung trennen

Die Gesamtleistung W (40088) ist vorzeichenbehaftet: positiv bei Bezug, negativ bei Einspeisung. Für den Loxone-Energiemonitor zerlegst du den Wert per Status-Baustein in zwei Größen:

  • Bezug = MAX(W, 0)
  • Einspeisung = MAX(-W, 0)

Die Energie-Zählerstände TotWhImp (Bezug) und TotWhExp (Einspeisung) liegen ohnehin in getrennten Registern — die kannst du direkt verdrahten.

Welche Register liefert mein Zähler?

Der WattWächter füllt W, TotWhImp und TotWhExp immer mit gültigen Werten (bei fehlenden Daten mit 0), damit Loxone das Gerät als gültigen Zähler akzeptiert. Phasenwerte und Frequenz sind nur befüllt, wenn dein Zähler die zugehörigen OBIS-Codes liefert. Eine Live-Übersicht liefert /api/v1/modbus/status.


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 API-Referenz.


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. SunSpec-Clients (Loxone, pymodbus mit SunSpec-Parser) rechnen das automatisch um.

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}}'