Auf unserem Haus ist eine Solaranlage installiert, die zur Warmwassererzeugung und zur Heizungsunterstützung dient. Leider haben die Klempner, die die Anlage damals installiert haben, bei der Montage der Anlage nicht besonders viel Wert auf eine vernünftige Verkabelung und sichere Montage des Solarreglers gelegt. Ursprünglich war der Regler direkt mit Kabelbindern an den isolierten Solarrohren befestigt und die Kabel überhaupt nicht zugentlastet. Entsprechend hatte nach ein paar Jahren der erste Regler Wackelkontakte auf der Hauptplatine durch die mechanische Belastung. Den Ersatzregler habe ich dann etwas besser montiert. Er ist auf eine zusätzliche Montageplatte geschraubt und die Kabel wurden zusätzlich über die Plate zugentlastet. So richtig zufrieden bin ich mit der Lösung auch noch nicht, aber leider ist die ganze Anlage so verbaut, dass es kaum andere Möglichkeiten gibt. Ansonsten müsste ich die gesamte Verkabelung der Anlage verlängern und umlegen.
Leider ist diese Lösung auch immer noch nicht technisch perfekt. Es kommt immer noch von Zeit zu Zeit vor, dass eines der Sensorkabel sich löst oder einen Wackelkontakt hat. Entsprechend misst die Anlage dann falsche Temperaturen und steuert die Solarpumpe und das Solarventil falsch an.
Im Sommer 2017 trat dann mal wieder eine Wackelkontakt auf, den ich aber erst zufällig bemerkt habe, als auch abends nach Sonnenuntergang die Solarpumpe noch lief. So was ist natürlich sehr ärgerlich, weil so die schöne Solarwärme nachts einfach wieder auf das Dach gepumpt wird und außerdem die Solarpumpe noch zusätzlich Strom verbrät. 😡 Um solche Fehler in Zukunft zu vermeiden, wollte ich das Monitoring an der Anlage verbessern. Zum Glück hat der neue Solarregler, ein Resol DeltaSol BS Plus , eine Busschnittstelle, die nur noch angezapft und ausgelesen werden muss…
Das Ziel
Der Solarregler DeltaSol BS Plus Regler soll mit einem Beaglebone ausgelesen werden, die Daten in einer Datenbank mitgeschrieben und grafisch aufbereitet werden. Dazu sollen die Daten des Solarreglers alle 5 Minuten in eine Round Robin Datenbank mit RRDTools geschrieben werden. Aus der Datenbank sollen dann entsprechend Diagramme erzeugt werden, die über den Webserver ausgeliefert werden.
Die Schnittstelle
Der DeltaSol BS Plus hat eine von Resol selbst definierte halb-duplex Schnittstelle, die sich VBus nennt. Bei meinem Regler ist dieser Bus als 2.Leiter Stromschnittstelle ausgeführt. Default-mäßig liegt dort eine Spannung von max. 8.2V mit 35mA an. Während einer Datenübertragung zieht jetzt ein Transistor im Regler den Bus bei einer “0” gegen GND. Zur Übertragung der “1” liegt die normale Betriebsspannung an. Die Übertragung selbst erfolgt normalerweise mit 9600 Baud mit 8 Datenbit, 1 Startbit und einem Stopbit.
Die vollständige Definition der Schittstelle und Parameter findet man z.B. unter VBus Spezifikation auf Github. Die Spezifikation dort wird übrigens direkt von Resol Mitarbeitern zur Verfügung gestellt, was ich persönlich sehr lobenswert finde! 👍
Um diese Schittstelle z.B. mit der seriellen Schnittstelle eines Beaglebone mitzulesen gibt es auf der Seite oben auch diverse Beispiel Schaltungen, die nicht besonders kompliziert aufgebaut sind. Allerdings lässt sich der Aufwand, wenn man nur einen Slave zum Auslesen an die Schnittstelle hängen möchte, sehr stark vereinfachen. Eigentlich reichen ein Optokoppler und zwei Widerstände. Wenn der Anschluss der Bus-Leitungen die Polung nicht beachtet werden soll, dann sind noch zusätzlich 4 Dioden als Gleichrichter notwendig.
Hier die vollständige Schaltung für den BaegleBone-Black. Als UART wird dort der UART 2 verwendet.
Zur Funktionsweise: Solange eine “1” auf dem Bus anliegt, schaltet der Optokoppler die 3.3V auf den Eingang des Beaglebone durch. Liegt eine “0” auf dem Bus an sperrt auch der Transistor im Optokoppler und über den Widerstand R1 wird das Eingangssignal auf GND gezogen.
Freigabe UART2 beim Beaglebone
Bevor der UART2 des Beaglebones benutzt werden kann muss der Eingangspin an Stecker P9 Pin 22 entsprechend konfiguriert werden.
sudo config-pin p9.22 uart
Diese Einstellung ist aber nur aktiv bis der Beaglebone das nächste Mal gebootet wird. Wie die Konfiguration automatisch beim Booten erfolgt, kann dem Beaglebone CANBus Artikel entnommen werden.
Dekodierung des Protokolls
Die Dekodierung des Protokolls erfolgt in einem Python Script. Dazu müssen erst mal das RRDTool und die Python-Lib dafür installiert werden:
sudo apt-get install rrdtool python-rrdtool
Außerdem mus das Python Serial Interface installiert werden: sudo pip install pyserial
Ein Verzeichnis unter “/home/debian/solarregler” anlegen mit: mkdir /home/debian/solarregler"
Nun das Script anlegen mit: nano /home/debian/solarregler/resol_logger.py
#!/usr/bin/python# Script for logging RESOL DeltaSol BS Plus to a RRDTOOL-Databank# Thomas Wedemeyer 2017# www.thomas-wedemeyer.de# Remark: My system is using the System schemata 9 -> WMZ is not testetimportserialimportsysimportrrdtoolimportostemperatur_list=[0.0forxinrange(4)]drehzahl1=0drehzahl2=0relmask=0errormask=0relTime1=0relTime2=0#wmz = 0 # remove comment tag for WMZ-Supportdefvbus_decode(rxd_list,byte_counter):globaltemperatur_list,drehzahl1,drehzahl2,relmask,errormask,relTime1,relTime2#,wmz#Check commandifrxd_list[5]==0x10:if((rxd_list[1]!=0x10)|(rxd_list[2]!=0x0)):returnFalsesystem=((rxd_list[4]<<8)|rxd_list[3])print"Systemcode: ",hex(system)datasets=rxd_list[8]#get the number of datasetsprint"Dataset=",datasets#copy the sepetts back to the msb of the bytes forxinrange(0,datasets-1,1):forbcntinrange(0,4,1):if(rxd_list[14+x*6]&(1<<bcnt))>0:rxd_list[10+bcnt+x*6]=rxd_list[10+bcnt+x*6]|0x80#crc is only for whiny people... so no crc checking here#stip protocol information of the sepetts & crcprotocolOffset=0foriinrange(0,datasets-1,1):forbytecntinrange(0,4,1):rxd_list[bytecnt+4*i]=rxd_list[10+bytecnt+4*i+protocolOffset]protocolOffset=protocolOffset+2ifsystem==0x4221:#DeltaSol BS Plus V1ifdatasets!=7:returnFalse#decoding of the message block according to VBusSpecificationResol.xml for Resol DeltaSol BS plustemperatur_list[0]=((rxd_list[1]<<8)|rxd_list[0])/10.0print"Temperatur 0 =",temperatur_list[0]temperatur_list[1]=((rxd_list[3]<<8)|rxd_list[2])/10.0print"Temperatur 1 =",temperatur_list[1]temperatur_list[2]=((rxd_list[5]<<8)|rxd_list[4])/10.0print"Temperatur 2 =",temperatur_list[2]temperatur_list[3]=((rxd_list[7]<<8)|rxd_list[6])/10.0print"Temperatur 3 =",temperatur_list[3]forxinrange(0,4,1):# convert to signed temperaturiftemperatur_list[x]>3276.0:temperatur_list[x]=temperatur_list[x]-6553.4drehzahl1=rxd_list[8]print"Drehzahl 1=",drehzahl1drehzahl2=rxd_list[9]print"Drehzahl 2=",drehzahl2relmask=rxd_list[10]print"Relmask=",hex(relmask)errormask=rxd_list[11]print"Errormask=",hex(errormask)time=((rxd_list[13]<<8)|rxd_list[12])print"Time =",time/60,":",time%60schema=rxd_list[14]print"Schema=",schemarelTime1=((rxd_list[17]<<8)|rxd_list[16])print"RelTime1 =",relTime1relTime2=((rxd_list[19]<<8)|rxd_list[18])print"RelTime2 =",relTime2#wmz= ((rxd_list[21]<<8) | rxd_list[20]) + (((rxd_list[23]<<8) | rxd_list[22])*1000) + (((rxd_list[25]<<8) | rxd_list[24])*1000000)#print "WMZ = ",wmzreturnTrueelifsystem==0x427b:# DeltaSol BS Plus V2ifdatasets!=9:returnFalse#decoding of the message block according to VBusSpecificationResol.xml for Resol DeltaSol BS Plustemperatur_list[0]=((rxd_list[1]<<8)|rxd_list[0])/10.0print"Temperatur 0 =",temperatur_list[0]temperatur_list[1]=((rxd_list[3]<<8)|rxd_list[2])/10.0print"Temperatur 1 =",temperatur_list[1]temperatur_list[2]=((rxd_list[5]<<8)|rxd_list[4])/10.0print"Temperatur 2 =",temperatur_list[2]temperatur_list[3]=((rxd_list[7]<<8)|rxd_list[6])/10.0print"Temperatur 3 =",temperatur_list[3]forxinrange(0,4,1):# convert to signed temperaturiftemperatur_list[x]>3276.0:temperatur_list[x]=temperatur_list[x]-6553.4drehzahl1=rxd_list[8]print"Drehzahl 1=",drehzahl1drehzahl2=rxd_list[12]print"Drehzahl 2=",drehzahl2relmask=rxd_list[10]print"Relmask=",hex(relmask)errormask=((rxd_list[21]<<8)|rxd_list[20])print"Errormask=",hex(errormask)time=((rxd_list[23]<<8)|rxd_list[22])print"Time =",time/60,":",time%60schema=rxd_list[17]print"Schema=",schemarelTime1=((rxd_list[11]<<8)|rxd_list[10])print"RelTime1 =",relTime1relTime2=((rxd_list[15]<<8)|rxd_list[14])print"RelTime2 =",relTime2#wmz= ((rxd_list[31]<<24) | (rxd_list[30]<<16) |(rxd_list[29]<<8) | rxd_list[28])*1000) + (((rxd_list[25]<<8) | rxd_list[24])*1000000)#print "WMZ = ",wmzreturnTrueelse:print"System not supported"returnTrueelse:returnFalsedefcheck_database():ifos.path.isfile("%s/resol_databank.rrd"%(os.path.dirname(os.path.abspath(__file__))))==False:print"Database not found -> Create Database"rrdtool.create("%s/resol_databank.rrd"%(os.path.dirname(os.path.abspath(__file__))),"--start","now","--step","300",# 300sec -> 5Min"--no-overwrite",# don't overwrite old database"DS:Temp_Collector:GAUGE:600:-25:150",# 600 sek -> 10Min Haertbeat, Min:-25, Max 150"DS:Temp_TankBottom:GAUGE:600:-25:150","DS:Temp_TankTop:GAUGE:600:-25:150","DS:Temp_Recirculation:GAUGE:600:-25:150","DS:PumpSpeed:GAUGE:600:0:100",# 600 Sec Heartbeat, Min:0, Max: 100%"DS:Valve:GAUGE:600:0:100","DS:Error:GAUGE:600:0:255","DS:PumpActiv:COUNTER:600:0:99999","DS:ValveActiv:COUNTER:600:0:99999",#DS:Wmz:COUNTER:600:0:99999999"RRA:AVERAGE:0.5:1:2016",# Average 1 point, 2016 points = 7 days / 5min interval"RRA:AVERAGE:0.5:12:8760",# Average 12 points (1hour), 8760 Points = 1 Year (24*365)"RRA:MIN:0.5:288:3650",#Min 288 points (1Day), 3650 Points = 10 Years"RRA:MAX:0.5:288:3650","RRA:LAST:0.5:288:3600")else:print"Database allready exits"defupdate_database():globaltemperatur_list,drehzahl1,drehzahl2,relmask,errormask,relTime1,relTime2# build data pointdata='N:'foriinrange(4):data+=str(temperatur_list[i])data+=':'data+=str(drehzahl1)data+=':'data+=str(drehzahl2)data+=':'data+=str(errormask)data+=':'data+=str(relTime1)data+=':'data+=str(relTime2)#data +=':'#data += str(wmz)printdata# insert data into round-robin-databaserrdtool.update("%s/resol_databank.rrd"%(os.path.dirname(os.path.abspath(__file__))),data)defmain():check_database()ser=serial.Serial(port="/dev/ttyO2",baudrate=9600)ser.close()ser.open()rxd_list=[0forxinrange(70)]#define a buffer for the receivercounter=0ifser.isOpen():print"Serial is open!"try:whileTrue:value=0data=ser.read()value=ord(data)#print "Value: " , hex(value) ifvalue==0xaa:#print "New Frame"if(counter>0):ifvbus_decode(rxd_list,counter)==True:update_database()ser.close()exit()counter=0rxd_list[counter]=valueif(counter<59):counter+=1exceptKeyboardInterrupt:ser.close()if__name__=="__main__":main()
Was macht das Script: Das Script öffnet die TTY2 (UART2), wartet auf eine vollständige Nachricht, dekodiert sie und schreibt sie dann in die RRD-Datenbank. Wenn beim ersten Starten des Programms noch keine RRD-Datenbank angelegt ist, wird diese automatisch unter dem Namen “resol_databank.rrd” angelegt. Wenn man das Script manuell startet, werden außerdem die aktuellen Daten auf der Console ausgegeben.
Die Dekoder-Funktion ist sehr einfach zusammen gebastelt und unterstützt nur die Anlagen Resol DeltaSol BS Plus V1 und V2! Für andere Resol-Anlagen muss die Dekodierungsfunktion angepasst werden…
Bevor das Script gestartet werden kann muss es ausführbar gemacht werden mit: chmod a+x resol_logger.py
Um das Skript alle 5 Minuten automatisch zu starten wird der Cron Dämon benutzt. Um dort einen neuen Job einzutragen fügt man nach dem Aufruf von crontab -e die folgende Zeile hinzu: */5 * * * * /home/debian/solarregler/resol_logger.py
Ausgabe der Temperaturkurven
Um die Auswertungsgrafiken automatisch zu erzeugen, werden zwei Bash-Scripte angelegt, die dann wieder um von Cron alle 5 Minuten bzw. einmal am Tag gestart werden.
Um die Skripte automatisch zu starten werden mit crontab -e die folgende Zeile hinzugefügt: */5 * * * * /home/debian/solarregler/solarlog_day.sh 0 0 * * * /home/debian/solarregler/solarlog_wmy.sh
Nun muss das HTML-Verzeichnis ausführbar gemacht werden mit sudo chmod a+w /var/www/html
Mit nano durch die “index.html” anlegen: nano /var/www/html/index.html