Datenvisualisierung auf einer Webseite – Erstellung eines Temperatur- und Valvestate-Graphen mit PHP und MySQL

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:

  1. Datenbankintegration: Es verbindet sich mit einer MySQL-Datenbank, um Sensordaten für den aktuellen Tag und den Vortag abzurufen.
  2. Grafikerstellung: Mit PHPs GD-Library werden Achsen, Linien und Datenpunkte gezeichnet.
  3. Dynamische Achsenskalierung: Die Skala passt sich automatisch den minimalen und maximalen Temperaturwerten an.
  4. Vergleichsdaten: Der Graph des Vortages wird in einer anderen Farbe überlagert, um Trends zu verdeutlichen.

Die tagesaktuelle Termperaturkurve in rot, die gestrige zum Vergleich in blau und der Status des Heizungsventils in grün.

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.

Leave a Reply

Your email address will not be published. Required fields are marked *

wetransco.de
Privacy Overview

This website uses cookies so that we can provide you with the best user experience possible. Cookie information is stored in your browser and performs functions such as recognising you when you return to our website and helping our team to understand which sections of the website you find most interesting and useful.