Feinstaubsensor – Teil 12: Sensordaten selbst speichern

Unser Eigenbau-Feinstaubsensor ist fertig und liefert deine Daten an die Feinstaubkarte und vielleicht einige andere Umweltdatenbanken. Von dort können wir uns die Daten auch zurückholen – die eigenen und auch die von fremden Sensoren. Aber es gibt auch die Möglichkeit, einen eigenen Server zu betreiben, an den der Feinstaubsensor seine Daten zusätzlich abliefert. Die kann man dann nach Belieben in einer eigenen Datenbank speichern, auswerten, grafisch aufbereiten und als Webseite anzeigen. In diesem Artikel zeige ich, wie die Sensordaten vom Feinstaubsensor auf einen eigenen Server kommen.

Zuerst verweise ich wieder auf die originale Anleitung des Stuttgarter Feinstaub Projekts, die man in jedem Fall gelesen haben sollte, wenn man sich an den Eigenbau eines Feinstaubsensors machen will.

Was wird benötigt?

Ein eigener Feinstaubsensor natürlich, so wie in dieser Artikelserie beschrieben. Und ein Server. Der kann sich im eigenen Heimnetz befinden, aber auch im Internet. Was die Server-Software angeht, so gibt es da mehrere Möglichkeiten. Man könnte zum Beispiel einen REST-API-Server aufsetzen. REST steht für Representational State Transfer und meint Maschine-zu-Maschine-Kommunikation über das HTTP-Protokoll. Es geht aber auch mit vertrauteren Mitteln, nämlich mit einem LAMP-Server, was nichts anderes ist, als ein Web-Server auf Linuxbasis mit PHP und MySQL. Den könnte man auf einem Raspberry Pi für das eigene Heimnetz installieren ober man verwendet einen Webspace bei einem Provider im Internet. Lediglich PHP als serverseitige Programmiersprache muss verfügbar sein und falls die Daten in einer Datenbank gespeichert werden sollen, dann eben MySQL oder etwas ähnliches.

Steht ein Web-Server mit PHP zur Verfügung, dann sind grob nur zwei Dinge zu tun, die ich nachfolgend beschreiben werde:

  • Erstellen eines PHP-Programms, das die Sensordaten entgegen nimmt und
  • Konfigurieren des Feinstaubsensors, damit dieser das PHP-Programm anspricht.

PHP-Programm, zum Empfang von Feinstaubdaten

Wir beginnen mit einem Zweizeiler um zu sehen, was der Feinstaubsensor überhaupt an Daten sendet:

<?php
$json = file_get_contents("php://input");
file_put_contents("feinstaub.log", $json);
?>

Alten PHP-Web-Programmier-Hasen wird hier komisch vorkommen, wie die Daten übernommen werden. Das passiert nicht, wie bei der Entgegennahme eines Web-Formulars mit $_GET[’name‘] oder $_POST[’name‘]. Das hat den einfachen Grund, dass der Feinstaubsensor kein Web-Formular emuliert und es den Feldnamen ’name‘ in diesem Fall gar nicht gibt. Der CONTENT_TYPE ist nicht multipart/form-data, wie bei Web-Formularen, sondern application\/json, es wird also reines JSON ohne irgendwelche zusätzliche Verpackung versendet.

Und was ist JSON? Ausgeschrieben JavaScript Object Notation, hat JSON mit JavaScript eigentlich gar nichts zu tun, sondern ist ein beliebtes Datenaustauschformat. Beliebt deshalb, weil man in beliebigen Programmiersprachen sehr einfach Objekte oder beliebige Datenstrukturen in JSON wandeln und dann als Textstring verschicken kann. So kann zum Beispiel JavaScript in einem Web-Browser Daten mit einem PHP-Programm auf einem Web-Server im Hintergrund austauschen, ohne dass der Anwender die WebSeite neu laden muss. Das heißt dann AJAX (Asynchronous JavaScript and XML), auch wenn statt XML JSON verwendet wird. So weit nur ein kleiner begrifflicher Einstieg, für diejenigen, denen diese Begriffe fremd sind.

Was macht das Programm? In der ersten Zeile (also eigentlich in der zweiten, wenn wir das einleitende <?php mit zählen) werden die vom Feinstaubsensor gelieferten Daten in die Variable $json gepackt. Und in der folgenden Zeile wird der Inhalt mit file_put_contents in eine Datei namens feinstaub.log geschrieben. Eigentlich ganz einfach. Dem PHP Programm geben wir einen Namen (ich nenne es sensor.php)  und legen es in ein Unterverzeichnis /feinstaub auf den Webserver.

Je nach dem, wie die Rechtestruktur am Webserver angelegt ist, müssen wir ggf dafür sorgen, dass das PHP-Programm Schreibrechte auf die Datei feinstaub.log hat. Dazu legen wir eine leere feinstaub.log im Verzeichnis /feinstaub an und geben ihr mit chmod den Wert 666, also Schreib/Lese-Rechte für Eigentümer, Gruppe und Andere. Das kann bei einem fremden oder entfernten Server mit dem FTP-Client erfolgen.

Damit ist der Webserver empfangsbereit.

Feinstaubsensor konfigurieren

An eigene API sendenJetzt brauchen wir Zugriff auf die Konfigurations-Webseite unseres Feinstaubsensors. Zur Erinnerung, dazu geben wir folgendes im Webbrowser ein:

http://feinstaubsensor-ID.local/

wobei ID durch die jeweilige Chip-ID zu ersetzen ist. Im Falle meines Sensors also:

http://feinstaubsensor-534958.local/

Alternativ dazu, oder wenn es mit diesem Namen nicht funktioniert, kann man natürlich auch direkt die IP-Adresse des Sensors verwenden, wie im Beispiel rechts. Vom Hauptmenü klicken wir uns auf die Konfigurationsseite und blättern nach unten bis An eigene API senden. Den Punkt markieren wir mit einem Häkchen und füllen dann die nächsten drei Zeilen aus. Bei Server muss natürlich der eigene Servername oder dessen IP-Adresse eingetragen werden und nicht meiner. Der Pfad hängt davon ab, wo die PHP-Datei am Server liegt und wie sie heißt und der Port ist üblicherweise 80 für Http. Danach das Speichern nicht vergessen.

Nun wird bei jedem Datenversand, der alle 145 Sekunden erfolgt, auch der eigene Server mit beliefert. Die Datei feinstaub.log sollte also innerhalb der nächsten 2½ Minuten einen Inhalt bekommen. Den können wir mit einem Texteditor anschauen. Es wird eine einzige ziemlich lange Zeile sein mit vielen geschweiften und eckigen Klammern. Das ist JSON und etwas in Form gebracht sieht der Inhalt dann folgendermaßen aus:

{
  "esp8266id": "534958",
  "software_version": "NRZ-2017-099",
  "sensordatavalues":[
    {"value_type":"SDS_P1","value":"4.47"},
    {"value_type":"SDS_P2","value":"4.03"},
    {"value_type":"temperature","value":"15.60"},
    {"value_type":"humidity","value":"75.20"},
    {"value_type":"samples","value":"635908"},
    {"value_type":"min_micro","value":"226"},
    {"value_type":"max_micro","value":"27791"},
    {"value_type":"signal","value":"-90"}
  ]
}

Bei anderen oder zusätzlichen Sensoren wird der Inhalt natürlich etwas anders sein, aber die Struktur wird hier schon klar. Unter den sensordatavalues kommen im Beispiel zuerst die beiden Feinstaubwerte, dann Temperatur und Luftfeuchtigkeit. Zu den drei folgenden habe ich keine Erklärung und der letzte Wert bei signal ist die WLAN-Signalstärke in dBm.

PHP-Programm zur Verarbeitung der Sensordaten

Das zweizeilige PHP-Programm oben ist gut geeignet um einen Einblick in die Datenstruktur zu bekommen. Diese ist allerdings etwas kryptisch und der Dateiinhalt wird bei jeder neuen Datenlieferung überschrieben. Aber nachdem wir nun die Struktur der JSON-Daten kennen, können wir uns die interessanten Werte extrahieren und dann weiter verarbeiten. Diese Weiterverarbeitung kann die Ablage in einer SQL-Datenbank sein, oder wie im folgenden Beispiel die Generierung einer CSV-Datei (für Excel zum Beispiel).

<?php

$log = "feinstaub.log";
$schluessel = array("SDS_P1", "SDS_P2", "temperature", "humidity", "signal");

$json = file_get_contents("php://input");
$daten = json_decode($json, true);
$sensoren = $daten["sensordatavalues"];

$zeit = date("Y-m-d H:i:s");
$zeile = $zeit.",";

foreach ($schluessel as $key) {
 $index = array_search($key, array_column($sensoren, "value_type"));  # ab PHP 5.5.0
 $zeile .= $sensoren[$index]["value"].",";
}

$zeile = rtrim($zeile, ",") ."\n";
file_put_contents($log, $zeile, FILE_APPEND);

?>
ok

