Motivation

Eines der vielleicht am häufigsten realisierten Projekte ist der Betrieb einer eigenen Wetterstation, ob nun als fertiges Gerät oder selber gebaut. Einerseits spart es den morgendlich Blick aus dem Fenster, andererseits ist es auch reizvoll, die Daten zu veröffentlichen, mit anderen Wetterexperten zu teilen und auch den Vergleich zu den eigenen historischen Daten zu haben um z.B. einen Wettertrend daraus abzuleiten.
Einige der Messwerte einer Wetterstation können aber auch perfekt für die Steuerung im SmartHome genutzt werden. So kann die Helligkeit die Außenbeleuchtung und die Rollläden besser steuern, als ein starrer Zeitplan oder die aus dem Internet geladenen oder auf Basis des Standortes selbst berechneten Sonnenauf- und -untergangs-Zeiten, die zudem den Bewölkungsgrad nur pauschal berücksichtigen können.
Der Regensensor kann beispielsweise genutzt werden, um die Bewässerungsautomatik kurzfristig zu deaktivieren und nicht erst die Ergebnisse der vielleicht vorhandenen Feuchtesensoren abzuwarten.
Auch eine Benachrichtigung bei beginnendem Regen und geöffneten Dachfenstern oder schlicht den mitzunehmenden Regenschirm wäre eine denkbare Anwendung.

Anzeige der Wetterdaten in der Hausautomation (EDOMI)

Das Konzept

Basis der Wetterstation ist eine, beispielsweise bei Aliexpress beziehbare, Outdoorwetterstation, bestehend aus Windrichtungsmesser, Windgeschwindigkeitsmesser, Regenmengenmesser und Temperatur- und Luftfeuchtesensor. Letztere sind in diesem Fall in dem für Wetterstationen typischen Wetterschutzgehäuse enthalten. Genutzt wird für diese Projekte aber nur das Gehäuse dieser Sensoren da es auch die Anschlüsse für die restlichen drei Einheiten aufnimmt.

Die Sensoren aus den Wind- und Regensensoren werden später durch einen ATMega328P verarbeitet und seriell an den als zentrale Einheit eingesetzten Raspberry Pi 3+ übertragen.
Die Technik im Inneren des Sonnenschutzgehäuses – Auswerteelektronik für Wind und Regen sowie Temperatur und Luftfeuchtesensor – kommen nicht zum Einsatz. Das Gehäuse nimmt später den Atmel sowie weitere Sensoren auf.

Als Nächstes stand die Frage im Raum, welche Sensoren aktuell für Anwendungen in der Amateurliga angeboten werden.
Am Ende fiel die Wahl auf den BH1750, BME280, BMP388, MCP9808, VEML6070 sowie einen AT42QT1012. Damit sind Temperaturen, Luftfeuchte, Luftdruck, Helligkeit, UV-Strahlung und ein Regensignal abgedeckt. Zusätzlich kommt ein  Feinstaubsensor vom Typ SDS011 zum Einsatz. Die Beschreibungen der Sensoren erfolgt im nächsten Abschnitt.

Basis Wetterstation

Als zentraler Rechner wird ein Raspberry Pi 3 mit LED-Display genutzt, der einerseits über USB (SDS011), serieller Schnittstelle und I²C-Bus die Daten der verschiedenen Komponenten einsammelt und sie andererseits über das Netzwerk an einen MQTT-Broker sendet, von dem sich die Hausautomation (EDOMI) die Werte zur Archivierung und Anzeige abholt. Als Netzwerkverbindung kann je nach Verfügbarkeit die Ethernetschnittstelle oder das W-LAN des RasPi’s genutzt werden.
Das Display ist im Normalbetrieb deaktiviert und kann über einen im IP65-Gehäuse verbauten RFID-Reader aktiviert werden.

Die Sensoren

Die meisten der Sensoren nutzen zur Datenübertragung einen I²C-Bus. Zum Glück haben sich die Hersteller der Chips bezüglich der Adressen, mit denen die Sensoren über den Bus angesprochen werden, angestimmt, sodass es zu keiner Adresskollision kommt und alle Sensoren parallel verdrahtet werden können. Bei einigen Sensoren wäre aber auch eine Anpassung der Adresse möglich.
Für die Auslesung der Daten wird in den meisten Fällen eine Softwarebibliothek benötigt. Einige der Bibliotheken sind für Python 2 andere für Python 3 geschrieben. Daher muss RasPi entsprechend vorbereitet sein. Dazu später mehr.

 

Der BH1750 ist ein Lichtmodul oder auch Lichtsensor mit dem die Helligkeit in Lux digital gemessen wird. 
Betriebsspannung: 3.3V oder 5V
Messbereich: 0 – 54612 Lux
I²C-Adresse: 0x23
Library: nicht erforderlich – Beispiel-Skript

Der VEML6070 ist ein UV-A Lichtsensor, der über einem I²C-Bus ausgelesen wird.
Betriebsspannung: 3.3V oder 5V
UV Spektrum Empfindlichkeit: 320-410nm
I²C-Adresse: 0x38, 0x39
Library: http://github.com/cmur2/python-veml6070

Der BME280 Sensor ist ein  Sensor, der sowohl Temperatur und Luftfeuchtigkeit, als auch den Luftdruck messen kann.
Betriebsspannung: 3.3V oder 5V
Temperatur -40°C bis +85°C
Luftdruck 300hPa bis 1100hPa
Luftfeuchtigkeit ±3% relative Genauigkeit
I²C-Adresse: 0x76
Library: nicht erforderlich – Beispiel-Skript

Der BMP388 ist der Nachfolger des BMP280 mit höherer Präzision. Er misst Luftdruck und Temperatur und gibt die Höhe über Meeresspiegel aus.
Betriebsspannung: 3.3V oder 5V
Temperatur: -40°C bis +80°C
Luftdruck: 300hPa bis 1250 hPa
Höhenmessung: +/- 0.5m
I²C-Adresse: 0x77 wenn SDO=1
Library: Adafruit_CircuitPython_BMP3XX

