Raspberry PI Tutorial #6: DHT11 Sensor ansteuern (Teil3), darstellen der Werte auf einer Webseite

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.

Raspberry PI mit DHT11 Sensor
Raspberry PI mit DHT11 Sensor

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“.

erstellen der Datei zum speichern des Indexes
erstellen der Datei zum speichern des Indexes

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()

Ausgabe des Indexes auf der Konsole
Ausgabe des Indexes auf der Konsole

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:

Ausgabe des Python Skriptes auf der Konsole
Ausgabe des Python Skriptes auf der Konsole

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:

Verzeichnisstruktur des DHT11Web Projektes
Verzeichnisstruktur des DHT11Web Projektes

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:

Fehlermeldung beim Ausführen eines Bash Scriptes
Fehlermeldung beim Ausführen eines Bash Scriptes

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.

CronTab
CronTab

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.

Ausführen des Python Skriptes auf der Konsole
Ausführen des Python Skriptes auf der Konsole

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>&nbsp;</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.

Webseite mit Daten des DHT11 Sensors
Webseite mit Daten des DHT11 Sensors

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.

7 Kommentare

  1. 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

    1. 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

      1. 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 🙁

      2. 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

Kommentar hinterlassen

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert