Odroid C2 (vs Raspberry PI 2)

Zuerst einmal noch ein frohes neues Jahr an alle!

Die Tage habe ich ein neues Spielzeug bekommen (nein, nicht zu Weihnachten). Es ist ein Odroid C2 mit eMMC-Speicherkarte. Den gab es im Komplettpaket mit Odroid, eMMC, Gehäuse und Netzteil für 70€. Der Odroid besitzt einen Quad-Core mit 1,5 GHz, 2 GB RAM und ein Gigabit-LAN welches er sich, im Gegensatz zur Raspberry-Architektur, nicht mit den USB-Ports teilen muss. Somit sollte die Gesamtleistung in allen Belangen deutlich höher ausfallen als die des Raspberry 2 oder 3. Auf der eMMC war ein Ubuntu 16.04 LTS mit Mate Desktop vorinstalliert.

Odroid Oberseite
Odroid Oberseite

Mehr lesen

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

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

Snippet – MySQL Datenbank sichern

Da der Raspberry PI ab und an die schlechte Angewohnheit hat, z.B. bei einem Stromausfall das Filessystem auf der SD-Karte zu schreddern weil z.B. noch Schreiboperationen liefen, musste eine Möglichkeit der automatisierten Sicherung der MySQL Datenbanken her.

Datenbank sichern

Hierzu habe ich mysqldump genutzt. Gesichert werden alle Datenbanken (–all-databases) inkl. deren Struktur und Daten des entsprechenden Benutzers (–user). Das Ergebnis des Dumps wird in eine Datei geschrieben und mit gzip komprimiert. Bei mir landet die Datei auf einem NAS welches unter /media gemountet ist.

Im Dateiname wird mittels `date +%Y%m%d` immer das aktuelle Datum eingemischt damit die Sicherung vom Vortag nicht überschrieben wird.

Das Shell-Script sieht dann folgendermaßen aus:

#!/bin/bash
sudo mysqldump --user=<benutzer> --password=<passwort> --all-databases | gzip > /media/nas/db_sicherung_`date +%Y%m%d`.sql.gz
exit 0

Das Script rufe ich dann täglich mittels Cron auf. In meinem Fall immer um 21:15 Uhr. Ausgaben des Scripts (auch Fehler!) schreibe ich nach /dev/null.

Also crontab -e aufrufen und folgende Zeile hinzufügen:

15 21 * * * sudo /usr/local/bin/sicherung.sh &> /dev/null

Mehr lesen