Suche Programmierer. Datenbank auslesen und visualisieren per Highcharts.

  • Hallo,

    ich suche jemanden der mir das folgende neu bzw. um schreibt. Im Grunde ist im Netz und auch hier alles vorhanden, aber ich habe keine Zeit mehr (Arbeit/Familie) um mich weiter in die Materie/den Code einzulesen.
    Für einen der sich auskennt sollte das allerdings fix gehen.
    Nicht unentlohnt versteht sich!


    Kurzbeschreibung:
    Datenerfassung von etwa 9 Temperatursensoren DS18B20 und 2x DHT22. Geschrieben wird in eine Datenbank, kein RRDTool.
    Visualisierung mittels Highcharts.
    Erfassungszeitraum ist etwa 4x 4 Monate (immer ein anderer Ort)
    Sich füllende Datenbank (MySQL) ist vorhanden.


    Beschreibung IST-Zustand:
    Mit diesem Thema hab ich begonnen (Temperaturaufzeichnung mit RRDTool und Highcharts)
    und festgestellt das ich RRDTool nicht möchte. Die Grafik und die Zeitauswahl aber schon.

    Hier hab ich weitergemacht und konnte mit der optischen Web-Ausgabe der Highcharts (ab Version 0.5?) gut leben:
    1-Wire Sensor mit Datenbank (liegt auch au GitHub=https://github.com/ThomasH-W/avrio)
    Letzte Aktuallisierung auf GitHub!

    Allerdings hagelts bei bei den *.php Dateien nur Fehler und die Zeiten passen nicht. (Aber wie ich sagte, mein Zeitbudget fürs Script ist erschöpft)
    Das füllen der MySQL-Datenbank klappt hingegen bis jetzt sehr gut.
    Ob mir SQLite lieber ist kann ich aus mangelder Erfahrung nicht sagen. Es liest sich hier und im Netz aber als ob es besser für den Raspberry wäre.
    Zumindest fällt mir MySQL noch nicht negativ auf.

    Es ist eine Tabelle (11 Spalten in MySQL) vorhanden welche mit Daten gefüllt werden (Script avrio.py von ThomasH > siehe Link oben)

    9x Temperatursensor vorhanden.
    4 sammeln zur Zeit Daten. 5 laufen also erst mal "blind" mit.
    2x Feuchte-Sensoren DHT-22 kommen noch dazu.


    Gewünscht und Gebraucht:
    Die passende Ausgabe mit Highcharts.
    Diese soll wie von Tafkas sein! ->Link
    Edit: Nur eine Stelle nach dem Komma (Beispiel: 22.357 Grad -> 22.3 Grad C)

    Allerdings diese zeitlichen Ausgaben:

    Letzten 12 Std.
    24 Std.
    48 Std.
    72 Std.
    1 Woche
    2 Wochen
    1 Monat
    3 Monate
    6 Monate

    Und mit dieser Ausgabe


    Datei/Skript soll natürlich ordentlich geschrieben sein. "Zukunftssicher" in PDO_MySQL bzw MySQLi
    Nachvollziehbar & mit Kommentaren. Also Edirbar für mich. (Bildet ja auch weiter)


    Vorhanden und läuft:
    -Temperatursensoren DS18B20
    -Datenbank MySQL
    -Skript zum Füllen der DB mittels Cronjob


    Wer kann / macht das?
    Kann oder möchte das wer aus dem Forum tun? Natürlich nicht für umsonst!
    Wen könnte ich anschreiben / ansprechen?


    Ich bin für jede Hilfe dankbar!

  • Suche Programmierer. Datenbank auslesen und visualisieren per Highcharts.? Schau mal ob du hier fündig wirst!

  • Interessant wäre das Script welches du zum erfassen der Sensor-Daten verwendest, um daraus ableiten zu können ob zumindest die Datenbank-Einträge brauchbar wären oder auch noch verändert werden müssten.

    Grundsätzlich sollte das aber eigentlich kein großer Aufwand sein - das einzige wäre die von Dir gewünschte "Zeitliche Ausgabe", wie diese aussehen soll; Auswählbar oder jeweils einzelne Graphen etc.
    Je nach dem wie die Datensätze aussehen kann dies aufwendiger sein oder aus dem Ärmel geschüttelt werden :D

    Auch schreibst du das der Standort sich ändern könnte - also sollte das ja auch noch irgendwie mit in die Datenbank mit rein, damit man evtl. auch noch auf spätere Datensätze zugreifen könnte um somit auch Vergleiche zu betrachten... Hier wäre dann denk ich auch eine gewisse Flexibilität für das "Sensor auslese"-Script erforderlich, also auch eine kleine Anpassung nötig.

    //EDIT: Achso, hab das überlesen: (Script avrio.py von ThomasH > siehe Link oben) ... Also https://github.com/ThomasH-W/avrio/blob/master/avrio.py
    Aber für die DHT22 wirds ja ein anderes sein.. ;)
    avrio.py würde ich aber nicht unbedingt verwenden - man kann es als Vorlage nutzen aber das meiste was das kann brauchst du imho gar nicht - beinhaltet aber auch einiges was man besser/anders machen könnte und scheint ja auch sein erstes Python Script gewesen zu sein also noch recht unerfahren :fies:
    Frage bleibt aber offen, wie deine Datenbank-Tabelle aussieht ... Da das im avrio.py ja Flexibel einstellbar ist.


  • Grundsätzlich sollte das aber eigentlich kein großer Aufwand sein - das einzige wäre die von Dir gewünschte "Zeitliche Ausgabe", wie diese aussehen soll; Auswählbar oder jeweils einzelne Graphen etc.


    Bin mir nicht sicher ob ich deine Frage jetzt richtig verstehe. Aber ich möchte es so wie das von Tafkas.
    Klick auf "24 Std.", dann werden die Messungen der letzten 24 Std von allen Sensoren in der Grafik gezeigt.
    Klick auf "2 Wochen", dann werden die Messungen der letzten 2 Wochen angezeigt. U.s.w.

    Zitat


    Auch schreibst du das der Standort sich ändern könnte - also sollte das ja auch noch irgendwie mit in die Datenbank mit rein, damit man evtl. auch noch auf spätere Datensätze zugreifen könnte um somit auch Vergleiche zu betrachten...


    Das ist nicht nötig. Da der ganze Aufbau dann wieder weg kommt und es andere Messpunkte gibt.
    Die Szenarien haben nicht viel gemein. Jeder Aufbau sollte seine eigene Datenbank bekommen.
    Oder meinst du das nur eine php-Datei auf die Datenbanken zugreift?
    Messung_Projekt1 Messung_Projekt2 Messung_Projekt3
    Letzten 12 Std.
    24 Std.
    48 Std.
    72 Std.
    1 Woche
    2 Wochen
    1 Monat
    3 Monate
    6 Monate


    Zitat

    Aber für die DHT22 wirds ja ein anderes sein.. ;)


    Richtig. Da les ich mich aber erst an einem freien Wochenende ein.

    Zitat


    avrio.py würde ich aber nicht unbedingt verwenden - man kann es als Vorlage nutzen aber das meiste was das kann brauchst du imho gar nicht - beinhaltet aber auch einiges was man besser/anders machen könnte ...


    Warum genau? Für ein erstes Script kanns aber schon viel.
    avrio.py schreibt alles in die Datenbank
    avrio --setup erstellt eine Datenbank und Tabelle
    avrio --get liest bzw erkennt neue Sensoren und schreibt sie in die Config
    Konnte die avrio.py soweit bearbeiten das es namentlich an meine bedürfnisse angepasst war.
    Wie könnte man das noch machen? Wenn es denn klappt ist ok.
    Ob ich die Sensor-ID von Hand eintrage ist mir relativ.

    Zitat

    Frage bleibt aber offen, wie deine Datenbank-Tabelle aussieht ... Da das im avrio.py ja Flexibel einstellbar ist.


    Hier mal ein Beispielbild mit 4 Spalten. Falls du das meinst.

    Was mir mir grad wieder aufgefallen ist, die Werte z.B. von 22.347 Grad C. Eine Stelle nach dem Komma langt voll aus.

  • Die Tabelle an sich würde Ich anders aufbauen - ich mag es nicht wenn dattime verwendet wird, da der Umgang damit umständlicher ist. Besser finde ich timestamp bzw Unixtime, also einfach ein int(11) als Typ - das kann man dann ziemlich einfach ohne großen Aufwand in alle mögliche Formate und Zeitzonen konvertieren und man kann auch mit SQL Querys besser damit hantieren.

    Auch würde ich den komplett ermittelten Sensor-Wert eintragen, ohne Kürzung. Also nicht bereits auf ein oder 2 Stellen hinter dem Kommata kürzen = 22.562 sondern nur 22562 eintragen und den Rest macht man dann via PHP. Das Python Script teilt den ausgegeben Wert halt bereits durch 1000 und somit muss der Spalten-Typ float bieten.

    Das alles frisst Performance und Datenbank-Platz. Auf "richtigen Rechnern" macht sich das kaum bemerkbar, aber gerade auf dem vergleichsweise schwachen PI ist das nicht unbedingt vorteilhaft ;)

    Ich guck mir das in den nächsten Tagen mal genauer an - vielleicht kann ich ein anderes Projekt von mir dafür "missbrauchen" wodurch ich dann nicht so viel Programmieraufwand hätte :fies:

  • Die Datenbank sollte "normalisiert" werden, denn so ist sie nicht wie gewollt zukunftssicher. Was passiert zum Beispiel, wenn ein Ort dazukommt? Die Datenbank sollte mindestens 2 Tabellen haben.
    1. Tabelle Sensoren: ID|ORT|NAME
    2. Tabelle Daten: ID|ID_SENSOR|DATUM|WERT
    Das wäre der richtige (nicht fertige) Weg. So könnt Ihr jederzeit Sensoren und Orte hinzufügen, ohne die Tabellen ändern zu müssen.
    Im Programm macht Ihr dann aus jeder Tabelle ein Datenbankobjekt und aus den Sensoren ebenfalls eine Klasse und das ganze wird schön übersichtlich!

  • Man könnte das auch mit nur einer Tabelle und schreibt dann einfach den ORT mit dem WERT in eine Spalte und nutzt ein eindeutiges Trennzeichen - das wäre auch sehr flexibel :fies:

    ID | TIMESTAMP | DATA

    1 | 134526475 | WOHNZIMMER;;2263;;4637
    2 | 134526475 | KUECHE;;2263

    erster Wert wäre Temperatur und zweiter Wert wäre Luftfeuchte falls vorhanden.

  • Noch ein paar Fragen:

    - Welchen Browser wirst du verwenden? Sofern du keinen IE nutzen wirst könnte man nämlich jQuery 2.x verwenden...
    - Sollen sich die Charts von selbst aktualisieren während sie betrachtet werden? (ajax)
    - Sollen mehrere Charts pro ORT angezeigt werden oder alle in einem Chart? Insbesondere wenn sowohl Temperatur als auch Luftfeuchte mit angezeigt werden soll könnte das in nur einem Chart problematisch werden - das würde ich zunächst trennen also Temperatur-Werte in einem Chart und Luftfeuchte in einem anderen.
    - Möchtest du die Charts auch mit einem Tablet/Handy betrachten können?
    - Soll die Seite übers Internet zugänglich gemacht werden und soll nur für berechtigte User einsehbar sein? (also Login erforderlich)


  • Die Tabelle an sich würde Ich anders aufbauen - ich mag es nicht wenn dattime verwendet wird, da der Umgang damit umständlicher ist. Besser finde ich timestamp bzw Unixtime, also einfach ein int(11) als Typ - das kann man dann ziemlich einfach ohne großen Aufwand in alle mögliche Formate und Zeitzonen konvertieren und man kann auch mit SQL Querys besser damit hantieren.


    OK, wahrscheinlich gibts deswegen die Fehler mit der Zeit im anderen Script.

    Zitat

    Auch würde ich den komplett ermittelten Sensor-Wert eintragen, ohne Kürzung. Also nicht bereits auf ein oder 2 Stellen hinter dem Kommata kürzen = 22.562 sondern nur 22562 eintragen und den Rest macht man dann via PHP. Das Python Script teilt den ausgegeben Wert halt bereits durch 1000 und somit muss der Spalten-Typ float bieten.


    Ob das so sinnig ist weiß ich nicht. Wie siehts denn dann mit dem Export der Daten für Exel aus?
    Aber Hauptsache es klappt am Ende sauber.


    Die Datenbank sollte "normalisiert" werden, denn so ist sie nicht wie gewollt zukunftssicher. Was passiert zum Beispiel, wenn ein Ort dazukommt? Die Datenbank sollte mindestens 2 Tabellen haben.
    1. Tabelle Sensoren: ID|ORT|NAME
    2. Tabelle Daten: ID|ID_SENSOR|DATUM|WERT
    .....

    Das habe ich jetzt schon öfters gelesen. Es klingt auch vernüftig.
    Wie sieht es denn hier mit einem Export aus?
    Ich bevorzuge zwar den grafischen Ausdruck am Ende, aber neugierig bin ich schon.



    Noch ein paar Fragen:


    Frage: Welchen Browser wirst du verwenden? Sofern du keinen IE nutzen wirst könnte man nämlich jQuery 2.x verwenden...
    Antwort: Wir verwenden den IE nicht. Firefox, Chrome, Safari. 

    Frage: Sollen sich die Charts von selbst aktualisieren während sie betrachtet werden? (ajax)
    Antwort: Nein, brauchts nicht. Die Messintervalle bewegen sich ohnehin zwischen 15und 25 Minuten

    Frage: Sollen mehrere Charts pro ORT angezeigt werden oder alle in einem Chart? Insbesondere wenn sowohl Temperatur als auch Luftfeuchte mit angezeigt werden soll könnte das in nur einem Chart problematisch werden - das würde ich zunächst trennen also Temperatur-Werte in einem Chart und Luftfeuchte in einem anderen.
    Antwort: Temperatur und Feuchte trennen macht wohl Sinn. 2Charts (Feuchte & Temperatur) untereinander würden gehen?
    Oder ist das zuviel für einen Browser? Ansonst alle Messpunkte in eine Chart.

    Frage: Möchtest du die Charts auch mit einem Tablet/Handy betrachten können?
    Antwort: Nein. Es wird stationär abgefragt. Evtl wird mal, wenn man im Netzwerk ist, das Handy genommen. Ist aber völlig egal.
    Wäre doch auch zuviel unötiger Aufwand.

    Frage: Soll die Seite übers Internet zugänglich gemacht werden und soll nur für berechtigte User einsehbar sein? (also Login erforderlich)
    Antwort: Nein. Keine Verbindung zur Ausenwelt ;) .

    Vorlage wäre wirklich das von Tafkas.
    Die optische Aufteilung eben in 12 Std.
    Die Zeit-Auswahl links.
    Sensorwahl unter der Chart.


  • Man könnte das auch mit nur einer Tabelle und schreibt dann einfach den ORT mit dem WERT in eine Spalte und nutzt ein eindeutiges Trennzeichen - das wäre auch sehr flexibel :fies:

    ID | TIMESTAMP | DATA

    1 | 134526475 | WOHNZIMMER;;2263;;4637
    2 | 134526475 | KUECHE;;2263

    Machen kann man das, aber normalerweise schreibt man keine zwei Werte in eine Zelle. Normalisierung bedeutet (sehr) sehr einfach gesagt, dass Du nur noch einen Wert in einer Tabellenzeile (3. Normalform) hast. Aber da muss man eine Menge Gehirn reinstecken, weil das sehr abstrakt wird. Und wer die 3. Normalform begriffen hat, ist schon echt gut dabei!

  • Ich hab damit bereits gestern angefangen und nutze " id,sensor_type,sensor_place,timestamp,temp,hum "

    Ist soweit auch schon fast fertig, nur Highcharts sträubt sich noch ein bissal :(

    Es gibt eine config.php in der alles nötige eingestellt werden kann und sieht zZt so aus:

    Spoiler anzeigen

    [code=php]<?php
    //------------------------------------------------------
    // MySQL
    //------------------------------------------------------
    $dbuser = "root";
    $dbpass = "passw0rd";
    $database = "measurements";
    //------------------------------------------------------
    // Selectable Chart Ranges.
    // Format: $Chart['<period>'] = '<chart title>';
    // Example: $Chart['12h'] = 'of the last 12 hours';
    //------------------------------------------------------
    $Chart['6h'] = 'of the last 6 hours';
    $Chart['12h'] = 'of the last 12 hours';
    $Chart['24h'] = 'of the last 24 hours';
    $Chart['2d'] = 'of the last 2 days';
    $Chart['3d'] = 'of the last 3 days';
    $Chart['1w'] = 'of the last week';
    $Chart['2w'] = 'of the last 2 weeks';
    $Chart['1m'] = 'of the last month';
    $Chart['3m'] = 'of the last 3 months';
    $Chart['6m'] = 'of the last 6 months';
    $Chart['1y'] = 'of the last year';
    //------------------------------------------------------
    $colors = array('#89A54E','#80699B','#3D96AE','#DB843D','#92A8CD','#A47D7C','#B5CA92');
    ?>[/php]
    Anhand "Selectable Chart Ranges" wird das Menü erstellt und der Chart-Title festgelegt..

  • Du bist ja fix. ;)
    Macht es eigentlich eine großen Unterschied ob man dann MySQL oder SQLite verwendet?
    Wie schreibst du für die Tabelle?
    So, wie ich es schon habe? (Quasi avrio-Skript)
    Oder so wie es Jörg meine?
    Beides ist OK. Nur das mit den 2 Werte in einer Zelle möchte ich nicht.
    Mal abgesehen davon das die Messpunkte ja garnicht zusammenliegen.

  • Ob MySQL oder SQLite oder PostgreSQL ist meistens eine Frage der zur Verfügung stehenden Performance, der Datenmenge und die spätere Kompatibilität.

    Für MySQL und PostgreSQL läuft halt permanent ein Dienst im Hintergrund, der trotz aktueller nicht-Verwendung dennoch CPU+RAM verbraucht.
    MySQL ist vergleichsweise langsam(er) aber am besten unterstützt sowie problemlos Netzwerkfähig, was auch PostgreSQL wäre.. MySQL verbraucht aber im Vergleich zu den anderen auch mehr RAM.
    SQLite sind aber nur Dateien und somit nicht ohne weiteres Netzwerkfähig, zudem anfälliger für Dateifehler... Dafür läuft aber nicht ständig ein Dienst und verbraucht somit auch weitaus weniger RAM..

    Da du die Daten aber auch in Excel importieren bzw exportieren willst, bietet sich MySQL schon irgendwie an - es gibt soweit ich weiß nämlich ein PlugIn direkt für Excel...


    Aktuell verwende ich nur eine Tabelle 'data' mit den Spalten: id, location, timestamp, temp, hum

    Spoiler anzeigen
    Code
    CREATE TABLE `data` (
     `id` bigint(11) NOT NULL AUTO_INCREMENT,
     `location` varchar(255) NOT NULL,
     `timestamp` int(11) NOT NULL,
     `temp` FLOAT(11) NOT NULL,
     `hum` FLOAT(11) NOT NULL,
     PRIMARY KEY (`id`),
     KEY `location` (`location`)
    ) ENGINE=InnoDB  DEFAULT CHARSET=latin1;

    Mehr als wie folgt gibt's aber noch nicht zu sehen, da ich immer noch an der Übergabe der Werte an Highcharts kämpfe - das verschlingt mehr Zeit als der ganze Rest :(

    -> http://RaspberryPI.RoXXs.org/charts/

    &quot;query output&quot;

    view-source:http://RaspberryPI.RoXXs.org/charts/chart.php?period=6h

    Mit dem Script zum erfassen und einfügen habe ich mich noch nicht befasst, werde aber denk ich das avrio-Script verwenden, nur leicht modifiziert ;)

  • Ich bin jetzt schon ein ganzes Stück weiter - habe allerdings noch ein etwas komisches Problem mit der Tooltip Anzeige für die Kennlinien :( Also die Detail-Anzeige wenn man mit der Maus über einer der Punkte geht..

    -> http://raspberrypi.roxxs.org/charts/?period=1y

    Wenn ich das noch gelöst kriege sollte die Web-Oberfläche fertig sein ;)
    Sieht zufällig jmd den Fehler? :huh:

  • Wohnzimmer und Badezimmer haben aber keine Umlaute, also müssten zumindest diese Tooltips gehen - aber Küche hat auch kein Umlaut da es bereits als 'K?che' in der Datenbank steht, da ging bereits was beim Adden schief :D

    Küche und Wohnzimmer haben auch hum-Werte aber Küche hat nur einen also bildet sich nur keine Linie... Die Datensätze sind nur zum testen - Hauptsache es steht was drin... Und das mit dem abwählen ist nur wegen dem z-index des Footers, das lässt sich ja schnell ändern.

    :denker: Hm naja muss ich morgen mal weiter gucken - hab zZt auch nicht sooo viel Zeit


    //EDIT: Habs :cool:

    Welcher Tooltip gefällt dir besser? Der vom "temp" oder der vom "hum" ?

    Aber die Probleme reißen nicht ab... Trotz korrekter Zeit im MySQL und useUTC: false Option, zeigt der Chart 2h zu wenig an - also z.B. um 23:43 insert'ed aber der Chart zeigt 21:43 ... :denker:

  • Sehr schön! :thumbs1:
    Der Tooltip von "hum" passt.

    Das wegen der Zeit hab ich auch erst gelesen.
    2 Dinger hab ich grad noch hier Link_1 und Link_2
    Das andere find ich eben nicht.


    Hab gesehen das Tafkas ein Script für Sunset/Sunrice drin hat. Ich dachte der Hintergrund wechselt "nur" alle 12 Std. und er hätte es etwas verschoben.
    Wie auch immer. Ist der Code überhaupt so vollständig?
    http://blog.tafkas.net/2012/11/26/cha…-in-highcharts/
    In der HTML steht ja auch noch etwas drin was in der Beschreibung nicht ist.
    Fehlt da nun was oder reichts mit dem Code?


    #Edit
    Kannst bitte noch die Schriftgröße für die ältere Generation anpassen? :daumendreh2:

  • Welche Schriftgröße? Vom Menü? ;)

    Das mit "000" anhängen hab ich bereits -> Convert Unixtime to Javatime -> * 1000
    Leider löst das nicht mein aktuelles Problem - aber das krieg ich auch noch irgendwie hin :denker:

    "Sunrise and Sunset" setz ich mal auf die ToDo-Liste ;)

  • Hm ich versteh grad leider nur die Hälfte von dem was du geschrieben hast

    - Das Zeit-Anzeige-Problem habe ich gelöst, lag an den globalen Highcharts Options die er zuvor nicht gefressen hatte.
    - font-size vom Menü und auch der xAxis sowie yAxis habe ich einheitlich auf 10pt hoch gesetzt.
    - Die Charts werden jetzt auch mit dem Theme 'grid' angezeigt, was ich persönlich besser finde - lässt sich einfach und schnell ändern da man nur das jeweile theme File laden muss (oder eben nicht). Ich finde allerdings den Tooltip des oberen Charts besser da die Werte besser zuzuordnen sind vor allem wenn sich Werte von mehreren Tagen anzeigen lässt... Das ist btw der default Tooltip.
    - Man kann fürs Tooltip einstellen das die Werte auf nur 2 Decimals (nach dem Kommata) gekürzt werden. Somit kann man weiterhin in die Datenbank längere Werte eintragen und brauch auch beim Auslesen hierfür keinen Aufwand betreiben.
    - Der obere Chart hat auch eine kleine Verzögerung beim zeichnen der Linien (animation duration) was ich persönlich auch schöner finde ;)


    Noch ein paar Details bevor ich das vergesse...:

    Bei der Gelegenheit poste ich auch gleich den derzeitigen Code, damit vielleicht jetzt bereits Kritik/Verbesserungen einfließen, oder Fragen beantwortet werden könnten.

    Ich habe mich bemüht das ganze möglichst Modular aufzubauen mit folgender Struktur:

    • index.php
      Hauptdatei

      Spoiler anzeigen

      [code=php]<?php
      //------------------------------------------------------
      require_once('include/global.php');
      //------------------------------------------------------
      ?>
      <!DOCTYPE html>
      <html xmlns="http://www.w3.org/1999/xhtml" lang="de">
      <head>
      <title>Sensor Infos</title>
      <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
      <meta name="robots" content="DISALLOW">
      <script src="js/jquery-1.11.3.min.js"></script>
      <script src="js/functions.js"></script>
      <style type=text/css>
      body { font-size: 8pt; color: black; font-family: Verdana,arial,helvetica,serif; margin: 0 0 0 0; }
      #header {
      position: absolute;
      left: 55px;
      }
      #menu {
      position: fixed;
      top: 15%;
      left: 10px;
      z-index: 5;
      width: 70px;
      background-color: #E0E0E0;
      border-radius: 10px;
      font-size: 10pt;
      color: black;
      font-family: Verdana,arial,helvetica,serif;
      }
      #content {
      position: absolute;
      z-index: 1;
      top: 5px;
      left: 55px;
      }
      #tempchart {
      position: absolute;
      z-index: 1;
      top: 20px;
      left: 15px;
      right: 7%;
      }
      #humchart {
      position: absolute;
      z-index: 1;
      top: 420px;
      left: 15px;
      right: 7%;
      }
      #footer {
      position: fixed;
      bottom: 0px;
      width: 100%;
      left: 50%;
      z-index: 0;
      }
      </style>
      </head>
      <body>
      <div id='header'> </div>
      <?php
      //------------------------------------------------------

      error_reporting(E_ALL);
      ini_set('track_errors', 1);
      ini_set('display_errors', 1);
      ini_set('log_errors', 1);
      ini_set('memory_limit', '64M');
      ini_set('max_execution_time', '30');
      @ob_implicit_flush(true);
      @ob_end_flush();
      $_SELF=$_SERVER['PHP_SELF'];
      $DURATION_start = microtime(true);

      //------------------------------------------------------
      // Menue
      //------------------------------------------------------

      echo "<div id='menu'>\n";
      foreach ($Chart AS $PERIOD => $DESCRIPTION) {
      echo "• <a href='?period=".$PERIOD."'>Last ".$PERIOD."</a><br/>\n";
      }
      echo "</div>\n";

      //------------------------------------------------------
      // Content / Charts
      //------------------------------------------------------

      echo "<div id='content'>\n";
      echo " <div id='tempchart'> </div>\n";
      echo " <div id='humchart'> </div>\n";

      if (isset($_GET['period'])) {
      $Period = $_GET['period'];
      ?>
      <script src='highcharts/js/highcharts.js'></script>
      <script src='highcharts/js/themes/grid.js'></script>
      <script src='highcharts/js/modules/exporting.js'></script>
      <script type='text/javascript'>
      $(function() {
      $(document).ready(function() {
      // Global Chart Options
      Highcharts.setOptions({
      global: {
      useUTC: false
      },
      xAxis: {
      type: "datetime",
      labels: {
      style: {
      "color":"#6D869F", "font-size":"10pt", "fontWeight":"bold"
      }
      }
      },
      legend: {
      enabled: true
      },
      credits: {
      enabled: false
      },
      colors: ["#4572A7", "#AA4643", "#89A54E", "#80699B", "#3D96AE", "#DB843D", "#92A8CD", "#A47D7C", "#B5CA92"]
      });

      // Chart: Temperatures
      $.getJSON("data.php?type=temp&period=<?php echo $Period; ?>", function(json) {
      var temp = new Highcharts.Chart({
      series: json,
      chart: {
      type: "spline",
      renderTo: "tempchart"
      },
      title: {
      text: "Temperatures <?php echo $Chart[$Period]; ?>"
      },
      tooltip: {
      crosshairs: true,
      useHTML: true,
      valueDecimals: 2,
      valueSuffix: ' °C'
      },
      yAxis: {
      title: {
      text: "Temperatures (°C)"
      },
      labels: {
      formatter: function() {
      return this.value +"°C"
      },
      style: {
      "color":"#6D869F", "font-size":"10pt", "fontWeight":"bold"
      }
      }
      },
      plotOptions: {
      series: {
      animation: {
      duration: 2000
      },
      lineWidth: 2,
      marker: {
      radius: 2
      }
      }
      },
      });
      });

      // Chart: Humidity
      $.getJSON("data.php?type=hum&period=<?php echo $_GET['period']; ?>", function(json) {
      var hum = new Highcharts.Chart({
      series: json,
      chart: {
      type: "spline",
      renderTo: "humchart"
      },
      title: {
      text: "Humidity <?php echo $Chart[$Period]; ?>"
      },
      yAxis: {
      title: {
      text: "Humidity (%)"
      },
      labels: {
      formatter: function() {
      return this.value +"%"
      },
      style: {
      "color":"#6D869F", "font-size":"10pt", "fontWeight":"bold"
      }
      }
      },
      tooltip: {
      crosshairs: true,
      formatter: function() {
      return "<b>"+ this.series.name +"</b><br/>"+ Highcharts.dateFormat("%H:%M", this.x) +" -> "+ this.y.toFixed(1) +"%";
      }
      },
      plotOptions: {
      series: {
      lineWidth: 1,
      marker: {
      radius: 2
      }
      }
      },
      });
      });
      });
      });
      </script>
      <?php
      }
      echo "</div>\n"; // END: content

      //------------------------------------------------------
      // Footer
      //------------------------------------------------------

      echo "\n<div id='footer'>\n";
      echo "<br/><br/><br/><br/>\n";
      $DURATION_end = microtime(true);
      $DURATION = $DURATION_end - $DURATION_start;
      echo "<p><font size='0'>Page generated in ".round($DURATION, 3)." seconds</font></p>\n";
      echo "</div>\n";

      ?>
      </body>
      </html>
      [/php]

    • data.php
      Ruft die Daten von der MySQL Datenbank ab und gibt diese als JSON Werte aus.
      Wird in der index.php im Menü etwas ausgewählt wird mithilfe von jQuery ein Aufruf der data.php mit Übergabe des Zeitraums (period) und dem Type (temp oder hum) durchgeführt, erst für temp um diesen Chart zu generieren und anschließend noch mal für den hum Chart.

      Spoiler anzeigen

      [code=php]
      <?php
      //------------------------------------------------------
      require_once('include/global.php');
      //------------------------------------------------------

      if (isset($_GET['type'])) {

      if ($_GET['type'] == 'temp') {
      $Type = 'temp';
      } else {
      $Type = 'hum';
      }

      if (isset($_GET['period'])) {
      $Period = $_GET['period'];
      $PeriodUnit = substr($Period, -1);
      $PeriodNum = str_replace($PeriodUnit, '', $Period);
      } else {
      $Period = '6h';
      $PeriodUnit = substr($Period, -1);
      $PeriodNum = str_replace($PeriodUnit, '', $Period);
      }

      $conid = mysql_con();
      $db_selected = mysqli_select_db($conid, $database);
      if (!$db_selected) { die('Cannot use Database '.$database.' : '.mysqli_error($conid)); }

      // Get each "location"
      $dataResult = array();
      $result = query("SELECT id,location FROM data GROUP BY location");
      while ($row = mysqli_fetch_array($result)) {
      $data = array();
      $data['name'] = $row['location'];
      // Get Data from each "location"
      $result2 = query("SELECT location,timestamp,".$Type." FROM data
      WHERE location = '".$row['location']."'
      AND timestamp >= UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL ".$PeriodNum." ".getPeriodUnit($Period)."))
      AND timestamp <= UNIX_TIMESTAMP()
      ORDER BY timestamp ASC
      ;");
      while ($row2 = mysqli_fetch_array($result2)) {
      $data['data'][] = array((float)($row2['timestamp']*1000), (float)$row2[$Type]);
      }
      array_push($dataResult, $data);
      }
      mysqli_close($conid);

      print json_encode($dataResult);
      }
      ?>
      [/php]

    • include/config.php
      Einzige Datei in der Einstellungen vorgenommen werden müssten.

      Spoiler anzeigen

      [code=php]
      <?php
      //------------------------------------------------------
      // MySQL
      //------------------------------------------------------
      $dbuser = "root";
      $dbpass = "passw0rd";
      $database = "measurements";
      /*
      CREATE TABLE `data` (
      `id` bigint(11) NOT NULL AUTO_INCREMENT,
      `location` varchar(255) NOT NULL,
      `timestamp` int(11) NOT NULL,
      `temp` FLOAT(11) NOT NULL,
      `hum` FLOAT(11) NOT NULL,
      PRIMARY KEY (`id`),
      KEY `location` (`location`)
      ) ENGINE=InnoDB DEFAULT CHARSET=latin1;
      */
      //------------------------------------------------------
      // Selectable Chart Ranges.
      // Format: $Chart['<period>'] = '<chart title>';
      // Example: $Chart['12h'] = 'of the last 12 hours';
      //------------------------------------------------------
      $Chart['6h'] = 'of the last 6 hours';
      $Chart['12h'] = 'of the last 12 hours';
      $Chart['24h'] = 'of the last 24 hours';
      $Chart['2d'] = 'of the last 2 days';
      $Chart['3d'] = 'of the last 3 days';
      $Chart['1w'] = 'of the last week';
      $Chart['2w'] = 'of the last 2 weeks';
      $Chart['1m'] = 'of the last month';
      $Chart['3m'] = 'of the last 3 months';
      $Chart['6m'] = 'of the last 6 months';
      $Chart['1y'] = 'of the last year';
      //------------------------------------------------------
      ?>
      [/php]

    • include/functions.php
      Globale Funktionen - zum Beispiel fürs verbinden zum MySQL sowie absetzen von Query's.

      Spoiler anzeigen

      [code=php]<?php

      // functions
      //------------------------------------------------------
      // http://www.w3schools.com/php/php_ref_mysqli.asp
      function query($query_string) {
      global $conid, $showqueries;
      if (!isset($conid) OR !$conid) { $conid = mysql_con(); }
      if (!isset($showqueries)) { $showqueries = 0; }
      if ($showqueries) { echo "Query: $query_string <br/>\n"; }
      $query_id = mysqli_query($conid, $query_string);
      if (!$query_id) { echo "Invalid SQL: $query_string <br/>\n"; }
      return $query_id;
      }
      function mysql_con() {
      global $dbuser, $dbpass, $dbhost, $database;
      $conid = mysqli_connect($dbhost, $dbuser, $dbpass, $database);
      if (mysqli_connect_errno()) { die('Error connecting to MySQL: ' . mysqli_connect_error()); }
      return $conid;
      }
      //------------------------------------------------------

      function getPeriodUnit($period) {
      $unit = substr($period, -1);
      switch ($unit) {
      case 's': return 'SECOND';
      case 'mi': return 'MINUTE';
      case 'h': return 'HOUR';
      case 'd': return 'DAY';
      case 'w': return 'WEEK';
      case 'm': return 'MONTH';
      case 'y': return 'YEAR';
      }
      }

      ?>[/php]

    • include/global.php
      Globale Variablen sowie Einbindungen von weiteren PHP Dateien.

      Spoiler anzeigen

      [code=php]
      <?php
      //if (!isset($_SESSION)) { session_start(); }

      $DEBUG=0;
      $showqueries=0;

      $Chart=array();
      require_once("config.php");
      require_once("functions.php");

      ?>
      [/php]

    • js/functions.js
      Globale JavaScript Funktionen - aktuell nur für die Sunset Geschichte

      Spoiler anzeigen

      [code=php]
      function computeSunrise(day, sunrise) {

      /* http://blog.tafkas.net/2012/11/26/cha…-in-highcharts/
      Sunrise/Sunset Algorithm taken from
      http://williams.best.vwh.net/sunrise_sunset_algorithm.htm
      inputs:
      day = day of the year
      sunrise = true for sunrise, false for sunset
      output:
      time of sunrise/sunset in hours
      */

      //lat, lon for Berlin, Germany
      var longitude = 13.408056;
      var latitude = 52.518611;
      var zenith = 90.83333333333333;
      var D2R = Math.PI / 180;
      var R2D = 180 / Math.PI;

      // convert the longitude to hour value and calculate an approximate time
      var lnHour = longitude / 15;
      var t;
      if (sunrise) {
      t = day + ((6 - lnHour) / 24);
      } else {
      t = day + ((18 - lnHour) / 24);
      };

      //calculate the Sun's mean anomaly
      M = (0.9856 * t) - 3.289;

      //calculate the Sun's true longitude
      L = M + (1.916 * Math.sin(M * D2R)) + (0.020 * Math.sin(2 * M * D2R)) + 282.634;
      if (L > 360) {
      L = L - 360;
      } else if (L < 0) {
      L = L + 360;
      };

      //calculate the Sun's right ascension
      RA = R2D * Math.atan(0.91764 * Math.tan(L * D2R));
      if (RA > 360) {
      RA = RA - 360;
      } else if (RA < 0) {
      RA = RA + 360;
      };

      //right ascension value needs to be in the same qua
      Lquadrant = (Math.floor(L / (90))) * 90;
      RAquadrant = (Math.floor(RA / 90)) * 90;
      RA = RA + (Lquadrant - RAquadrant);

      //right ascension value needs to be converted into hours
      RA = RA / 15;

      //calculate the Sun's declination
      sinDec = 0.39782 * Math.sin(L * D2R);
      cosDec = Math.cos(Math.asin(sinDec));

      //calculate the Sun's local hour angle
      cosH = (Math.cos(zenith * D2R) - (sinDec * Math.sin(latitude * D2R))) / (cosDec * Math.cos(latitude * D2R));
      var H;
      if (sunrise) {
      H = 360 - R2D * Math.acos(cosH)
      } else {
      H = R2D * Math.acos(cosH)
      };
      H = H / 15;

      //calculate local mean time of rising/setting
      T = H + RA - (0.06571 * t) - 6.622;

      //adjust back to UTC
      UT = T - lnHour;
      if (UT > 24) {
      UT = UT - 24;
      } else if (UT < 0) {
      UT = UT + 24;
      }

      //convert UT value to local time zone of latitude/longitude
      localT = UT + 1;

      //convert to Milliseconds
      return localT * 3600 * 1000;
      }

      function dayOfYear() {
      var yearFirstDay = Math.floor(new Date().setFullYear(new Date().getFullYear(), 0, 1) / 86400000);
      var today = Math.ceil((new Date().getTime()) / 86400000);
      return today - yearFirstDay;
      }
      [/php]

    • js/jquery-1.11.3.min.js
      compressed jQuery...
    • highcharts/js/highcharts.js
      HighCharts Dateien wie sie auch im Archive zu finden sind..
    • highcharts/js/modules/exporting.js
      HighCharts Dateien wie sie auch im Archive zu finden sind..
    • highcharts/js/themes/grid.js
      HighCharts Dateien wie sie auch im Archive zu finden sind..

    Ich werde mich bemühen noch ein paar mehr Kommentare einzufügen sobald ich fertig bin, sowie den Source auf meinem Github ablegen um es einfacher herunterladen zu können.

  • Klasse!
    Es wird. Und ja, Fragen hab ich. :thumbs1:


    Zitat

    - Der obere Chart hat auch eine kleine Verzögerung beim zeichnen der Linien (animation duration) was ich persönlich auch schöner finde undefined


    Finde ich persönlich jetzt nicht. Wo kann man das denn alles einstellen? OK
    In welchem Abschnitt werden denn eigentlich die Tooltip bearbeitet? OK


    Könntest du noch bitte kurz das PHP in der Index-Datei erklären?
    Warum die DIVs als Echo usw. Kenne das so nicht.

Jetzt mitmachen!

Du hast noch kein Benutzerkonto auf unserer Seite? Registriere dich kostenlos und nimm an unserer Community teil!