Im 3. Teil dieser Tutorialreihe zum DHT11 Sensor am Raspberry PI möchte zeigen wie man die zuvor aufgezeichneten Daten (in eine CSV Datei) auf einer Webseite darstellen kann.
Aufbau
erweitern des Quellcodes
Bevor wir jedoch die Daten aus der CSV Datei verwenden können müssen wir noch einen Index sowie einen Zeitstempel hinzufügen.
Index erzeugen
Der Index soll immer mit jedem Messvorgang hochgezählt werden. Als erstes müssen wir uns eine Variable definieren welche wir dann mit jedem Messvorgang um eins erhöhen wollen (dieses nennt man auch Incrementieren).
index = 0 def readDht11Values(): global index index = index + 1 ....
Damit wir die Variable “index” in der Funktion “readDht11Values” manipulieren können müssen wir diese zuvor als “global” setzen.
Doch was passiert wenn wir unser Skript beenden und neustarten?
Genau – der Index beginnt wieder bei 0.
Index in einer Datei speichern und laden
Damit wir immer den korrekten Index verwenden speichern wir uns diese eine Zahl in einer Datei. Die Datei benennen wir einfach mit “index.store”.
Nun müssen wir nur noch diese Datei laden und den Index auslesen und mit einem neuen ersetzen.
#öffnen der Datei index.store im lese - Modus indexFile = open("index.store", "r") #erzeugen einer Variable mit dem initialen Wert 0 index = 0 #für jede Zeile in der Datei... for zeile in indexFile: #die Zeile in ein Integer (ganzahliger Wert) umwandeln und zuweisen index = int(zeile) #den Wert der Variable um eines erhöhen index = index + 1 #eventuell eine Ajusgabe auf der Konsole #print(index) #öffnen der Datei index.store im schreib-Modus indexFile = open("index.store", "w") #schreiben des aktuellen Wertes der Variable index in die Datei #es wird der aktuelle Wert dabei überschrieben #zusätzlich muss noch der Wert in ein String umgewandelt werden indexFile.write(str(index)) #schließen der Datei indexFile.close()
Bauen wir nun den Code in unser bestendes Skript ein.
Das laden & speichern des Indexes wird jeweils in einer Funktion ausgelagert.
index = 0 def readIndex(): indexFile = open("index.store", "r") index = 0 for z in indexFile: index = int(z) indexFile.close() return index def updateIndex(): global index index = index + 1 indexFile = open("index.store", "w") indexFile.write(str(index)) indexFile.close()
In der Funktion “readDht11Values” rufen wir nun als erstes die Funktion “readIndex” auf.
def readDht11Values(): global index index = readIndex() ...
Wenn das lesen der Werte geklappt hat, also wenn Daten empfangen wurden, dann soll der Index aktualisiert werden.
if humidity is not None and temperature is not None: ... updateIndex() ...
Unser Skript sollte nun wie folgt aussehen:
import Adafruit_DHT import time from datetime import datetime sensor = Adafruit_DHT.DHT11 pin = 4 csvFile = open("dht11values.csv", "a") index = 0 def readIndex(): indexFile = open("index.store", "r") index = 0 for z in indexFile: index = int(z) indexFile.close() return index def updateIndex(): global index index = index + 1 indexFile = open("index.store", "w") indexFile.write(str(index)) indexFile.close() def readDht11Values(): global index index = readIndex() timestamp = datetime.now() humidity, temperature = Adafruit_DHT.read_retry(sensor, pin) if humidity is not None and temperature is not None: print('Index={0:d} Time={1:s} Temperatur={2:0.1f}*C Luftfeuchtigkeit={3:0.1f}%'.format(index,str(timestamp),temperature, humidity)) csvFile.write(str(index) + ';'+ str(timestamp) +';') csvFile.write('{0:0.1f}*C; {1:0.1f}%'.format(temperature, humidity)) csvFile.write('\n') updateIndex() else: print('Fehler beim empfangen der Daten. Bitte versuche es erneut!') try: while True: readDht11Values() time.sleep(2) except KeyboardInterrupt: csvFile.close() pass
Und folgende Ausgabe auf der Konsole erzeugen:
In der Grafik habe ich den Abbruch des Skriptes gelb markiert. Es ist gut zu erkennen dass, das Skript beim neustarten mit dem Index fortfährt.
Apache Webserver
Für die Webseite benötigen wir einen Webserver mit php, wie man diesen auf einem Raspberry PI installiert, habe ich im Tutorial Raspberry PI : Installation erläutert.
Zunächst erzeugen wir uns unsere spätere Ordnerstruktur unter “/var/www/html/”
sudo mkdir dht11web cd dht11web sudo mkdir data sudo mkdir js sudo mkdir images
Wenn wir alle Ordner angelegt haben sollte der Verzeichnisbaum wie folgt aussehen:
In dem Ordner
- “data” wird die CSV Datei,
- “js” werden die JavaScript Dateien,
- “images” werden die Bilder
abgelegt.
kopieren der CSV Datei
Um die Daten aus der CSV Datei in der Webseite verarbeiten zu können, kopieren wir uns diese in den Ordner “data”.
Dieses können wir mit dem Befehl “cp” auf der Kommandozeile erledigen.
sudo cp ~/dht11csv_web/dht11values.csv .
Diesen Befehl setzen wir in dem Ordner “/var/www/html/dht11web/data” ab!
erstellen eines CronJobs
Da der Raspberry PI autonom arbeiten soll, lagern wir den Befehl in ein BashScript (*.sh) aus und erstellen einen CronJob welcher alle 5 min. diese Aufgabe erledigt.
erstellen eines Bash Scriptes
Damit wir in unserer CronTab später einen besseren Überblick haben erstellen wir uns ein kleines Bash Script welches den Befehl ausführt. Hierbei ist darauf zu achten dass, das Script mit root Rechten ausgeführt wird, somit müssen wir das Verzeichnis anpassen.
sudo cp /home/pi/dht11csv_web/dht11values.csv .
Erstellen wir also unsere Datei “copyCSV.sh” mit dem Befehl “sudo nano copyCSV.sh” und fügen den obrigen Code ein. Mit der Tastenkombination Strg+O speichern wir dieses und mit Strg+X verlassen wir den Editor.
Wenn wir nun das Script auf der Kommandozeile mit “sudo ./copyCSV.sh” aufrufen erhalten wir folgende Fehlermeldung:
Der Fehler beruht auf eine Fehlende Berechtigung der Datei. Diese Berechtigung wird mit dem Befehl “sudo chmod +x copyCSV.sh” gegeben.
Nun können wir erfolgreich das Script auf der Kommandozeile ausführen.
erstellen eines CronTab Eintrages
Zur CronTab gibt es ein sehr guten Wiki Artikel unter https://wiki.ubuntuusers.de/Cron/, in diesem Abschnitt möchte ich gerne zeigen wie du einen Eintrag erstellst welcher einen Befehl alle 5min. ausführt.
Zunächst öffnen wir die CronTab Datei mit
sudo crontab -e
Nachdem öffnen sehen wir bereits Beispieleinträge welche aufzeigen wie diese Datei befüllt wird.
Fügen wir also nun folgende Zeile ein:
5 * * * * sudo /var/www/html/dht11web/data/copyCSV.sh > /var/www/html/dht11web/data/messageLog.log 2>&1
Was machen wir hier genau?
Wir erstellen einen Job welcher
- alle 5 Minuten,
- zu jeder Stunde,
- an jedem Tag des Monats,
- in jedem Monat,
- an jedem Tag der Woche
den Befehl “sudo /var/www/html/dht11web/data/copyCSV.sh” startet.
Die Ausgabe welcher der Befehl zu folge hat wird in die Datei “messageLog.log” geschrieben.
Intervallmessung durchführen
Nachdem wir nun unseren Quellcode des Python Skriptes angepasst haben und eine CronJob eingerichtet haben welcher unsere Messdaten in ein Verzeichnis unseres Webservers kopiert können wir nun unsere Webseite erstellen. Wir müssen jedoch immernoch das Python Skript händisch auf der Konsole ausführen und dann ist diese geblockt bis wir das Skript per Strg+C abbrechen.
anpassen des Python Skriptes
Wollen wir also nun eine “kleine” Anpassung durchführen so das unser Python Skript einmal ausgeführt wird und sich dann beendet. Dazu müssen wir nur die Endlosschleife entfernen. Wir benötigen also nur noch folgenden Aufruf:
readDht11Values() csvFile.close()
Wenn wir nun auf der Konsole das Python Skript “dht11.py” aufrufen, wird eine Messung durchgeführt und gespeichert, danach beendet sich das Programm von alleine.
Damit ist aber noch nicht alles getan, denn der spätere CronJob läuft als ein bestimmter User und vor allem in einem Verzeichnis. Daher müssen wir nun das Python Skript so anpassen das absolute Pfade zu den Dateien “index.store” & “dht11values.csv” erstellt werden. Damit wir uns etwas Schreibarbeit sparen erstellen wir eine Variable und geben dieser als Wert den Pfad.
folder = "/home/pi/dht11csv_web/"
Nun müssen wir nur noch überall dort wo wir einen Zugriff auf die Dateien erstellen diese Variable hinzufügen, zbsp.
def readIndex(): indexFile = open(folder+"index.store", "r") ...
erstellen eines weiteren CronJobs
Nun erstellen wir uns einen weiteren CronJob welcher alle 2 Minuten eine Messung durchführt.
2 * * * * sudo python3 /home/pi/dht11csv_web/dht11.py > /var/www/html/dht11web/data/pyMsgLog.log 2>&1
Was machen wir hier genau?
Wir haben nun ein Job erstellt der:
- alle 2min,
- zu jeder Stunde,
- an jedem Tag des Monats,
- in jedem Monat,
- an jedem Tag der Woche
den Befehl “python3 …” ausführt, das Ergebnis dieser Ausgabe wird in die Datei “pyMsgLog.log” geschrieben. In dieser Datei finden wir Fehler und auch die “normale” Konsolenausgabe mit unseren Messwerten.
Aufbau der Webseite
Die Webseite soll zunächst die Daten in einer Tabelle darstellen (später werden wir diese Daten aufarbeiten und in einem Diagramm darstellen). Für diese “arbeit” benötigen wir nun eine nächste Skriptsprache, ich verwende hier Php.
HTML Grundgerüst
Aber zunächst einmal das Grundgerüst in HTML. Jede Webseite beginnt in der Datei “index.html”. Bzw. wenn wir eine URL in der Adresse eingeben und mit Enter/Return bestätigen versucht der Webserver eine solche Datei zurück zu liefern.
Das Skripten von HTML Dateien in einem Editor unter Linux ist doch etwas unschön daher habe ich mir einen FTP Benutzer eingerichtet und werde nun mit dem Editor PS-Pad auf den Raspberry PI zugreifen und von dort die Dateien erstellen & bearbeiten.
<html> <head> <title>DHT11 Sensorwerte</title> </head> <style> table { border-collapse: collapse; } table, th, td { border: 1px solid black; } </style> <body> <table > <thead> <tr> <th> </th> <th>Zeit</th> <th>Temperatur</th> <th>Luftdruck</th> </tr> </thead> <tbody> </tbody> </table> </body> </html>
Da wir zunächst nur die Daten in eine Tabelle ausgeben wollen benötigen wir zunächst eine Tabelle mit 4 Spalten (Index, Zeit, Temperatur und Luftdruck). In dem HTML Tag “<tbody” werden dann die Daten aus der CSV Datei geladen und über eine Schleife ausgegeben.
<?php //laden der Daten aus der Datei $content = file_get_contents('./data/dht11values.csv',true); //splitten an jedem Zeilenumbruch, Ergebnis ist ein Array mit allen Zeilen $lines = explode("\n", $content); //löschen der ersten Zeile, diese enthält "nur" die Spaltenüberschriften unset($lines[0]); //für jeden Eintrag im Array mache ... foreach ($lines as $line){ //die Zeile an jedem Semikolon splitten, als Ergebnis erhält man ein Array welches alle Zellen enthält $data = explode(";", $line); //Wenn alle Einträge im Array belegt sind dann ... if(strlen($data[0]) > 0 && strlen($data[1]) > 0 && strlen($data[2]) > 0 && strlen($data[3]) >0) { //Begin einer Zeile in der Tabelle echo "<tr>"; //Ausgeben einer Zelle mit dem Index echo "<td>".$data[0]."</td>"; //Ausgeben einer Zelle mit dem Formatierten Wert des Datums echo "<td>".convertDateTime($data[1])."</td>"; //Ausgeben der Temperatur, es wird am Sternchen * gesplittet so das man nur den Zahlenwert erhält echo "<td>".explode("*",$data[2])[0]."</td>"; //Ausgeben des Luftdrucks, es wird am % gesplittet so das man nur den Zahlenwert erhält echo "<td>".explode("%",$data[3])[0]."</td>"; //Beenden der Zeile in der Tabelle echo "</tr>"; } } ?>
Das Formatieren des Zeitstempels habe ich in eine Funktion ausgelagert. Der Block mit dieser Funktion wird vor dem ersten HTML Tag geschrieben.
<?php function convertDateTime($dateTime){ $date = new DateTime($dateTime); return date_format($date, 'd.m.Y H:i:s'); } ?> <html> ...
fertige Webseite mit Sensordaten
Nach diesem Tutorial werden nun die Sensordaten des DHT11 Sensors auf einer Webseite tabellarisch dargestellt.
Ausblick
Als nächstes werden wir nun ein Liniendiagramm implementieren und ein Event erzeugen welches die Seite periodisch Aktualisiert.
Download
Hier findest du alle Sourcen zu diesem Tutorial bequem zum Download.
Guten Tag,
Ich bin gerade dabei deine Anleitung vorzubereiten und habe bemerkt, dass du bisher noch nicht vorgestellt hast, wie man die Daten jetzt in einem Diagramm darstellt, bzw. (Was mir am wichtigsten wäre) wie sich die Website periodisch aktualisiert.
Ich hoffe, dass du das bald noch machen könntest, damit ich meine Website auch wirklich umsetzen kann.
Bis dahin,
MfG Leon Horvath
Hallo,
ich habe den Beitrag erstellt und dieser geht heute abend 18 Uhr online.
Melde dich doch einfach nochmal sollte etwas fehlen oder unklar geblieben sein.
Gruß,
Stefan Draeger
Hallo,
ich verstehe ab Aufbau der Webseite nicht mehr was ich da machen soll.
Kann mir da einer vilt Bitte weiterhelfen?
Hi,
wo gibt es denn genau Probleme?
Gerne helfe ich dir weiter.
VG
Stefan Draeger
Mein Problem ist es das ich wenn ich die Webseite aufrufe da nur der Code steht. Ich habe auch festgestellt, dass meine Sensor Daten sich nicht aktualisieren 🙁
Kleines Update php war nicht richtig installiert. Jetzt habe ich nur noch Probleme, damit das der Sensor nicht Automatisch angeht.
Ps: Danke schon mal
LG
Hadi