Dynamische Visualisierung von Sensordaten mit PHP: Ein praktisches Beispiel In diesem Blogeintrag möchte ich Ihnen ein praktisches PHP-Skript vorstellen, das Sensordaten aus einer Datenbank ausliest, verarbeitet und als ansprechenden Graphen visualisiert. Die Daten stammen von
Dynamische Visualisierung von Sensordaten mit PHP: Ein praktisches Beispiel
In diesem Blogeintrag möchte ich Ihnen ein praktisches PHP-Skript vorstellen, das Sensordaten aus einer Datenbank ausliest, verarbeitet und als ansprechenden Graphen visualisiert. Die Daten stammen von einem BME68x-Sensor, der verschiedene Umgebungsparameter wie Temperatur, Feuchtigkeit und Luftdruck misst. Dieser Sensor ist ideal für Projekte, die Umwelt- und Klimadaten aufzeichnen und analysieren möchten.
Das hier gezeigte Skript greift auf die Datenbank zu, in der die Messwerte des Sensors gespeichert sind, und erzeugt basierend auf den aufgezeichneten Werten einen dynamischen Graphen. Dieser Graph zeigt die Innentemperatur über den Verlauf des aktuellen Tages sowie den Ventilstatus, während auch die Temperatur des Vortages berücksichtigt wird.
Das folgende PHP-Skript zeigt, wie man Daten aus einer MySQL-Datenbank abruft und visuell darstellt, indem es einen Graphen mit Temperatur- und Ventilstatus (Valvestate) über den Tag erstellt. Es bietet eine solide Grundlage, um IoT-Daten oder andere zeitbasierte Messwerte in einer ansprechenden Grafik darzustellen.
1. Ziel des Skripts
Dieses PHP-Skript wurde entwickelt, um Sensordaten wie Raumtemperatur und Ventilstatus aus einer MySQL-Datenbank zu visualisieren. Es generiert dynamisch ein Bild im PNG-Format, das den Verlauf der Daten für den aktuellen Tag anzeigt. Optional werden auch Daten des Vortages dargestellt, um Vergleiche zu ermöglichen.
2. Funktionen und Merkmale
Das Skript enthält folgende Hauptmerkmale:
- Datenbankintegration: Es verbindet sich mit einer MySQL-Datenbank, um Sensordaten für den aktuellen Tag und den Vortag abzurufen.
- Grafikerstellung: Mit PHPs GD-Library werden Achsen, Linien und Datenpunkte gezeichnet.
- Dynamische Achsenskalierung: Die Skala passt sich automatisch den minimalen und maximalen Temperaturwerten an.
- Vergleichsdaten: Der Graph des Vortages wird in einer anderen Farbe überlagert, um Trends zu verdeutlichen.

