Python (Raspberry Pi · Linux · macOS · Windows)¶
Mit Python lässt sich der WattWächter TTL plattformübergreifend auslesen – egal ob direkt am Raspberry Pi über dessen GPIO-UART oder an Mac, Linux und Windows über einen einfachen USB-TTL-Adapter.
Die meisten modernen Zähler senden ihre Daten im SML-Format (Smart Message Language) mit 9600 Baud, 8N1. Wir verwenden dafür pyserial zum Einlesen der seriellen Schnittstelle und smllib zum Dekodieren der Telegramme.
So funktioniert die Kommunikation
Die meisten Zähler senden ihr Telegramm von sich aus zyklisch (Push) – bei ihnen genügt zum Auslesen die Empfangsleitung (grün / RXD). Die gelbe TX-Leitung ist nur für die übrigen Zähler nötig, die erst per Befehl zum Senden aufgefordert werden müssen.
Welche Zähler müssen zum Senden aufgefordert werden?
Moderne SML-Zähler (z. B. EMH eHZ/eBZD, EasyMeter Q3A/Q3D, eBZ, Holley DTZ541, Iskra MT 681) senden ohne Anfrage – hier genügt die RX-Leitung. Das obige Beispiel-Skript ist auf diese Zähler ausgelegt.
Eine Anfrage über die TX-Leitung benötigen dagegen vor allem ältere Zähler mit IEC-62056-21-Protokoll (Modus C, „D0"-ASCII). Sie liefern ihr Telegramm erst, nachdem die Anfragesequenz /?!\r\n gesendet wurde. Laut unseren Zähler-Skripten betrifft das diese Modelle:
| Hersteller | Modelle |
|---|---|
| Apator | 12EC3 / 12EC3G |
| Baylan | BM, BT |
| Elster / Honeywell | AS1350, AS1440, AS1500, AS2018, AS3500, T510 |
| EMH | ITZ, LZQJ-XC |
| Iskra | AM550, MT 171, MT 174, MT 382 |
| Itron | ACE3000 Typ 260, ACE6000 |
| Kamstrup | 382 |
| Landis + Gyr | E230, E350, E650, ZMB120, ZMD120, T550 |
| Logarex | LK11BL, LK13BD, LK13BO |
| MetCom | MCS301 |
| PAFAL | 20EC3gr |
| Siemens | TD-3511 |
| ZPA | ZE311, ZE314 |
Zusätzlich benötigen einige M-Bus-Wärme-/Wasserzähler (Engelmann SensoStar, MetCom Ultramess C3, Sensus Pollucom F) ein Weck-Telegramm. Für all diese Zähler reicht das obige reine SML-Skript nicht aus – sie sprechen ein anderes Protokoll und müssen aktiv abgefragt werden.
Voraussetzungen am Zähler
Damit der Zähler die vollständigen Momentanwerte (Leistung, Phasen) sendet, muss am Zähler meist die PIN deaktiviert und der erweiterte Infomodus (Inf → On) aktiviert sein. Die PIN erhältst du bei deinem Netzbetreiber.
Verkabelung¶
Die vier Adern werden 1:1 verbunden (die Kreuzung von RX/TX erfolgt bereits auf der Platine des Lesekopfs).
| Kabel (TTL) | Funktion | Raspberry-Pi-Pin |
|---|---|---|
| Braun | VCC | Pin 1 — 3V3 |
| Grün | RXD (Daten) | Pin 10 — GPIO15 / RXD |
| Gelb | TXD | Pin 8 — GPIO14 / TXD — optional |
| Weiß | GND | Pin 6 — GND |
Adernfarben: braun → VCC, grün → RXD, gelb → TXD (gestrichelt = optional), weiß → GND.
UART am Raspberry Pi freigeben
Den seriellen Port aktivierst du mit sudo raspi-config → Interface Options → Serial Port: Login-Shell über Serial deaktivieren, serielle Hardware aktivieren. Danach neu starten. Die Schnittstelle ist dann unter /dev/serial0 erreichbar.
Für PC, Mac oder Server verbindest du den WattWächter TTL über einen handelsüblichen USB-TTL-Adapter (z. B. CP2102, FT232, CH340).
| Kabel (TTL) | Funktion | USB-TTL-Adapter |
|---|---|---|
| Braun | VCC | 3V3 (oder 5V) |
| Grün | RXD (Daten) | RXD |
| Gelb | TXD | TXD — optional |
| Weiß | GND | GND |
Adernfarben: braun → VCC, grün → RXD, gelb → TXD (gestrichelt = optional), weiß → GND.
Installation¶
pip install pyserial smllib
Beispiel-Skript¶
import serial
from smllib import SmlStreamReader
# --- Serielle Schnittstelle -------------------------------------------------
# Raspberry Pi (GPIO-UART): /dev/serial0
# USB-TTL-Adapter (Linux): /dev/ttyUSB0
# macOS: /dev/tty.usbserial-XXXX
# Windows: COM3
PORT = "/dev/serial0"
# OBIS-Codes, die uns interessieren (Kurzform -> Bezeichnung)
OBIS = {
"1.8.0": "Bezug", # Wh, aus dem Netz bezogen
"2.8.0": "Einspeisung", # Wh, ins Netz eingespeist
"16.7.0": "Leistung", # W, aktuelle Wirkleistung (vorzeichenbehaftet)
}
ser = serial.Serial(PORT, baudrate=9600, bytesize=8,
parity="N", stopbits=1, timeout=2)
stream = SmlStreamReader()
print(f"Lese WattWächter TTL an {PORT} ...")
while True:
data = ser.read(ser.in_waiting or 1)
if not data:
continue
stream.add(data)
frame = stream.get_frame()
if frame is None:
continue # Telegramm noch nicht vollständig
for entry in frame.get_obis():
name = OBIS.get(entry.obis.obis_short)
if name is None:
continue
value = entry.get_value() # bereits skaliert (Wert × 10^Scaler)
if entry.obis.obis_short in ("1.8.0", "2.8.0"):
print(f"{name:12s}: {value / 1000:.3f} kWh")
else:
print(f"{name:12s}: {value:.0f} W")
print("-" * 30)
Beispiel-Ausgabe:
Lese WattWächter TTL an /dev/serial0 ...
Bezug : 12345.678 kWh
Einspeisung : 4321.000 kWh
Leistung : 437 W
------------------------------
Weitere Werte auslesen
Ergänze das OBIS-Dictionary um weitere Codes (z. B. 36.7.0, 56.7.0, 76.7.0 für die Phasenleistungen oder 32.7.0, 52.7.0, 72.7.0 für die Phasenspannungen). entry.get_value() liefert den Wert bereits korrekt skaliert, entry.unit enthält den Einheiten-Code. Eine Übersicht gängiger OBIS-Codes findest du unter Weitere OBIS-Kennzahlen.
Fehlerbehebung¶
| Problem | Mögliche Ursache / Lösung |
|---|---|
Permission denied auf /dev/... |
Benutzer zur Gruppe dialout hinzufügen: sudo usermod -aG dialout $USER, danach neu anmelden |
Keine Daten / frame bleibt None |
Lesekopf nicht korrekt über der IR-Diode positioniert; grünes Kabel (RXD) nicht am RX des Adapters; falscher Port |
| Nur Energiezähler, keine Leistung/Phasen | Am Zähler PIN deaktivieren und Inf → On setzen |
| Am Raspberry Pi kommt nichts an | Serielle Login-Shell deaktiviert? Port /dev/serial0 statt /dev/ttyAMA0 verwenden |
| Werte um Faktor 10/100 falsch | In seltenen Fällen get_obis() durch parse_frame() ersetzen (siehe smllib-Doku) |