Der MCP9808 ist ein hochgenauer Temperatursensor. Er wird über I²C ausgelesen und kann in drei Genauigkeitstufen arbeiten.
Betriebsspannung: 3.3V oder 5V
Temperatur: -40°C bis +125°C
Genauigkeit: +0.5°C, +0.25°C, +0.125°C, +0.0625°C
I²C-Adresse: 0x18 – 0x1D je nach Beschaltung A0-A2
Library: Adafruit_CircuitPython_MCP9808

Der AT42QT1012 ist ein kapazitiver Berührungssensor. Neben der berührungsempfindlichen Fläche kann der Sensor auch mit einer Art Antenne erweitert werden, die den Chip auf Veränderungen durch Annäherung von Körpern, wie z.B. Regentropfen, regieren lässt.

Betriebsspannung: 3.3V oder 5V
Library: keine (digitaler Ausgang – GPIO-Status-Abfrage)

Der RC522 ist ein 13,56Mhz RFID-Reader. Er kommuniziert direkt mit dem verwendeten RasPi über die SPI Schnittstelle, die vorher aktiviert werden muss (s.u.).
In diesem Projekt wird er genutzt, um das LED-Display zu aktivieren um die aktuellen Daten lokal anzeigen zu lassen.

Betriebsspannung 3.3V
Library: https://github.com/mxgxw/MFRC522-python

Das ST7735 LCD Modul ist ein 1,8″ TFT Display mit einer Auflösung von 128×160 Pixeln in Vollfarben.
Das Display wird direkt an die SPI-Schnittstelle des RasPi angeschlossen, die vorher aktiviert werden muss (s.u.).

Betriebsspannung: 3.3V oder 5v
Library: https://github.com/pimoroni/st7735-python

Der SDS011 ist ein Feinstaubsenor-Modul und nutzt das Prinzip der Laserstreuung, um Partikel zwischen 0,3 und 10 µm in der Luft zu erkennen. Über einen digitalen Ausgang werden die Daten übertragen. Dank integriertem Lüfter arbeitet das Modul exakt und zuverlässig.

Betriebsspannung: 5V
Datenübertragung: USB

Software: Python Script auf GitHub

Das Zusammenwirken der Komponenten - Testaufbau

Bevor die einzelnen Komponenten montiert werden, ist es sehr empfehlenswert, die gesamte Schaltung auf Steckbrettern zu verdrahten. Neben der elektrischen Funktionsprobe ist nur so die korrekte Funktion der unterschiedlichen Softwarekomponenten zu testen.
Aufgrund der aus verschiedenen Quellen stammenden Bibliotheken und Beispielskripte ist es am Ende pflegeleichter, jedem Sensor sein eigenes Skript für das Auslesen der Werte und Übertragen an den MQTT-Broker zu spendieren.
Es gibt nur ein Python-Skript, was beim Booten des RasPi’s mit gestartet wird. Das ist das Skript zur Ansteuerung des TFT LCD-Display und zur Abfrage des RC522. Dieses Programm muss ständig aktiv sind, da jederzeit ein RFID-Coin an den Leser gehalten werden kann.
Die Skripte zum Auslesen der einzelnen Sensoren werden in geeigneten Abständen durch diverse Cronjobs gestartet. Alle Skripte arbeiten dabei nach dem gleichen Prinzip: Initialisierung des Sensors, Auslesen der Werte, Speichern in einer JSON-Datei zum späteren Anzeigen auf dem Display und Übertragung (publishen) an den MQTT-Broker.

Schaltung Wetterstation für Raspberry 3+ und Arduino Pro-Mini

Wie im Schaltplan zu erkennen ist, werden die Wind- und Regensensoren durch den Arduino-Pro-Mini (ATMega328p) vorverarbeitet und seriell (RX/TX) an den RasPi übertragen. Er ersetzt somit die ausgebaute Elektronik der original Wetterstation. Die restlichen Sensoren in dem Außengehäuse werden direkt per I²C übertragen.

Der Windrichtungsgeber schaltet je nach Position eines durch die Windfahne positionierten Magneten einen Readkontakt. Bei Zwischenpositionen können auch zwei Kontakte gleichzeitig geschlossen werden. Die zugeschalteten Widerstände (R3-R10) bilden zusammen mit R1 einen Spannungsteiler, dessen Wert über den analogen Eingang A0 des ATMega gemessen und in die äquivalente Windrichtung umgerechnet werden.

Die Windgeschwindigkeit leitet sich aus der Anzahl der durch Interrupt im ATMega gezählten Kontaktschließungen des Windmesser-Readkontaktes ab. Die  für den hier verwendeten Windmesser typischen Konstanten (Umdrehungen pro m/s) sind im Arduino-Sketch fest hinterlegt.

Der Regenmesser basiert auf einer Messwippe, deren jeweils oben stehende Seite von den aufgefangenen Regentropfen gefüllt wird. Ist die Schaufel der Wippe schwer genug, kippt sie um und das Füllen wird auf der gegenüberliegenden Seite fortgesetzt, bis die Wippe zurückkippt. Jeder Kippvorgang wird durch einen Readkontakt registriert und im ATMege durch Interrupt gezählt. Die Menge (l/m³) je Kippvorgang ist spezifisch und im Arduino-Sketch hinterlegt.

Der Regensensor nutzt die externe Antenne, die als kleine Spule ausgeführt ist, durch die im Regenmesser gesammelten Tropfen auf die Wippe fallen. Sobald der Regen einsetzt und die ersten Tropen durch den Trichter laufen, löst der Sensor aus. Nach Regenende und Trocknung des Einlaufs wird die Meldung zurückgesetzt.  Das Verfahren funktioniert grundsätzlich auf dem Steckbrett. In der Praxis sind äußere Einflüsse leider dafür verantwortlich, dass der Sensor dauerhaft ein Signal ausgibt. Da ist noch etwas Feinjustierung notwendig.

