Amazon Kindle als Statusdisplay

startseite_intranet
Webseite – Übersicht

Die Anzeige aller im Haus gesammelter Daten über eine Webseite ist nett (siehe Screenshot), benötigt aber einen Rechner, Handy, Tablet etc. wo diese Seite im Browser anzeigt wird. Unschön dabei ist, das wenn man schnell die Außentemperatur wissen will, immer erst das Gerät anschalten oder aufwecken muss, einen Webbrowser öffnen muss, die Webseite aufruft und wartet bis die Seite geöffnet wird. Das ist für einen kurzen Blick auf die Temperatur eher unpraktisch.

Es musste eine Anzeigemöglichkeit für die wichtigsten Daten her, welche immer angeschaltet ist. Nach kurzer Recherche im Internet stellte sich ein Amazon Kindle als Statusdisplay aufgrund des E-Paper-Displays als stromsparender Anwärter auf diesen Job heraus. Also schnell einen Kindle 4 (kein Touch) im Internet für ~ 30€ besorgt (da ich bisher keinen besaß).

Lösungsansätze

Es gibt verschiedene Ansätze den Kindle als Statusdisplay (z.B. für Wetterinformationen) zu nutzen. Es gibt Lösungen wie hier beschrieben die den Browser des Kindle nutzen. Besser gefielen mir aber die Lösungen die den Bildschirmschoner des Kindle zur Anzeige eines Statusbildes nutzen. Das macht unter anderem die Lösung für FHEM, als auch die Lösung von Matthew Petroff (von dem die FHEM-Lösung wahrscheinlich auch abgeleitet ist). Basierend auf dieser Lösung ist ausserdem diese hier mit weiterführenden Erklärungen der nötigen Schritte.
Ich bevorzuge anstelle der FHEM-Lösung die ursprüngliche Version, da ich nicht nur Daten aus FHEM anzeigen will, sondern auch Daten aus meiner MySQL-Datenbank und zusätzliche Informationen die direkt aus dem Internet stammen.

Den Kindle habe ich übrigens mittels „Kindersicherung“ der FritzBox vom Internet gekappt falls Amazon auf komische Gedanken kommt und mir mit einem gutgemeinten Online-Update irgendwas zerschießt.

Die Lösung, den Bildschirmschoner des Kindle mit einem im Netzwerk bereitgestelltem Bild zu nutzen, benötigt verschiedene Anpassungen des Kindle als auch einige Komponenten die auf einem Server bereit gestellt werden müssen (bei mir der Raspberry PI).

Beschreibung

Die Beschreibung der nötigen Schritte und die benötigte Software sind hier (ursprüngliche Idee), hier (erweiterte Anleitung) und hier (ausführliche Anleitung) erklärt. Aus diesem Grund liste ich die nötigen Arbeitsschritte hier nur Stichpunktartig auf:

Kindle:

    • Jailbreak des Kindle damit man damit auch anständig arbeiten kann
    • SSH für Kindle (USBNetwork Hacks)
    • Kite installieren um nach dem Starten des Kindle das Init-Script (init_daten.sh) ausführen zu lassen
      • Stoppen von powerd
        • das ist nötig, da im Sleep-Mode der Cronjob nicht ausgeführt wird
        • („@reboot“ in crontab funktioniert leider auf dem Kindle nicht)
      • Stoppen von framework
    • Cronjob einrichten um alle x Minuten/Stunden ein aktualisiertes Bild per wget zu laden
      • vi /etc/crontab/root
      • */10 5-22 * * * /mnt/us/anzeige/zeige_daten.sh (alle 10 Minuten zwischen 5 und 22 Uhr

Server:

      • Python-Script anpassen
        • Das Python-Script sammelt die Daten, ersetzt die Platzhalter im SVG und erstellt daraus das PNG mittels rsvg-convert und pngcrush
      • Cronjob zur Ausführung des Python-Script anlegen
        • */3 * * * * /var/www/kindle/daten-script.sh &> /dev/null
      • SVG-Template anpassen (mit Editor und/oder Inkscape)
        • hier mein SVG für die Datenanzeige und
        • hier für zwischen 23 und 5 Uhr
        • bei Änderungen mit Inkscape nicht im „Inkscape-Format“ abspeichern sondern als „Normales SVG“
      • rsvg-convert installieren (sudo apt-get install librsvg2-bin)
      • pngcrush installieren (sudo apt-get install pngcrush)

(Achtung! Die Dateinamen der Scripte in den Cron-Beispielen und die Dateinamen der SVG´s sind anders als in den oben verlinkten Anleitungen)

Python-Script

Das ursprüngliche Python-Script holt das Wetter von einer Webseite aus den USA. Das Wetter dort hat mich nicht wirklich interessiert und so habe ich das Script etwas angepaßt und verschiedene Wetterdaten und Statusinformationen aus meinen eigenen Quellen (FHEM und MySQL) gezogen.
Zur Anzeige der Wettericons nutze ich teilweise die Bilder aus dem ursprünglichen SVG. Einige habe ich allerdings gelöscht da ich hier z.B. keinen Blizzard erwarte. Die Werte für den Luftdruck und die Windstärke sowie die Auswahl des aktuellen Wetter-Icon erfolgt über die „condition“ des FHEM-Yahoo-Wetter-Moduls.

Mein Pythonscript sieht wie folgt aus:

Bitte das Update im nachfolgendem Artikel beachten!!

#!/usr/bin/python2

import codecs
import telnetlib
import MySQLdb
from datetime import datetime, time

################
# Hole FHEM data
################
tnet_host= "localhost"
tnet_port= 7072
def fhem_task(fcmd):
    tc= telnetlib.Telnet(tnet_host,tnet_port)
    tc.write(fcmd)
    erg= tc.read_until( "\n" )
    tc.close()
    return erg

# Abwesenheit
if "off" in fhem_task("list ABWESENHEIT STATE\r\n"):
    abwesenheit="nicht gesetzt"
if "on" in fhem_task("list ABWESENHEIT STATE\r\n"):
    abwesenheit="gesetzt"

# Fenster
anz_fenster_offen=0;
if "open" in fhem_task("get FensterHeizung param STATE\r\n"):
    anz_fenster_offen += 1
if "open" in fhem_task("get FensterKeller param STATE\r\n"):
    anz_fenster_offen += 1
if "open" in fhem_task("get FensterGaestezimmer1 param STATE\r\n"):
    anz_fenster_offen += 1
if "open" in fhem_task("get FensterGaestezimmer2 param STATE\r\n"):
    anz_fenster_offen += 1
if "open" in fhem_task("get FensterBuero1 param STATE\r\n"):
    anz_fenster_offen += 1
if "open" in fhem_task("get FensterBuero2 param STATE\r\n"):
    anz_fenster_offen += 1
if "open" in fhem_task("get FensterWohnen1 param STATE\r\n"):
    anz_fenster_offen += 1
if "open" in fhem_task("get FensterWohnen2 param STATE\r\n"):
    anz_fenster_offen += 1
if "open" in fhem_task("get FensterWohnen3 param STATE\r\n"):
    anz_fenster_offen += 1
if "open" in fhem_task("get FensterWohnen4 param STATE\r\n"):
    anz_fenster_offen += 1
if "open" in fhem_task("get FensterGaestebad param STATE\r\n"):
    anz_fenster_offen += 1
if "open" in fhem_task("get FensterSpind param STATE\r\n"):
    anz_fenster_offen += 1
if "open" in fhem_task("get FensterBad1 param STATE\r\n"):
    anz_fenster_offen += 1
if "open" in fhem_task("get Garagentor param STATE\r\n"):
    stat_garage="offen"
else:
    stat_garage="geschlossen"

# Wetter
try:
    condition=str.split(fhem_task("get MeinWetter condition\r\n"))
    if len(condition)==5:
        wetter_icon=condition[3] + " " + condition[4]
    else:
        wetter_icon=condition[3]
except:
    wetter_icon="Fehler"


luftdruck=str.split(fhem_task("get MeinWetter pressure\r\n"))
tendenz=str.split(fhem_task("get MeinWetter pressure_trend_txt\r\n"))
wind=str.split(fhem_task("get MeinWetter wind_speed\r\n"))

####################
# Datenbank auslesen
####################
db = MySQLdb.connect(host="localhost", user="", passwd="", db="")        		
cur = db.cursor()
cur.execute("SELECT MIN(aussentemperatur), MAX(aussentemperatur) FROM temperaturen WHERE timestamp >= DATE(NOW()) ORDER BY timestamp")
for row in cur.fetchall():
    mintemp=round(row[0],2)
    maxtemp=round(row[1],2)

db.close()


################
# Preprocess SVG
################
now = datetime.now()
now_time = now.time()
if time(5,50) <= now.time() <= time(22,50):  
    # Open SVG to process
    output = codecs.open('daten_script_preprocess.svg', 'r', encoding='utf-8').read()
    # Insert icons and temperatures
    output = output.replace('ICON_ONE',wetter_icon.decode("utf-8"))
    output = output.replace('WETTER_BESCHR',wetter_icon.decode("utf-8"))
    output = output.replace('TEMP_A',fhem_task("get Aussenthermometer param temperature\r\n"))
    output = output.replace('LUFT_A',fhem_task("get Aussenthermometer param humidity\r\n"))
    output = output.replace('MIN_TEMP',str(mintemp))
    output = output.replace('MAX_TEMP',str(maxtemp))
    output = output.replace('DRUCK_A',luftdruck[3])
    output = output.replace('TENDENZ',tendenz[3])
    output = output.replace('WIND_A',wind[3])
    output = output.replace('ANZ_FENSTER',str(anz_fenster_offen))
    output = output.replace('STAT_ABWESENHEIT',abwesenheit)
    output = output.replace('STAT_GARAGE',stat_garage)
    output = output.replace('DATUM_UHRZEIT',datetime.strftime(datetime.now(), '%d.%m.%Y %H:%M:%S'))
    # Write output
    codecs.open('daten_script_output.svg', 'w', encoding='utf-8').write(output)
else:
    # Open SVG to process
    output = codecs.open('sleep_preprocess.svg', 'r', encoding='utf-8').read()
    output = output.replace('DATUM_UHRZEIT',datetime.strftime(datetime.now(), '%d.%m.%Y %H:%M:%S'))
    # Write output
    codecs.open('daten_script_output.svg', 'w', encoding='utf-8').write(output)

Es werden verschiedene Information aus FHEM mittels Telnet abgefragt und Daten aus einer MySQL-Datenbank (min/max-Werte) ermittelt. Neben Temperaturwerten zeige ich z.B. auch noch den Status der Fenster, des Garagentores und der Abwesenheitssteuerung an. Zwischen 5:50 und 22:50 Uhr wird alle 10 Minuten ein aktualisiertes Bild der Statusinformationen angezeigt, in der anderen Zeit ein Bild eines schlafenden Männchens. In dieser Zeit fragt der Kindle auch kein neues Bild per wget ab.

Ergebnis

Im Ergebnis schaut es dann so aus:

kindle_statusdisplay

Und hier die Anzeige für Nachts:

kindle_statusdisplay2

Als Erweiterung werde ich evtl. noch während der Heizperiode mit dem Kachelofen oder abhängig der Puffertemperatur, die obere und untere Temperatur des Pufferspeichers anstelle des Luftdruckes und der Windinformation anzeigen. Die Puffertemperatur ist in der Regel unser Maßstab ob Holz nachgelegt werden muss oder nicht.

Es könnte z.B. auch eine Display füllende Information ausgegeben werden wenn eine der Batterien der Fensterkontakte leer ist oder ein Warnhinweis falls die Heizung eine Störung oder anstehende Wartung anzeigt. Im Python-Script würde ich dann analog dem "Schlaf-Bild" ein spezielles PNG aus einem weiteren SVG erzeugen.
 

Chris

Gaszähler auslesen – Darstellung der Daten

Gaszähler auslesen – Darstellung der Daten

Jedes Jahr im Januar ist es wieder soweit – die Daten des Gaszähler müssen an den Lieferanten übermittelt werden. Dabei ist mir aufgefallen, das meine Darstellung der Daten aus der MySQL-Datenbank noch verbesserungswürdig sind. So hatte ich z.B. nur den Verbrauch der letzten 7 Tage wie im Artikel http://blog.bubux.de/gaszaehler-auslesen/ beschrieben ausgewertet. Da fehlt doch noch eine monatliche und jährliche Übersicht!

Nochmal zur Erinnerung: Ich speichere jede gezählte Umdrehung des Gaszählers als separate Zeile in einer MySQL-Datenbank.

Gaszaehler mySQL Datenbank

Daher müssen die einzelnen Zeilen nach den jeweiligen Kriterien gruppiert und dann das Ergebnis der Spalte „zaehlerstand“ summiert werden. Beim Stromzähler wird hingegen immer der aktuelle Gesamtverbrauch in die Datenbank geschrieben und aus den Datenbankeinträgen für die Tages-, Monats- und Jahresverbräuche die Differenz gebildet um entsprechende Auswertungen zu erzeugen.

Auswertung der letzten 12 Monate

Folgendes SQL-Statement ermittelt aus den einzelnen Datenbankeinträgen die Summe gruppiert nach Jahr und Monat. Dabei werden die letzten 12 Monate mittels „INTERVAL“ betrachtet.

SELECT
     CASE DATE_FORMAT(timestamp,'%m')
          WHEN 1 THEN 'Jan'
          WHEN 2 THEN 'Feb'
          WHEN 3 THEN 'Mär'
          WHEN 4 THEN 'Apr'
          WHEN 5 THEN 'Mai'
          WHEN 6 THEN 'Jun'
          WHEN 7 THEN 'Jul'
          WHEN 8 THEN 'Aug'
          WHEN 9 THEN 'Sep'
          WHEN 10 THEN 'Okt'
          WHEN 11 THEN 'Nov'
          WHEN 12 THEN 'Dez'
     ELSE 'fehler' END as Monat,
     year(timestamp), sum(zaehlerstand)
FROM gaszaehler WHERE year(timestamp) >= YEAR(CURRENT_DATE - INTERVAL 12 MONTH) AND  month(timestamp) >= month(CURRENT_DATE - INTERVAL 12 MONTH)
GROUP BY month(timestamp), year(timestamp) ORDER BY timestamp;

Auswertung der Jahresverbräuche

Fehlt noch der jährliche Verbrauch. Das SQL-Statement ist einfacher aufgebaut als vorheriges und summiert den Verbrauch pro Jahr für alle in der Datenbank enthaltenen Daten an.

SELECT 
     year(timestamp), sum(zaehlerstand) 
FROM gaszaehler
GROUP BY year(timestamp) ORDER BY timestamp;

Im Ergebnis sieht das Ganze dann wie folgt aus. Die Grafik zur Anzeige der Daten der letzten 12 Monate erstelle ich wie in diesem Artikel beschrieben

Gaszaehler Uebersicht

Viel Spaß bei der Kontrolle des Gasverbrauchs 🙂

Gruß

Chris

c´t Schlagseite 8/2015

Eben lag die neue c´t im Briefkasten. Die Schlagseite ist mal wieder wunderbar und paßt gut zu den Themen in diesem Blog.

Über Sinn und Unsinn bei der Heimautomatisierung kann man ja vortrefflich diskutieren. Wenn für das Aufwecken des Tablet/Handy, dem Öffnen der App oder Intranet-Seite und anschliessendem Klick auf „Licht Wohnzimmer an“ mehr Zeit benötigt wird als den Lichtschalter an der Wand zu betätigen, ist man meistens schon am Ziel der (intelligenten) Hausautomation vorbei geschossen…

Vor- und Rücklauf vom Kachelofen mittels 1-wire Temperatursensoren messen

Unser Kachelofen von Brunner hat einen Wärmetauscher (HKD2.2) und versorgt damit den Wasserspeicher im Keller mit warmem Wasser. Aus diesem wird dann die Fußbodenheizung und das Warmwasser versorgt. Die Steuerung des Kachelofens übernimmt die EAS (Elektronische Abbrandsteuerung). Leider hat Brunner in der EAS keine mir bekannte Schnittstelle um die verschiedenen Temperaturen, die Statis der Abbrandsteuerung oder den Schalter der Kachelofentüre abzufragen.

IMG_5564

Die Abbrandsteuerung selber ist unabhängig von der eigentlichen Hydraulik zum Anschluss des Kachelofens an den Warmwasserspeicher. Hier nutzen wir eine Steuerung der Fa. ÖkoCentro mit einer SHR 10-Steuerung von EnergieControl. Aber auch diese hat keine mir bekannte Schnittstelle die man einfach abfragen kann.

IMG_7931

Als eine einfache Möglichkeit zumindest schon mal die Vorlauf- und Rücklauftemperatur zu messen, dachte ich an 1-Wire Temperatursensoren der Firma DALLAS (DS18B20). Diese sind günstig (z.B. 1,85€ bei Reichelt) und einfach am Raspberry PI zu implementieren. Die Vorbereitungen zur Nutzung der 1-Wire Sensoren ist nicht aufwändig und schnell passiert.

Sollen die 1-wire Sensoren nur parasitär versorgt werden, folgendes in der Shell eingeben um das entsprechende Kernelmodul zu laden:

sudo modprobe w1-gpio

oder wenn ein Pullup zur externen Stromversorgung genutzt wird, folgendes Kommando:

sudo modprobe w1-gpio pullup=1

Sollen Temperaturen über 70°C gemessen werden, wird zwecks der Genauigkeit der Sensoren die zusätzliche Stromversorgung mittels Pullup-Widerstand angeraten.
Mehr zum Thema Anschlussarten gibt es z.B. hier.

Unabhängig ob Pull-Up oder nicht wird noch ein weiteres Kernelmodul benötigt:

sudo modprobe w1-therm

Ab der Kernelversion 3.18.3 muss noch eine Anpassung gemacht werden. Die Kernelversion wird wie folgt bestimmt:

uname -r

Ist die Kernelversion > 3.18.3 muss in der Datei

/boot/config.txt

folgende Zeile eingefügt werden

# activating 1-wire with pullup
dtoverlay=w1-gpio-pullup

Ich habe die Sensoren mit separater Versorgung am Raspberry Pi angeschlossen. Wie das funktioniert, ist im Internet in verschiedensten Foren etc. zu lesen. Ich hatte mal eine kleine Platine rs232_raspi.fzz mit einem MAX3232 für eine RS232-Schnittstelle für das M-Bus-Projekt in Fritzing aufgebaut. Da hat auch noch der eine 4.7K Widerstand und eine Steckerleiste für 1-wire drauf gepaßt (oben Links zu sehen).

fritzing_1wire_rs232

Sind die Kernelmodule geladen, alle Konfigurationen gemacht und alles angeschlossen, sollten die 1-wire Sensoren nach einem Neustart des Raspberry PI unter

cd /sys/bus/w1/devices

auftauchen.

Die Sensoren werden z.B. per „cat“ mit folgendem Befehl ausgelesen

cat 28-000005db9cea/w1_slave

Das Ergebnis sollte dann ähnlich wie folgt aussehen:

08 03 4b 46 7f ff 08 10 9e : crc=9e YES
08 03 4b 46 7f ff 08 10 9e t=48500

Die Temperatur wird hinter dem „t=“ in der zweiten Zeile ausgegeben. Um diese Temperatur z.B. mit PHP abzufragen und für weitere Verarbeitung nutzen zu können, folgendes PHP-Code-Schnipsel

[cc lang=“php“]

[/cc]

Die Temperatur schreibe ich dann per Script, welches 10-minütig per Cron aufgerufen wird, in eine MySQL-Datenbank und stelle das Ergebnis in einer Webseite dar.

kachelofen_webseite

Soviel zum Thema 1-wire. Die größere Herausforderung wird es aber, an die Werte der EAS heran zu kommen. Diese sind in soweit auch interessanter, als das diese Steuerung den Abbrand überwacht und informiert ob das Feuer heiß genug ist (>450°C) um den geregelten Abbrand zu starten, zu heiß (>750°C) und eine Gewisse Panik zu erzeugen, der Abbrand beendet ist und Holz nach gelegt werden könnte oder die Türe des Kachlofens nicht richtig geschlossen ist.

Ein Anfang ist aber gemacht und die EAS eine neue Herausforderung.

Gruß
Chris

M-BUS – Wasserzähler

Von einem Freund habe ich einen M-Bus (Meter-Bus) Wasserzähler und einen Wärmemengenzähler bekommen. Der M-BUS ist ein Feldbus der über eine Zweidrahtleitung über sehr lange Strecken genutzt werden kann. Ein M-BUS Master fragt dabei alle Slaves im Strang oder Stern ab. Die Slaves werden teilweise vom M-Bus-Master mit Spannung versorgt. Mehr Details im Wiki.

Techem Wärmemengenzähler Techem Wasserzähler

Der M-Bus-Master sollte in meinem Fall aus dem Raspberry und einem Pegelwandler bestehen. Pegelwandler gibt es fertig von verschiedenen Herstellen (Relay, Wachendorff). Diese sind aber, je nachdem für wieviele Slaves sie ausgelegt sind, relativ teuer.

Mein erster Ansatz war daher der Selbstbau eines Pegelwandlers inkl. RS232-Schnittstelle zum Anschluss an den Raspberry Pi. Im Netz gibt es verschiedene Beschreibungen einer einfachen Schaltung und einige wenige Forumseinträge die sich mit dem Selbstbau eines Pegelwandlers bzw. generell mit M-Bus beschäftigen. Leider habe ich in keinem Forum eine Diskussion gefunden die bis zu einem funktionierenden System verfolgt wurde. Egal, ich habe es trotzdem mal versucht…

 

Selbstbau Pegelwandler

Auf der Platine ist der Pegelwandler inkl. einer RSA232 Schnittstelle die aus einem MAX3232 besteht. Die +/-15 Volt für den Pegelwandler habe ich mittels DCDC-Wandler IH0515S der Fa. XP Power erzeugt. Dieses Bauteil kann man z.B. bei Farnell beziehen. Dann das Ganze auf Lochraster zusammen gelötet und mit dem Raspi und dem Wärmemengenzähler verbunden.

IMG_7707 IMG_7708

max3232Lochrasterplan

Damit der Raspberry das M-Bus Protokoll versteht bzw. auch den Pegelwandler korrekt anspricht, habe ich die Bibliotheken von rSCADA genutzt. Die Quellen liessen sich problemlos auf dem Raspberry kompilieren. Im Ergebnis wurden verschiedene Binaries erzeugt um mit Slaves im Bus zu kommunizieren.

Meine ersten Versuche waren jedoch nicht von Erfolg gekrönt. Es wurde kein M-Bus-Gerät gefunden. Bei der Fehlersuche habe ich zuerst die serielle Schnittstelle separat getestet und mit Minicom über den Raspberry mit einem anderen Linux-Rechner kommuniziert. Das hat funktioniert. Die weitere Fehlersuche z.B. am M-Bus-Ausgang sind allerdings am fehlenden Oszilloskop gescheitert, da das Multimeter mich nicht wirklich weiter brachte.

Etwas niedergeschlagen habe ich dann nach drei Tagen erfolgloser Fehlersuche nochmal nach fertigen Pegelwandlern gesucht und ein relativ preiswertes Gerät gefunden und kurzerhand gekauft. Damit fiel die Fehlerquelle „Pegelwandler“ schon mal weg.

 

Versuch mit gekauftem Pegelwandler

Der neue Pegelwandler benötigt zum Anschluss an den Raspberry Pi auch eine RS232 Schnittstelle. Um mir diesmal das Löten zu ersparen, hab ich erstmal den kompletten Versuchsaufbau auf einem Breadboard gesteckt. Der Pegelwandler läuft mit einer Spannung von 12-24 V DC weshalb ich den DCDC-Wandler aus dem ersten Versich auch nicht benötige.

IMG_7739

Die Binaries von rSCADA unterscheiden zwischen TCP- und seriell angeschlossenen M-Bus-Pegelwandlern. Da meiner seriell angeschlossen ist, benutze ich „mbus-serial-scan“ um nach angeschlossenen Slaves im Bus zu suchen.
Als Argumente übergebe ich „-d“ für den Debugmodus, „-b 2400“ um 2400 Baud zu nutzen und „/dev/ttyAMA0“ als serielle Schnittstelle des Pi.

Zu meiner Überraschung wurde direkt ein Slave (der Wasserzähler) erkannt. Sehr schön…

pi@raspberry2 ~/libmbus-0.8.0/bin $ ./mbus-serial-scan -d -b 2400 /dev/ttyAMA0
Scanning primary addresses:
0 [2014-12-13 13:59:21] SEND (005): 10 40 00 40 16
[2014-12-13 13:59:21] RECV (001): E5

Found a M-Bus device at address 0
1 [2014-12-13 13:59:22] SEND (005): 10 40 01 41 16
2 [2014-12-13 13:59:22] SEND (005): 10 40 02 42 16
3 [2014-12-13 13:59:22] SEND (005): 10 40 03 43 16
4 [2014-12-13 13:59:22] SEND (005): 10 40 04 44 16
5 [2014-12-13 13:59:23] SEND (005): 10 40 05 45 16
6 [2014-12-13 13:59:23] SEND (005): 10 40 06 46 16
7 [2014-12-13 13:59:23] SEND (005): 10 40 07 47 16
...

Hat man alle angeschlossenen M-Bus-Geräte per Scan identifiziert, können mit einem weiteren Programm die Daten des Gerätes ausgelesen werden. Auch hier gibt es wieder das Argument „-d“ für den Debugmodus, „-b“ zur Einstellung der Baudrate, das Device der seriellen Schnittstelle und die vom Scan ermittelte Device-Nummer. Im Fall meines Wasserzählers ist das die „0“. Die Daten der Slaves werden in einer einfachen XML-Struktur zurück geliefert.

./mbus-serial-request-data -d -b 2400 /dev/ttyAMA0 0
[2014-12-13 14:06:46] SEND (005): 10 5B 00 5B 16
[2014-12-13 14:06:47] RECV (084): 68 4E 4E 68 08 00 72 12 37 16 46 68 50 49 07 B6 00 00 00 0C 14 53 42 00 00 8C 10 12 35 53 42 00 0B 3B 00 00 00 8C 20 14 53 42 00 00 8C 30 14 00 00 00 00 04 6D 21 0F CD 1C 4C 14 02 00 00 00 42 6C BF 1C 42 EC 7E DF 1C 0A 92 2A 00 10 0A 92 2B 00 10 3E 16
mbus_frame_print: Dumping M-Bus frame [type 4, 84 bytes]: 68 4E 4E 68 08 00 72 12 37 16 46 68 50 49 07 B6 00 00 00 0C 14 53 42 00 00 8C 10 12 35 53 42 00 0B 3B 00 00 00 8C 20 14 53 42 00 00 8C 30 14 00 00 00 00 04 6D 21 0F CD 1C 4C 14 02 00 00 00 42 6C BF 1C 42 EC 7E DF 1C 0A 92 2A 00 10 0A 92 2B 00 10 3E 16



46163712
TCH
73

Water
182
00
0000



Instantaneous value
Volume (1e-2  m^3)
4253
2014-12-13T14:06:47



Instantaneous value
Volume (1e-4  m^3)
425335
2014-12-13T14:06:47

...

Da es auf Anhieb funktioniert hat, habe ich die serielle Schnittstelle auf Lochraster gebannt und der Pegelwandler wird mittels kleinem 12V-Netzteil betrieben. Dann Raspberry Pi, Pegelwandler und Platine im Netzwerkschrank verstauen…

MAX3232

 

Software

Den Stand des Wasserzählers protokolliere ich nun einmal täglich um 23:59 Uhr. Das wird per Cronjob und einem kleinen PHP-Script erledigt. Warum PHP? Weil ich zu ungeduldig war und das schnell umsetzen wollte und schon das ein oder andere ähnliche PHP-Script fertig hatte.

Das Script ruft das Binary wie oben erklärt per shell_exec auf. Ab dem XML-Teil parse ich die Ausgabe und extrahiere die relevanten Informationen die dann in eine MySQL-Tabelle geschrieben werden.

[cc lang=“php“]
‚));
$xmloutput = new SimpleXMLElement($xmloutput);
$zaehlerID=$xmloutput->SlaveInformation->Id;
$zaehlerStand=$xmloutput->DataRecord[1]->Value/10000;

$mysqlhost=““;
$mysqluser=““;
$mysqlpwd=““;
$connection=mysql_connect($mysqlhost,$mysqluser,$mysqlpwd) or die („Verbindungsversuch fehlgeschlagen“);
$mysqldb=““;
mysql_select_db($mysqldb,$connection) or die(„Konnte die Datenbank nicht waehlen.“);
$sql = „INSERT INTO wasserzaehler (timestamp,zaehlerid,zaehlerstand) VALUES (CURRENT_TIMESTAMP,$zaehlerID,$zaehlerStand)“;
$query = mysql_query($sql) or die(„Anfrage 1 nicht erfolgreich“);
?>;
[/cc]

Da ich auf meiner Webseite aber nicht nur den absoluten Wasserverbrauch darstellen möchte sondern auch den Tagesverbrauch, kommt folgendes SQL zum Einsatz. Hier werden die Einträge aus zwei aufeinanderfolgenden Zeilen subtrahiert um die Differenz zum Vortag zu bestimmen.

SELECT 
    CASE DATE_FORMAT(wz1.timestamp,'%w')
	WHEN 0 THEN 'Sonntag'
	WHEN 1 THEN 'Montag'
	WHEN 2 THEN 'Dienstag'
	WHEN 3 THEN 'Mittwoch'
	WHEN 4 THEN 'Donnerstag'
	WHEN 5 THEN 'Freitag'
	WHEN 6 THEN 'Samstag'
    ELSE 'fehler' END,
	wz1.zaehlerstand - IFNULL(wz2.zaehlerstand, 0) AS verbrauch
    FROM wasserzaehler wz1
    LEFT JOIN wasserzaehler wz2
    ON wz2.timestamp = (
	SELECT MAX(timestamp)
	FROM wasserzaehler wz3
	WHERE wz3.timestamp < wz1.timestamp
   )
