ESP32 Projekt: Anzeige für Wettervorhersage
Wie wird das Wetter morgen und übermorgen? Die klassischen Wetterstationsprojekte, die man in der Szene sieht können darüber keine Aussage machen, weil sie nur die aktuellen Temperaturwerte anzeigen und loggen. Wir wählen hier einen anderen Ansatz: Mit dem ESP32 (https://az-delivery.de/products/esp32-developmentboard) laden wir die Wettervorhersage für die nächsten fünf Tage von https://openweathermap.org herunter und zeigen sie auf einem kleinen OLED Display (https://az-delivery.de/products/0-96zolldisplay) an.
Das Projekt wird in drei Abschnitten umgesetzt: (1) Vorbereitung der Hardware, (2) Generierung eines API Keys für OpenWeatherMap, um auf Daten zugreifen zu können, und (3) Software zum Herunterladen und Anzeigen der Wettervorhersage.
Hardware
- ESP32 (https://az-delivery.de/products/esp32-developmentboard) für das Herunterladen der Wettervorhersage und die Ansteuerung des Displays
- Display, z.B. https://az-delivery.de/products/0-96zolldisplay als Anzeige für die Wetterdaten
Das Display wird mit 3.3 V und GND an das ESP32 angeschlossen. Für die I²C Verbindung werden außerdem SCL mit G22 und SDA mit G21 verbunden. Die folgende Abbildung zeigt die fertig verkabelte Hardware.
Auf dem Mini-Display können sogar Grafiken angezeigt werden. Mit den Beispielsketchen der Library sind diese nur wenige Klicks entfernt.
Openweathermap API
OpenWeatherMap ist eine IT Firma, die Wetterdaten zur Verfügung stellt. In kleinem Rahmen sogar kostenlos. Um Wetterdaten zu erhalten muss man HTTP Anfragen an die OpenWeatherMap API (https://openweathermap.org/forecast5) senden. Dazu ist eine Authentifizierung per API-Key erforderlich. Sie können Ihren API-Key auf der Preis Seite erhalten (https://openweathermap.org/price). Er sieht zum Beispiel so aus:
a254362gegeg715dce096a37b32c17aa
Wenn Sie jetzt im Browser die URL http://api.openweathermap.org/data/2.5/forecast?q=Karlsruhe,de&appid=a254362gegeg715dce096a37b32c17aa aufrufen, erhalten Sie die Wettervorhersage für die nächsten fünf Tage für Karlsruhe. Ersetzen Sie in der URL Karlsruhe durch den Namen ihrer Stadt und den API Key hinter appid= durch Ihren.
Die zurückgegebenen Daten sind im JSON Format. Das Attribut temp gibt die vorhergesagte Temperatur in Kelvin für den Zeitpunkt dt an. Letzteres ist ein Unix Timestamp, der noch einmal in lesbarer Form als dt_txt mitgeschickt wird. Beispiel für den Anfang einer Response:
Software
Dieser Abschnitt skizziert die Funktionsweise des Programms. Für ein tiefergehendes Verständnis ist ein Blick in den Code (https://github.com/Simsso/Microcontroller-Examples/tree/master/ESP32/WeatherDisplay) unerlässlich. Die Dateien des Projekts haben folgende Aufgaben:
- WeatherDisplay.ino: Arduino-Sketch, Main Programm: Verbindet alle Komponenten.
- OpenWeatherMapAPI.h: OpenWeatherMap API Header Datei: Definiert, wie auf die OpenWeatherMap zugegriffen werden kann.
- OpenWeatherMapAPI.cpp: OpenWeatherMap API Code Datei: Implementiert die Methoden der vorherstehenden Header Datei.
- OLEDScreen.h: OLED Screen Header Datei: Steuert das OLED Display an und gibt die Wetter Daten grafisch aus.
- WeatherForecastSample.cpp: Wettervorhersagen-Sample: Klasse, die eine einzelne Wettervorhersage repräsentiert.
API Zugriffe mit dem ESP32
Um die Wetterinformationen anzuzeigen, muss das ESP32 die oben auszugsweise gelisteten Daten von der OpenWeatherMap API erhalten. Der Code für das Herunterladen und Auslesen befindet sich wegen seiner Länge in separaten C++ Dateien, die im Hauptsketch mit #include eingebunden werden. Um das Beispielprogramm ausführen zu können müssen Sie also den gesamten Ordnerinhalt von https://github.com/Simsso/Microcontroller-Examples/tree/master/ESP32/WeatherDisplay herunterladen und dann die .ino Datei mit der Arduino IDE öffnen. Im Programm werden in regelmäßigen Zeitabständen (standardmäßig 2 Stunden) Wetterdaten heruntergeladen. Diese werden mit der ArduinoJson Library (https://github.com/bblanchon/ArduinoJson/) geparsed, das heißt so augelesen, dass sie im Programm weiterverwendet werden können.
Um den Code in Betrieb nehmen zu können müssen Sie neben der Installation der Libraries
- Ihre WLAN SSID und das Passwort eintragen (Datei WeatherDisplay.ino),
- die Stadt, für die eine Vorhersage gemacht werden soll aktualisieren (Datei WeatherDisplay.ino, Variable query) und
- einen gültigen OpenWeatherMap API Key eintragen (Datei WeatherDisplay.ino, Variable apiKey).
Anzeige der Wettervorhersage
Das Display, das die heruntergeladenen Wetterdaten anzeigt, kann zum Beispiel mit der Ai_Ardulib_SSD1306 Library angesteuert werden (https://github.com/acrobotic/Ai_Ardulib_SSD1306). Diese enthielt zum Zeitpunkt der Veröffentlichung dieses Posts einen Bug, der in dieser Pull-Request behoben wurde: https://github.com/barbeque/Ai_Ardulib_SSD1306/tree/patch-1. Laden Sie den Code daher sicherheitshalber über letzteren Link herunter.
In der aktuellen Ausführung zeigt das WeatherDisplay-Programm die Wettervorhersage plain, das heißt ohne weitere Formatierung, an. Sie sind herzlich dazu eingeladen, die Anzeige zum Beispiel mit Wolken-/Sonnengrafiken aufzuhübschen (englisches Tutorial dazu: http://www.instructables.com/id/Display-Images-on-OLED-Screen-With-Arduino-ATtiny8/.
Bei dieser Wettervorhersage -- 13°C -- ist das mit dem ESP32 Herumspielen einfach die beste Beschäftigung.
29 Kommentare
Andreas Wolter
@Bernd Schröder:
Der Link für das Abrufen der Wetterdaten sieht so aus:
http://api.openweathermap.org/data/2.5/weather?q=STADT,LAENDERCODE&APPID=APIKEY
Zuerst müsste also der Standort bestimmt (Stadt und Laendercode) und in diesen Link eingefügt werden. Eventuell hilft die Library “WifiLocation” weiter: https://www.arduino.cc/reference/en/libraries/wifilocation/
Ansonsten bräuchte man GPS und muss daraus aber den Städtenamen und den Ländercode generieren.
Das Zusammensetzen des Links kann man dann mit Substrings erledigen, denke ich. Ich denke, es ist möglich.
Eine weitere Möglichkeit wäre ein Webinterface, in das man den aktuellen Standort per Smartphone oder PC über den Browser eingibt.
Grüße,
Andreas Wolter
AZ-Delivery Blog
Bernd Schröder
Kann man das auch für mobile Anwendung mit wechselnden Standorten machen?
Also so, dass die Software den momentanen Standort ermittelt und dann für diesen Standort das Wetter angezeigt wird.
Yuu
Vielen Dank für den Artikel, hat bei mir wunderbar geklappt :)
Warum es bei manchen nicht geklappt hat weiß ich nicht, ich stellte mich als totaler Anfänger nur etwas an mit den Installieren der richtigen Liberies. Auf geheiß mancher Kommentare hab ich ArduinoJson 5.10 installieren lassen statt 6, keine Ahnung ob es mit 6 auch genauso gut klappt.
Hab rechts am Display nur diverse Pixel… Pixelfehler? Sieht etwas schmutzig aus aber sonst erkennt man alles gut. Vielleicht probiere ich mal das ganze an größere Displays.
Anonymous
Dieses Projekt ist das erste mit WIFI und ESP32 für mich. Ich fand die Idee spannend und habe mich nun etliche Stunden damit beschäftigt. Ich habe die Treiber installiert, das Board richtig im IDE angemeldet und den Sketch samt Libraries nun auch übertragen kommen.
Dennoch läuft es noch nicht. Ich bekomme zwar den Versuch angezeigt, dass die Kommunikation nach außen passieren soll, im IDE wird “connecting…connecting…” angezeigt, aber denn laufe ich immer auf einen Timeout. Dies Wifi Zugangsdaten sind es definitiv nicht und so komme ich also nicht weiter.
Kann jemand helfen?
Johannes Sock
Hi Horst,
danke für deinen Kommentar v. 12.03.. Die ArduinoJson Lib Version 5 hat mir bisher auch noch nicht weiter geholfen, wobei ich den Verdacht habe, dass sich hier noch ein Problem mit der Ai_Ardulib_SSD1306 Library auftut, zumal der Link auf https://github.com/barbeque/Ai_Ardulib_SSD1306/tree/patch-1 lt. Projektbeschreibung (soll einen Bugfix beinhalten) nicht funktioniert. Alles in allem muss ich feststellen, dass dieses Projekt für Anfänger wie mich eine Nummer zu hoch zu sein scheint. Schade drum, aber ich werfe hier zur Zeit das Handtuch.
Eine überarbeitete ausgereifte Installationsanweisung mit aktuellen und korrekten Downloadlinks wäre schon toll.
LG Johannes
Horst Koch
Hallo Johannes,
ich bin auch Anfänger und habe mich mit der Software ziemlich “abgequält”. Funktioniert hat einfach die Installation der ArduinoJson Lib Version 5, also die alte Version vor dem Compilieren.
Viele Grüße
Horst
edu
Gibt es bereits ein ausgeführtes Projekt mit einem seriellen Feinstaubsensor am ESP32 der per WLAN die Daten an eine z.b. Handy oder PC-App oder direkt per WLAN an eine entfernte Datenbank sendet?
danke f. eine Info – edu
Thomas
Läuft das nicht irgendwie in ein Wetterabo? Die FreeCalls sind doch begrenzt.
Zitat
We accumulate the total number of calls from all the API keys that belong to your account.
When your account exceeds the limit, we do not block your account immediately but send you an automatic notification by email asking you to switch to one of our paid subscriptions within the reasonable term. If we do not get any feedback from you, we suspend your account. To renew your account, contact us
Zitat Ende
Johannes Sock
Kann mich Uli Engel nur anschließen. Das Projekt scheitert bei mir ann der Compilerei mit der Fehlermeldung
Arduino: 1.8.12 (Windows 10), Board: “ESP32 Dev Module, Disabled, Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS), 240MHz (WiFi/BT), QIO, 80MHz, 4MB (32Mb), 921600, None”
OpenWeatherMapAPI.cpp:13:11: error: DynamicJsonBuffer is a class from ArduinoJson 5. Please see arduinojson.org/upgrade to learn how to upgrade your program to ArduinoJson version 6
DynamicJsonBuffer jsonBuffer(20000); // response body size is usually around 15 kByte ^OpenWeatherMapAPI.cpp:13:29: error: expected constructor, destructor, or type conversion before ‘(’ token
DynamicJsonBuffer jsonBuffer(20000); // response body size is usually around 15 kByte ^sketch\OpenWeatherMapAPI.cpp: In static member function ‘static ArduinoJson::JsonObject* OpenWeatherMapAPI::getData(String, String)’:
OpenWeatherMapAPI.cpp:102:22: error: ‘jsonBuffer’ was not declared in this scope
JsonObject& root = jsonBuffer.parseObject(resBody); ^OpenWeatherMapAPI.cpp:103:13: error: ‘ArduinoJson::JsonObject {aka class ArduinoJson6141_0000010::ObjectRef}’ has no member named ‘success’
if (!root.success()) { ^Mehrere Bibliotheken wurden für “WiFi.h” gefunden
Benutzt: C:\Users\Socke\AppData\Local\Arduino15\packages\esp32\hardware\esp32\1.0.4\libraries\WiFi
Nicht benutzt: C:\Program Files (x86)\Arduino\libraries\WiFi
exit status 1
DynamicJsonBuffer is a class from ArduinoJson 5. Please see arduinojson.org/upgrade to learn how to upgrade your program to ArduinoJson version 6
Ungültige Bibliothek C:\Users\Socke\Documents\Arduino\libraries\Microcontroller-Examples-master in keine Header-Dateien (.h) in C:\Users\Socke\Documents\Arduino\libraries\Microcontroller-Examples-master gefunden gefunden
Ungültige Bibliothek C:\Users\Socke\Documents\Arduino\libraries\Microcontroller-Examples-master in keine Header-Dateien (.h) in C:\Users\Socke\Documents\Arduino\libraries\Microcontroller-Examples-master gefunden gefunden
Dieser Bericht wäre detaillierter, wenn die Option
“Ausführliche Ausgabe während der Kompilierung”
in Datei → Voreinstellungen aktiviert wäre.
Scheint ein Problem mit der ArduinoJson.h-Datei in der Version 5 zu sein.
Wer hat hier schon eine Lösung gefunden, die einem Neuling in diesem Metier weiter helfen kann?
Ich wäre hierfür sehr dankbar, da ich dadurch vielleicht etwas mehr über die Codierung lernen könnte.
Gruß Johannes
Michael
Kann ich das Projekt auch mit einem ESP8266 umsetzen?
Was muss ich ändern?
Ulli Engel
Hallo,
kann man auch einen ESP8266 verwenden?
Was muss ich dann ggf. anpassen?
Danke
Ulli Engel
Hi,
ich lese den Blog regelmäßig und habe heute diese Wetterstations-Anleitung darin gefunden. Vielleicht ist dieser Blog nur für Profis gedacht. Mich interessiert aber dieses Thema und ich würde es gerne nachbauen. Doch für einen Anfänger wie ich es bin ist die Darstellung sehr kurz (zu kurz). Und wenn ich die Kommentare der Profis lese, frage ich mich, wie Anfänger hier zu einem gewünschte “Erlebnis” kommen sollen. Ich würde mich über mehr Ausführlichkeit mit mehr Informationen freuen.
Gruß Ulli
Gerhart Ullmann
Hallo,
könntet Ihr bitte die Gelbe Schriftfarbe in Blau oder Rotändern? Das wäre deutlich besser zu sehen.
Gruß
Gerhart
Thomas
Und diese Datei noch: OpenWeatherMapAPI.h
—snip
#include
#include “SSD1306.h”
#include “WeatherForecastSample.cpp”
class OpenWeatherMapAPI
{
public:
static void init(const char* ssid, const char* password);
static bool getForecast(
String query,
String appId,
WeatherForecastSample** forecast,
int* count);
private:
static String getResponse(String query, String appId);//static JsonObject* getData(String query, String appId);
static JsonObject getData(String query, String appId);
};
#endif
—snap
Thomas
Hallo,
evtl, das mal testen, wenn ArduinoJson Lib 6.10.1
Datei: OpenWeatherMapAPI.cpp
—snip
#include
#include
#include
#include “OpenWeatherMapAPI.h”
#include “WeatherForecastSample.cpp”
const char *owmApiHost = “api.openweathermap.org”;
const uint64_t timeout = 5000; // [ms]
DynamicJsonDocument jDoc(30000);
void OpenWeatherMapAPI::init(const char* ssid, const char* password)
while (WiFi.status() != WL_CONNECTED) { } Serial.println("Connected with IP address " + WiFi.localIP().toString());{
Serial.println("Connecting to " + String(ssid));
WiFi.begin(ssid, password);
};
bool OpenWeatherMapAPI::getForecast(String query, String appId, WeatherForecastSample** forecast, int* count)
*count = 20; forecast = new WeatherForecastSample[count]; for (int i = 0; i < *count; i++) { (*forecast)[i].dt = (root)[“list”][i][“dt”].as(); (*forecast)[i].temp = (root)[“list”][i][“main”][“temp”].as(); (*forecast)[i].description = (root)[“list”][i][“weather”]0[“description”].as(); (*forecast)[i].dtTxt = (root)[“list”][i][“dt_txt”].as(); if ((*forecast)[i].dtTxt.length() == 0) { *count = i; break; } } Serial.println("Forecast data parsed "); return true;{
//const JsonObject* root = OpenWeatherMapAPI::getData(query, appId);
//const
JsonObject root = OpenWeatherMapAPI::getData(query, appId);
if (root.isNull()) return false;
};
String OpenWeatherMapAPI::getResponse(String query, String appId)
String path = “/data/2.5/forecast?q=” + query + “&appid=” + appId; client.print( “GET " + path + " HTTP/1.1\r\n" + “Host: " + owmApiHost + “\r\n” + “Connection: close\r\n” + “Pragma: no-cache\r\n” + “Cache-Control: no-cache\r\n” + “User-Agent: ESP32\r\n” + “Accept: text/html,application/json\r\n\r\n”); uint64_t startMillis = millis(); while (client.available() == 0) { if (millis() – startMillis > timeout) { Serial.println(“Client timeout”); client.stop(); return ""; } } String resHeader = "", resBody = ""; bool receivingHeader = true; while(client.available()) { String line = client.readStringUntil(‘\r’); if (line.length() == 1 && resBody.length() == 0) { receivingHeader = false; continue; } if (receivingHeader) { resHeader += line; } else { resBody += line; } } client.stop(); Serial.print("Data received successfully (request body size: " + String(resBody.length()) + " bytes)"); Serial.println(resBody); return resBody;{
WiFiClient client;
const int httpPort = 80;
if (!client.connect(owmApiHost, httpPort)) {
Serial.println(“Connection failed”);
return "";
}
};
JsonObject OpenWeatherMapAPI::getData(String query, String appId)
if (contentLength > 0) { // parse JSON DeserializationError error = deserializeJson(jDoc, resBody); if (error) { Serial.println(error.c_str()); Serial.println(“return nullptr”); return root; //nullptr; } else { Serial.println(error.c_str()); root = jDoc.as(); Serial.println(“return &root”); //return &root; return root; } } return root; //nullptr;{
String resBody = OpenWeatherMapAPI::getResponse(query, appId);
const int contentLength = resBody.length();
JsonObject root;
};
—snap
Peter Pi.
Nachtrag: Mit der Version 5 der ArduinoJson Lib läuft es. Es gibt auch eine Anleitung zum Update von V5 auf V6.
Peter Pi.
Scheint mit den o.a. Quellen nicht zu kompilieren, weil die lib von version 5 auf 6 ein upgrade bekommen hat.
“DynamicJsonBuffer is a class from ArduinoJson 5. Please see arduinojson.org/upgrade to learn how to upgrade your program to ArduinoJson version 6”
Ich wollte das nur schnell zusammenbauen und probieren, da die Hardware bereits vorliegt, also wenn jemand Rat weiß, Danke!
Peter F.
Interessanter Ansatz, jedoch ist openwether hier ein Overkill
Mit wttr.in wäre es einfacher und man bekommt eine kleine Grafik dazu.
Wer würde es versuchen, bin leider selbst kein Programmierer?
Joerg
Und wenn man openweathermap auf deutsch umstellt, kommen die Umlaute nicht richtig raus. Hat jemand einen Vorschlag?
Wolfgang
Schade, dass alles ausverkauft ist! Hätte es gerne nachgebaut.
Wolfgang Noel
Hallo Thomas, mit der Änderung funktioniert es jetzt einwandfrei. Danke
Thomas
Hallo,
bei mit wurde die Wetterdaten auch immer nur beim ersten Mal geladen.
Abhilfe brachte bei mit eine Anpasung der OpenWeatherMapAPI.cpp
Oben der der globalen Variablendeklaration DynamicJsonBuffer jsonBuffer(20000); auskommentieren und …
—snip
const char *owmApiHost = “api.openweathermap.org”;
const uint64_t timeout = 5000; // [ms]
//DynamicJsonBuffer jsonBuffer(20000); // response body size is usually around 15 kByte → auskommentieren
void OpenWeatherMapAPI::init(const char* ssid, const char* password)
—snap
…eine Kopie davon in der Funktion JsonObject* OpenWeatherMapAPI::getData(String query, String appId)
—snip
JsonObject& root = jsonBuffer.parseObject(resBody);JsonObject* OpenWeatherMapAPI::getData(String query, String appId)
{
String resBody = OpenWeatherMapAPI::getResponse(query, appId);
const int contentLength = resBody.length();
if (contentLength > 0) {
// parse JSON
DynamicJsonBuffer jsonBuffer(20000); // response body size is usually around 15 kByte → hierhin kopieren
—snap
Beim Aufruf der Funktion wird jedes Mal der Buffer neu erstellt.
Bitte mal testen.
Gruß Thomas
Eveline Purps
Das ist doch mal eine konkrete Anleitung – bin gespannt ob es funktioniert. Sowas könnte man doch gut weiter machen….
Wolfgang Noel
Hallo, leider werden bei mir die Daten nicht aktualisiert. Soll eigentlich doch alle 2 Stunden erfolgen. Scripte so wie in der Vorlage übernommen. Nach Neustart wird auch aktualisiert.
Wolfgang
Hallo, ich habe alle Programmteile so übernommen. Programm funktioniert auch soweit. Leider werden die Daten nicht automatisch aktualisiert. Nach einem Neustart sind die aktuellen Daten dann da. Woran kann das liegen?
Stephan
Hallo zusammen,
habe das Programm heute morgen mal umgesetzt, gefällt mir.
@Michael … du brauchst meines Erachtens den Patch nicht mehr; die Library funktioniert auch so.
Ich habe eine einzige Sache geändert: Und zwar in der OLEDScreen.h habe ich in Zeile 15 aus (font5x7) (font8x8) gemacht, sonst wurde bei mir der Text nicht korrekt auf dem Display ausgegeben …
Gruß, Stephan
Michael
Hallo leider ist der link https://github.com/barbeque/Ai_Ardulib_SSD1306/tree/patch-1 nicht mehr online, das bauen bricht leider mit einem fehler ab. wo kann ich den patch noch downloaden? bitte um eure hilfe.
MFG
Michael
Marcus
Herrliche Spielerei – jetzt macht mein Raspberry / OpenHab die Wettervorhersage nicht mehr selbst, sondern bekommt sie direkt gemeinsam mit den gemessenen Wetterdaten von einem ESP32. Und dieser zeigt die Werte auch noch in der Garage auf dem OLED an.
Stephan
Tolle Idee! Jetzt noch eine Variante mit Solarbetrieb wäre cool!