Die übrigen Sensoren für Temperatur, Luftfeuchte, Luftdruck, Licht und UV-A Strahlung sind über den gemeinsamen I²C-Bus direkt am RasPi angeschlossen. Bei der Sensorauswahl haben sich ein paar Messwert-Redundanzen ergeben, daher ist der MCP9808 im Inneren des Gehäuses gelandet, sodass gerade im Hochsommer auch diese Temperatur gemeldet wird. Die restlichen Sensoren sind im Wetterschutzgehäuse der Außeneinheit untergebracht. Die beiden Licht-Sensoren sitzen auf dem Gehäuse unter einer Quarzglasglocke. Quarzglas ist notwendig, da es durchlässig für die UV-A Strahlung ist.
Da einige der Sensoren einen Spannungswandler 5V->3.3V an Board haben und andere nur mit 3.3V arbeiten sind die Sensoren so verschaltet, dass der Wandler des BMP388 dafür genutzt wird. Das spart eine Ader in der Leitung vom RasPi zum Außengehäuse. Als Verbindungsleitung kommt ein 6poliges Telefonverlängerungskabel zum Einsatz, welches auf beiden Seiten eine RJ12-6P6C-Stecker besitzt.

Testaufbau der Wetterstation

Das TFT-LCD-Display sowie der RFID-Reader sind per SPI ebenfalls direkt am RasPi angeschlossen. Für die Auswahl des gerade addressierten Gerätes hält der RasPi an den Anschlüssen CE0 und CE1 zwei Select-Ausgänge bereit.
Das LCD-Display wird beim Erkennen einer vorher in dem Skript MRFC522.py hinterlegten Seriennummer eines RFID-Chips über die Ausgang GPIO17 des RasPi ein- bzw. ausgeschaltet.

Die Datenhaltung - MQTT

Unabhängig von verschiedenen Systemen lassen sich die Daten am besten in einem allgemein erreichbaren Zwischenspeicher lagern. Dafür bietet sich ein MQTT-Dienst an. In meinem Fall läuft auf einem der Server der Open Source Message Broker mosquitto. Der Dienst wird auch von anderen SmartHome-Komponenten – z.B. der Bewässerung – genutzt, um Daten auszutauschen. Die Messwerte der Sensoren einerseits bilden die Basis für eine Visualisierung in der Homeautomatisierung, können andererseits aber auch an Standalone-Displays, die in verschiedenen Räumen angebracht oder aufgestellt sind, per MQTT verteilt werden.

Neben der Nutzung als Datendrehscheibe wird in diesem Projekt – genau wie bei dem Bewässerungsprojekt – MQTT auch zur Ausgabe von Befehlen an die Wetterstation genutzt.

Der Regenmesser soll die Menge in der Zeit von 0:00 bis 24:00 zählen. Der Zählwert entspricht einer internen Variable im Arduino-Sketch. Damit um 0:00 dieser Zählwert wieder bei 0 zu zählen beginnt, wird das Sketch beim Setzen einer 1 im Topic weather/sensor/am328p/totalRainReset veranlasst, die Variable auf Null zu setzen. Bei tagelangem Dauerregen entstünde so quasi ein Sägezahn in einem Diagramm.

MQTT-Topic-Baum der Wetterstation

Inbetriebnahme

Zunächst soll der RasPi für den Betrieb der I²C basierenden Sensoren und der beiden über die SPI-Schnittstelle angeschlossenen Bauteile (Display und RFID-Reader) fit gemacht werden.

Nachdem der RasPi per SD-Karte sein Betriebssystem bekommen hat, sind noch ein paar Konfigurationen notwendig, damit er auch über die beiden Schnittstellen kommunizieren kann.

Hinweis: Alle Angaben beziehen sich auf die Raspian Version 9 (stretch). Für die Aktivierung des SSH-Zugangs und evtl. der WLAN-Anbindung bitte die Anweisungen zur Installation des Raspian beachten. Üblicherweise werden direkt am PC auf den FAT32-Teil der SD-Karte für diese beiden Anwendungsfälle zwei speziell Dateien geschrieben, bevor die SD-Karte für den ersten Start in den RasPi eingeschoben wird.

Nach dem Booten und dem Login (nach dem ersten Start ist der User: pi und das Passwort: raspberry) muss der I²C-Bus zunächst aktiviert werden. Für diesen Zweck wird das RasPi Konfigurationsprogramm gestartet. Da der User Pi keine Root-Rechte besitzt, muss dem Kommando ein sudo vorangestellt werden, dass ihm einmalig Root-Rechte erteilt:

 

				
					sudo raspi-config
				
			

Unter dem Menüpunkt „5 Interface Options->P5 I2C“ wird die Frage, ob das I2C-Interface enabled werden soll, mit  <JA> beantwortet. Da wir schonmal hier an dieser Stelle sind, wird unter „->P4 SPI“ auch diese Schnittstelle mit aktiviert.
Die Änderungen werden in der Datei /boot/config.txt gespeichert. Hier müssen noch ein paar weitere Parameter unkommentiert bzw. eingetragen werden. Vor dem nächsten Reboot sollte die config.txt wie folgt aussehen.

				
					dtoverlay=spi-bcm2708
dtoverlay=i2c-bcm2708
dtparam=i2c_arm=on
dtparam=i2c1_baudrate=10000 # real 100 kHz - siehe forum.weihenstephan.org/forum/phpBB3/viewtopic.php?t=684
dtparam=spi=on

				
			

Nach dem Reboot können nun die I²C-Tools und Python Bibliotheken installiert werden. Diese geschieht mit den folgenden Kommandos gleich für Python 2 und Python 3, da wir, wie oben beschrieben, Module nutzen, die diese eine oder andere Versionen von Python voraussetzen.

				
					sudo apt-get update