ORDER BY wz1.timestamp

Das Ergbnis sieht dann wie folgt aus. Ich war etwas erschreckt wieviel Wasser man an machen Tagen verbraucht!

wasserzaehler_screenshot

 

Selbstgebauter Pegelwandler

Da ich ja nun weiß das es generell funktioniert und der Fehler meiner ersten Lösung definitiv im selbstgebauten Pegelwandler liegt, werde ich den Versuch mit dem Selbstbau demnächst nochmal angehen.

 

Sobald der Wärmemengenzähler in den Wasserlauf des Kachelofens eingebaut ist, werde ich nochmal einen kurzen Beitrag schreiben. Das Prinzip des Auslesens ist ja analog zum Wasserzähler aber vielleicht gibt es in den zurückgelieferten Daten unterschiede.

 

Viel Spaß mit dem M-Bus!

Chris

Gaszähler auslesen

Nachdem meine Versuche den M-BUS Wärmemengen- und Wasserzähler auszulesen vorerst beendet sind und ich auf den bestellten M-BUS-Pegelwandler warte, habe ich mich dem Auslesen des Gaszählers gewidmet. Das sollte erfolgsversprechender sein…

Als ich dann endlich den vor langer Zeit bestellten Reed-Kontakt mk 471 b (z.B. bei Reichelt) wieder gefunden habe, konnte es losgehen.

Der Gaszähler hat an der letzten Stelle des Zählwerkes bei der „0“ einen Magneten wodurch der Reedkontakt bei jeder vollen Umdrehung einmal geschlossen wird. Diese Impulse gilt es mit einem Raspberry über einen GPIO zu zählen.

IMG_7735

Den Reedkontakt habe ich natürlich wieder professionell mit Klebeband fixiert…hält!

IMG_7734

Angeschlossen ist der Reedkontakt über eine Zwillingslitze direkt an einen GPIO und Ground des Raspberry PI. Pull-Up braucht es keinen. Dieser wird per Software aktiviert.

Den GPIO lese ich mit einem Python-Script aus welches in einer Endlosschleife ausgeführt wird und den GPIO (im Beispiel Pin „21“) jede Sekunde abfragt. Sekundenweise sollte reichen, da ich nicht hoffe das unser Gaszähler so schnell läuft. Immer wenn der Reed geschlossen wird, wird eine „1“ und der Zeitstempel in die Spalten „timestamp“ und „zaehlerstand“ einer MySQL-Tabelle geschrieben. Das passiert immer nur beim Wechsel des Reed von „Offen“ nach „Geschlossen“. Sollte der Zähler genau mit dem Magnet am Reed stehen bleiben, passiert nichts.

[cc lang=“python“]

import RPi.GPIO as GPIO
import time
import MySQLdb

GPIO.setmode(GPIO.BOARD)
GPIO.setwarnings(False)

# GPIO definieren
REED_gas = 21
# definierten GPIO als Eingang setzen
GPIO.setup(REED_gas, GPIO.IN, pull_up_down=GPIO.PUD_UP)

status_alt=1
while True:
status_aktuell = GPIO.input(REED_gas)
# REEDKONTAKT geoeffnet
if status_aktuell == 1:
#print „Kontakt offen“
status_alt=GPIO.input(REED_gas)
# REEDKONTAKT geschlossen
elif status_aktuell==0:
#print „Kontakt geschlossen“
if status_alt!=status_aktuell:
status_alt=GPIO.input(REED_gas)
# Datenbankverbindung
db = MySQLdb.connect(host=“„, user=“„, passwd=“„, db=“„)
# Impuls in Datenbank eintragen
cursor = db.cursor()
cursor.execute(„““INSERT INTO gaszaehler (timestamp,zaehlerstand) VALUES (CURRENT_TIMESTAMP,1)“““)
db.commit()
cursor.close()
time.sleep(1)
[/cc]

IMG_7737

Da das Python-Script sich ab und an mal verabschiedet, starte ich es per Cronjob wieder neu falls der Prozess nicht mehr vorhanden ist. Ich sollte vielleicht lieber das Problem im Python-Script suchen und das ein oder andere Try&Catch einbauen, aber es geht auch erstmal so…

#!/bin/sh
if ps -ef | grep -v grep | grep gaszaehler.py ; then
    exit 0
else
    sudo python /usr/local/bin/gaszaehler.py &
    logger "gaszaehler.py neu gestartet"
    exit 0
fi

Zur Auswertung benutze ich dann folgende SQL-Querys:

SELECT sum(zaehlerstand) FROM gaszaehler

Hiermit werden alle gespeicherten „Ticks“ summiert. Dazu muss noch ein Startwert addiert werden da der Gaszähler ja schon eine zeitlang lief.
Dieser Startwert muss ggf. ab und an mal korrigiert werden falls der Raspi mal einen Umlauf des Zählers nicht mitbekommt weil z.B. der unwahrscheinliche Fall eintritt, dass das Klebeband nicht gehalten hat oder wahrscheinlicher, das Python-Script bzw. der ganze Raspi nicht mehr läuft. Den Startwert gebe ich als eine Zahl inkl. der drei Nachkommastellen an und addiere das Ergebnis der SQL-Query dazu. Um die richtige „Einheit“ kümmere ich mich in der Ausgabe auf der PHP-Seite mit folgender PHP-Zeile:

[cc lang=“php“]
$sql = „SELECT sum(zaehlerstand) FROM gaszaehler“;
$query = mysql_query($sql) or die(„Anfrage nicht erfolgreich“);
while ($wert = mysql_fetch_array($query)) {
$gesamtwert=($wert[0]*10)+$gas_startwert;
}
$gesamtwert=substr($gesamtwert,0,-3).“.“.substr($gesamtwert,-3,2);
[/cc]

Der Faktor 10 muss hier beachtet werden, da ja nur alle 0,01m³ Impulse gezählt werden und ich den Startwert mit allen drei Nachkommastellen angegeben habe. Die zweite Zeile setzt das Komma in der Ausgabe an die richtige Stelle.

Eine tägliche Auswertung funktioniert z.B. mit folgender SQL-Query:

SELECT CASE DATE_FORMAT(timestamp,'%w')
          WHEN 0 THEN 'Sonntag'
          WHEN 1 THEN 'Montag'
          WHEN 2 THEN 'Dienstag'
          WHEN 3 THEN 'Mittwoch'
          WHEN 4 THEN 'Donnerstag'
          WHEN 5 THEN 'Freitag'
          WHEN 6 THEN 'Samstag'
          ELSE 'fehler' END,
          sum(zaehlerstand)
FROM gaszaehler
WHERE DATE(timestamp)>=DATE_SUB(NOW(),INTERVAL 7 DAY)
GROUP BY day(timestamp)
ORDER BY timestamp

Auch hier bekommt man den korrekten Wert in m³ durch einfache Kommaverschieberei in der Ausgabe. Den Faktor 10 benötigt man bei der täglichen Auswertung nicht:

[cc lang=“php“]substr(($wert[1]),0,-2).“.“.substr(($wert[1]),-2)[/cc]

In der Webseite sieht das folgendermaßen aus:

gaszaehler_screenshot

Das alles läuft erst drei Tage und es gibt bestimmt noch Verbesserungspotential aber ein Anfang ist gemacht.

Gruß
Chris

Wohnzimmer PC

Wer kennt das nicht: Die Musik landet heutzutage auf Handys und Festplatten und der gute alte CD-Player geht in Rente. Die Hersteller bietet verschiedene Lösungen an um MP3 & Co. im Wohnzimmer abzuspielen z.B. mit dem Handy und Bluetooth-Lautsprecher aber das finde ich alles nicht wirklich brauchbar und zu unflexibel.

Ein kleiner Media-PC für´s Wohnzimmer musste her. Meine Wahl fiel auf einen Zotac ZBOX ID41. Das ist nicht mehr der neuste Rechner, dafür aber relativ preiswert und von der Leistung völlig ausreichend für meine Zwecke. Alternativen mit diesem Formfaktor gibt es aber aktuell viele…von Zotac, die Intel NUCs und andere…

zbox

Der Rechner hat einen 1,8 GHz Atom mit 512 MB Hautpspeicher, eine nvidia Grafikkarte und jede Menge Schnittstellen. Als Betriebssystem habe ich ein Ubuntu 14.04 LTS installiert. Als Mediacenter kommt XBMC (demnächst „Kodi“) in der Version aktuell „13.2 Gotham“ zum Einsatz.

Die Steuerung des Rechners geschieht über einen rahmenlosen 19″ Touchscreen von ELO den ich günstig in der Bucht gesteigert habe. Der Touchscreen sollte in die Wand eingelassen werden, der Rechner sollte irgendwo hinter der Couch verschwinden.

Unser Hausbau kam gelegen um alles ordentlich im Wohnzimmer zu integrieren. Oben links im Bild ist die Ausparung für den Monitor. Per Kabelkanal geht es an eine Ausgangsdose die später hinter der Coach ist. In den Kabelkanal kommt ein DVI-Kabel, ein USB-Kabel für den Touchscreen und ein Steuerkabel um bei dem ELO-Monitor das externe Bedienelement für Helligkeit, Kontrast etc. bei Bedarf anschliessen zu können.

IMG_1756

Den Monitor habe ich mit einem ausgeschnittenen Alu-Rahmen verschraubt den ich den Verputzern als Maß für die Ausparung gegeben hatte. das Netzteil des Monitors habe ich hinter dem Monitor in einer extra Vertiefung verstaut.

IMG_1832

Durch das dicke Leerrohr am Boden kamen ein HDMI-Kabel für den Fernsehanschluss (der ist in der Ecke wo das Abflussrohr steht), und ein mit 10m relativ langes optisches TOS-Link-Kabel welches aber problemlos funktioniert.

IMG_1834

Der Alu-Ramen inkl. Monitor habe ich mit Montagekleber in die Aussparung geklebt. Das hält sehr gut, ich bekomme es aber auch wieder (mit etwas Gewalt) ab.

Fertig eingebaut schaut das Ganze dann so aus. Um den Halterahmen zu verdecken ist um den Monitor ein weißer Alurahmen der mit doppelseitigem Klebeband befestigt ist. Für beide Rahmen nochmal vielen Dank an Ingo für das Schneiden mit dem Waterjet:-)

xbmc1

Der Rechners startet direkt per Autostart ins XBMC. Die Daten (Bilder, Musik, Videos) liegen auf einem NAS im Keller. Als Skin für den XBMC kommt der Touchscreen-freundliche „Joggler“ zum Einsatz. Ohne Tastatur geht es leider nicht immer daher hängt eine sehr kleine Bluetooth-Tastatur von Keysonic (KSK-3200 RF) mit integriertem Trackball dran.

xbmc2

XBMC zeigt nach wenigen Minuten ohne Touch und weitere Eingaben eine Diashow als „Bildschirmschoner“ mittels entsprechendem Slideshow-Plugin. Nach einer Stunde geht der Monitor in Standby.

Leider unterstützt XBMC (noch) keine vernünftige Dualscreen-Lösung so das ich Filme per VLC, etc. direkt aus dem Ubuntu schaue.

Da der Rechner fast immer läuft wenn jemand Zuhause ist, bietet er sich auch für die Startseite der Hausautomatisierung im Browser-Vollbildmodus an.

Gruß

Chris

 

 

 

Homematik – Zisterne

Update 04.03.2019: Zur Messung des Füllstands der Zisterne habe ich einen neuen Artikel mit einem anderen Ansatz zur Messung verfasst. Dieser ist hier zu finden.

Wieder eins von diesen Projekten wo viele sagen: „Das braucht doch kein Mensch!“. Richtig, das braucht man auch nicht nicht. Es macht aber Spaß (zumindest mir), den Füllstand meiner Zisterne zu wissen ohne das ich den Betondeckel rausheben muss und mir dabei die Finger quetsche.

Im Homematik-Sensor-Park gibt es einen kapazitiven Füllstandssensor (Hm-Sen-Wa-Od) als Bausatz der diese Aufgabe übernehmen kann. Wobei „Bausatz“ nicht wirklich schwierige Lötarbeiten erfordert, sondern nur ein paar wenige Teile auf die Platine gelötet werden müssen (Batteriehalter, Funkmodul) und das Ganze in dem mitgelieferten Gehäuse verpackt werden muss.

IMG_3376

Nach dem Zusammenbau der Platine müssen die Messleitungen auf die entsprechende Länge (Tiefe der Zisterne und geplante Einbauhöhe des Sensors) angepaßt werden. Die Enden der Messleitungen müssen mit den beiligenden „Schrumpftüllen“ die mit Kleber gefüllt sind wasserdicht verschlossen werden. Dazu reicht ein normaler Fön.

Alle 10 cm von unten beginnen müssen nun die Stege befestigt werden. Dieser Abstand  wird später für die Kalibrierung benötigt. Dann die Anschlussleitungen durch die Kabeldurchführungen an den beiden Klemmen befestigen.

Reichweite

Ich war zuerst skeptisch ob das mit dem Funk vom Kellerraum bis in die Zisterne funktioniert?! Immerhin ist der Sensor etwa 1m unter der Erde im Betonverlies gefangen… Bei mir sind es aber nur 10m Luftline zwischen Sensor und COC und es gibt keine Probleme mit der Funkverbindung.

Anlernen

Zum Anlernen des Sensors muss die Zentrale in den Anlernmodus gebracht werden

set <zentrale> hmPairForSec <zeit_in_sekunden>

und danach am Füllstandssensor die „Sensor“-Taste für <1 Sekunde gedrückt werden. Die Setup-LED blinkt in der Anlernphase und erlischt bei Erfolg. Danach sollte in der fhem.cfg ein ähnlicher Eintrag wie folgt erstellt worden sein:

define Zisterne CUL_HM 226555
attr Zisterne .devInfo 020101
attr Zisterne .stc 60
attr Zisterne IODev COC
attr Zisterne autoReadReg 4_reqStatus
attr Zisterne expert 2_full
attr Zisterne firmware 1.2
attr Zisterne model HM-Sen-Wa-Od
attr Zisterne peerIDs
attr Zisterne rawToReadable 10:0 72:1000 144:2000 216:3000 288:4000
attr Zisterne room Garten
attr Zisterne serialNr KEQ0365449
attr Zisterne subType sensor
#attr Zisterne hmClass sender
define FileLog_Zisterne FileLog /var/log/fhem/Zisterne-%Y.log Zisterne
attr FileLog_Zisterne logtype text
attr FileLog_Zisterne room Garten

Ich hab im Beispiel den kryptischen Gerätenamen durch „Zisterne“ ersetzt. Das neue Gerät sollte dann auch in der FHEM-Weboberfläche im Raum „Garten“ zu finden sein.

Mit dem folgenden Befehl werden die Register des Füllstandsmessers ausgelesen:

get Zisterne regList

Dabei sollte folgende Ausgabe zu sehen sein. Werden die Register nicht korrekt ausgelesen kann das u.a. mit einer zu alten Version von FHEM zusammenhängen. In der Version 5.3 war das Gerät anscheinend noch nicht implementiert . Mit der FHEM Version 5.5 funktionierte das Auslesen der Register korrekt.

list:         register | range              | peer     | description
   0: cyclicInfoMsgDis |   0 to 255         |          | cyclic message
   0: intKeyVisib      |     literal        |          | visibility of internal channel options:visib,invisib
   0: localResDis      |     literal        |          | local reset disable options:on,off
   0: pairCentral      |   0 to 16777215    |          | pairing to central
   0: transmDevTryMax  |   1 to 10          |          | max message re-transmit
   1: caseDesign       |     literal        |          | case desing options:verticalBarrel,horizBarrel,rectangle
   1: caseHigh         | 100 to 10000cm     |          | case hight
   1: caseLength       | 100 to 10000cm     |          | case length
   1: caseWidth        | 100 to 10000cm     |          | case width
   1: fillLevel        | 100 to 300cm       |          | fill level
   1: ledOnTime        |   0 to 1.275s      |          | LED ontime
   1: meaLength        | 110 to 310cm       |          | 
   1: transmitTryMax   |   1 to 10          |          | max message re-transmit
   1: useCustom        |     literal        |          | use custom options:on,off
   1: waterUppThr      |   0 to 256         |          | water upper threshold
   1: waterlowThr      |   0 to 256         |          | water lower threshold
   4: expectAES        |     literal        | required | expect AES options:on,off
   4: fillLvlLoThr     |   0 to 255         | required | fill level lower threshold
   4: fillLvlUpThr     |   0 to 255         | required | fill level upper threshold
   4: peerNeedsBurst   |     literal        | required | peer expects burst options:on,off

Kalibrierung

Vor der Kalibrierung des Sensors müssen noch verschiedene Zisternen-spezifische Werte im Register gesetzt werden. Dazu gehören:

  • Behälterform
  • Behälterhöhe
  • Behälterdurchmesser
  • Länge der Messkabel
  • maximale Füllhöhe

Ich habe die maximale Füllhöhe und die Behälterhöhe auf die gleichen Werte gesetzt. Mit den folgenden Kommandos werden die Register gesetzt:

set Zisterne regSet caseDesign verticalBarrel
set Zisterne regSet caseHigh 130
set Zisterne regSet caseWidth 200
set Zisterne regSet meaLength 180
set Zisterne regSet fillLevel 130

Danach sollten die Werte in der Geräteübersicht in den Readings angezeigt werden:

zisterne_readings

Das „set_“ vor den Werten sollte dann nach mindestens 24 Stunden verschwinden. Bei Problemen mit dem korrekten Setzen der Register wurde u.a. hier behandelt.

Sind die Werte alle korrekt gesetzt, erfolgt die eigentliche Kalibrierung des Sensors die nur bei komplett gefüllter Zisterne erfolgen kann. Dazu wie in der Anleitung beschrieben verfahren:

ziosterne_kalibrierung

Falls die grüne LED nicht erlischt, d.h. die Tabelle noch nicht vollständig gefüllt ist, hat das evtl. mit einer falschen angabe der Messleitungslänge und/oder der maximalen Füllhöhe in den Registern zu tun.

Befestigung in der Zisterne

Ich habe den Sensor auf ein Brett geschraubt welches ich lose auf den Rand eines Betonringes in der Zisterne gelegt habe. Das sollte ausreichen!

IMG_6937

IMG_6935

Daten loggen

Den Füllstand der Zisterne lasse ich wieder per Cron stündlich in eine MySQL-Datenbanktabelle schreiben. Dazu wird der Sensor wieder per Telnet abgefragt:

get_zisterne.php

[cclN_php]
„;
$mysqluser=““;
$mysqlpwd=““;
$connection=mysql_connect($mysqlhost, $mysqluser, $mysqlpwd) or die („Verbindungsversuch fehlgeschlagen“);
$mysqldb=““;
mysql_select_db($mysqldb,$connection) or die(„Konnte die Datenbank nicht waehlen.“);
$sql = „INSERT INTO zisterne (timestamp,fuellstand) VALUES (CURRENT_TIMESTAMP,$zustand[1])“;
$query = mysql_query($sql) or die(„Anfrage 1 nicht erfolgreich“);
?>
[/cclN_php]

Visualisierung

Die Visualisierung habe ich wieder in meine „Intranet“-Seite eingebaut und neben dem aktuellen Füllstand in % wird noch ein Graph der letzten 14 Tage gezeichnet.

zisterne

Da wir die Tage einen Rollrasen verlegt haben wo ich ordentlich gießen musste und es zwei Tage später geregnet hat, sieht man das auch schön im Graphen. Die Einheit der X-Achse muss noch angepaßt werden.

Und mal wieder viel Spaß beim Datenloggen

Chris

Viessmann Heizung auslesen

Viessmann Heizung auslesen

Wir haben eine Viessmann Vitodens 200-W Gastherme inkl. Solaranlage die über eine Vitotronic verfügt. Die Vitotronic hat eine optische Schnittstelle (Optolink) über die z.B. ein Heizungsmonteur verschiedene Parameter der Anlage auslesen und auch setzen kann.

vitotronic

Dafür benötigt man normalerweise den entsprechenden Lesekopf und eine passende Software der Fa. Viessmann.

Auf https://github.com/openv/openv/ gibt es aber auch eine andere Lösung! Die Jungs dort haben viel Arbeit in die „Entschlüsselung“ des Viessmann-Protokolls gelegt und verschiedene Anwendungen zur einfachen Kommunikation mit der Heizung entwickelt.

An dieser Stelle nochmal meinen Dank an dieses hervorragende Forum!

Unter den verschiedenen Bauanleitungen für die Leseköpfe habe ich mich für die USB-Variante entschieden. Mein Raspi hat ja noch einen USB-Anschluss frei und kann neben der Stromzählung und der Homematic-Steuerung auch noch die Heizung auslesen. Nicht das ihm langweilig wird…

Das Platinchen muss über die V-Ausfräsung hinter der Klappe befestigt werden. Das habe ich dann „professionell“ mit einem Papp-V und Tape erledigt. Hält!!

platine_frei

platine_tape

Ich nutze den vcontrold und da hier eine perfekte Anleitung zur Installation dieser Komponente auf dem Raspberry PI beschrieben ist, will ich auf die Installation und Konfiguration des Daemons auch nicht näher eingehen.

Der Daemon bietet eine Schnittstelle die z.B. mittels Telnet abgefragt werden kann. Ich nutze die Template-Möglichkeit (wird z.B. hier erklärt) um mehrere Heizungswerte auszulesen und die gelieferten Werte weiter verarbeiten zu können. Dazu wird bei mir folgendes Script (update.sh) alle 6 Minuten per Cron aufgerufen:

if [ !$(pgrep vclient) ]
then vclient -h localhost:3001 -f /opt/vcontrold/vc-commands.txt -t /opt/vcontrold/update.tmpl -x /opt/vcontrold/update.sh
fi

Der Eintrag in der Crontab sieht dann folgendermaßen aus:

*/6 * * * * sudo /opt/vcontrold/update.sh

Die Datei vc-commands.txt enthält eine Liste alle Werte die ausgelesen werden sollen. Diese Werte müssen auch in der „vito.xml“ definiert sein.

getTempA
getTempWWist
getTempKist
getTempKol
getTempSpu
getBrennerStarts
getBrennerStunden1
getSolarStunden
getSolarLeistung
getPumpeStatusSolar
...

In der Datei update.tmpl werden die zurück gelieferten Werte in Variablen geschrieben und können dann beliebig weiter verarbeitet werden (z.B. eine Prüfung ob ein Wert zurück gegeben wurde).

if  [ "x$E1" != x ]; then
     echo -n `date`  >>/tmp/vc-err.txt
     echo "es ist ein Fehler aufgetreten: $C1:$E1" >>/tmp/vc-err.txt
     exit 1;
fi
if  [ "x$E2" != x ]; then
     echo -n `date`  >>/tmp/vc-err.txt
     echo "es ist ein Fehler aufgetreten: $C2:$E2" >>/tmp/vc-err.txt
     exit 1;
fi
...

Auf der openv-Webseite wird oft eine rrdb zur Auswertung der Daten genutzt. Da ich mich mit einer rrdb nicht wirklich gut auskenne, schreibe ich die Daten lieber in eine MySQL-Datenbank.

Tabellenstruktur

Hierfür habe ich folgende Tabellen in der MySQL-Datenbank angelegt:

Tabellenstruktur für Tabelle brenner