Was macht das Programm? Der Name der Logdatei bleibt unverändert, er wird jetzt nur in der Variablen $log gespeichert. Dann definiere ich ein Array $schluessel aus all den value_types, die von Interesse sind, um später nur nach diesen zu suchen. Jetzt kommt die bekannte Zeile, die die gesamte Datenstruktur entgegen nimmt und in der Variablen $json speichert. JSON ist ja an sich nur ein Textstring und damit zwar halbwegs gut lesbar, aber nur schwer programmmäßig zu bearbeiten. Einfacher tun wir uns, wenn wir den JSON-String in ein assoziatives Array überführen. Das macht json_decode und das Ergebnis ist dann ein assoziatives Array, das von der Struktur her dem JSON-String entspricht. Die Sensorwerte stecken in einem Array mit Namen sensordatavalues und diesen Teil der Datenstruktur hole ich mir zur Vereinfachung in die Variable $sensoren.

Nachdem die Daten vom Feinstaubsensor keinen Zeitstempel enthalten, müssen wir die Serverzeit dazu verwenden um die Sensordaten mit einer Uhrzeit zu verbinden. Dazu holen wir uns einen Datum-Zeit-String in die Variable $zeit.

Dieser Zeitstempel wird auch der erste Eintrag in $zeile. Diese Zeile wird später eine Datenzeile in der CSV-Datei, aber zuerst müssen wir sie zusammenbauen und dabei die einzelnen Werte durch Komma trennen.

Die Musik spielt dabei in der folgenden foreach-Schleife. Hier werden der Reihe nach alle interessierenden value_types aus dem Array $schluessel durchlaufen. Die erste Zeile innerhalb der Schleife sucht nach dem jeweiligen value_type und merkt sich dessen Position im Array in der Variablen $index. Mit diesem Index können wir dann den zugehörigen Wert unter value extrahieren und ihn gleich zusammen mit einem Komma an $zeile hinten anhängen.

An dieser Stelle ließe sich theoretisch auch eine Datenbank versorgen. Die Bezeichnung (analog zu value_type)  haben wir in $key und den Wert (entsprechend value) bekommen wir mit $sensoren[$index]["value"].

Nach der foreach-Schleife haben wir alle Werte in der Variablen $zeile beisammen, aber unschönerweise steht am Ende ein Komma zu viel. Das entfernen wir mit rtrim und fügen gleichzeitig eine Zeilenschaltung (\n) ans Zeilenende an. Jetzt brauchen wir die Zeile nur noch in die Logdatei zu schreiben. Das passiert wie im ersten Programm mit file_put_contents, aber mit dem Unterschied, dass wir die Datei nicht jedes mal überschreiben, sondern die neue Zeile an die vorhandene Datei hinten anhängen (FILE_APPEND).

Und so sieht der Inhalt der Logdatei dann auszugsweise aus:

2017-11-05 15:37:25,4.50,3.50,8.50,99.90,-89
2017-11-05 15:39:53,2.67,1.43,8.50,99.90,-90
2017-11-05 15:42:22,1.37,1.00,8.30,99.90,-89
2017-11-05 15:44:51,2.20,0.90,8.00,99.90,-84

Und was soll das ok am Ende des Programms? Das ok steht außerhalb des PHP-Bereichs, ist also kein Programmbestandteil, sondern eine Rückmeldung an den Feinstaubsensor. Ob der das wirklich auswertet, weiß ich nicht, im ersten Beispiel geht es ja auch ohne diese Rückmeldung. Aber ich hab das in einem Programmierbeispiel gesehen und schaden wird es kaum.

Weiterverarbeiten

Ich habe hier lediglich gezeigt, wie wir die Sensordaten unseres Feinstaubsensors auf einen eigenen Server bekommen und beispielhaft in einer CSV-Datei ablegen können. Damit ist aber noch nicht viel gewonnen, da diese Datei sehr schnell recht groß und unhandlich wird. Viel praktischer wäre es bei großen Datenmengen, die Werte in eine Datenbank zu speichern, MySQL zum Beispiel – PHP verfügt dazu über geeignete Module. Dann könnte ein weiteres PHP-Programm die Werte wieder aus der Datenbank extrahieren und in Form einer Webseite darstellen, zum Beispiel die aktuellen Werte mit Minimum und Maximum der letzten 24 Stunden. Denkbar wäre auch eine grafische Aufbereitung mit entsprechenden Tagesverläufen. Kreativen Programmierern sind hier kaum Grenzen gesetzt, den Umfang dieses Feinstaubprojekts würde das allerdings sprengen.

 


Weitere Artikel in dieser Kategorie:

Schreiben Sie einen Kommentar

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