sudo apt-get install python-smbus python3-smbus python-dev python3-dev i2c-tools
				
			

Ob die i2c-tools funktionieren und alle oben eingetragenen Einstellungen korrekt sind,  kann mit folgendem Kommando getestet werden.

				
					sudo i2cdetect -y 1
				
			

Das Kommando scannt den I²C-Bus beginnend ab Adresse 0x03 bis zur Adresse 0x77 durch und trägt an den Stellen in der Adressmatrix die passende Adresse ein, wenn ein Sensor geantwortet hat. Wenn alle Sensoren angeschlossen sind, könnte die Ausgabe des Kommandos wie folgt aussehen:

				
					____ 0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- 18 -- -- -- -- -- -- --
20: -- -- -- 23 -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- 38 39 -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- 76 77

				
			

Sensor-Skripte downloaden, entpacken und testen

Nun können die Python-Module für die einzelnen Sensoren aus den oben in der Aufstellung angegebenen Quellen geladen und einzeln getestet werden.
Dies soll anhand eines der Sensoren gezeigt werden. Die Installation der Python-Skripte ähneln sich. Für einige  Sensoren werden neben den Demo-Skripten auch gleich die passenden Bibliotheken installiert, sodass einem Sensor-Funktionstest nichts entgegen spricht.
Nach der Anmeldung auf der Konsole des RasPi „landet“ man als User immer im eigenen Home-Verzeichnis. Für den Benutzer pi ist das Verzeichnis /home/pi. Grundsätzlich spricht nichts dagegen, alle Skripte in diesem Ordner zu installieren und auch von hier aus zu starten.
Für den Sensor BMP388 kann das wie folgt aussehen. Der Parameter -O des Kommandos wget gibt den Dateinamen an, unter dem der Download gespeichert werden soll. Anschließen wird die Datei entpackt und wenn vorhanden das setup.py Skript ausgeführt.

Hinweis: Aufgrund der sehr unterschiedlichen Strukturen der ZIP-Archive, ist es empfehlenswert, die Dateien der einzelnen Sensoren in jeweils einen eignen Unterordner zu verschieben und dort zu entpacken. Die für das Projekt benötigten Skripte aus den entpackten Daten können nach den erfolgreichen Tests in das gemeinsame, übergeordneten Verzeichnis kopiert werden. Das klappt allerdings beim VEML6070-Modul nicht – zumindest nicht ohne in die Python-Programmierung einzusteigen.

				
					wget -O bmp388.zip https://github.com/adafruit/Adafruit_CircuitPython_BMP3XX/archive/master.zip
mkdir bmp388
mv bmp388.zip bmp388
cd bmp388
unzip bmp388.zip
cd Adafruit_CircuitPython_BMP3XX
python3 setup.py
				
			

In diesem Beispiels wird durch das Entpacken ein weiteres Unterverzeichnis erzeugt (Adafruit_CircuitPython_BMP3XX) in dem das Setup-Skript liegt.
Die Beispieldateien liegen in einem weiteren Ordner namens examples. Ist der Sensor bereits erfolgreich verdrahtet und hat das i2cdetect-tool auch die Adresse (0x77) angezeigt, kann ein erster Test gestartet werden.

				
					cd examples
python3 bmp3xx_simpletest.py
> Pressure: 1035.7  Temperature: -1.32

				
			

Werden – wie hier im Beispiel – die Werte korrekt ausgegeben, war der Test des ersten Sensors erfolgreich und es können nach derselben Vorgehensweise auch die übrigen Sensoren getestet werden.

Die Skripte für den Cronjob zusammenstellen

Nachdem nun für alle verwendeten Sensoren die Beispielskripte und ggf. notwendigen Bibliotheken installiert worden, müssen die Skripte etwas modifiziert werden. Denn die Daten sollen ja an einen MQTT-Broker übertragen werden, damit alle Systeme, die diese Wetterdaten benötigen, die aktuellen Werte bekommen.
Ein zweiter einzufügender Schritt in die Skripte ist die Speicherung der aktuellen Werte in einer lokalen .json-Datei je Sensor. Diese Dateien bilden die Datenbasis für die Anzeige der Wetterdaten auf dem internen Display und dienen gleichzeitig dazu, lokal prüfen zu können, ob alle Daten durch die Cronjobs ausgelesen werden und aktuell sind.
Natürlich wäre es durchs auch möglich, diesen alternativen Speicher auszulassen und die anzuzeigenden Werte auch vom MQTT-Broker zu abonnieren.

Auch hierfür soll anhand des schon im vorigen Abschnitts in Betrieb genommen Sensors BMP388 gezeigt werden, welche Schritte in jedem der Sensor-Skripte notwendig sind.
Das im vorigen Kapitel beispielhaft installierte und ausgeführte Skript bmp3xx_simpletest.py ist relative simpel aufgebaut und unten dargestellt.

				
					import time
import board
import busio
import adafruit_bmp3xx

# I2C setup
i2c = busio.I2C(board.SCL, board.SDA)
bmp = adafruit_bmp3xx.BMP3XX_I2C(i2c)

# SPI setup
# from digitalio import DigitalInOut, Direction
# spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
# cs = DigitalInOut(board.D5)
# bmp = adafruit_bmp3xx.BMP3XX_SPI(spi, cs)

bmp.pressure_oversampling = 8
bmp.temperature_oversampling = 2

while True:
    print("Pressure: {:6.1f}  Temperature: {:5.2f}".format(bmp.pressure, bmp.temperature))
    time.sleep(1)
				
			

