EBUS
Dieser Artikel beschreibt die Ankopplung von Heizungssysteme mit EBUS-Interface an FHEM.
Der EBUS ist eine bei vielen Heizungssystemen vorhandene serielle Schnittstelle mit zwei Leitungen. Verwendet werden dabei 2400 Baud als Geschwindigkeit und die beiden Signalpegel
- logisch 1: 15–24 V
- logisch 0: 9–12 V
Eine direkte Ankopplung des EBUS an FHEM via Pegelwandlung und serielle Schnittstelle ist nicht empfehlenswert, weil der EBUS mehrere (bis zu 25) Master-Devices erlaubt, die wiederum mehreren Slave-Devices (bis zu 228) Befehle geben. Erforderlich ist also eine Bus-Arbitration, d.h., jedes Master-Device muss sich mit den anderen über die Benutzung des Bus verständigen. Dieses relativ aufwändige Protokoll würde FHEM eine erhebliche Last aufbürden.
Stattdessen wurde der Weg beschritten, einen separaten Raspberry Pi (Modell B) an den EBUS zu koppeln und darauf die Software ebusd laufen zu lassen. Diese wird dann in einer beliebigen FHEM-Instanz als ECMD-Device definiert und somit per telnet abgefragt.
Achtung: es hat sich als nicht praktikabel erwiesen, den EBUS mit einem Pegelwandler direkt an den seriellen GPIO-Port des Raspberry Pi anzuschließen. Die ständig einlaufenden Synchronisationssignale auf dem EBUS, sowie die interne Verarbeitung der GPIO-Signale im Raspberry führen zu einer intolerablen Latenz, d.h., die EBUS-Signale kommen verspätet bei der Software ebusd an (bis zu 90 Minuten wurden beobachtet). Stattdessen sollte grundsätzlich ein Seriell-USB-Konverter verwendet werden und der Raspberry Pi über einen USB-Port mit dem EBUS verbunden werden.
Interface
Eigenbau
Hierbei wird ein kommerziell für ca. 6,00 € erhältliches USB-Modul mit ein paar Zusatzbauteilen im Wert von ca. 5,00 € versehen. Das Interface hat die folgenden Eigenschaften:
- Verpolungssicherer EBUS-Anschluss
- Galvanische Trennung zwischen EBUS und USB
Die Bauteile sind unkritisch und können durch äquivalente Teile ersetzt werden. Allerdings sollte bei der Zenerdiode auf eine Belastbarkeit mit 1,3 W geachtet werden, und beim USB-Modul darauf, dass dieses einen FTDI-Chip enthält.
Achtung': Der Autor übernimmt keine Haftung für die Anwendung dieser Schaltung. Zur Vermeidung von Schäden durch unsachgemäße Bedienung des Heizungssystems wird ausdrücklich empfohlen, nur die lesende Hälfte des Interfaces nachzubauen und sich auf ein passives Lauschen auf dem EBUS zu beschränken. Dazu einfach den Zweig mit Darlington-Transistor aus der Schaltung nicht einbauen.
Kommerzielles Interface
Ein kommerziell erhältliches Interface EBUS/USB findet man hier
Software
Als Software kommt auf dem Raspberry Pi der ebusd = EBUS-Dämon zum Einsatz (aktuell im Dezember 2014 die Version 0.5). Achtung, diese Software befindet sich aktuell in der Weiterentwicklung, unerwünschte Effekte können nicht ausgeschlossen werden.
Der ebusd wird auf dem Raspberry Pi nach der beiliegenden Anleitung übersetzt und installiert. Wichtigste Bestandteile der Installation sind
- ebusd - das eigentliche Programm zur Kommunikation mit dem EBUS
- ebusctl - ein Programm zur lokalen Ansteuerung des ebusd
- Das Verzeichnis /etc/ebusd => Alle hier liegenden Dateien im CSV(Comma Separated Value)-Format werden beim Start des ebusd eingelesen und als Kommandos für das Heizungsystem verwendet.
- Die Datei /etc/default/ebusd mit Startoptionen für den ebusd.
# Default settings for ebusd. This file is sourced by /bin/sh from # /etc/init.d/ebusd. # Options to pass to ebusd EBUSD_OPTS="--acquiretime 6000 -l All -d /dev/ttyUSB0"
Konfiguration ebusd
Der ebusd wird entweder über ebusctl oder über telnet durch Klartextkommandos abgefragt, die lauten z.B. cyc broad OutTemp.
- In einer CSV-Datei in /etc/ebusd muss festgelegt werden, in welche bzw. aus welcher binären Folge dieses Kommando übersetzt wird. Konkret handelt es sich z.B. bei cyc broad OutTemp um die Außentemperatur, die im Heizungssystem zyklisch als Broadcast gesendet wird und vom ebusd einfach mitgelesen wird.
- Für unterschiedliche Heizungssysteme werden unterschiedliche CSV-Dateien benötigt. Einige dieser Dateien liegen hier vor, allerdings bisher nur für wenige Geräte.
Vorgehensweise bei einem neuen Heizungssystem
- Zunächst das Interface mit dem EBUS verbinden. Ein funktionierendes Interface eines der beiden obigen Typen lässt sich daran erkennen, dass die EBUS-LED ständig schwach flackert - das sind die auf dem Bus einlaufenden Synchronisationssignale (Hexadezimal AA).
- Desweiteren den ebusd starten, und zwar mit den Optionen -f (Läuft im Vordergrund) und -l All (Alle Daten werden geloggt). Diese Optionen produzieren Unmengen an Daten, und zwar immer in Form von Telegrammen der Art
10feb5160301f00d44
Hierin bedeutet
Byte | Bedeutung | hier |
0x10 | Adresse des Senders | Zentrale |
0xfe | Adresse des Empfängers | Alle Geräte |
0xb5 | Herstellercode | Vaillant |
0x16 | Klasse des Kommandos | Broadcast |
0x03 | Anzahl Datenbytes | 3 |
0x01 | Bedeutung des Wertes | OutTemp = Außentemperatur |
0xf0 | LSB des Wertes | 240 |
0x0d | MSB des Wertes | 12 |
0x44 | CRC |
Damit ergibt sich in diesem Falle eine Außentemperatur von (12*256 + 240)/256 = 12,9375 Grad Celsius. In der zugehörigen CSV-Datei wird dem ebusd diese Übersetzung mitgeteilt durch eine Zeile der Form
c;broad;OutTemp;Aussentemperatur;;FE;B516;3;01;1;temp;md;2,3;d2b;1.0;°C;-;-;;;;;;;;;;;;;;;;;;;;;;;;
Diese CSV-Dateien können z.B. mit OpenOffice aus einem Spreadsheet generiert werden, indem dieses als CSV-Datei mit Separator ";" gespeichert wird.
- Bei einem vollkommen unbekannten Heizungssystem sollte man den Hersteller fragen, welche Codes er verwendet
- Für Vaillant- und Wolf- Heizungssysteme liegen in dem oben genannten Verzeichnis ein paar Beispiele vor, die man als Ausgangspunkt nehmen kann.
Konfiguration FHEM
Im nachfolgenden Beispiel wird der EBUS über das Modul ECMD angebunden und mit drei Klassen von Kommandos versehen:
define EBUS ECMD telnet 192.168.0.192:8888 attr EBUS classdefs HK.SOL.class=/opt/fhem/FHEM/ebus_solar.cfg:HK.WW.class=/opt/fhem/FHEM/ebus_ww.cfg:HK.Hz.class=/opt/fhem/FHEM/ebus_hz.cfg attr EBUS room System #-- define HK.Hz ECMDDevice HK.Hz.class attr HK.Hz IODev EBUS attr HK.Hz group heating attr HK.Hz room Verbrauch #-- define HK.WW ECMDDevice HK.WW.class attr HK.WW IODev EBUS attr HK.WW group heating attr HK.WW room Verbrauch #-- define HK.SOL ECMDDevice HK.SOL.class attr HK.SOL IODev EBUS attr HK.SOL group solarGenerator attr HK.SOL room Solaranlage
Diese drei Klassen werden als separate Files mit den in der EBUS-Definition stehenden Dateinamen ebus_hz.cfg, ebus_ww.cfg sowie ebus_solar.cfg angelegt. Alle drei ECMD-Devices werden nun zyklisch (z.B. jede Minute) abgefragt mit
define EBUS.Timer at +*00:01:00 get HK.Hz A.Temp;;get HK.Hz state;;get HK.WW state;;get HK.SOL state attr EBUS.Timer group heatingControl attr EBUS.Timer room Verbrauch attr EBUS.Timer verbose 0
Heizkreis
Die erste Datei definiert die FHEM-Readings für die Abfrage von Außentemperatur und Heizkreis:
# Außentemperatur get A.Temp cmd {"cyc broad temp\n"} get A.Temp expect ".*" get A.Temp postproc { my $rval;\ if(($_ eq "")||($_ eq "no data stored") ){\ $rval = "err";\ }else{\ $rval=sprintf("%5.2f °C",$_);\ }\ $rval; } # Heizkeis HK1 get state cmd {"cyc mv HK1_temp\n"} get state expect ".*" get state postproc { my ($bval,$rval,$tval,$pval,$qval,$sval,$xval,$zval);\ my $hash = $defs{"%NAME"};\ if( ($_ eq "")||($_ eq "no data stored") ){\ $bval = "err";\ $rval = "err";\ $tval = "err";\ $pval = "err";\ $qval = "err";\ $sval = "err";\ $xval = "err";\ $zval = "err";\ }else{\ my @values=split(' ',$_);\ if( $values[0] < 15 ){\ $bval = "err";\ $rval = "err";\ $tval = "err";\ $pval = "err";\ $qval = "err";\ $sval = "err";\ $xval = "err";\ $zval = "err";\ } else { \ $bval = HzBedarf();\ $rval = sprintf("%5.2f °C",$values[0]);\ $tval = sprintf("%5.2f °C",$values[1]);\ if( $values[2] == 0 ){\ $pval = "OFF";\ $qval = "0 0";\ }elsif( $values[2] == 1 ){\ $pval = "ON (HK)";\ $qval = "80 0";\ }elsif( $values[2] == 2 ){\ $pval = "ON (WW)";\ $qval = "0 80";\ }else{\ $pval = "unknown";\ $qval = "err";\ }\ $sval = sprintf("%d",$values[2]);\ $xval = sprintf("%5.2f %5.2f %s %5.2f",\ $values[0],$values[1],$qval,$bval);\ $zval = sprintf("VL.T %5.2f °C, RL.T %5.2f °C, %s",\ $values[0],$values[1],$pval);\ }\ }\ readingsSingleUpdate($hash, "VL.T", $rval, 1);\ readingsSingleUpdate($hash, "RL.T", $tval, 1);\ readingsSingleUpdate($hash, "Pumpe", $pval, 1); \ readingsSingleUpdate($hash, "Pumpe.P", $qval, 1); \ readingsSingleUpdate($hash, "Bedarf", $bval, 1); \ readingsSingleUpdate($hash, "Status", $sval, 1);\ readingsSingleUpdate($hash, "reading", $xval, 1);\ $zval; }
Warmwasserkreis
Die zweite Datei definiert die FHEM-Readings für den Warmwasserkreis:
# Warmwasserkreis get state cmd {"cyc broad getstatus_WW\n"} get state expect ".*" get state postproc { my ($rval,$tval,$bval,$pval,$xval,$zval);\ my $hash = $defs{"%NAME"};\ if( ($_ eq "")||($_ eq "no data stored") ){\ $rval = "err";\ $tval = "err";\ $bval = "err";\ $pval = "err";\ $xval = "err";\ $zval = "err";\ }else{\ my @values=split(' ',$_);\ if( $values[1] < 15 ){\ $rval = "err";\ $tval = "err";\ $bval = "err";\ $pval = "err";\ $xval = "err";\ $zval = "err";\ }else {\ $rval = sprintf("%5.2f °C",$values[0]);\ $tval = sprintf("%5.2f °C",$values[1]);\ $bval = ($values[2] == 80) ? "ON (WW)" : "OFF";\ $pval = ($values[3] == 1) ? "ON" : "OFF";\ $xval = sprintf("%5.2f %5.2f %5.2f %d %d",\ $values[0],0.0,$values[1],$values[2],$values[3]);\ $zval = sprintf("SF1.T %5.2f °C, %s",\ $values[0],$pval);\ }\
Solarkreis
Die dritte Datei definiert die FHEM-Readings für den Solarkreis:
# Solarkreis get state cmd {"cyc broad getstatus_SOL\n"} get state expect ".*" get state postproc { my ($rval,$pval,$qval,$lval,$yval,$xval,$zval);\ my $hash = $defs{"%NAME"};\ if( ($_ eq "")||($_ eq "no data stored") ){\ $rval = "err";\ $pval = "err";\ $qval = "err";\ $lval = "err";\ $yval = "err";\ $xval = "err";\ $zval = "err";\ }else{\ my @values=split(' ',$_);\ $rval = sprintf("%5.2f °C",$values[0]);\ $pval = ($values[1] == 1)?"ON":"OFF";\ $qval = ($values[1] == 1)?65:0;\ $lval = sprintf("%5.2f %%",$values[2]);\ $yval = sprintf("%d",$values[3]);\ $xval = sprintf("%5.2f %d %5.2f %d",\ $values[0],$qval,$values[2],$values[3]);\ $zval = sprintf("Coll.T %5.2f °C, %s",\ $values[0],$pval);\ }\ readingsSingleUpdate($hash, "Coll.T", $rval, 1);\ readingsSingleUpdate($hash, "Pumpe", $pval, 1);\ readingsSingleUpdate($hash, "Pumpe.P", $qval." W", 1);\ readingsSingleUpdate($hash, "Load", $lval, 1);\ readingsSingleUpdate($hash, "Yield", $yval, 1);\ readingsSingleUpdate($hash, "reading", $xval, 1);\ $zval; }
Watchdog
Überwachung in FHEM
In FHEM kann man ein Notify einrichten, um bei einem Verbindungsabbruch mehrere Versuch zum Reconnect durchzuführen
define EBUS.N notify (EBUS.*DISCONNECTED.*)|(HK.Hz:A.Temp.*err) { EBUSrecover("notify EBUS.N",0)} attr EBUS.N group deviceDetector attr EBUS.N room Alarm
Das dabei aufgerufene Perl-Programm besteht nur aus wenigen Zeilen:
sub EBUSrecover($$) { my ($evt,$num) = @_; Log 1,"[EBUS] Recover triggered from $evt, attempt No. $num"; if(Value("EBUS") ne "opened"){ if( $num < 7){ $num++; fhem("set EBUS reopen"); fhem("define EBUSrecoverdly at +00:00:05 {EBUSrecover('EBUSrecover',$num)}"); }else{ fhem("set Device.warn EBUS") } } }
Überwachung auf dem Raspberry Pi
Es empfiehlt sich auch, den ebusd kontinuierlich zu überwachen und ggf. neu zu starten. Mit der nachfolgenden Beschreibung wird alle 5 Sekunden die Existenz des Programms abgefragt und dieses bei einem versagenden Test neu gestartet.
Dazu wird auf dem Raspberry nach überall erhältlichen Anleitungen der Watchdog Timer installiert und die Datei /etc/watchdog.conf mit den beiden Parametern
test-timeout = 10 interval = 5
besetzt. Ferner wird die Datei /etc/watchdog.d/ebusd angelegt mit dem Inhalt
#!/bin/sh # description: watchdog helper file for ebusd case "$1" in 'test') #--- Test for ebusd if [ -s /var/run/ebusd.pid ] ; then RUN=`ps -ef | grep ebusd.*USB0 | grep -v grep` if [ "$RUN" != "" ] ; then #echo "ebusd is already running" exit 0 else echo "ebusd defunct at "`date` exit 1 fi else echo "ebusd not running, return 1 at "`date` exit 1 fi ;; 'repair') #-- Restarting ebusd echo "ebusd restarting at "`date` /etc/init.d/ebusd start RETVAL=$? exit 0 ;; *) exit 0 ;; esac