3. Detaillierte Erklärung des Skripts
Datenbankverbindung
Das Skript verbindet sich mit der MySQL-Datenbank, indem es die Zugangsdaten bereitstellt:
$servername = "192.168.x.y";
$username = "dbuser";
$password = "db_password";
$dbname = "sensor_data";
$conn = new mysqli($servername, $username, $password, $dbname);
if ($conn->connect_error) {
die("Verbindung zur Datenbank fehlgeschlagen: " . $conn->connect_error);
}
Datenabruf für den aktuellen Tag
Ein SQL-Statement wird verwendet, um die Daten des aktuellen Tages abzufragen:
$currentDate = date("Y-m-d");
$sql = "SELECT time, Innentemperatur, valvestate FROM sensor_data WHERE date = '$currentDate' ORDER BY time";
$result = $conn->query($sql);
Die Ergebnisse werden in einem Array gespeichert, das später für die Grafikerstellung genutzt wird.
Erstellung der Grafik mit GD-Library
Die Hauptaufgabe des Skripts ist das Zeichnen eines Graphen. Hierzu wird ein Bild mit vorgegebenen Dimensionen erzeugt:
$width = 900; // Breite
$height = 240; // Höhe
$img = imagecreatetruecolor($width, $height);
// Hintergrundfarbe setzen
$background = imagecolorallocate($img, 255, 255, 255);
imagefilledrectangle($img, 0, 0, $width, $height, $background);
Achsen und Beschriftungen
Vertikale und horizontale Achsen werden gezeichnet. Die x-Achse repräsentiert die Zeit, während die y-Achse die Temperatur und optional den Ventilstatus anzeigt.
imageline($img, 50, 50, 50, $height - 50, $axisColor); // Vertikale Achse
imageline($img, 50, $height - 50, $width - 50, $height - 50, $axisColor); // Horizontale Achse
Die x-Achse wird mit Zeitwerten in Stunden beschriftet, die dynamisch berechnet werden:
for ($timestamp = $dayStart; $timestamp <= $dayEnd; $timestamp += $xLabelFrequency * 3600) {
$x = 50 + ($timestamp - $dayStart) / (24 * 60 * 60) * ($width - 100);
imagestring($img, 3, $x, $height - 30, date("H:i", $timestamp), $axisColor);
}
Datenpunkte und Linien zeichnen
Die Temperaturwerte werden skaliert, um in das Bildformat zu passen, und mit roten Linien dargestellt:
$y = $height - 50 - (($entry['temperature'] - $minTemperature) / $temperatureRange) * ($height - 100);
Die Valvestate-Daten werden mit grünen Linien gezeichnet:
$y = $height - 50 - ($entry['valvestate'] / 100) * ($height - 100);
4. Vergleich mit dem Vortag
Ein zusätzlicher Abschnitt des Skripts ruft die Daten des Vortages ab und überlagert diese mit einer hellblauen Linie auf dem Graphen:
$yesterday = date("Y-m-d", strtotime($currentDate . " -1 day"));
$sqlYesterday = "SELECT time, Innentemperatur FROM sensor_data WHERE date = '$yesterday' ORDER BY time";
Dies ermöglicht die Darstellung von Temperaturtrends und Unterschieden zwischen den beiden Tagen.
5. Bildausgabe
Am Ende wird das Bild als PNG-Datei ausgegeben und der Speicher freigegeben:
header("Content-Type: image/png");
imagepng($img);
imagedestroy($img);
6. Hier das ganze script:
<?php
$servername = "192.168.x.y";
$username = "dbuser";
$password = "db_password";
$dbname = "sensor_data";
// Verbindung zur Datenbank herstellen
$conn = new mysqli($servername, $username, $password, $dbname);
// Überprüfen, ob die Verbindung erfolgreich war
if ($conn->connect_error) {
die("Verbindung zur Datenbank fehlgeschlagen: " . $conn->connect_error);
}
// Datum für den aktuellen Tag
$currentDate = date("Y-m-d");
// SQL-Abfrage, um alle relevanten Daten für den aktuellen Tag abzurufen
$sql = "SELECT time, Innentemperatur, valvestate FROM sensor_data WHERE date = '$currentDate' ORDER BY time";
$result = $conn->query($sql);
if ($result->num_rows > 0) {
// Daten sammeln
while ($row = $result->fetch_assoc()) {
$data[] = array(
'time' => date("H:i", strtotime($row['time'])),
'temperature' => floatval($row['Innentemperatur']),
'valvestate' => intval($row['valvestate'])
);
}
// GD-Bild erstellen
$width = 900; // Breite des Graphen
$height = 240; // Höhe des Graphen
$img = imagecreatetruecolor($width, $height);
// Hintergrundfarbe festlegen
$background = imagecolorallocate($img, 255, 255, 255);
imagefilledrectangle($img, 0, 0, $width, $height, $background);
// Linienfarben
$lineColor = imagecolorallocate($img, 255, 0, 0);
$valvestateColor = imagecolorallocate($img, 15, 215, 110); // Gelbe Linie
$axisColor = imagecolorallocate($img, 0, 0, 0); // Schwarze Achsen
// Achsen zeichnen
imageline($img, 50, 50, 50, $height - 50, $axisColor); // Vertikale Achse
imageline($img, 50, $height - 50, $width - 50, $height - 50, $axisColor); // Horizontale Achse
// Basisfrequenz für die Beschriftung (zum Beispiel alle 3 Stunden)
$baseLabelFrequency = 28;
// Initialisiere die LabelFrequency mit der Basisfrequenz
$xLabelFrequency = $baseLabelFrequency;
// Beschriftung der x-Achse (Zeit)
$dayStart = strtotime($currentDate . ' 00:00:00');
$dayEnd = strtotime($currentDate . ' 23:59:59');
$xLabelFrequency = 2;
for ($timestamp = $dayStart; $timestamp <= $dayEnd; $timestamp += $xLabelFrequency * 3600) {
$x = 50 + ($timestamp - $dayStart) / (24 * 60 * 60) * ($width - 100); // Anpassung auf 24 Stunden
imagestring($img, 3, $x, $height - 30, date("H:i", $timestamp), $axisColor);
}
// Beschriftung der y-Achse (Temperatur) für den aktuellen Tag
$maxTemperature = max(array_column($data, 'temperature'));
$minTemperature = min(array_column($data, 'temperature'));
$temperatureRange = $maxTemperature - $minTemperature;
$yLabelFrequency = $temperatureRange / 10; // 10 Temperaturwerte auf der y-Achse
for ($i = 0; $i <= 10; $i++) {
$y = $height - 50 - ($i * ($height - 100) / 10);
$label = number_format($minTemperature + $i * $yLabelFrequency, 1);
imagestring($img, 3, 20, $y + 5, $label, $axisColor);
}
// Temperaturwerte zeichnen für den aktuellen Tag
$values = array();
foreach ($data as $entry) {
if (isset($entry['time'])) {
$timestamp = strtotime($currentDate . ' ' . $entry['time']);
// Umrechnung der Zeit in die x-Position im 24-Stunden-Graph
$x = 50 + ($timestamp - strtotime($currentDate)) / (24 * 60 * 60) * ($width - 100);
// Korrektur, um sicherzustellen, dass x im erwarteten Bereich liegt
$x = max(50, min($width - 50, $x));
$y = $height - 50 - (($entry['temperature'] - $minTemperature) / $temperatureRange) * ($height - 100);
$values[] = $x;
$values[] = $y;
// Debug-Ausgabe
# echo "time: {$entry['time']}, x: $x, y: $y<br>";
}
}
// Zeichnen der Linie (offen) für den aktuellen Tag
imagesetthickness($img, 2);
if (count($values) >= 4 && count($values) % 2 === 0) {
imageopenpolygon($img, $values, count($values) / 2, $lineColor);
}
// // Beschriftung der y-Achse (Valvestate)
// $maxValvestate = max(array_column($data, 'valvestate'));
// $minValvestate = min(array_column($data, 'valvestate'));
// $valvestateRange = $maxValvestate - $minValvestate;
// $valvestateYLabelFrequency = $valvestateRange / 10; // 10 Valvestate-Werte auf der y-Achse
// for ($i = 0; $i <= 10; $i++) {
// $y = $height - 50 - ($i * ($height - 100) / 10);
// $label = number_format($minValvestate + $i * $valvestateYLabelFrequency, 1) . '%';
// imagestring($img, 3, $width - 40, $y + 5, $label, $axisColor);
// }
// Valvestate-Werte zeichnen
$valvestateValues = array();
foreach ($data as $entry) {
if (isset($entry['time'])) {
$timestamp = strtotime($currentDate . ' ' . $entry['time']);
$x = 50 + ($timestamp - strtotime($currentDate)) / (24 * 60 * 60) * ($width - 100);
$x = max(50, min($width - 50, $x));
$y = $height - 50 - ($entry['valvestate'] / 100) * ($height - 100);
$valvestateValues[] = $x;
$valvestateValues[] = $y;
}
}
// Zeichnen der Linie (Valvestate) für den aktuellen Tag
imagesetthickness($img, 2);
if (count($valvestateValues) >= 4 && count($valvestateValues) % 2 === 0) {
imageopenpolygon($img, $valvestateValues, count($valvestateValues) / 2, $valvestateColor);
}
// Vortag-Daten abrufen
$yesterday = date("Y-m-d", strtotime($currentDate . " -1 day"));
$sqlYesterday = "SELECT time, Innentemperatur FROM sensor_data WHERE date = '$yesterday' ORDER BY time";
$resultYesterday = $conn->query($sqlYesterday);
if ($resultYesterday->num_rows > 0) {
// Daten für den Vortag sammeln
$dataYesterday = array();
while ($rowYesterday = $resultYesterday->fetch_assoc()) {
$dataYesterday[] = array(
'time' => strtotime($yesterday . ' ' . $rowYesterday['time']),
'temperature' => floatval($rowYesterday['Innentemperatur'])
);
}
// Beschriftung der y-Achse (Temperatur) für den Vortag
$maxTemperatureYesterday = max(array_column($dataYesterday, 'temperature'));
$minTemperatureYesterday = min(array_column($dataYesterday, 'temperature'));
$temperatureRangeYesterday = $maxTemperatureYesterday - $minTemperatureYesterday;
$yLabelFrequencyYesterday = $temperatureRangeYesterday / 10; // 10 Temperaturwerte auf der y-Achse
for ($i = 0; $i <= 10; $i++) {
$y = $height - 50 - ($i * ($height - 100) / 10);
$label = number_format($minTemperatureYesterday + $i * $yLabelFrequencyYesterday, 1);
imagestring($img, 3, $width - 40, $y + 5, $label, $axisColor);
}
// Temperaturwerte zeichnen für den Vortag (gestern)
$valuesYesterday = array();
for ($i = 0; $i < count($dataYesterday) - 1; $i++) {
$x1 = 50 + (($dataYesterday[$i]['time'] - $dataYesterday[0]['time']) / (24 * 60 * 60)) * ($width - 100);
$y1 = $height - 50 - (($dataYesterday[$i]['temperature'] - $minTemperatureYesterday) / $temperatureRangeYesterday) * ($height - 100);
$x2 = 50 + (($dataYesterday[$i + 1]['time'] - $dataYesterday[0]['time']) / (24 * 60 * 60)) * ($width - 100);
$y2 = $height - 50 - (($dataYesterday[$i + 1]['temperature'] - $minTemperatureYesterday) / $temperatureRangeYesterday) * ($height - 100);
if ($x2 <= $width - 50) { // Zeichne nur, wenn x2 im Graphenbereich liegt
$valuesYesterday[] = $x1;
$valuesYesterday[] = $y1;
$valuesYesterday[] = $x2;
$valuesYesterday[] = $y2;
}
}
// Zeichnen der Linie für den Vortag (gestern)
$yesterdayColor = imagecolorallocate($img, 173, 216, 230); // Hellblaue Farbe
imagesetthickness($img, 1);
imageopenpolygon($img, $valuesYesterday, count($valuesYesterday) / 2, $yesterdayColor);
}
// Bild ausgeben
header("Content-Type: image/png");
imagepng($img);
// Bildspeicher freigeben
imagedestroy($img);
} else {
echo "Keine Daten für den aktuellen Tag gefunden.";
}
// Verbindung zur Datenbank schließen
$conn->close();
?>
Hier bitte IP-Adresse, “dbuser” und “db_password” durch die eigenen Zugangsdaten der Datenbank ersetzen.
Mit diesem PHP-Skript lassen sich einfach und effektiv Sensordaten visualisieren. Es bietet eine flexible Basis, um weitere Funktionen wie zusätzliche Datentypen, erweiterte Skalierungen oder interaktive Elemente hinzuzufügen.
In einem folgenden Beitrag werde ich ein PHP Script beschreiben das Langzeitdaten visualisiert. Dabei lassen sich beliebige Daten wie Temperatur, Luftdruck, Luftqualität oder Luftfeuchtigkeit beschreiben.