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.

ESP32 Wetterstation

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:

  1. Die Funktionsweise der Wetterstation
  2. Die verwendeten Umweltsensoren
  3. OpenWeather API
  4. Die Basisstation
  5. Funkmodule
  6. 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:

OpenWeatherMap
OpenWeatherMap
On
On
1
1
2
2
3
3
Sensor 0
Sensor 0
Solar
Panel
Solar...
ESP32
ESP32
ESP32 S3
ESP32 S3
SEN55
SEN55
SCD41
SCD41
ESP-NOW
ESP-NOW
ESP-NOW
ESP-NOW
ESP-NOW
ESP-NOW
I2C
I2C
I2C
I2C
BME280
BME280
I2C
I2C
Base Station
Base Station
WIFI
WIFI
On
On
1
1
2
2
3
3
Sensor 1
Sensor 1
Solar
Panel
Solar...
ESP32
ESP32
BME280
BME280
I2C
I2C
On
On
1
1
2
2
3
3
Sensor 2
Sensor 2
Solar
Panel
Solar...
ESP32
ESP32
BME280
BME280
I2C
I2C
Screen
Screen
LDR
LDR
A/D
A/D

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. 

BME280

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:

bme280.png

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.

SEN55

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:

0.1
0.1
µm
µm
1.0
1.0
2.5
2.5
4
4
10
10
Verbrennungsgase
Verbrennungsgase
Tabakrauch
Tabakrauch
Bakterien
Bakterien
Hausstaubmilbenkot
Hausstaubmilbenkot
Sporen/Schimmel
Sporen/Schimmel
Pollen
Pollen
Bodenstaub
Bodenstaub
Straßenverkehr
Straßenverkehr
100
100

Der Aufbau des SEN55 sieht folgendermaßen aus:

sen55.png

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.

SCD41

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:

scd41.png

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.

Sunton ESP32-S3 7 Zoll Display

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:

ESP32 Wetterstation Schaltplan

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. 

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.

Main task
Main task
Brightness task
Brightness task
Sensor task
Sensor task
Weather task
Weather task
Clock task
Clock task
Networking
Networking
Display
Display
GFX
GFX
LVGL
LVGL
GUI
GUI
ESP-NOW
ESP-NOW
Sensors
Sensors
Sensors
Sensors
Sensors
Sensors
Sensors
Sensors
Callback
Callback
Semaphore
Semaphore
Event loop
Event loop

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:winter.png

Sommer:summer.png

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:

ESP32 Wetterstation Schaltplan

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:

ESP32 Wetterstation Funkmodul

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:

Base station
Base station
Wireless sensor
Wireless sensor
Init Wi-Fi & ESP-NOW
Init Wi-Fi & ESP-NOW
Init Wi-Fi & ESP-NOW
Init Wi-Fi & ESP-NOW
Register call-backs
Register call-backs
Register call-backs
Register call-backs
Read
peer & channel
from NVS
Read...
Send data
Send data
Deep sleep
Deep sleep
Send
 pairing request
channel n
Send...
Answer
received?
Answer...
Store peer & channel
in NVS
Store peer & channel...
Loop channel
1 .. MAX_CHANNEL
Loop channel...
Error
Error
Error
Error
OK
OK
OK
OK
CallBack
CallBack
No
No
Yes
Yes

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.

Konversation wird geladen