ESP32 Wetterstation mit Wettervorhersage, Funksensoren und Messung der Luftqualität
In diesem Artikel möchte ich euch meine neue Wetterstation mit einigen sehr interessanten neuen Features vorstellen. Seit der letzten Version auf Raspberry Pi Basis wurden sämtliche Komponenten neu entwickelt und basieren nun auf den ESP32 Mikrocontroller. Auch diesmal ist der komplette Aufbau wieder Open-Source und wird an den entsprechenden Stellen verlinkt.
Die folgende Liste zeigt die wichtigsten Features der ESP32 Wetterstation:
- Basisstation mit ESP32-S3 und 7 Zoll Display
- Selbst entwickelter Funksensor auf ESP32 Basis
- Akku der Funksensoren wird über Solarzellen geladen
- Sehr niedriger Stromverbrauch der Funksensoren durch ESP-NOW Protokoll
- Maximal 8 Funksensoren einfach per DIP Schalter konfigurierbar
- Funksensoren verbinden sich vollautomatisch mit der Basisstation
- Jeder Funksensor misst Temperatur, Luftfeuchtigkeit und Luftdruck
- Die Basisstation liefert Temperatur, Luftfeuchtigkeit, VOC-Index, Feinstaub-, NOₓ- und CO₂-Gehalt
- Detaillierte Wettervorhersage über ein freies API für die kommenden 48 Stunden bzw. 7 Tage
- Das Display passt sich automatisch an die Helligkeit der Umgebung an
- Professioneller Look mit einem Gehäuse aus dem 3D-Drucker
Inzwischen ist die Wetterstation seit einigen Wochen in Betrieb, daher möchte ich hier das Projekt vorstellen und die einzelnen Komponenten im Detail beschreiben.
Die Bauanleitung gliedert sich in die folgenden Punkte:
- Die Funktionsweise der Wetterstation
- Die verwendeten Umweltsensoren
- OpenWeather API
- Die Basisstation
- Funkmodule
- Fazit
Die Funktionsweise der Wetterstation
Die Wetterstation besteht aus zwei zentralen Komponenten. Die Basisstation mit dem Display zur Anzeige aller Daten und den Sensoren, die regelmäßig die Werte für Temperatur, Luftfeuchtigkeit und Luftdruck schicken. Zusätzlich befinden sich Umweltsensoren zur Messung der Luftqualität in der Basisstation. Das folgende Diagramm verdeutlicht den Aufbau und zeigt, wie die Komponenten miteinander kommunizieren:
Für die Übertragung der Daten von den Funkmodulen wird das sehr schnelle ESP-NOW Protokoll verwendet. Die Kommunikation der Umweltsensoren mit den ESP32 Mikrocontrollern erfolgt über das I2C Protokoll. Per DIP Schalter wird jedem Funkmodul eine Nummer zugeordnet, damit die einzelnen Module eindeutig identifiziert werden können. Die Funkmodule werden mit je einem 18650 Lithium-Ionen-Akku betrieben, der über 6 Volt Solarzellen geladen wird. Die Daten für die Wettervorhersage werden über ein freies API alle 10 Minuten per WLAN abgerufen. Derzeit verwende ich dafür das API von OpenWeatherMap, aber es könnte auch jedes andere API verwendet werden.
Die verwendeten Umweltsensoren
Die wichtigsten Komponenten der Wetterstation sind die Umweltsensoren. Die Wahl der geeigneten Sensoren ist aber schwierig, da die Auswahl sehr groß ist und die Genauigkeit teilweise zu wünschen übrig lässt, speziell bei günstigen Sensoren aus chinesischer Produktion. Neben der Qualität war auch die Verfügbarkeit von ESP32 Bibliotheken sehr wichtig, da die Entwicklung solcher Treiber sehr zeitraubend sein kann. Ich habe mich schließlich für Sensoren von Bosch Sensortec und Sensirion entschieden, die ich hier kurz vorstellen möchte.
Bosch Sensortec BME280
Der BME280 ist ein Umweltsensor, der speziell für mobile Anwendungen von der Firma Bosch Sensortec entwickelt wurde. Er dient zur Messung von Luftfeuchtigkeit, Luftdruck und Temperatur. Der Sensor basiert auf der MEMS-Technologie (Micro-Electro-Mechanical Systems), die eine hohe Präzision und Langzeitstabilität der Messungen gewährleistet.
Der BME280 kann über eine I2C- oder SPI-Schnittstelle kommunizieren, was die Integration in verschiedene Projekte erleichtert. Dank seines geringen Stromverbrauchs ist er besonders für batteriebetriebene Anwendungen geeignet. Ein Nachteil des BME280 ist, dass er sich im Dauerbetrieb etwas erwärmt, was den Temperaturwert verfälschen kann. Da er auf den Funkmodulen aber nur alle 10 Minuten einen Wert liefert, spielt das für die Wetterstation keine Rolle.
Das folgende Blockschaltbild zeigt den Aufbau des Sensors:
Einige wichtige Eckdaten des BME280:
Versorgungsspannung: | 1.71 V bis 3.6 V |
Stromverbrauch: | 1.8 µA @ 1 Hz Luftfeuchtigkeit und Temperatur 2.8 µA @ 1 Hz Luftdruck und Temperatur 3.6 µA @ 1 Hz Luftfeuchtigkeit, Luftdruck und Temperatur 0.1 µA im Sleep Mode |
Messbereich: | -40 °C bis +85 °C 0 bis 100 % rel. Luftfeuchtigkeit 300 bis 1100 hPa |
Sensirion SEN55
Der SEN55 von Sensirion ist ein sehr präziser Umweltsensor, der Partikel, flüchtige organische Verbindungen (VOCs), Stickoxide (NOx), relative Luftfeuchtigkeit und Temperatur messen kann. Der Partikelsensor nutzt eine Laser-basierte Technologie zur Messung von Partikeln der Kategorien PM1, PM2.5, PM4 und PM10. Der NOx und VOC Sensor basieren auf Metal Oxid Gas Sensoren (MOX). Die Kommunikation mit dem Sensor erfolgt über eine I2C Schnittstelle.
Durch die Vielzahl der Parameter, die der SEN55 Sensor messen kann, eignet er sich besonders gut zur Überwachung der Qualität der Raumluft. Speziell der breite Bereich der Feinstaubmessung kann eine Vielzahl von Partikeln in der Umgebungsluft registrieren. Die folgende Übersicht zeigt einige Beispiele von Feinstaub-Partikel und ihrer Größe:
Der Aufbau des SEN55 sieht folgendermaßen aus:
Einige wichtige Eckdaten des SEN55:
Versorgungsspannung: | 4.5 V bis 5.5 V | |
Stromverbrauch: | Durchschnittlich 63 mA Maximal 110 mA |
|
Messbereich: | Temperatur +10 °C bis +40 °C Luftfeuchtigkeit 20 % bis 80 % Partikel 0 bis 1000 μg/m3 |
|
Lebensdauer | > 10 Jahre |
Sensirion SCD41
Der SCD41 ist ein hochpräziser CO2-Sensor von Sensirion. Er wurde entwickelt, um genaue Messungen der Kohlendioxidkonzentration in der Umgebungsluft durchzuführen. Der Sensor basiert auf der NDIR (Non-Dispersive Infrared) -Technologie, bei der die Absorption von Infrarotlicht in einem Gas gemessen wird.
Die Genauigkeit der Messung hängt auch von Faktoren wie Temperatur und Feuchtigkeit ab, daher enthält der SCD41 auch einen Feuchtigkeits- und Temperatursensor, um solche Abweichungen kompensieren zu können. Die Kommunikation erfolgt über eine I2C Schnittstelle.
Das folgende Blockschaltbild zeigt den Aufbau des Sensors:
Einige wichtige Eckdaten des SCD41:
Versorgungsspannung: | 2.4 V bis 5.5 V |
Stromverbrauch: | Durchschnittlich 15 mA Maximal 205 mA |
Messbereich: | 400 bis 5000 ppm |
OpenWeather API
Neben den Werten der Sensoren benötigt die Wetterstation noch die Daten für die Wettervorhersage. Dazu verwende ich wieder das API von OpenWeatherMap. Wie man zu einem OpenWeather API Account kommt, habe ich im Beitrag über die Raspberry Pi Wetterstation schon ausführlich erklärt. Inzwischen habe ich mir auch die APIs von Tomorrow.io und Open-Meteo genauer angeschaut, aber ich habe bis jetzt noch keinen triftigen Grund gefunden, das API zu wechseln. Die Wettervorhersage von OpenWeatherMap ändert gelegentlich ihre Meinung, speziell bei allem, was mehr als 3 Tage in der Zukunft liegt, aber für einen groben Überblick reicht es auf jeden Fall. Vielleicht hat hier jemand Erfahrungswerte, wie das bei anderen Wetter APIs ist? Im Großen und Ganzen bin ich mit OpenWeatherMap sehr zufrieden und da ich weit unter den 1000 Calls pro Tag bin, hat die Nutzung bisher auch nichts gekostet.
Um das Lesen der Wetterdaten zu vereinfachen, habe ich mir eine OpenWeatherData Klasse in C++ geschrieben, die den JSON String parst und die Listen und Werte in entsprechende Variablen schreibt. Der Aufruf des APIs sieht dann folgendermaßen aus:
HTTPClient http; String openweather_url = "https://api.openweathermap.org/data/3.0/onecall"; String openweather_lat = "48.2xxx"; String openweather_lon = "16.3xxx"; String openweather_units = "metric"; String openweather_lang = "de"; String openweather_appid = "xxx"; String apiURL = openweather_url + "?lat=" + openweather_lat + "&lon=" + openweather_lon + "&units=" + openweather_units + "&lang=" + openweather_lang + "&appid=" + openweather_appid; http.begin(apiURL); int httpResponseCode = http.GET(); if (httpResponseCode == HTTP_CODE_OK) { BasicJsonDocument<SpiRamAllocator> doc(65536); DeserializationError error = deserializeJson(doc, http.getStream()); if (error) { log_e("deserializeJson() failed: %s", error.c_str()); } else { JsonObject root = doc.as<JsonObject>(); OpenWeatherData data = OpenWeatherData::from_json(root); xSemaphoreTake(mutex, portMAX_DELAY); disp_weather(data); xSemaphoreGive(mutex); } } http.end();
Für die Deserialization der Daten wird ArduinoJson verwendet, da diese Bibliothek speziell für Mikrocontroller entwickelt wurde und auch das PSRAM des ESP32 unterstützt.
Mehr braucht es eigentlich nicht, um das REST-API von OpenWeatherMap zu verwenden. Andere Wetter-APIs funktionieren auf die gleiche Art und Weise und unterscheiden sich nur durch den Umfang der Daten und den Aufbau des JSON Struktur. Es wäre also prinzipiell kein großer Aufwand, auf ein anderes API umzusteigen, wenn sich dadurch irgendwelche Vorteile ergeben.
Die Basisstation
Die Basisstation der Wetterstation wurde von Grund auf neu entwickelt. Der Raspberry Pi und das Raspberry 7 Zoll Display wurden durch ein deutlich günstigeres ESP32 Display ersetzt. Das gesparte Geld habe ich für hochwertige Umweltsensoren von Sensirion verwendet, damit die Wetterstation noch mehr interessante Daten anzeigen kann.
Hardware
Die zentrale Komponente für die Basisstation ist das Sunton ESP32-S3 7 Zoll Display. Auf dem Display befindet sich ein ESP32-S3 Mikrocontroller mit 8M PSRAM und 16M Flash. Die Grundlagen zu diesem Display habe ich bereits in einem früheren Artikel beschrieben.
Neben den Sensoren wird nur noch der Fotowiderstand mit einem Spannungsteiler für die Regelung der Helligkeit benötigt. Der Schaltplan dazu schaut folgendermaßen aus:
Für die Verkabelung habe ich eine kleine Streifenrasterplatine verwendet, auf der auch die Widerstände eingelötet sind. Diese Lösung ist zwar nicht sehr schön, aber es funktioniert. Das Display bietet leider die 5 Volt Versorgungsspannung, die die Sensoren benötigt, auf keinem der Stecker an. Daher habe ich zwei Kabel für Masse und 5 Volt direkt auf dem Display angelötet. Auf den folgenden Fotos kann man die Verkabelung der Sensoren und des Fotowiderstands mit dem Display sehen.
Software
Die Software der Basisstation ist mit dem Arduino Framework entwickelt worden, da es dafür alle nötigen Bibliotheken gibt, die ich benötigt habe.
- ArduinoJSON: Parsen der OpenWeather Daten
- LovyanGFX: Treiber für das Display
- LVGL: Grafikbibliothek für die Anzeige
- Sensirion SCD4x: Treiber für den CO2 Sensor
- Sensirion SEN5x: Treiber für den SEN55 Umweltsensor
Das folgende Diagramm zeigt den Aufbau der Software. Nach dem Start wird für jede Aufgabe ein Thread gestartet. Diese aktualisieren den jeweiligen Bereich in der Anzeige. Da LVGL nicht threadsafe ist, wird der Zugriff über eine Semaphore gesteuert, damit immer nur ein Thread zugreifen kann.
Das Layout der GUI Komponente wurde zum größten Teil mit SquareLine Studio erstellt. Lediglich die Widgets für die beiden Charts musste ich selbst entwickeln. Leider ist die LVGL Dokumentation über das Erstellen eigener Widgets sehr mangelhaft bis nicht vorhanden. Entsprechend viel Zeit hat mich dieser Teil auch gekostet.
Der Source Code liegt auf GitHub. Im Moment gibt es noch keinen Setup Screen. Daher müssen wie WLAN Einstellungen in der Datei network.cpp und die Parameter für das Wetter API in weather_task.cpp eingetragen werden.
User Interface
Das Design des User Interfaces ist weitgehend identisch zur vorherigen Version. Nur die Daten der Umweltsensoren sind dazugekommen. Ursprünglich wollte ich die Rohdaten der Sensoren anzeigen, aber schließlich bin ich überzeugt worden, dass farbige Indikatoren übersichtlicher sind. Die Anzeige gliedert sich in drei Teile, den Daten des Wetter APIs (Aktuelle Werte, stündliche und tägliche Vorhersage), die Daten der Basisstation und die Daten der Funkmodule. Die folgenden Bilder zeigen zwei Beispiele für die Anzeige:
Winter:
Sommer:
Die blauen und violetten Balken stellen die Niederschlagsmenge für Regen und Schnee dar, wobei Blau für Regen und Violett für Schnee steht. Die Intensität der Farbe zeigt die Wahrscheinlichkeit für Niederschlag an. Der gelbe Hintergrund symbolisiert den Bewölkungsgrad, je gelber desto weniger Wolken.
Gehäuse
Natürlich braucht die Wetterstation auch ein schönes Gehäuse. Der Ausdruck erfolgt wieder mit meinem guten alten Ender 3 in PLA. Zur Sicherheit habe diesmal alle Teile mit Support gedruckt, was zwar gut funktioniert hat, das Entfernen des Supports war aber streckenweise sehr mühsam.
Alle Teile sind entweder einfach zusammengesteckt oder verschraubt. Einzige Ausnahme ist der Fotowiderstand, den habe direkt auf das kleine transparente Teil geklebt, das dann wiederum einfach in den Rahmen gesteckt wird.
Die STL Files zum selber Drucken liegen auf GitHub.
Das Funkmodul
Das Funkmodul ist eine Premiere. Bisher habe ich immer nur die Platinen entworfen und dann die einzelnen Komponenten von Hand eingelötet. Das waren einerseits normale Bauteile wie Widerstände, Kondensatoren usw. oder fertige Komponenten wie ein ESP32 Modul, Laderegler und Ähnliches. Diesmal habe ich das komplette Funkmodul vom Schaltplan bis zu Platine selbst entworfen. Dazu habe EasyEDA verwendet und das Funkmodul von JLCPCB produzieren lassen. Das hat sehr gut funktioniert und auch mit dem Support bin ich sehr zufrieden.
[Update 07.04.2024] Ich habe jetzt auch eine günstigere Variante mit einem ESP32-C6 gebaut. Details dazu findet ihr in diesem Artikel.
Hardware
Das Funkmodul ist mit dem Fokus auf möglichst niedrigen Stromverbrauch entworfen worden. Als Mikrocontroller kommt ein ESP32 zum Einsatz. Ich kenne keinen anderen universell einsetzbaren Mikrocontroller, der einen so niedrigen Stromverbrauch im Deep Sleep Modus hat und außerdem noch WLAN Funktionalität bietet. Als Spannungsregler verwende ich einen XC6220, zum Laden der Akkus habe ich mich für den TP4056 entschieden. Auf die klassischen LEDs, die den Ladezustand anzeigen, habe ich verzichtet, um auch wirklich jedes µA zum Laden zu verwenden. Der gesamte Schaltplan sieht folgendermaßen aus:
Mit dieser Schaltung komme ich im Deep Sleep Modus auf einen Stromverbrauch von 12,8 µA. Ich denke, das ist ein sehr guter Wert. Andere Boards, wie beispielsweise das ESP32-DevKitC von Espressif, verbrauchen hier ein Vielfaches. Anfangs hatte ich einen Wert von über 200 µA, was mich ziemlich geschockt hat. Nach etwas Recherche war das Problem aber schnell gefunden. Es ist ganz wichtig, die verwendeten Pins mit gpio_reset_pin() zurückzusetzen, bevor der Deep Sleep Modus gestartet wird.
Das fertige Board sieht folgendermaßen aus:
Die Platine hätte sicher noch etwas kompakter entworfen werden können, aber für meinen ersten Versuch bin ich eigentlich zufrieden. Leider hatte mein erster Entwurf noch einen Fehler und auch JLCPCB hat ein Bauteil falsch eingebaut. Der Support war aber sehr hilfsbereit und hat mir für einen sehr günstigen Preis neue Boards geschickt. Die funktionieren jetzt perfekt.
Auf den folgenden Fotos sieht man den Vergleich der 3D Ansicht in EasyEDA mit dem physischen Board und auch den Vergleich mit dem früheren Design, das noch auf fertigen Komponenten basierte.
Der Schaltplan, die Platine und die Stückliste sind auf oshwlab.com.
Software
Die Software für das Funkmodul ist in C mit dem ESP-IDF Framework geschrieben. Der Aufbau ist simpel und besteht aus dem Initialisieren der Hardware, dem Einlesen der Sensordaten und dem Verschicken der Daten. Danach ist der Sensor für 10 Minuten im Deep Sleep Modus. Ein kompletter Durchlauf vom Aufwachen des ESP32 bis zum Starten des Deep Sleep Modus benötigt etwa 800 ms. Daraus kann man jetzt die theoretisch maximale Laufzeit (wenn nicht geladen wird) errechnen. Ich gehe dabei von einem Verbrauch von 200 mA während der Messung aus:
0,8s x 200mA + 600s x 0,0128mA = 167,68mAs
Eine voller Lithium-Ionen-Akku hat eine Kapazität von 2000 mAh oder 7.200.000 mAs. Dividiert man die beiden Werte, erreicht man 7200000 / 167,68 = 42939 Zyklen. Mit einer Zykluslänge von 600,8 Sekunden sind das 42939 x 600,8s = 25.797.710 Sekunden, was wiederum ca. 299 Tagen entspricht. Mit diesem Wert bin ich sehr zufrieden.
[Update 30.01.2024] Inzwischen konnte ich mit dem Power Profiler Kit II den genauen Stromverbrauch ermitteln. Es hat sich herausgestellt, dass meine Schätzung viel zu pessimistisch war.
Die Kommunikation der Funkmodule mit der Basisstation erfolgt über das ESP-NOW Protokoll. Sowohl Sender als auch Empfänger müssen dafür den gleichen WLAN Kanal benutzen. Normalerweise ist das auch kein Problem, da man einfach beim Sender und Empfänger den gleichen WLAN-Kanal einstellt. Da die Basisstation aber auch das klassische WLAN verwendet, wird es etwas komplizierter. Sobald sich die Basisstation mit dem WLAN verbindet, wird ihr vom Router irgendein WLAN-Kanal zugewiesen. Die Funkmodule wissen davon aber nichts und müssen zunächst irgendwie herausfinden, welchen WLAN Kanal sie benutzen sollen. Dazu habe ich einen Algorithmus entwickelt, damit WLAN und ESP-NOW gleichzeitig verwendet werden können. Das folgende Diagramm zeigt den Ablauf:
Sollte nach erfolgreichem Pairing ein Problem auftreten, wird der aktuelle Peer einfach gelöscht und das Pairing von vorne gestartet. Damit ist auch der Fall abgedeckt, wenn der Router eine Zeit lang nicht verfügbar ist oder sich der WLAN Kanal geändert hat.
Der Source Code des Funkmoduls ist auf GitHub.
Gehäuse
Das Gehäuse der Funkmodule ist weitgehend identisch zur vorherigen Version. Lediglich die Bohrungen wurden für die neue Platine angepasst.
Im Moment habe ich nur ein Gehäuse für zwei Solarzellen entworfen. Bei dem geringen Stromverbrauch der neuen ESP32 Platinen hätte sicher auch eine Solarzelle gereicht, oder vielleicht gleich eine Version ohne Solarzellen und man wechselt einfach bei Bedarf den Akku aus. Aber jetzt ist die Ladeelektronik nun mal mit dabei, da will ich sie auch verwenden.
Die STL Files zum selber Drucken liegen auf GitHub.
Fazit
Auch diesmal habe ich wieder sehr viel gelernt, vor allem über GUI Programmierung mit LVGL. Inzwischen gibt es nicht mehr viel, was ich noch daran verbessern könnte. Die Wetterstation ist zu einem festen Bestandteil in meinem Alltag geworden und ich möchte sie nicht mehr missen.
Ein paar Kleinigkeiten könnte ich aber doch noch verbessern:
- Setup Screen um Geokoordinaten, WLAN Passwort und den OpenWeather API Key eingeben zu können.
- Die Daten an einen Server übertragen, um sie auch mit dem Smartphone anzeigen zu können
- OTA Update
- Größeres Display, um die Daten etwas übersichtlicher und lesbarer anzeigen zu können, ich habe nur bisher keines gefunden.
Über Feedback würde ich mich sehr freuen. Noch mehr, wenn ihr mich bei der Weiterentwicklung unterstützen würdet.