Wie schon beschrieben, ist es empfehlenswert, der besseren Übersicht wegen, alle produktiven Skripte in die Ordnerebene über den Bibliotheks- und Beispielordnern zu kopieren und ggf. namentlich etwas anzupassen. Im Beispiel wäre das dann z.B. /home/pi/bmp388read.py.
Der hier als Beispiel dienende Sensor BMP388 wird über einem auf Python3 basierenden Skript ausgelesen. Neben den zusätzlich benötigten json- und datetime-Bibliotheken muss auch die PAHO-MQTT-Bibliothek eingebunden werden. Da sie nicht zum Python-Standard gehört, muss sie vorher mit dem folgenden Kommando installiert werden. Bei anderen Sensor-Skripten wird auch die Python2-Version benötigt. Die Installation erfolgt dann statt mit dem Kommando pip3 mit dem Kommando pip.

				
					sudo pip3 install paho-mqtt
sudo pip install paho-mqtt
				
			

Nach dem MQTT-Setup, bei dem die IP-Adresse oder der DNS-Name des MQTT-Brokers angegeben werden muss, erfolgt in diesem speziellen Fall das Auslesen einer JSON-Datei, die durch einen anderen Cronjob gefüllt wird.
Der BMP388 kann die Höhe über N.N. ausgeben. Das ist bei einer lokalen Verwendung der Messwerte relativ witzlos, da man ja weiß, wie hoch man wohnt. Wenn die Daten aber in ein Wetterportal hochgeladen werden, kann man den Wert sozusagen dynamisch mitgeben. (Könnten man natürlich auch als festen Wert hinterlegen 😉 ).
Wie im Screenshot des MQTT-Baumes zu erkennen ist, scheint die Ermittlung der Höhe auch nicht zuverlässig berechnet zu werden. Es dürften hier eher +6m als -6m sein. Zur möglichen Ursache folgen ein paar Gedanken am Ende des Abschnittes.

Damit der BMP388 die Höhe ermitteln kann, benötigt er den aktuellen Luftdruck auf Meeresspiegelhöhe. Die Differenz zu seinem aktuell gemessenen Luftdruck und der bekannten Abnahme des Luftdrucks pro Höhenmeter, ergibt die Höhe des Standortes über N.N. Wenn man jetzt nicht zufällig eine Wetterstation auf Meereshöhe im Zugriff hat, kann man sich den Wert auch von einem Wetterportal mit Angabe eines geeigneten Ortes auslesen.
Genau diese Aufgabe übernimmt das Skript, welches den gesamten empfangenen Datensatz in die JSON-Datei schreibt (getWeatherFromOWM.py). Der Wert main.pressure wird dann dem bmp.sea_level_pressure-Objekt zugewiesen und dient als Referenz für die Berechnung innerhalb des BMP388.

				
					import json
import requests

Response = requests.get("http://api.openweathermap.org/data/2.5/weather?q=<Ort>&APPID=<API ID>")
WeatherData = Response.json()
#print(json.dumps(WeatherData, indent = 4, sort_keys = True))
with open('/home/pi/weather-owm.json', 'w') as json_file:
  json.dump(WeatherData, json_file)
json_file.close()

				
			

Im Abschnitt MQTT wird ein Zeitobjekt erzeugt, was z.B. als letzte Auslese-Zeit an den MQTT-Broker mit übertragen werden kann. Damit haben andere Systeme einen Überblick, wie alt der Messwert ist. Diese Funktion ist hier allerdings noch nicht umgesetzt.
Anschließen werden die drei Werte an den MQTT-Broker übertragen. Da die letzten Werte im MQTT-Broker dauerhaft vorgehalten werden sollen, wird das Retain-Bit gesetzt. Diesen Verhalten hat den Vorteil, dass Geräte, die sich neu mit dem MQTT-Broker verbinden, sofort alle Werte bekommen und nicht erst warten müssen, bis die Cronjobs in der Wetterstation gelaufen sind.

Im letzten Abschnitt des Skripts (Save in Json-File) werden die Daten des bmp-Objektes, was durch die Bibliotheken im Hintergrund ständig mit den aktuellen Messwerten des Sesnors gefüllt wird, in ein Json-Objekt geschrieben und anschließend in einer .json-Datei gespeichert, die das LCD-Display-Skript zur Anzeige nutzt.

				
					import time
import board
import busio
import json
import adafruit_bmp3xx
from datetime import datetime
import paho.mqtt.client as mqtt

# MQTT Setup
mqttc=mqtt.Client()
mqttc.connect("<IP MQTT Broker>",keepalive=60)
mqttc.loop_start()
lastMsg = 0

#Json
data = {}

# I2C setup
i2c = busio.I2C(board.SCL, board.SDA)
bmp = adafruit_bmp3xx.BMP3XX_I2C(i2c)

with open("/home/pi/weather-owm.json", "r") as json_file:
   WeatherData = json.load(json_file)
json_file.close()

bmp.sea_level_pressure = WeatherData['main']['pressure']
bmp.pressure_oversampling = 8
bmp.temperature_oversampling = 2
#bmp.sea_level_pressure = 1013.25


# MQTT
#while True:
dt = datetime.now()
now_seconds = time.mktime(dt.timetuple()) + dt.microsecond/1e6
lastMsg = now_seconds
(result,mid) = mqttc.publish("weather/sensor/bmp388/pressure", payload=bmp.pressure, qos=2, retain=True)
(result,mid) = mqttc.publish("weather/sensor/bmp388/temperature", payload=bmp.temperature, qos=2, retain=True)
(result,mid) = mqttc.publish("weather/sensor/bmp388/altitude", payload=bmp.altitude, qos=2, retain=True)

# Save in Json-File
data['BMP388'] = []
data['BMP388'].append({
     'Pressure': bmp.pressure,
     'Temperature': bmp.temperature,
     'Altitude': bmp.altitude
})
with open('/home/pi/weather-bmp388.json', 'w') as json_file:
  json.dump(data, json_file)

json_file.close()
mqttc.disconnect()
mqttc.loop_stop()
				
			