CREATE TABLE IF NOT EXISTS `brenner` (
 `timestamp` datetime NOT NULL,
 `brennerstarts` float NOT NULL,
 `brennerstunden` float NOT NULL,
 `brennerstatus` int(11) NOT NULL,
 KEY `timestamp` (`timestamp`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

Tabellenstruktur für Tabelle solar

CREATE TABLE IF NOT EXISTS `solar` (
 `timestamp` datetime NOT NULL,
 `solarstunden` float NOT NULL,
 `solarleistung` float NOT NULL,
 `solarpumpe` int(11) NOT NULL,
 KEY `timestamp` (`timestamp`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

Tabellenstruktur für Tabelle temperaturen

CREATE TABLE IF NOT EXISTS `temperaturen` (
 `timestamp` datetime NOT NULL,
  `aussentemperatur` float NOT NULL,
  `warmwasser` float NOT NULL,
  `speicher_unten` float NOT NULL,
  `kollektor` float NOT NULL,
  `vorlaufsolltemperaturM2` float NOT NULL,
  KEY `timestamp` (`timestamp`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

Tabellenstruktur für Tabelle snapshot

CREATE TABLE IF NOT EXISTS `snapshot` (
 `timestamp` datetime NOT NULL,
 `brennerstatus` float NOT NULL,
 `brennerstarts` float NOT NULL,
 `brennerstunden` float NOT NULL,
 `solarstunden` float NOT NULL,
 `solarleistung` float NOT NULL,
 `aussentemperatur` float NOT NULL,
 `warmwasser` float NOT NULL,
 `speicher_unten` float NOT NULL,
 `kollektor` float NOT NULL,
 `kesseltemperatur` float NOT NULL,
 `vorlauftemperaturM2` float NOT NULL,
 `vorlaufsolltemperaturM2` float NOT NULL,
 `raumsolltemperaturM1` float NOT NULL,
 `raumsolltemperaturM2` float NOT NULL,
 `raumsolltemperaturredM1` float NOT NULL,
 `raumsolltemperaturredM2` float NOT NULL,
 `warmwassersoll` float NOT NULL,
 `kesseltemperatursoll` float NOT NULL,
 `pumpestatusM1` float NOT NULL,
 `pumpestatusSP` float NOT NULL,
 `pumpestatussolar` float NOT NULL,
 `statusstoerung` varchar(100) CHARACTER SET latin1 COLLATE latin1_german1_ci NOT NULL,
 `systemzeit` varchar(100) CHARACTER SET latin1 COLLATE latin1_german1_ci NOT NULL,
 `error0` varchar(500) CHARACTER SET latin1 COLLATE latin1_german1_ci NOT NULL,
 `BetriebArt` varchar(20) NOT NULL,
 `BetriebArtM2` varchar(20) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

Die snapshot-Tabelle soll keine Historie enthalten, sondern immer nur eine Zeile mit den alle zwei Minuten ausgelesen Werten. Das hat sich nach ein paar Monaten Betrieb als sinnvoll erwiesen, da der Raspberry doch ganz schön ackern muss um aus einer Tabelle mit > 150.000 Zeilen (und wachsend) die aktuellen Werte auf der Webseite anzuzeigen.

Werte in Datenbank speichern

In oben beschriebene Datei update.tmpl werden dann die Werte genutzt, um sie in der MySQL-Datenbank zu speichen. Dazu nutze ich den Aufruf des Kommandozeilentools „mysql“ welches bei der Installation der Datenbank enthalten ist.

Hier die drei Aufrufe mit INSERT-Statements für die Temperatur-, die Solar und die Brennerdaten. In $1, $2 , $3, … stehen die Werte gemäß der Reihenfolge der vc-commands.txt.

mysql --user= --password=  -e "INSERT INTO temperaturen (timestamp,aussentemperatur,warmwasser,kollektor,speicher_unten,vorlaufsolltemperaturM2) values (CURRENT_TIMESTAMP,$1,$2,$4,$5,$13);"
mysql --user= --password=  -e "INSERT INTO solar (timestamp,solarstunden,solarleistung) values (CURRENT_TIMESTAMP,$9,$10);"
mysql --user= --password=  -e "INSERT INTO brenner (timestamp,brennerstarts,brennerstunden,brennerstatus) values (CURRENT_TIMESTAMP,$6,$7,IFNULL('$19',0));"

Für die Snapshot-Tabelle nutze ich ein Update-Statement da diese Tabelle immer nur eine Zeile enthalten soll.

mysql --user= --password=  -e "UPDATE snapshot SET timestamp=CURRENT_TIMESTAMP,aussentemperatur=$1,warmwasser=$2,kollektor=$4,speicher_unten=$5,solarstunden=$9,solarleistung=$10,brennerstarts=$6,brennerstunden=$7,kesseltemperatur=$12,vorlauftemperaturM2=$13,vorlaufsolltemperaturM2=$14,raumsolltemperaturM1=$15,raumsolltemperaturM2=$16,raumsolltemperaturredM1=$17,raumsolltemperaturredM2=$18,brennerstatus=IFNULL('$19',0),warmwassersoll=$20,kesseltemperatursoll=$21,pumpestatusM1='$22',pumpestatussp='$23',pumpestatussolar='$24',statusstoerung='$R25',systemzeit='',error0='$R27',BetriebArt='$R29',BetriebArtM2='$R30';"

Wenn der Cron eingerichtet ist, sollte die Datenbank alle 6 Minuten mit den ausgelesenen Werten gefüllt werden.

Anzeige der Daten

Die Anzeige der Daten erfolgt mittels Webseite und PHP-Script. Auf einer Übersichtsseite lese ich die Snapshot-Tabelle aus. Das geht Dank der Snapshot-Tabelle auch recht schnell.

temp_uebersicht

Auf einer weiteren Seite werden zusätzliche Werte aus der Snapshot-Tabelle angezeigt.

temp_details

Weil die Erstellung des Graphen nicht auf der Snapshot-Tabelle basiert, dauerte die Anzeige dieser Seite auch immer recht lange. Daher wird die PNG-Grafik für den Graph nun viertelstündlich per Script erstellt und nur noch das fertige Bild in der Webseite angezeigt.

Hier der Crontab-Eintrag für das Script zur Erstellung der Grafik:

*/15 * * * * sudo php /var/www/heizung/update_graph.php &> /dev/null

und das Script selber. Zur Erstellung des Graphen nutze ich pChart.

[cclN_php]
= NOW()“;
$query = mysql_query($sql) or die(„Anfrage nicht 1 erfolgreich“);
while ($wert = mysql_fetch_array($query)) {
$aussentemperatur[]=$wert[0];
$warmwasser[]=$wert[1];
$speicher_unten[]=$wert[2];
$kollektor[]=$wert[3];
$vorlauf[]=$wert[4];
if ($zeit_tmp==$wert[5])
{ $zeit_tmp=NULL; }
else
{ $zeit_tmp=$wert[5]; }
$zeit[]=$zeit_tmp;
}
mysql_close($connection);

$MyData = new pData();
$MyData->addPoints($aussentemperatur,“TempA“);
$MyData->addPoints($warmwasser,“TempW“);
$MyData->addPoints($speicher_unten,“TempS“);
$MyData->addPoints($kollektor,“TempK“);
$MyData->addPoints($vorlauf,“TempV“);
$MyData->addPoints($zeit,“Labels“);

$MyData->setSerieWeight(„TempA“,5);
$MyData->setSerieWeight(„TempS“,5);
$MyData->setSerieWeight(„TempW“,5);
$MyData->setSerieWeight(„TempK“,5);
$MyData->setSerieWeight(„TempV“,5);

$MyData->setSerieOnAxis („TempA“, 0);
$MyData->setSerieOnAxis („TempS“, 0);
$MyData->setSerieOnAxis („TempK“, 0);
$MyData->setSerieOnAxis („TempW“, 0);
$MyData->setSerieOnAxis („TempK“, 0);
$MyData->setAxisName(0,“Temperatur“);

$MyData->setSerieDescription(„Labels“,“Uhrzeit“);
$MyData->setSerieDescription(„TempA“,“Aussentemperatur“);
$MyData->setSerieDescription(„TempS“,“Speicher“);
$MyData->setSerieDescription(„TempW“,“Warmwasser“);
$MyData->setSerieDescription(„TempK“,“Kollektor“);
$MyData->setSerieDescription(„TempV“,“Vorlauf“);
$MyData->setAbscissa(„Labels“);

$serieSettings = array(„R“=>0,“G“=>0,“B“=>200);
$MyData->setPalette(„TempA“,$serieSettings);
$serieSettings = array(„R“=>200,“G“=>0,“B“=>0);
$MyData->setPalette(„TempS“,$serieSettings);
$serieSettings = array(„R“=>0,“G“=>200,“B“=>0);
$MyData->setPalette(„TempW“,$serieSettings);
$serieSettings = array(„R“=>0,“G“=>100,“B“=>100);
$MyData->setPalette(„TempK“,$serieSettings);
$serieSettings = array(„R“=>100,“G“=>100,“B“=>100);
$MyData->setPalette(„TempV“,$serieSettings);

$myPicture = new pImage(1205,330,$MyData);
$myPicture->Antialias = FALSE;
$myPicture->drawRectangle(0,0,1204,329,array(„R“=>111,“G“=>143,“B“=>204));
$myPicture->setFontProperties(array(„FontName“=>“/var/www/heizung/fonts/verdana.ttf“,“FontSize“=>11));
$myPicture->drawText(200,35,“Temperaturen der letzten 3 Tage“,array(„FontSize“=>12,“Align“=>TEXT_ALIGN_BOTTOMMIDDLE));
$myPicture->setFontProperties(array(„FontName“=>“/var/www/heizung/fonts/verdana.ttf“,“FontSize“=>7));
$myPicture->setGraphArea(50,40,1155,300);
$labelSkip = floor(count($zeit)/48);
$scaleSettings = array(„XMargin“=>10,“YMargin“=>10,“Floating“=>TRUE,“GridR“=>180,“GridG“=>180,“GridB“=>180,“DrawSubTicks“=>FALSE,“CycleBackground“=>TRUE,“ScaleSpacing“=>10,“LabelSkip“=>$labelSkip,“DrawYLines“=>array(0));
$myPicture->drawScale($scaleSettings);
$myPicture->drawLegend(600,20,array(„Style“=>LEGEND_NOBORDER,“Mode“=>LEGEND_HORIZONTAL));
$myPicture->Antialias = FALSE;
$myPicture->setShadow(TRUE,array(„X“=>1,“Y“=>1,“R“=>0,“G“=>0,“B“=>0,“Alpha“=>10));
$myPicture->drawLineChart(array(„DisplayValues“=>FALSE,“DisplayColor“=>DISPLAY_AUTO));
$myPicture->Render(„/var/www/heizung/tmp/temperaturen.png“);
?>
[/cclN_php]

Hier noch eine Übersichtsseite verschiedener Durchschnittswerte. Diese werden direkt mit Aufruf der Seite generiert was entsprechend lange dauert. Diese Seite schaue ich aber nicht so oft an…

temp_details2

Viel Spaß mit dem Auslesen der Viessmann-Heizung!
Chris

ehz – Baudrate in „Autostart“

Um die Baudrate für den Lesekopf im Artikel http://blog.bubux.de/?p=89 beim Start des Raspi automatisch zu setzen, kann der stty-Befehl in der Datei /etc/rc.local eingefügt werden.

Das schaut dann z.B. so aus:

#!/bin/sh -e
#
# rc.local
#
# This script is executed at the end of each multiuser runlevel.
# Make sure that the script will "exit 0" on success or any other
# value on error.
#
# In order to enable or disable this script just change the execution
# bits.
#
# By default this script does nothing.

# Print the IP address
_IP=$(hostname -I) || true
if [ "$_IP" ]; then
  printf "My IP address is %s\n" "$_IP"
fi

sudo stty -F /dev/ttyUSB1 1:0:8bd:0:3:1c:7f:15:4:5:1:0:11:13:1a:0:12:f:17:16:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0

exit 0

Bei jedem Neustart des Raspi wird die Baudrate für den Lesekopf, der im Beispiel das Device „ttyUSB1“ ist, nun automatisch gesetzt. Das Device muss natürlich ggf. angepaßt werden.

Gruß Chris