Erfahrungen mit Raspberry Pi und Banana Pi M2 Berry
Aktuelles Projekt
Messungen von Wetter- und Umweltdaten auf dem Balkon, mitten in der Stadt, in der Nähe einer verkehrsreichen Strasse, das schien mir sehr interessant. Allerdings sind die verwendeten "Messinstrumente" aus Kostengründen nicht sehr geeignet. Ihr Einsatz ist in Innenräumen vorgesehen. Weiterhin bringen sie wenig Präzision mit, so dass die absoluten Werten ungenau sein können. Beim CCS811 zeigt sich, dass bei Frost keine Werte für TVOC und eCO2 vorhanden sind.Die Anzeige ist für Google-Chrome und Opera ausgelegt, da Firefox einige CSS Einstellungen nicht unterstützt, besonders bei responsiven Darstellungen.
So sieht der gesamte Messaufbau aus:
Openweathermap
Mit dem Cronjob werden die Wetterdaten pro Stunde geholt.50 * * * * /usr/bin/php /home/username/WETTERverzeichnis/openwetterxml.php
<?php
$i=0;
while (($abfragexml = file_get_contents ('http://api.openweathermap.org/data/2.5/weather?id=2950157&appid=meine-API-ID&lang=de&units=metric&mode=xml')) === false)
{
sleep(30);
$i++;
if($i > 3)
{
echo "Abfrage 4 mal gescheitert!";
break;
}
}
//Verbindung zur Datenbank wetter herstellen
require_once "/home/username/WETTER/verbindungzurdatenbank.php";
//xml Objekt herstellen
$xml = simplexml_load_string($abfragexml);
//einzelne Werte aus dem XML-String holen
$stand = html_entity_decode($xml->lastupdate['value']);
$temp = html_entity_decode ($xml->temperature['value']);
$feuchte = html_entity_decode ($xml->humidity['value']);
$druck = html_entity_decode ($xml->pressure['value']);
//Eingabe in Datenbank
$mysqlimaximumminimum->query ("INSERT INTO `maximumminimum` SET `DatumUhrzeit` = '$stand', `Temperatur` = '$temp' `Luftdruck` = '$druck', `Feuchte` = '$feuchte'");
?>
Mit dem Cronjob werden die Vorhersagedaten geholt.
28 3 * * * /usr/bin/php vorhersageabfrage.php 2>&1 >>/home/username/log/vorhersageabfrage.log
Leider klappt das nicht problemlos, deshalb wird ggf. 4x wiederholt.<?php
$datum = date("Y-m-d");
$vorhersage = "/media/username/Datensicherung/openwmvorhersage/vorhersage-".$datum.".xml";
$i = 0;
while (($abfragexml = file_get_contents ("http://api.openweathermap.org/data/2.5/forecast?id=2950157&appid=meine-API-ID&lang=de&units=metric&mode=xml")) === false)
{sleep(30);
$i++;
if ($i > 3)
{
echo "Abfrage nach 4 Versuchen gescheitert!" . $datum . "\n\n";
break;
}
}
//Datei speichern
$wetterdatei = fopen($vorhersage,"w");
fwrite($wetterdatei,$abfragexml);
fclose($wetterdatei);
#E-Mailausgabe
echo "Daten wurden geholt! " . $datum . "\n\n";
?>
Maximum- und Minimumwerte
In der Datei werden die Maximum- und Minimumwerte der Temperaturen des Tages geprüft, die Werte und die Zeit, wann die Temperaturen aufgetreten sind, in einer anderen Tabelle gespeichert. Gleichzeitig wird von Luftfeuchtigkeit und Luftdruck der Mittelwert erzeugt und gespeichert. Auch die aktuelle Temperatur des Banana Pi wird ermittelt und gespeichert. In der kostenlosen Variante bekomme ich die Wetterdaten 2 Stunden später, deshalb läuft der Cronjob erst nach Mitternacht.
#aus den stündlichen Werten max und min holen und in wetterdb eintragen
20 3 * * * /usr/bin/php /home/username/cron-mittelwerte-erechnen.php
<?php
//Speichern von Wetterdaten in einer MySQL Datenbank, damit sie angezeigt werden koennen.
require_once "/home/WETTER/dbverbindung.php";
//temperatur banana pi
$BananaTemp = exec("cat /sys/devices/virtual/thermal/thermal_zone0/temp");
//gestern
$yesterday = new DateTime('yesterday');
$datum = $yesterday->format('Y-m-d');
//Abfrage in der "maxmin" Datenbank
//ermitteln der maximalen Temperatur mit Zeitpunkt
$MaxTemp = $mysqliTemperatur->query("SELECT DatumUhrzeit, Temperatur FROM maxmin WHERE DatumUhrzeit LIKE '%".$datum."%' ORDER BY Temperatur DESC LIMIT 1");
$row = mysqli_fetch_array($MaxTemp);
$BerlMaxTemp = $row['Temperatur'];
$MaxTime = explode ('T', $row['DatumUhrzeit']);
//ermitteln der minimalen Temperatur mit Zeitpunkt
$MinTemp = $mysqliTemperatur->query("SELECT DatumUhrzeit, Temperatur from maxmin where DatumUhrzeit like '%".$datum."%' ORDER BY Temperatur ASC LIMIT 1");
$row2 = mysqli_fetch_array($MinTemp);
$BerlMinTemp = $row2['Temperatur'];
$MinTime = explode ('T', $row2['DatumUhrzeit']);
//ermitteln des Durchschnitts von Luftdruck
$Dru = $mysqliTemperatur->query("SELECT avg(Luftdruck) as 'ludru' from maxmin where DatumUhrzeit like '%".$datum."%'");
$row3 = mysqli_fetch_array($Dru);
$LuDru = round($row3['ludru'],0);
//ermitteln des Durchschnitts von Luftfeuchtigkeit
$Feu = $mysqliTemperatur->query("SELECT avg(Feuchte) as 'lufeu' from maxmin where DatumUhrzeit like '%".$datum."%'");
$row4 = mysqli_fetch_array($Feu);
$LuFeu = round($row4['lufeu'],0);
//einfügen in Datenbank
$eingabeTest = $mysqliTemperatur->query("INSERT INTO `wettertabelle`(`Datum`,`BerlMaxTemp`,`MaxZeit`,`BerlMinTemp`,`MinZeit`,`LuDru`,`LuFeu`,`RaspiTemp`) VALUES ('$datum','$BerlMaxTemp','$MaxTime[1]','$BerlMinTemp','$MinTime[1]','$LuDru','$LuFeu','$BananaTemp')");
mysqli_close($mysqliTemperatur);
?>
Eine Verbindung zur Datenbank wird hergestellt, die MySQL-Tabelle abgefragt und die Daten mit einer HTML-Tabelle ausgegeben. Die while-Schleife sorgt dafür, dass alle Datensätze ausgegeben werden. Die php-Datei mit der Verbindungseinstellung zur Datenbank liegt nicht auf dem Webspace des Apache-Servers.
Verbindung zur Datenbank<?php
$mysqliTemperatur = new mysqli("localhost", "mysqlbenutzer", "mysqlpassword", "mysqldatenbankname");
$mysqliverbindung->set_charset("utf8mb4");
if (mysqliverbindung->connect_error)
{
echo "Fehler bei der Verbindung: " . mysqli_connect_error(); exit();
}
?>
<?php
//Ausgabe der gesamten Datenbank
$ergebnis = $mysqliTemperatur->query("SELECT * FROM mysqltabelle ORDER BY Datum DESC");
//Ausgabe der Tabelle
while($zeile = $ergebnis->fetch_array()) {
$date = new DateTime(htmlspecialchars($zeile['Datum']));
echo "<tr align=center>
<td bgcolor=#FFEC8B>" . $date->format('d.m.y') . "</td>
<td bgcolor=#ff6a6a>" . $zeile['BerlMaxTemp'] . "</td>
<td bgcolor=#ff6a6a>" . substr($zeile['MaxZeit'],0,5) . "</td>
<td bgcolor=#6699ff>" . $zeile['BerlMinTemp'] . "</td>
<td bgcolor=#6699ff>" . substr($zeile['MinZeit'],0,5) . "</td>
<td bgcolor=#ccffff>" . $zeile['LuDru'] . "</td>
<td bgcolor=#ffff66>" . $zeile['LuFeu'] ."</td>\
<td bgcolor=#EEC591>" . $zeile['RaspiTemp'] . "</td>
</tr>";
}
mysqli_close($mysqliTemperatur);
?>
Wetter-Balkendarstellung der Max- und Minwerte
Die Idee mit der Balkendarstellung war der erste Versuch, eine grafische Darstellung per PHP zu erreichen. Inspiriert hat mich dabei ein Artikel bei "www.schattenbaum.net". Spätere Grafikdarstellungen wurden dann mit Python's -matplotlib- realisiert. Die Datei für die Herstellung der Verbindung zur MySQL Datenbank liegt nicht auf dem Apache2 Webspace. Ist der Temperaturwert gleich 0, wird das Bild "balkenpixelnull.png" ausgegeben. Bei größer 0 wird "balkenpixelrot.png" und im anderen Fall "balkenpixelblau.png" ausgegeben. Die Balkenlänge richtet sich nach der Temperaturangabe. Bei 0 Grad ist die Balkenlänge einfach "rot-weiß-blau". Hier ist nur der PHP-Code für die Max-Tabelle dargestellt, da die Min-Tabelle ähnlich aufgebaut ist.<?php
$ergebnis = $mysqliTemperatur->query("SELECT Datum, BerlMaxTemp FROM mysqltabelle ORDER BY Datum DESC");
echo "<table cellpadding=0 cellspacing=0><caption>Openweather Max Wert für Berlin </caption>";
while($tag = $ergebnis->fetch_array()) {
if ($tag["BerlMaxTemp"] > 0)
{$balken = "balkenpixelrot.png";
$balkenlaenge = $tag["BerlMaxTemp"];}
else if ($tag["BerlMaxTemp"] < 0)
{$balken = "balkenpixelblau.png";
$balkenlaenge = abs($tag["BerlMaxTemp"]);}
else {$balken = "balkenpixelnull.png";
$balkenlaenge = 1;
}
$date = new DateTime(htmlspecialchars($tag["Datum"]));
echo "<tr><td>" . $date->format("d.m.y") . " </td><td><img src=".$balken." width=" . $balkenlaenge * 10 . " height="10"</td><td>" . $tag["BerlMaxTemp"] . "°C </td></tr>";
}
echo "</table>"
?>
Werte von openweathermap je Stunde anzeigen
Bei der Anzeige der Werte ist es sinnvoll, das Tagesdatum farbig hervorzuheben. So kann man leichter beim Scrollen den gewünschten Tag finden. Weiterhin zeigte sich, dass die Anzeige über den gesamten Zeitraum ungünstig ist. Deshalb wurde die Anzeige auf einen Monat begrenzt. Mit der Auswahl des Datums wird der anzuzeigende Monat ausgewählt.
<?php
setlocale(LC_TIME, "de_DE.utf8");
require_once "navi-hts1.php";
$datumpunkte = date("d.m.Y");
$heutestrich = date("Y-m-d");
$heutejahrmonat = ("Y-m");
?>
<!-- Ausgabe des ausgewählten Datum oder Ausgabe des aktuellen Tagesdatums -->
<h3>Monatliche Anzeige</h3>
<?php
if (isset($_POST["ADAT"]) && ($_POST['auswahldatum'] != 0)){
$auswahldatum= date("Y-m-d", strtotime($_POST['auswahldatum']));
//echo "<br>" . $auswahldatum . "<br>";
}
else
{$auswahldatum = $heutestrich;
//echo "<br>" . $auswahldatum . "<br>";
}
/*Öffnen der DB und erzeugen der MySQL Verbindung */
require_once "/home/verbindungdb.php";
$tagesfarbe = array ("White", "LightYellow", "Pink", "LightSalmon", "Orange", "Tomato","MediumSpringGreen", "SandyBrown", "SteelBlue", "Coral", "Khaki", "Orchid", "PaleGoldenrod", "LightCoral", "Thistle", "DarkKhaki", "Crimson", "SpringGreen", "Goldenrod", "DimGray", "Peru", "LightSlateGray", "DarkTurquoise", "LimeGreen", "SlateBlue", "PaleGreen", "PaleVioletRed", "Chartreuse", "LightSalmon", "Orange", "YellowGreen", "Aqua");
$anzeigedatum = substr($auswahldatum,0,7);
//echo $anzeigedatum;
$ausgabe = $mysqlimaxmin->query("SELECT * FROM `maxmin` WHERE `DatumUhrzeit` LIKE '%".$anzeigedatum."%' ORDER BY `DatumUhrzeit` DESC ");
?>
<form name="DATUM" action="<?php echo htmlspecialchars($_SERVER['PHP_SELF']);?>" method="post">
<input type="date" name="auswahldatum" value=<?php echo date("Y-m-d", strtotime($auswahldatum));?> min="2019-01-26" max=<?php echo $heutestrich;?> />
<input type="submit" value="Anzeigen" name="ADAT">
</form>
<?php
//Tabelle für die Ausgabe
echo "<table border=1 bordercolor=#00099 align=left cellpadding=1><caption><h3>Wetterdaten je Stunde für Berlin</h3></caption><thead align=center><th>Datum</th><th>Uhrzeit</th><th>Temp. °C</th><th>Druck hPa</th><th>Feucht %</th></thead>";
while($zeile = $ausgabe->fetch_array()) {
$datumzeit = htmlspecialchars($zeile['DatumUhrzeit']); //ISO8601 Zeit holen
$werttagesfarbe = intval(date_format (new DateTime($datumzeit),'d')); //Zahl für den Index von $tagesfarbe formatieren
//Ausgabe der Tabelle
echo "<tr align=center>
<td bgcolor=".$tagesfarbe[$werttagesfarbe] .">" . date_format (new DateTime($datumzeit),'d.m.Y') . "</td>
<td bgcolor=".$tagesfarbe[$werttagesfarbe] .">" . date_format (new DateTime($datumzeit),'H:i') . "</td>
<td bgcolor=Wheat>" . htmlspecialchars($zeile['Temperatur']) . "</td>
<td bgcolor=#ccffff>" . htmlspecialchars($zeile['Luftdruck']) . "</td>
<td bgcolor=#ffff66>" . htmlspecialchars($zeile['Feuchte']) . "</td>
<tr>";
}
echo "</table>";
mysqli_close($mysqlimaxmin);
?>
Vorschauwerte von openweathermap anzeigen
Bei der Anzeige der Vorschau war es sinnvoll, den Namen des Tages farbig hervorzuheben. Auch bei der Anzeige der einzelnen Werte zeigte die farbliche Unterscheidung gute Wirkung.
<?php
setlocale(LC_TIME, "de_DE.utf8");
$datum = date("Y-m-d");
$text = '';
function tagesfarbe ($tagesdatum){
$tagesfarbe = array("Silver", "DarkBlue", "Lime", "SaddleBrown", "Aquamarine", "RoyalBlue", "#FFFF00");
$zeit = strtotime($tagesdatum);
return $tagesfarbe[date("w", $zeit)];
}
function tageshintergrund ($tagesdatum){
$tageshintergrund = array("#2F4F4F", "#ffff74", "#ff00ff", "#00eb6c", "#A0522D", "#00FA9A", "#104e8b");
//$tageshintergrund = Sonntag, Montag, Dienstag, Mittwoch, Donnerstag, Freitag, Samstag
$zeit = strtotime($tagesdatum);
return $tageshintergrund[date("w", $zeit)];
}
//Trennung von Datum und Zeit
function datumzeit ($wertausxml)
{
$getrennt = explode ("T", $wertausxml);
return $getrennt;
}
//Wochentag in Deutsch
function wochentag ($tagesdatum){
$wochentage = array("Sonntag", "Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag");
$zeit = strtotime($tagesdatum);
return $wochentage[date("w", $zeit)];
}
// Umrechnung m/s in Bft
function windstaerke($n, $num)
{
$zwre = pow((($num - 0.07)/0.834), 2);
return round (pow($zwre, (1/$n)));
}
$xmldatei = "/home/vorschauverzeichnis/forecast-".$datum.".xml";
if (file_exists($xmldatei)){
$xml = simplexml_load_file($xmldatei);
//var_dump($xml);
}
$text .= $xml->location->name . " mit Stand vom : " . strftime("%A") . ", dem " . date("d.m.Y") "<br><br>";
$SoWiZeit = $xml->location->timezone; //Winter 3600, Sommer 7200
$sonnenaufgang = explode("T", $xml->sun['rise']);
$sonneauf = strtotime($sonnenaufgang[1])+ $SoWiZeit;
$text .= "<font color=orange>Sonnenaufgang : </font>	" . date("H:i",$sonneauf) . " Uhr<br>";
$sonnenuntergang = explode("T", $xml->sun['set']);
$sonneab = strtotime($sonnenuntergang[1])+ $SoWiZeit;
$text .= "<font color=red>Sonnenuntergang : </font>	" . date("H:i", $sonneab) . " Uhr<br><br>";
$text .= "<b>V o r h e r s a g e : </b><br><br>";
foreach ($xml->forecast->time as $key){
$text .= "<p><font color=DarkBlue>---- Zeitraum: <strong><font style=background-color:". tageshintergrund(datumzeit($key['from'])[0]) ."; color=" . tagesfarbe(datumzeit($key['from'])[0]) . ">" . wochentag(datumzeit($key['from'])[0]) . "</font></strong>, " . datumzeit($key['from'])[1] . " bis " . datumzeit($key['to'])[1] . "</font><br><font color=MediumVioletRed>Temperatur: " . $key->temperature['value'] . " °C </font><br><font color=MediumVioletRed>gefühlt wie: " . $key->feels_like['value'] . " °C </font><br><font color=DodgerBlue>Niederschlag: " . $key->precipitation['type'] . "</font><br><font color=DarkGreen> Luftdruck: " . $key->pressure['value'] . " hPa</font><br><font color=DarkOliveGreen> Luftfeuchte: " . $key->humidity['value'] . " % </font><br><font color=SteelBlue> Wolken: " . $key->clouds['all'] . " % -> " . $key->clouds['value'] . "</font><br><font color=DodgerBlue> Windrichtung: " . $key->windDirection['code'] . "</font><br><font color=RoyalBlue> Windstärke: " . windstaerke(3, $key->windSpeed['mps']) . "</font></p><br>";
}
echo $text;
?>
Balkonmessung
Die Messung auf dem Balkon ist auf die Werte von eCO², tVOC, Temperatur, Luftdruck und Luftfeuchtigkeit ausgerichtet. Für die Messung und Berechnung von eCO² und tVOC ist die Platine CCS811 eingesetzt. Für Temperatur, Druck und Feuchtigkeit verwende ich BME280. Das Programm ist in Python geschrieben und verwendet Programmteile aus verschiedenen (z.B. Adafruit) Veröffentlichungen im Internet. Seit Anfang 2023 habe ich einen SDS011 in Betrieb, der die Feinstaubkonzentration für Partikel zwischen 2,5 µg/m³ und 10µg/m³ ermittelt. Da die Messung per Cronjob ungünstig war (Aufwärmphase), habe ich eine Zeitsteuerung der Messung alle 10 Minuten eingebaut. Gestartet wird das Script per /etc/rc.local
#!/usr/bin/python # -*- coding: utf8 -*- # Quelle: Dragos Iosub, Bucharest 2020 https://itbrainpower.net # https://gist.github.com/kadamski/92653913a53baf9dd1a8 # https://zefanjas.de/wie-man-feinstaub-mit-einem-raspberry-pi-messen-kann/ from multiprocessing import connection import time, sqlite3 from datetime import datetime from time import sleep from ccs811_logfile_ausgabe_v3 import * from bme280 import * import serial, struct, sys, subprocess, locale #Messzeiten startliste = ["00","10","20","30","40","50"] # feste Werte sds011 DEBUG = 0 # bei 1 werden die einzelnen Bytes angezeigt CMD_MODE = 2 CMD_QUERY_DATA = 4 CMD_DEVICE_ID = 5 CMD_SLEEP = 6 CMD_FIRMWARE = 7 CMD_WORKING_PERIOD = 8 MODE_ACTIVE = 0 MODE_QUERY = 1 PERIOD_CONTINUOUS = 0 #USB-Serial für sds011 ser = serial.Serial() ser.port = "/dev/ttyUSB0" ser.baudrate = 9600 ser.open() ser.flushInput() byte, data = 0, "" def dump(d, prefix=''): with open("/home/pi/SENSE/tagesdb-feinstaub-"+datetime.now().strftime("%Y-%m-%d")+".log", "a") as logdatei: logdatei.write(prefix + ' '.join(x.encode('hex') for x in d)) logdatei.write("\n\r") def construct_command(cmd, data=[]): assert len(data) <= 12 data += [0,]*(12-len(data)) checksum = (sum(data)+cmd-2)%256 ret = "\xaa\xb4" + chr(cmd) ret += ''.join(chr(x) for x in data) ret += "\xff\xff" + chr(checksum) + "\xab" return ret if DEBUG: dump(ret, '> ') return ret def process_data(d): r = struct.unpack('<HHxxBB', d[2:]) pm25 = r[0]/10.0 pm10 = r[1]/10.0 checksum = sum(ord(v) for v in d[2:8])%256 return [pm25, pm10] def process_version(d): r = struct.unpack('<BBBHBB', d[3:]) checksum = sum(ord(v) for v in d[2:8])%256 def read_response(): byte = 0 while byte != "\xaa": byte = ser.read(size=1) d = ser.read(size=9) return byte + d if DEBUG: dump(d, '> ') return byte + d def cmd_set_mode(mode=MODE_QUERY): ser.write(construct_command(CMD_MODE, [0x1, mode])) read_response() def cmd_query_data(): ser.write(construct_command(CMD_QUERY_DATA)) d = read_response() values = [] if d[1] == "\xc0": values = process_data(d) return values def cmd_set_sleep(sleep): mode = 0 if sleep else 1 ser.write(construct_command(CMD_SLEEP, [0x1, mode])) read_response() # 1 = sleep 0 = work def cmd_set_working_period(period): ser.write(construct_command(CMD_WORKING_PERIOD, [0x1, period])) read_response() def cmd_firmware_ver(): ser.write(construct_command(CMD_FIRMWARE)) d = read_response() process_version(d) def cmd_set_id(id): id_h = (id>>8) % 256 id_l = id % 256 ser.write(construct_command(CMD_DEVICE_ID, [0]*10+[id_l, id_h])) read_response() #start CCS811, data update rate at 60sec; test 1 sekunden ccs811Begin(CCS811_driveMode_60sec) while True: global humidity22 global temperature22 try: if time.strftime("%M") in startliste: tagzeit=datetime.now() #prüfen, ob tagesdb existiert, wenn nicht erzeugen tagdb = '/home/username/db/DB-'+datetime.now().strftime("%Y-%m-%d")+'.sqlite3' #SQLite3 DB öffnen bzw. erzeugen connection = sqlite3.connect(tagdb) # #prüfen, ob in tagdb eine tabelle vorhanden ist? cursor = connection.cursor() connection.execute("CREATE TABLE IF NOT EXISTS ccsdhtmessung (TagZeit Text, Temperatur Real, Feuchte Real, Co2 Int, Tvoc Int, Luftdruck Real, PM25 Real, PM10 Real)") # Daten BME280 auslesen temperatur,pressure,luftfeuchte = readBME280All() #BME280 Luftdruck druck = (pressure) # ohne Korrekturwert lt. Höhe # Prüfen, ob die Daten im Limit sind, sonst Standardwerte einsetzen if ((luftfeuchte > 0) and (luftfeuchte < 99.9)): humidity22 = luftfeuchte else: humidity22 = 99.9 if ((temperatur > -40) and (temperatur < 80)): temperature22 = temperatur else: temperature22 = 21.0 #sds011 auslesen # sds011 ein cmd_set_sleep(0) time.sleep(5) # zeit zum Ansaugen cmd_set_working_period(PERIOD_CONTINUOUS) cmd_set_mode(MODE_QUERY) for t in range(5): values = cmd_query_data() if values is not None and len(values) == 2: pm25=values[0] pm10=values[1] time.sleep(5) cmd_set_sleep(1) #sds011 aus #CCS811 #Status auf Fehler prüfen #bei Fehler - Fehlergrund speichern #bei Fehler - Hardware Reset und Neustart if (ccs811CheckForError() == True): ccs811PrintError() #ccs811HWReset() ccs811Begin(CCS811_driveMode_60sec) else: ccs811SetEnvironmentalData(int(humidity22),int(temperature22)) if ccs811CheckDataAndUpdate(): CO2 = ccs811GetCO2() tVOC = ccs811GetTVOC() connection.execute ("INSERT INTO messung (TagZeit, Temperatur, Feuchte, Co2, tVOC, Luftdruck, PM25, PM10) VALUES (?,?,?,?,?,?,?,?);", (tagzeit,round(temperature22,1),round(humidity22,1),CO2,tVOC,round(druck),pm25,pm10)) connection.commit() endzeit=datetime.now() connection.close() time.sleep(60) #jede Minute prüfen, ob startliste enthalten ist except IOError: time.sleep(20) ccs811Begin(CCS811_driveMode_60sec) # Ende Schleife für ständige Messung
Balkonmessung
Eingesetzt wird hier der Raspberry Pi Zero 2 W. Die Messwerte werden in einer SQLite DB gespeichert, die in regelmäßigen Abständen (per CRON alle 15 Minuten) an den Banana Pi M2 Berry gesendet werden. Dort sind die Daten dann über die Seite https://mh48.de sichtbar.DB nach Banana Pi
05,15,25,35,45,55 * * * * /bin/sh /home/pi/cron-dbnachbpm2b.sh
#!bin/bash
sshpass -p "sshpassword" rsync -az /home/messung.db username@ip-vom-banana-pi:/home/CCS811.db
Erstellung der Grafik zu den Messwerten des Vortages per Cronjob um 1 Uhr
#!/usr/bin/python3 # -*- coding: utf8 -*- import numpy as np import matplotlib, locale matplotlib.use('Agg') # backend für png muss vor pyplot stehen!! import matplotlib.pyplot as plt plt.style.use('grayscale') import sqlite3, time from datetime import datetime, timedelta locale.setlocale(locale.LC_ALL, 'de_DE.utf8') #Datum vom Vortag yesterday = datetime.now() - timedelta(days=1) gestern = yesterday.strftime('%Y-%m-%d') gesterntitle = yesterday.strftime('%A, den %d. %B %Y') #Datum für Bildname gesternpng = yesterday.strftime('%Y-%m-%d') # Erzeugen Verbindung zur DB connection = sqlite3.connect("/home/username/db/DB-"+yesterday.strftime('%Y-%m-%d')+".sqlite3") cursor = connection.cursor() # Holen der Messwerte cursor.execute("SELECT Temperatur, Co2, tVOC, TagZeit, Feuchte, PM25, PM10 FROM messung") data = cursor.fetchall() connection.close() # Erzeugen der Listen für die einzelnen Werte xtagzeit = [] ytemp = [] yco2 = [] ytvoc = [] yfeucht = [] ypm25 = [] ypm10 = [] # Einlesen der Messwerte in die Listen for row in data: xtagzeit.append(row[3]) ytemp.append(row[0]) yco2.append(row[1]) ytvoc.append(row[2]) yfeucht.append(row[4]) ypm25.append(row[5]) ypm10.append(row[6]) # Plot zentral anpassen: plt.rcParams['figure.figsize'] = (12, 8) plt.rcParams['font.size'] = 12 plt.rcParams['lines.linewidth'] = 1 plt.rcParams['axes.facecolor'] = "lightblue" # Speichern in speicherplatz = "/var/www/html/messung/" # x-Achse als Zahl der Messungen xachse = np.linspace(0, 24, len(xtagzeit)) # Erzeugen der 6 einzelnen Bereiche fig, axs = plt.subplots(6) fig.suptitle('Balkonmessung am '+gesterntitle) # Beschriftung und Werteausgabe der einzelnen Bereiche axs[0].plot(xachse,ytemp, "-r", label='Temperatur °C') axs[0].legend(loc='best') axs[0].set_xticks(np.arange(24)) axs[0].grid() axs[1].plot(xachse,yco2, "-c", label='CO² ppm') axs[1].legend(loc='best') axs[1].set_xticks(np.arange(24)) axs[1].grid() axs[2].plot(xachse,ytvoc, "-g", label='TVOC ppb') axs[2].legend(loc='best') axs[2].set_xticks(np.arange(24)) axs[2].grid() axs[3].plot(xachse,yfeucht, "-b", label='Feuchte %') axs[3].legend(loc='best') axs[3].set_xticks(np.arange(24)) axs[3].grid() axs[3].set_xlabel('Uhrzeit am '+gesterntitle, color ='black') axs[4].plot(xachse,ypm25, "-m", label='2.5 µg/m³') axs[4].legend(loc='best') axs[4].set_xticks(np.arange(24)) axs[4].grid() axs[5].plot(xachse,ypm10, "-k", label='10 µg/m³') axs[5].legend(loc='best') axs[5].set_xticks(np.arange(24)) axs[5].grid() plt.xticks(np.arange(24)) # Zum Testen bevor ein Bild gespeichert wird #plt.grid() #plt.show() #matplotlib.use('Agg') ausgeschaltet ? # Speichern des Bildes plt.savefig(speicherplatz + gesternpng + "-balkonmessung.png", dpi=100)