Nachdem auch die Skripts für die übrigen Sensoren auf dieser Weise angepasst worden, sind nur noch die Cronjobs zu konfigurieren. Danach sollten die Daten per MQTT-Explorer zu sehen sein.
Da die Python-Skripte im Kontext des Users pi laufen, werden auch die Cronjobs mit folgendem Befehl in seinem Kontext angelegt.

				
					crontab -e
				
			

Es wird der Systemeditor gestartet. Meist wird beim ersten Start nach der Festlegung des Standardeditors gefragt. Wer mit dem vi umgehen kann, nutzt diesen. Alternativ funktioniert natürlich auch nano.
Die nachfolgenden Zeilen sollten so oder so ähnlich (bezüglich abweichender Dateinamen) eingetragen werden.
Wie zu erkennen ist, werden die meisten Skripte jede Minute gestartet. Der Feinstaubsensor hat nur eine begrenzte Lebenszeit, daher bekommt er zwischen zwei Messungen 5 Minuten Erholungszeit.

				
					*/1 * * * * /usr/bin/python3 /home/pi/bmp388read2.py >/dev/null 2>&1
*/1 * * * * /usr/bin/python2 /home/pi/mcp9808.py >/dev/null 2>&1
*/1 * * * * /usr/bin/python2 /home/pi/veml6070/veml6070.py >/dev/null 2>&1
*/1 * * * * /usr/bin/python2 /home/pi/bme280.py >/dev/null 2>&1
*/1 * * * * /usr/bin/python2 /home/pi/bh1750.py >/dev/null 2>&1
*/1 * * * * /usr/bin/python2 /home/pi/wind_rain.py >/dev/null 2>&1
*/5 * * * * /usr/bin/python2 /home/pi/sds011.py >/dev/null 2>&1
* */1 * * * /usr/bin/python2 /home/pi/getWeatherFromOWM.py >/dev/null 2>&1

				
			

Das Wetter aus dem beschriebenen Ort in Meereshöhe wird jede Stunde einmal abgerufen. (Dieses Verfahren führt im Übrigen auch zu den Höhenungenauigkeiten, da sich der lokale Luftdruck innerhalb der Stunde schon geändert haben kann. Das Timing birgt auf jeden Fall Optimierungspotential.)
Die Umleitungen der Ausgaben am Ende der Zeilen dienen dazu, Fehlerausschriften ins Nirvana zu schicken, da die Jobs im Hintergrund laufen und keine Console zur Ausgabe von Texten zur Verfügung haben.

Das LCD-Display-Skript

Das TFT-LCD-Display ST7735 wird in diesem Setup per SPI angesteuert. Das verwendete Skript basiert auf der mittlerweile fünfteiligen Dokumentation von Bruce E. Hall, W8BH (Teil 2, 3, 4, 5). Für dieses Projekt wird es um die Sensor spezifischen Funktionen (Auslsen der .json-Dateien) und die Abfrage des RFID-Readers RC522 erweitert.
Die Daten, die das Display anzeigen soll, werden teilweise aus dem System geholt (Zeit, CPU-Temperatur, IP-Adresse). Die Sensordaten liest das Skript aus den von den Cronjobs angelegten .json-Dateien aus.
Damit der RFID-Reader jederzeit auf die Annäherung eines Chips reagieren kann, wird das Skript während des Bootvorganges gestartet und läuft nach einer Initialisierungsphase in einer ständigen Abfrageschleife für die Erkennung des RFID-Chips. Wurde dieser erkannt, wird die Hintergrundbeleuchtung des Displays aktiviert und im 2-Sekunden-Abstand die Screens für die verschiedenen Sensoren angezeigt. Am Ende wird das Display gelöscht und die Hintergrundbeleuchtung wieder deaktiviert.
Damit das Programm einen speziellen RFID-Chip erkennt, muss die ID vorher einmal mit dem Reader-Beispielskript oder durch Unkommentieren der Zeilen 17-18, 22 ausgelesen und im Display-Skript (Zeile 23)  hinterlegt werden. (Die Zeilennummern beziehen sich auf den Codeausschnitt)
Zur Veranschaulichung der Funktion folgt hier nur die Hauptschleife des Programms:

				
					#########################################################################
#   Main Program
#

tWait = 5
print "Weather Display 1.0"
spi = InitSPI()                     #initialize SPI interface
InitGPIO()                          #initialize GPIO interface
InitDisplay()                       #initialize TFT controller
#RunTextTests()                      #run suite of text tests
while True:
   # Scan for cards
   (status,TagType) = MIFAREReader.MFRC522_Request(MIFAREReader.PICC_REQIDL)
   # Get the UID of the card
   (status,uid) = MIFAREReader.MFRC522_Anticoll()
   # If we have the UID, continue
#   print "Status RC522: " + str(status)
#   print "UID RC522: " + str(uid)
#   GPIO.output(LEDA, GPIO.HIGH)
   if status == MIFAREReader.MI_OK:
      # Print UID
#      print("Card UID: "+str(uid[0])+","+str(uid[1])+","+str(uid[2])+","+str(uid[3]))
      if uid[0] == 183  and uid[1] == 217 and uid[2] == 123 and uid[3] == 75:
          # LED Hintergrundbeleuchtung ein (GPIO 17)
          GPIO.output(LEDA, GPIO.HIGH)

          ShowInfo()                           #Show IP, CPUTemp, Time
          time.sleep(tWait)
          ShowBMP388()                        #Show Pressure, Temperature, Altitude
          time.sleep(tWait)
          ShowMCP9808()                       #Show Temperature indoor
          time.sleep(tWait)
          ShowVEML6070()                      #Show UV Raw, UV-A, UV-A-Index
          time.sleep(tWait)
          ShowBH1750()                        #Show Lightlevel
          time.sleep(tWait)
          ShowBMP280()                        #Show Temperature, Humidity, Pressure
          time.sleep(tWait)
          ShowAM328P()                        #Show WindSpeed, WindDirection, max WIns, TotalRain, IsRain
          time.sleep(tWait)
          ShowSDS011()                        #Show PM2,5, PM10
          time.sleep(tWait)

          ClearScreen()
          # LED Hintergrundbeleuchtung aus (GPIO 17)
          GPIO.output(LEDA, GPIO.LOW)

