Mich interessieren natürlich die Messwerte meiner Bewässerungsanlage. Aber es ist etwas umständlich dafür jedesmal in die Datenbank zu sehen. Deshalb werde ich eine kleine Visualisierung für meine Messdaten machen.In dieser möchte ich jeweils die letzten gemessenen Daten als Text sowie zwei Trend-Grafiken, einmal mit den Daten der letzten 24h und einmal die Daten der letzten Woche anzeigen.
Das Mittel der Wahl für mich, nachdem ich sowieso schon einen Webserver konfiguriert habe, ist natürlich die Visualisierung mittels einer kleinen Website. Aber das ist nicht der einzige Grund, es ist ein genereller Trend Front-Ends in Webtechnologien umzusetzen denn die Vorteile liegen auf der Hand: einfaches Deployment und weite Unabhängigkeit der Clientplattform (speziell auch im mobilen Bereich). Darüber hinaus haben sich in den letzten Jahren die Möglichkeiten dieser Technologie rapide gesteigert (Websockets, HTML5+SVG) und auch die Performance der Engines reicht mittlerweile aus um Trends und Grafiken flüssig zu rendern.
Die Querys die ich für die Abfrage der Daten verwende, habe ich vorher in der Workbench ausprobiert und zur besseren Lesbarkeit unten separat eingefügt. Damit die Daten gut interpretierbar sind, werden die Daten für die 24h auf Stunden, die Daten für die Wochenanzeige auf Vierteltage gemittelt. Den Wert für die Bodenfeuchtigkeit rechne ich zur besseren Darstellung auf einen Prozentwert (100%=1024) um.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
# Aktueller Datensatz SELECT DATE_FORMAT(`dt`, '%H:%i') as `dtshort`,`temp`,`humidity`,`soil` FROM `tbllogging` ORDER BY dt desc LIMIT 1; # letzte 24h (auf Stunden gemittelt) SELECT DATE_FORMAT(`dt`, '%H:%i') `dtshort`, AVG(`temp`) `temp`, AVG(`humidity`) `humidity`, (AVG(`soil`)/1024)*100 `soil` FROM `tbllogging` WHERE TIMESTAMPDIFF(HOUR,`dt`, NOW()) <= 24 GROUP BY DATE_FORMAT(`dt`, '%Y-%m-%d %H') ORDER BY dt asc; # letzte Woche (auf Vierteltage gemittelt SELECT DATE_FORMAT(MIN(`dt`), '%a %H:00') as `dtshort`, AVG(`temp`) `temp`, AVG(`humidity`) `humidity`, (AVG(`soil`)/1024)*100 `soil` FROM `tbllogging` WHERE TIMESTAMPDIFF(HOUR,`dt`, NOW()) <= 168 GROUP BY DATE_FORMAT(`dt`, '%Y-%m-%d'),FLOOR(EXTRACT(HOUR FROM `dt`)/6) ORDER BY dt asc;s |
Normalerweise würde ich die Querys in Stored Procedures kapseln aber leider mag die mysqli Extension Stored Procedure mit mehreren Rowsets nicht besonders, deshalb findet ihr die Querys hartkodiert im Code.
Für die Anzeige der Trenddaten greife ich auf eine fertige Charting Libary zurück. In dem Fall verwende ich Chart.js. Es ist sicher nicht die umfangreichste Libary, aber sie ist einfach, schnell und vor allem Open Source und gratis (MIT License).
Der Code für meine Webseite sieht wie folgt aus.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 |
<!doctype html> <?php ini_set('display_errors', 1); error_reporting(E_ALL); // MariaDB Server $servername = "serverip"; $username = "dbuser"; $password = "dbpassword"; $dbname = "templogg"; // Create connection $conn = new mysqli($servername, $username, $password, $dbname); // Check connection if ($conn->connect_error) { die("Connection failed: " . $conn->connect_error); } // Aktuelle Daten $sql = "SELECT DATE_FORMAT(`dt`, '%H:%i') as `dtshort`,`temp`,`humidity`,`soil` FROM `tbllogging` ORDER BY dt desc LIMIT 1;"; $actual = $conn->query($sql); if ($actual->num_rows > 0) { // output data of each row while($row = $actual->fetch_assoc()) { $actdt = $row["dtshort"]; $acttemp = number_format($row["temp"],0); $acthum = number_format($row["humidity"],0); $actsoil = number_format($row["soil"],0); } } else { echo "0 results"; } // 24h Daten $sqlday = "SELECT DATE_FORMAT(`dt`, '%H:%i') `dtshort`, AVG(`temp`) `temp`, AVG(`humidity`) `humidity`, (AVG(`soil`)/1024)*100 `soil` FROM `tbllogging` WHERE TIMESTAMPDIFF(HOUR,`dt`, NOW()) <= 24 GROUP BY DATE_FORMAT(`dt`, '%Y-%m-%d %H') ORDER BY dt asc;"; $daydt=""; $daytemp=""; $dayhum=""; $daysoil=""; $dayresult = $conn->query($sqlday); if($dayresult->num_rows > 0) { while($row = mysqli_fetch_array($dayresult, MYSQLI_ASSOC)) { $daydt.='"'.$row["dtshort"].'",'; $daytemp.=$row["temp"].","; $dayhum.=$row["humidity"].","; $daysoil.=$row["soil"].","; } } else { echo "0 results"; } // Daten letzte Woche $sqlweek = "SELECT DATE_FORMAT(MIN(`dt`), '%a %H:00') as `dtshort`, AVG(`temp`) `temp`, AVG(`humidity`) `humidity`, (AVG(`soil`)/1024)*100 `soil` FROM `tbllogging` WHERE TIMESTAMPDIFF(HOUR,`dt`, NOW()) <= 168 GROUP BY DATE_FORMAT(`dt`, '%Y-%m-%d'),FLOOR(EXTRACT(HOUR FROM `dt`)/6) ORDER BY dt asc;"; $weekdt=""; $weektemp=""; $weekhum=""; $weeksoil=""; $weekresult = $conn->query($sqlweek); if($weekresult->num_rows > 0) { while($row = mysqli_fetch_array($weekresult, MYSQLI_ASSOC)) { $weekdt.='"'.$row["dtshort"].'",'; $weektemp.=$row["temp"].","; $weekhum.=$row["humidity"].","; $weeksoil.=$row["soil"].","; } } else { echo "0 results"; } // Verbidnung schließen $conn->close(); ?> <html> <head> <title>Temp+Humidity Logger v1</title> <meta charset="utf-8" /> <meta name = "viewport" content ="width = 600"> <style type="text/css"> h1 {color:black;font-size: 350%;margin:0;} h2 {color:black;font-size: 150%;maring:0;} h3 {color:black;font-size: 80%;margin:0;} </style> <script type="text/javascript" src="Chart.min.js"></script> </head> <body> <center> <table border=0 width=600> <?php echo '<tr><th width=100><h2>' .$actdt.' </h2></th><th><h1>' . $acttemp. ' °C</h1></th><th width=120><h2>Luft: ' . $acthum. "%<br>Boden: ".$actsoil."</h2></th></tr>"; ?> </table> <hr> <span class="h3">die letzten 24h</span><br> <canvas id="DayChart" width="580" height="300"></canvas><br> <hr> <span class="h3">die letzte Woche</span><br> <canvas id="WeekChart" width="580" height="300"></canvas><br> <table border=0 width=600> <tr><th style="background-color:rgba(150,150,255,0.6)">Luftfeuchte</th></tr> <tr><th style="background-color:rgba(0,0,130,0.6)">Temperatur</th></tr> <tr><th style="background-color:rgba(172,194,132,0.6)">Boden</th></tr> </table> <script type="text/javascript"> // Global Chart definition Chart.defaults.global.scaleBeginAtZero=true; Chart.defaults.global.showTooltips=false; Chart.defaults.global.scaleOverride= true; Chart.defaults.global.scaleSteps=10; Chart.defaults.global.scaleStepWidth=5; Chart.defaults.global.scaleStartValue=10; // Day Chart var DayChartData = { labels : [<?php echo $daydt; ?>], datasets : [ { label: "Humidity", fillColor : "rgba(150,150,255,0.4)", strokeColor : "#9999FF", pointDot : false, bezierCurve : false, data : [<?php echo $dayhum; ?>] }, { label: "Soil", fillColor : "rgba(172,194,132,0.4)", strokeColor : "#ACC26D", pointDot : false, bezierCurve : false, data : [<?php echo $daysoil; ?>] }, { label: "Temp", fillColor : "rgba(0,0,130,0.4)", strokeColor : "#0000AA", pointDot : false, bezierCurve : false, data : [<?php echo $daytemp; ?>] } ] } var day = document.getElementById("DayChart").getContext("2d"); new Chart(day).Line(DayChartData); // Week Chart var DayChartData = { labels : [<?php echo $weekdt; ?>], datasets : [ { label: "Humidity", fillColor : "rgba(150,150,255,0.4)", strokeColor : "#9999FF", pointDot : false, bezierCurve : false, data : [<?php echo $weekhum; ?>] }, { label: "Soil", fillColor : "rgba(172,194,132,0.4)", strokeColor : "#ACC26D", pointDot : false, bezierCurve : false, data : [<?php echo $weeksoil; ?>] }, { label: "Temp", fillColor : "rgba(0,0,130,0.4)", strokeColor : "#0000AA", pointDot : false, bezierCurve : false, data : [<?php echo $weektemp; ?>] } ] } var week = document.getElementById("WeekChart").getContext("2d"); var weekchart = new Chart(week).Line(DayChartData); </script> </center> </body> </html> |
Das Ergebnis ist eine, wie ich meine, recht ansehnliche Website die auch auf Mobilgeräte einen guten Eindruck hinterlässt.