de|en

Wetterdatenerfassung mit dem USB-WDE1

Raspberry Pi als Wetterstation

Diese Seite erläutert einige oft nachgefragte technische Aspekte zur Erfassung und Verarbeitung der Wetterdaten in meiner Hobbywetterstation. Basis der Lösung sind die Wettersensoren KS 300 und S 300 sowie der USB-Wetterdaten-Empfänger USB-WDE1 von ELV. Als Steuerrechner dient ein kleiner, sparsamer Linux-Computer auf ARM Basis, wie Linksys NSLU2 oder Raspberry Pi.

USB-Wetterdaten-Empfänger USB-WDE1

Wetterdatenempfänger USB-WDE1 von ELV Der USB-Wetterdaten-Empfänger USB-WDE1 empfängt drahtlos auf 868 MHz die Daten diverser Wettersensoren von ELV. Ihn gibt es als Bausatz und Fertiggerät bei ELV zu kaufen.

Der Empfänger wird an einen USB-Port des Computers angeschlossen. Dieser übernimmt auch gleich die Stromversorgung, sodass kein zusätzliches Netzteil erforderlich ist. Die Datenübertragung erfolgt über ein einfaches serielles ASCII-Protokoll, das von ELV gut dokumentiert wurde. Damit steht diversen Experimenten und kreativen Eigenbaulösungen nichts mehr im Weg!

USB-WDE1 und Linux

Die NSLU2 - ein kompakter und stromsparender Linuxcomputer
Die alte Wetterstation auf Basis der NSLU2 mit externem USB-Hub zur Ansteuerung von WDE-1, 1-wire Bus und Luftdrucksensor. Die neue Lösung mit Raspberry Pi (Bild links oben) integriert alles in ein Gehäuse.

Als Datenerfassungsrechner verrichtete eine NSLU2 mit Debian GNU/Linux 5.0 (Lenny) fünf Jahre klaglos ihren Dienst. Die NSLU2 hat zwei USB-Ports. An einem steckt ein 2GB USB-Stick, der Betriebssystem und Daten aufnimmt. Der andere USB-Port wird durch einen Hub erweitert, sodass sich mehrere USB-Geräte anschließen lassen. Eins davon ist der USB-WDE1.

Ende 2013 habe ich die NSLU2 durch einen Raspberry Pi Model B ersetzt. Dieser hat ebenfalls zwei USB-Ports, allerdings sind beide frei verfügbar, da sich das Betriebssystem auf einer zusätzlichen Speicherkarte befindet.

Die USB-Schnittstelle des USB-WDE1 wird durch den USB-Seriell-Wandler CP 2102 von Silicon Labs realisiert. Das für die Ansteuerung zuständige Kernelmodul cp2101 ist in modernen Linuxen bereits enthalten. Beim Anschluss des USB-WDE1 sollten daher im Systemlog sofort die entsprechenden Meldungen auftauchen:

$ dmesg
usb 1-2.1: Product: ELV USB-WDE1 Wetterdatenempfänger
usb 1-2.1: Manufacturer: Silicon Labs

Das udev Subsystem legt dann auch gleich ein entsprechendes Devicefile an, in der Regel wird das /dev/ttyUSB0 sein. Dieses Device verhält sich aus Sicht eines Linux Anwendungsprogramms wie eine serielle Schnittstelle und kann daher mit beliebigen Terminalprogrammen, wie zum Beispiel minicom, bedient werden. Hat man noch andere USB-Seriell-Wandler am Computer angeschlossen, kann das Device auch /dev/ttyUSB1 oder ähnlich heißen. Wichtig ist, die Baudrate auf 9600 bit/s einzustellen.