GPIO.cleanup()
spi.close()                         #close down SPI interface
print "Done."


#   END  ###############################################################

				
			

Nachdem dieses Skript läuft, muss es noch in den Startprozess des RasPi’s eingebunden werden. Dazu wird zunächst ein entsprechendes Start/Stopp-Skript im Ordner /etc/init.d/ angelegt. Der Name kann z.B. weatherstation lauten.

				
					#! /bin/sh
### BEGIN INIT INFO
# Provides: noip
# Required-Start: $syslog
# Required-Stop: $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: noip server
# Description:
### END INIT INFO

case "$1" in
    start)
        echo "Wetter-Station wird gestartet"
        # Starte Programm
        nohup python /home/pi/tft_display.py >> /var/log/weatherstation.log 2>&1 &
        ;;
    stop)
        echo "Wetter-Station wird beendet"
        # Beende Programm
        pkill -SIGTERM -f /home/pi/tft_display.py
        ;;
    restart)
        echo "Wetter-Station wird neu gestartet"
        # Restart Programm
        pkill -SIGTERM -f /home/pi/tft_display.py
        nohup python /home/pi/tft_display.py >> /var/log/weatherstation.log 2>&1 &
        ;;
    *)
        echo "Benutzt: /etc/init.d/weatherstation {start|stop|restart}"
        exit 1
        ;;
esac

exit 0

				
			

Damit das Script in den verschiedenen Runleveln des Raspian-Systems automatisch gestartet und gestoppt wird, sind im INIT-Bereich die jeweiliges Runlevel unter Start und Stopp einzutragen. (Siehe Zeile 6,7)
Zudem muss das Skript auch als ausführbar konfiguriert werden. Das wird mit dem nachfolgenden Kommando erledigt.

				
					sudo chmod +x /etc/init.d/weatherstation
				
			

Und zu guter Letzt muss nun noch dafür gesorgt werden, dass das Skript in den im Kopf definierten Runleveln auch starte bzw. gestoppt wird. Das wird mit dem folgenden Kommando erledigt.

				
					sudo update-rc.d weatherstation defaults
				
			

Feinstaubsensor

Der verwendete Feinstaubsensor SDS011ist eine komplett montierte Baugruppe, die im Grund nur noch ein Gehäuse benötigt.
Grundsätzlich können die ermittelten Messwerte klassisch seriell oder mithilfe des mitgelieferten Wandlers auch per USB übertragen werden.
Da die serielle Schnittstelle schon für die Anbindung des ATMega vergeben ist und die Beispielskripte ohnehin auf der Übertragung per USB basieren, fiel die Entscheidung auf diese Lösung.

Als Gehäuse für den Feinstaubsensor dient ein
OBO Bettermann Kabelabzweigkasten T 60. In diesem Gehäuse findet ein passender Rahmen Platz, der sowohl die Verbindung zwischen den Luftein- und -auslässen des Feinstaubsensors nach außen herstellt, als auch den seriell/USB-Wandler aufnimmt und natürlich dem Feinstaubsensormodul einen festen Sitz verleiht. Dieser Rahmen ist als 3D-Druckvorlage auf Thingiverse zu finden.
Aus dem Gehäuse führt ein max. 10m langes USB-Kabel direkt in eine der USB-Buchsen des RasPi. Über diese USB-Verbindung wird das Modul auch mit Energie versorgt.

Das Modul wird in größeren zeitlichen Abständen als die übrigen Sensoren per Cronjob-Skript abgefragt und nach jeder Datenübertragung in einen Schlafmodus versetzt, da die Gesamtbetriebszeit auf ca. 8000 Stunden begrenzt ist.

Wind und Regen

Nun fehlt noch die Auswertung der Wind- und Regensignale. Aufgrund der vorhandenen Elektronik im Wetterschutzgehäuse für die Auswertung der Windsignale und eines Temperaturwertes, war es naheliegend, neben den Außensensoren auch die eigene Elektronik für die Wind- und Regenwerte in diesem Gehäuse unterzubringen, nachdem die  Originalelektronik entfernt wurde.
Als Rechner dient ein ATMege 328P in Form eines Arduino-Pro-Minis mit zwei Stiftleisten, der zusammen mit den übrigen Sensoren auf zwei Lochrasterplatinen montiert ist, die so zugeschnitten sind, dass auch die vorhandenen Aussparungen für die Aufnahme der RJ12-Buchsen für dies Wind- und Regensensoren wieder genutzt werden.

Eine dritte RJ12-Buchse wird auf der Platine, die im ehemaligen Batteriefach Platz gefunden hat montiert, um die Verbindungsleitung zum RasPi aufzunehmen. Die Buchse wird so auf die Platine montiert, dass sie in eine eingefräste Öffnung an der Rückseite des Batteriefaches passt.
Durch die Öffnung in der Aufnahme am oberen Ende und einer entsprechenden Bohrung im Deckels des Außengehäuses werden die vier notwendigen Verbindungen zu den UV- und Lichtsensoren verlegt

JBL UV-C Quarzglasset

Diese Sensoren werden ebenfalls auf einer kleinen Lochrasterplatine montiert und zum Schutz vor Witterungseinflüssen unter einem Quarzglasdom platziert.
Ein kleines Marmeladenglas würde zwar grundsätzlich auch seinen Schutzzweck erfüllen, führt aber im Fall der UV-Messwerte zu Verfälschungen, da normales Glas einen großen Teil der UV-Strahlung schluckt.
Das Quarzglas für den Dom stammt aus einem Wasserfilter für Aquarien und kann zum Beispiel hier bezogen werden.
Der Dom wird mithilfe einer aus dem 3D-Drucker stammenden Manschette mithilfe von vier im Deckel des Wetterschutzgehäuses gekonterten Schrauben gehalten. Durch das Zusammendrücken der Gummidichtung ist die Verbindung wasserdicht.
Der Aufbau des relativ schweren Quarzglasdomes macht das Wetterschutzgehäuse sehr „kopflastig“. Es empfiehlt sich daher – wie in der Grafik erkennbar – am Montagering des Doms einen weitere Halterung zu montieren.

