Wetterstation

Wie ist das Wetter im Garten?

In meinem Garten steht jetzt eine kleine Wetterstation, die über WLAN ihre Daten an ein Raspberry Pi schickt und hier hier veröffentlicht. Im Folgenden beschreibe ich kurz, was ich gemacht habe, damit die Daten direkt auf meinem Server landen und nicht unwissentlich auch noch an den Hersteller in China.

Anfang November 2020 habe ich mir für den Garten eine WLAN-fähige Wetterstation gekauft. Eigentlich sendet sie ihre Daten an registrierungspflichtige Internet-Wetterportale. Darauf hatte ich keine Lust, ich wollte meine Daten selber behalten. Dafür habe ich mit hostapd und dnsmasq eine WLAN-Access-Point auf einem Raspberry Pi eingerichtet. Dafür gibt es viele Anleitungen im Internet, die sich durch Klenigkeiten unterscheiden (z.B. UFW als iptables-frontend oder netfilter-/iptables-persinstent. Des weiteren hab ich für den Internet-Zugriff einen USB-Surfstick angeschlossen, der Plug & Play war und ohne extra Konfiguration direkt funktioniert hat.

Über die Handy-App der Wetterstation habe ich das Raspi als Empfänger der Wetterdaten eingetragen. Nun wußte ich aber gar nicht, wie die Daten übermittelt werden. Mit tcpdump und wireshark hab ich heraus gefunden, dass die Daten der Wetterstation unverschlüsselt per http-post an den Server geschickt werden. Nach einer gewissen Zeit brach das jedoch ab. Beim weiteren Durchsuchen des Paket-Dumps ist mir dann aufgefallen, dass die Wetterstation versucht Daten von der Seite ecowitt.net abzufragen. Außerdem braucht die Wetterstation eine Internet-Verbindung, um über NTP die Zeit einzustellen.

Nun müssen beim ersten Einrichten der Wetterstation über die Handy-App meine Positionsdaten and ecowitt übermittelt worden sein. Denn es wird abgefragt, wann Sonnenauf- und -untergang sind. Da hab ich nicht aufgepasst und das “Kleingedruckte” nicht gelesen, wenn es denn überhaupt erwähnt wurde. Mit Hilfe des dnsmasqd, leite ich die Anfrage der Wetterstation für die IP-Adresse des Herstellers nun auch an meine lokale “Webseite” um, welche die angefragten Daten nun übermittelt. Und siehe da ich kriege immer aktuelle Wetterdaten. Das PHP-Skript was das übernimmt ist unten angefügt. Zusätzlich könnte ich auch die NTP-Anfragen auf mein Raspi umleiten, dies halte ich aber nicht für nötig.

Jetzt kann ich mir immer das aktuelle und vergangene Wetter im Garten anschauen: Gartenwetter

<?php
date_default_timezone_set('Europe/Berlin');

$pgconn = pg_connect("host=localhost dbname=weather user=weather password=<very secret password>")
        or die("Could not connect to DB: ". pg_last_error()."\n");


if (!empty($_POST)) {
  $metdata['dateos'] = date("Y-m-d H:i:s");
  foreach($_POST as $key => $value) {
    ## TODO: this must be more flexible. Hardcoded "fields" and return values.
    if ($key == 'fields') {
      ## Return requested field as json string
      ## the original server replies without 'Content-Type', so as text/html
      # header("Content-Type: text/plain");
      header("Content-Type: application/json");
      echo '{"timezone":"'. date_default_timezone_get() .'",';
      echo '"utc_offset":"'. date('Z') .'",';
      echo '"dst":"'. date('I') .'",';
      echo '"date_sunrise":"'. date_sunrise(time(), SUNFUNCS_RET_STRING, 50.0, 8.283, 90, date('Z')/3600) .'",';
      echo '"date_sunset":"'. date_sunset(time(), SUNFUNCS_RET_STRING, 50.0, 8.283, 90, date('Z')/3600) .'"}';
      exit;
    }
    ## Unit conversion:
    ## Temperature values come in as Degree Fahrenheit
    if (preg_match('/^temp/', $key)) {
      $value = ($value - 32.0) * 5.0 / 9.0;
    ## pressure comes in as inches of Mercury
    } elseif (preg_match('/^barom/', $key)) {
      $value = $value / 0.02953;
    ## wind speed comes in as miles per hour
    } elseif (preg_match('/gust|speed/', $key)) {
      $value = $value / 2.237;
    ## Rain comes in as inches per hour
    } elseif (preg_match('/rain/', $key)) {
      $value = $value * 25.4 / 60.0;
    }

    ## create a new list with the converted values
    if ($key == 'dateutc') {
      $metdata['dateutc'] = $value;
      $metdata['date'] = "$value UTC";
    } elseif ($key == 'baromrelin') {
      $metdata['rpressure'] = $value;
    } elseif ($key == 'baromabsin') {
      $metdata['apressure'] = $value;
    } elseif ($key == 'tempf') {
      $metdata['temperature'] = $value;
    } elseif ($key == 'humidity') {
      $metdata['humidity'] = $value;
    } elseif ($key == 'winddir') {
      $metdata['winddirection'] = $value;
    } elseif ($key == 'windspeedmph') {
      $metdata['windspeed'] = $value;
    } elseif ($key == 'windgustmph') {
      $metdata['windgust'] = $value;
    } elseif ($key == 'rainratein') {
      $metdata['precipitation'] = $value;
    } elseif ($key == 'eventrainin') {
      $metdata['eprecipitation'] = $value;
    } elseif ($key == 'solarradiation') {
      $metdata['radiation'] = $value;
    } elseif ($key == 'uv') {
      $metdata['uv'] = $value;
    } elseif ($key == 'wh65batt') {
      $metdata['wh65batt'] = $value;
    }
  }
  $res = pg_insert($pgconn, 'weather', $metdata);
}

?>

See also