Um die vom Wetterdatenempfänger gelieferten Daten über die serielle Schnittstelle auszulesen, arbeitete auf der NSLU2 das Tool socat jahrelang ohne Probleme. Auf dem RaspberryPi hingegen zeigte socat im Zusammenspiel mit dem Wetterdatenempfänger diverse Schwächen, wie häufige Fehlermeldungen WRONG VAL, WRONG CMD und FullBuff->Reset sowie unvollständige Ausgaben. Bessere Erfahrungen machte ich hier mit pyserial, einem Python Modul zur Ansteuerung der seriellen Schnittstelle. Falls noch nicht vorhanden, kann man es mittels des Paketmanagers leicht nachinstallieren:

sudo apt-get install python-serial

Ein damit erstelltes kleines Python Script serialmon.py (Downloadlink am Ende des Artikels) dient zur testweisen Ausgabe der Daten vom USB-WDE1 auf der Konsole:

#!/usr/bin/python -u

import serial
import sys
import os

# serial port of USB-WDE1
port = '/dev/ttyUSB0'

# MAIN
def main():
  # open serial line
  ser = serial.Serial(port, 9600)
  if not ser.isOpen():
    print "Unable to open serial port %s" % port
    sys.exit(1)

  while(1==1):
    # read line from WDE1
    line = ser.readline()
    line = line.strip()
    print line
    
if __name__ == '__main__':
  main()

Nach dem Start von serialmon.py sollte man nach kurzer Zeit die regelmäßigen Ausgaben des Wetterempfängers sehen, zum Beispiel:

$1;1;;;;;;13,0;;;;;;;;58;;;;18,9;39;0,0;2680;0;0
$1;1;;;;;;13,0;;;;;;;;58;;;;18,9;39;0,0;2680;0;0
$1;1;;;;;;13,0;;;;;;;;58;;;;18,9;39;0,0;2680;0;0
$1;1;;;;;;12,9;;;;;;;;58;;;;18,9;39;0,0;2680;0;0
$1;1;;;;;;12,9;;;;;;;;58;;;;18,8;40;0,0;2680;0;0

Jede Zeile stellt einen kompletten Datensatz dar, der aus 25 durch Semikolon getrennten Feldern besteht. Die ersten drei Felder sind unveränderlich, dann folgen die Temperaturmeßwerte (°C) von acht Sensoren (z.B. S 300) und dann die Feuchtewerte (%) dieser acht Sensoren. Danach kommen Temperatur (°C), Luftfeuchte (%), Windgeschwindigkeit (km/h), Niederschlag (Wippenschläge) und Regensensor (0/1) des Kombisensors KS 200 oder KS 300. Das letzte Feld mit dem unveränderlichen Wert 0 steht für das Ende des Datensatzes.

Datenerfassung mit RRDtool

Für eine langfristige Wetterdatenaufzeichnung sollen die Datensätze in eine Datenbank geschrieben werden. Die dabei entstehende Datenmenge ist allerdings immens, wenn man zum Beispiel über zehn Jahre hinweg einen Datensatz pro Minute erfassen und speichern möchte. Es ist daher angebracht, sich zunächst entsprechend des geplanten Anwendungsfalls einige Gedanken über eine sinnvolle Begrenzung der Datenmenge zu machen. So ist es im Hobbybereich vielleicht völlig ausreichend, nur aller 15 Minuten einen Datensatz abzuspeichern. Auch interessiert mich nach einem Jahr nicht mehr die genaue Temperatur zu einer bestimmten Tageszeit - Tagesdurchschnitt, Minimal- und Maximalwerte reichen dann völlig aus.

Die Speicherung der Messwerte übernimmt RRDtool. Dabei handelt es sich im Kern um eine Round Robin Datenbank mit einem sehr leistungsfähigen System zur Erzeugung von Grafiken. RRDtool ist OpenSource, hervorragend dokumentiert und lässt sich unter Linux komfortabel über den Packagemanager installieren:

sudo apt-get install rrdtool python-rrdtool

Am Beginn der Arbeit mit RRDtool steht die Definition der Datenbank. Hierbei muss man sich zunächst Gedanken über zeitliche Auflösung und Umfang der zu speichernden Daten machen. Die für den vorliegenden Anwendungsfall «Hobbywetterstation» getroffene Festlegung ist:

Daraus ergibt sich dann der folgende Aufruf von rrdtool zum Anlegen der Datenbank (Script create_weather_rrd.s - Downloadlink am Ende des Artikels):

rrdtool create weather.rrd --step 900 \
DS:temps1:GAUGE:1200:-40:50 \
DS:temps2:GAUGE:1200:-40:50 \
DS:temps3:GAUGE:1200:-40:50 \
DS:temps4:GAUGE:1200:-40:50 \
DS:temps5:GAUGE:1200:-40:50 \
DS:temps6:GAUGE:1200:-40:50 \
DS:temps7:GAUGE:1200:-40:50 \
DS:temps8:GAUGE:1200:-40:50 \
DS:hums1:GAUGE:1200:0:100 \
DS:hums2:GAUGE:1200:0:100 \
DS:hums3:GAUGE:1200:0:100 \
DS:hums4:GAUGE:1200:0:100 \
DS:hums5:GAUGE:1200:0:100 \
DS:hums6:GAUGE:1200:0:100 \
DS:hums7:GAUGE:1200:0:100 \
DS:hums8:GAUGE:1200:0:100 \
DS:temps9:GAUGE:1200:-40:50 \
DS:hums9:GAUGE:1200:0:100 \
DS:winds9:GAUGE:1200:0:200 \
DS:rains9:DERIVE:1200:0:U \
DS:israins9:GAUGE:1200:0:1 \
RRA:AVERAGE:0.5:1:960 \
RRA:MIN:0.5:96:3600 \
RRA:MAX:0.5:96:3600 \
RRA:AVERAGE:0.5:96:3600

Der Parameter --step 900 legt das grundlegende Datenerfassungsintervall auf 900 Sekunden (15 Minuten) fest.

Anschließend folgt die Definition der Data Sources (DS) - für jeden Sensor eine. Die Namen der Data Sources folgen dabei dem Schema tempsn für einen Temperatur- und humsn für einen Feuchtesensor mit n = 1..9 der Sensornummer. Der Temperaturbereich ist auf -40..50 °C eingeschränkt. winds9 ist der Windmesser am Kombisensor und israins9 der Regensensor. Alle diese Data Sources sind vom Type GAUGE, das heißt der Messwert wird in der Datenbank so gespeichert, wie ihn der USB-WDE1 liefert.

Interessant ist rains9: Hierbei handelt es sich um den Niederschlagsmengenmesser. Er ist in Form einer Wippe realisiert, die nach dem Aufstauen einer definierten Wassermenge einmal wippt und dabei einen Zähler hochzählt. Der WDE1 liefert diesen Zählerstand und damit eine Maßzahl für die absolute Niederschlagsmenge seit Inbetriebnahme der Wetterstation. In der Regel interessiert jedoch die Regenmenge je Zeiteinheit, also pro Stunde oder pro Tag. Daher ist die enstprechende Data Source hier mit dem Typ DERIVE definiert, der automatisch eine Differenzierung des Wertes nach der Zeit vornimmt.

Am Ende steht die Festlegung der Round Robin Archives (RRA), die für die eigentliche Datenspeicherung verantwortlich sind. Die erste Definition legt fest, dass 960 Samples unverdünnt, das heißt mit mit einer Schrittweite von 1, gespeichert werden (vergleiche dazu die oben getroffenen Festlegungen). Die folgenden drei RRAs bewirken die Speicherung von 3600 Minimal-, Maximal- und Durchschnittswerten. Die Berechnung dieser drei Größen erfolgt dabei für jeweils 96 Samples, das ist genau ein Tag (96 * 900 Sekunden).

Das Ausführen des Befehls legt die knapp 2 Megabyte große Datei weather.rrd an. Die Dateigröße ändert sich nicht mehr, egal wieviele Datensätze eingefügt werden - schließlich handelt es sich um eine Round Robin Datenbank!