Der Arduino Sketch für Wind- und Regenmessung

Der ATMega328P (Arduino-Pro-Mini) hat zwei Aufgaben zu erfüllen. Die eine Aufgabe besteht in der Erfassung von Windrichtung, Windgeschwindigkeit sowie Regenmenge und Auswertung des Regensensors. Die zweite Aufgabe  ist die serielle Kommunikation mit dem RasPi zur Übertragung der ermittelten Werte.
Für die Auswertung der Wind- und Regenmengensignale wird auf die SDL_Weather_80422 class Bibliothek von SwitchDoc Labs zurückgegriffen.
Sowohl die Regenmengenmessung, als auch die Windgeschwindigkeitsmessung basieren auf Zählung von Impulsen an zwei im Sketch definierten Eingängen des ATMega328P. Damit der Sketch sich nicht nur mit Warten auf den nächsten Impuls beschäftigt, werden solche Funktionen üblicherweise per Interrupt-Service-Routinen realisiert. Für die Definition der Interrupt-Steuerung ist eine weitere Bibliothek namens PinChangeInt erforderlich.
Beide Bibliotheken werden nicht über die Arduino-IDE-Bibliotheksverwaltung eingebunden. Sie werden stattdessen direkt in das Verzeichnis des Arduino-Skripts kopiert. Daher hat die #include-Direktive eine leicht andere Syntax, als bei Bibliotheken, die sich im Standard-Library-Pfad befinden.

				
					#include "SDL_Weather_80422.h"
#include "PinChangeInt.h"
				
			

Somit werden im Hintergrund die Werte ständig aktualisiert. Die Hauptschleife des Sketches liest diese Werte bei jedem Durchlauf aus und ermittelt den aktuell noch nicht zufriedenstellend funktionierenden Regenstatus. Außerdem wird in der Schleife der Empfangspuffer der seriellen Schnittstelle abgefragt und je nach Inhalt (Ziffer 1, 2, 3, 4 oder 5) der vom RasPi-Python-Skript (wind_rain.py) angefragte Messwert zurückgegeben. Das Python-Skript auf dem RasPi speichert diese Werte ebenfalls in einer .json-Datei (weather-am328p.json) und sendet sie an den MQTT-Broker.

Hinweis: Im Arduino-Skript sind einige Zeilen auskommentiert, die für das Debugging während der Inbetriebnahme hilfreich sein können.

Die Montage

Die Innenraumkomponenten (Raspi, Netzteil, evtl. Switch, RFID-Reader, Kabelverbindungsbox) finden in einem IP65 Gehäuse platz. Die Montage wird vereinfacht, wenn eine Hutschiene eingebaut wird, die nicht zur Standardausstattung des Beispiel-Gehäuses zählt.

Der RasPi wird in einem Hutschienengehäuse untergebracht. Damit alle notwendigen Leitungen flexibel an die Ein-/Ausgänge angeschlossen werden können, werden diese mithilfe eines Flachbandkabels in eine neben dem Gehäuse positionierte Anschlussbox geführt auf deren Lochrasterplatte zwei 12-polige Federkraftklemmleisten sowie das Gegenstück zum Flachbandkabel aufgelötet und auf der Unterseite mit Schaltlitze verbunden werden.

An diese beiden Leisten können nun alle Leitungen angeklemmt werden – die Leitung zur Außeneinheit, die Verbindung zum RFID-Reader und die Verbindung zum Display, auch wenn dieses im Gehäuse sitzt. Für den Anschluss der Außeneinheit wird ein RJ12 6p6c-Telefon-Kabel verwendet.
Es ist empfehlenswert, auch auf der Seite des Gehäuses eine RJ12-Buchse zu montieren, da sich die Adern eines Telefonkabels wegen des innen liegenden Nylonfadens sehr schlecht löten lässt.

Oben rechts im Bild ist der Innentemperatursensor zu erkennen, der an den I²C-Bus des RasPi’s angeschlossen ist und von dem es dann weiter zur Außeneinheit geht.

Damit auch das USB-Kabel für den Anschluss der Feinstaubsensoreinheit erst nach der Wandmontage des Gehäuses angeschlossen werden kann, wird im Gehäuse eine USB-A-Buchse mit Durchbruch nach außen montiert und an den RasPi angeschlossen.

 

Im rechten Bereich sitzt das 5V-Netzteil sowie drei Reihenklemmen zur Aufnahme des 230V-Anschlusses. Bei der Verdrahtung der 230V-Seite des Netzteiles ist auf berührungssichere Montage zu achten. Bei Verwendung von flexibler Anschlussleitung unbedingt Aderendhülsen nutzen.

An der linken Außenwand ist der RFID-Reader mit „Panzer“-Klebeband montiert. Somit kann durch das Gehäuse von außen der Chip gelesen werden.

Ein Netzwerk-Switch ist nicht zwingend erforderlich. In diesem Fall werden aber noch zwei weitere Netzwerkgeräte angeschlossen. Daher bot es sich an, alles im Wetterstationsgehäuse zusammenzuführen.

Die Außeneinheit wird z.B. mithilfe einer Mauerhalterung am Giebel angebracht. Dabei ist darauf zu achten, dass der Glasdom im Laufe des Tages nicht durch den Mast oder andere Komponenten abgeschattet wird.

Und nachdem alle Daten übermittelt werden, kann zum Beispiel die ganz oben abgebildete Grafik in der Hausautomation – in diesem Fall Edomi – erstellt werden.

Viel Spaß beim Nachbauen.