Beim Umzug der Datenbank von der NSLU2 auf den Raspberry Pi galt es zu beachten, dass die Binärdaten nicht kompatibel sind, da es sich um zwei unterschiedliche Prozessorarchitekturen (ARMv5/XScale und ARMv6/ARM11) handelt. Man darf daher nicht einfach die Datenbankdatei weather.rrd von einer Hardware auf die andere kopieren, sondern muss den Umweg über einen Datenbank-Dump im XML-Format gehen:

# auf NSLU2:
rrdtool dump weather.rrd > weather.xml
# auf Raspi:
rrdtool restore weather.xml weather.rrd -f

Aufnahme läuft!

Zum Einfügen der Daten in die Datenbank erweitert man das Python Script. Am Anfang ist zusätzlich der Import des Moduls rrdtool notwendig:

# ...
import rrdtool

Anstelle der Ausgabe print line erfolgt jetzt die Umwandlung der vom USB-WDE1 gelieferten Daten in ein Format, welches rrdtool versteht (Vollständiger Sourcecode in recweather.py - siehe Downloadlink am Ende des Artikels);

    # ...
    #print line
    
    data = line.split(';')
    if (len(data) == 25 and data[0] == '$1' and data[24] == '0'):
      # data is valid 
      # re-format data into an update string for rrdtool
      for i, val in enumerate(data):
        data[i] = ('U' if val == '' else val.replace(',', '.'))
      update = 'N:' + ':'.join(data[3:24])
      # insert data into database
      rrdtool.update(
        "%s/weather.rrd" % (os.path.dirname(os.path.abspath(__file__))),
        update)
      # terminate the program - we get invoked regularly from cron
      break

Ist eine komplette Zeile mit Messwerten eingetroffen, schreibt rrdtool.update die aufbereiteten Daten in die Round Robin Datenbank, die sich im gleichen Verzeichnis wie das Script befinden muss. Dabei erfolgt eine Durchschnittsbildung und Grenzwertüberprüfung entsprechend den beim Erzeugen der Datenbank angegebenen Regeln.

Die abschließende break Anweisung beendet Schleife und Programm. Wenn man sie weglassen würde, dann verbleibt das Programm in einer Endlosschleife und schreibt alle eintreffenden Daten sofort in die Datenbank. Das kann je nach Anzahl der funkenden Sensoren relativ häufig sein. Falls die Datenbankdatei auf einer SD-Karte oder einem USB-Speicherstick liegt, dann sollte man bedenken, dass diese Medien nur eine bestimmte Anzahl von Schreibzyklen vertragen. Außerdem muss man natürlich ein ständig laufendes Programm regelmäßig darauf überwachen, dass es sich nicht durch einen Fehler beendet hat und es dann neu starten.

In meiner Wetterstation startet daher der cron Dienst das Script aller fünf Minuten. Nach dem erfolgreichen Empfang eines Datensatzes und dem Update der Datenbank beendet es sich sofort. Der entsprechende Eintrag in die cron-Datei des Benutzers lautet

3-58/5 * * * * $HOME/weather/recweather.py >> $HOME/weather/recweather.log 2>&1

Grafik

Eine hervorragende Eigenschaft von rrdtool ist die eingebaute Grafikengine, mit der sich anprechende Grafiken komfortabel erstellen lassen. Eine Grafik mit dem Temperaturverlauf der letzten Woche an den Sensoren temps5 und temps9 erstellt man zum Beispiel durch:

rrdtool graph tempweek.png \
  -s 'now - 1 week' -e 'now' \
  DEF:temps5=weather.rrd:temps5:AVERAGE \
  LINE2:temps5#000000:Keller \
  DEF:temps9=weather.rrd:temps9:AVERAGE \
  LINE2:temps9#0000FF:Außen 

Die Parameter -s und -e spezifizieren den Zeitbereich. Mit DEF werden die auszugebenden Variablen durch RRD-Dateiname, Data Source und Konsolidierungsfunktion definiert. LINE2 zeichnet eine Linie für jede Variable unter Angabe der Farbe und des Namens. RRDtool kümmert sich dann selbständig um eine ansprechende Skalierung und Beschriftung der Grafik:

Temperaturverlauf über eine Woche
Temperaturverlauf über eine Woche

Um Durchschnitt, Minimal- und Maximalwerte ansprechend darzustellen, ist etwas mehr Vorabeit notwendig. Schlüssel zum Ziel ist hier die Berechnung eines virtuellen Datensatzes mittels CDEF, der die Differenz zwischen Minimal- und Maximalwerten enthält. Diesen gibt man dann mittels AREA als Fläche aus; der Parameter STACK bewirkt, dass die Fläche nicht an der X-Achse beginnt, sondern auf die vorhergehende LINE1 aufgestapelt wird:

rrdtool graph tempmonth.png \
  -s 'now - 1 month' -e 'now' \
  DEF:tempmins9=weather.rrd:temps9:MIN \
  DEF:tempmaxs9=weather.rrd:temps9:MAX \
  DEF:temps9=weather.rrd:temps9:AVERAGE \
  CDEF:tempranges9=tempmaxs9,tempmins9,- \
  LINE1:tempmins9#0000FF \
  AREA:tempranges9#8dadf588::STACK \
  LINE1:tempmaxs9#0000FF \
  LINE2:temps9#0000FF:Außen
Minimal-, Maximal- und Durchschnittswerte während eines Monats
Minimal-, Maximal- und Durchschnittswerte während eines Monats

Es regnet!

Die grafische Darstellung von Luftfeuchte und Windgeschwindigkeit folgt dem gleichen Schema, da es sich hier auch um Absolutwerte handelt. Einer Sonderbehandlung bedarf jedoch die Niederschlagsmenge: Wie schon erklärt, liefert der Regenmengenmesser die Anzahl der Wippenschläge als Meßwert. Aufgrund der Typdefinition DERIVE beim Anlegen der Datenbank differenziert diese den Meßwert und speichert den Wert «Wippenschläge pro Sekunde». Um daraus jetzt die Regenmenge in Millimetern pro Tag zu berechnen, benötigt man als erstes die Information, welcher Niederschlagsmenge ein Wippenschalg entspricht. Hier hilft die Dokumentation des WDE1 weiter:

1 Wippenschlag =  295 ml/m² = 0.295 mm

Da ein Tag 24 Stunden mit je 3600 Sekunden hat, kann man nun die virtuelle Variable rainpd per CDEF berechnen, die die gesuchte Niederschlagsmenge pro Tag liefert. Das Beispiel zeigt weiterhin, wie man die Gesamtregenmenge pro Monat rainpm berechnet und als Text mittels GPRINT ausgibt:

rrdtool graph rainmonth.png \
  -s '01.05.2010' -e '31.05.2010' \
  -v mm/d \
  DEF:rains9=weather.rrd:rains9:AVERAGE \
  CDEF:rainpd=rains9,3600,*,24,*,0.295,* \
  CDEF:rainpm=rainpd,30,* \
  VDEF:totalrain=rainpm,AVERAGE \
  GPRINT:totalrain:"Total %6.0lf mm/Mon" \
  LINE2:rainpd#0000FF
Tägliche Niederschlagsmengen im Mai 2010
Tägliche Niederschlagsmengen im Mai 2010

Fazit

Dieser Artikel behandelt nur Teilaspekte des Aufbaus meiner Wetterstation. Nicht beschrieben sind hier der Luftdruckmesser mit dem BMP085 und das 1-wire Interface für Temparatur- und Luftdruckmessung - dazu an anderer Stelle mehr. Ausdrücklich möchte ich darauf hinweisen, dass die beschriebenen Techniken nur für den Hobby- und Privatbereich geeignet sind.