Arduino Lektion 69: OpenWeatherMap Daten auf OLED Display anzeigen
In diesem Tutorial möchte ich beschreiben, wie man Daten von der offenen Schnittstelle OpenWeatherMap.org auf einem OLED Display anzeigt.
Daten für den Standort "Braunschweig" auf dem NodeMCU mit OLED Display von Openweathermap.org
Ich nutze dazu den NodeMCU mit 0,96 Zoll OLED Display, dieser Microcontroller wurde mir vom OnlineShop Makershop.de kostenfrei zur Verfügung gestellt.
Den NodeMCU mit 0,96Zoll OLED Display kann man für knap 13,75€ unter https://www.makershop.de/plattformen/arduino/nodemcu-oled/ beziehen.
Dieses Tutorial sollte jedoch auch mit anderen, baugleichen Microcontrollern (wie zbsp. der Wemos D1 mini mit OLED Display) funktionieren.
Anmelden bei OpenWeatherMap.org
Damit man den Service von OpenWeatherMap.org nutzen kann, muss man sich anmelden. Die Anmeldung ist kostenfrei und schnell erledigt.
OpenWeatherMap - erstellen eines Accounts
Nachdem die Daten eingegeben wurden, ist man angemeldet. (Es gibt also kein DoubleOptIn, was sehr fraglich ist.)
Unter dem Menüpunkt "API keys" findet man nun den Standard Api key. Welchen wir nun nutzen werden um die Daten von der Schnittstelle abzugreifen.
Preise
Die Nutzung der Schnittstelle ist im Basispaket kostenlos, jedoch ist die Nutzung dann etwas eingeschränkt.
Man kann dann zbsp. "nur" 60 aufrufe pro Minute starten und auch die Verfügbarkeit des Services ist mit 95% gegeben. Weitere Einschränkungen können unter https://openweathermap.org/price eingesehen werden. In meinem Fall reicht das Basispaket völlig aus.
Für die API gibt es bereits zahlreiche Implementierungen für diverse Sprachen.
Leider ist für den Arduino nichts dabei, aber so schwierig ist das ja nicht. Man muss ja nur einen Request an die API senden und erhält ein JSON zurück.
Ich nutze neben der Arduino IDE noch zusätzlich das Tool Postman, dieses Tool gibt es als Erweiterung für Google Chrome und als Standalone Lösung.
Beispiel Aufruf mit Postman
Starten wir zuerst einen Aufruf der API mit dem API key sowie der Stadt Braunschweig.
http://api.openweathermap.org/data/2.5/weather?q=Braunschweig,DE&appid=3ee177bfa6d11e51f87055hierfehltnochetwas
Als Response erhalten wir im Standardfall einen JSON.
{
"coord": {
"lon": 10.53,
"lat": 52.26
},
"weather": ,
"base": "stations",
"main": {
"temp": 284.81,
"pressure": 1019,
"humidity": 58,
"temp_min": 284.15,
"temp_max": 285.15
},
"visibility": 10000,
"wind": {
"speed": 2.6,
"deg": 210
},
"clouds": {
"all": 0
},
"dt": 1538295600,
"sys": {
"type": 1,
"id": 4925,
"message": 0.0088,
"country": "DE",
"sunrise": 1538284651,
"sunset": 1538326624
},
"id": 3221017,
"name": "Brunswick",
"cod": 200
}
Diesem JSON Objekt können wir nun zbsp. die Daten für den Sonnenaufgang die aktuelle Windgeschwindigkeit sowie Luftdruck, Luftfeuchtigkeit usw. entnehmen.
erzeugen einer Internetverbindung
Wollen wir also loslegen, als erstes schreiben wir unser Sketch das wir in das lokale Netzwerk gelangen und das wir somit Internetzugang haben.
Wenn man das Paket "ESP8266 by ESP8266 Community" installiert so sind einige Beispielsketche enthalten.
Paket "esp8266 by ESP8266 Community"
Paket "esp8266 by ESP8266 Community"
Ich verwende aus diesen Beispielen das Beispiel "BasicHttpClient" als basis für den nachfolgenden Sketch.
#include
#include
#include
#include
ESP8266WiFiMulti WiFiMulti;
const char* SSID = "";
const char* PW = "";
const String url ="http://api.openweathermap.org/data/2.5/weather?q=Braunschweig,DE&appid=3ee177bfa6d11e51hierfehltnochetwas";
void setup() {
Serial.begin(115200);
Serial.println();
delay(500);
WiFiMulti.addAP(SSID, PASSWORD);
}
void loop() {
if((WiFiMulti.run() == WL_CONNECTED)) {
HTTPClient http;
Serial.println(" begin...");
http.begin(url);
Serial.println(" GET...");
int httpCode = http.GET();
if(httpCode > 0) {
Serial.printf(" GET... code: %d\n", httpCode);
if(httpCode == HTTP_CODE_OK) {
String response = http.getString();
Serial.println(response);
}
} else {
Serial.printf(" GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
}
http.end();
}
delay(10000);
}
In dem Beispiel habe ich die SSID, das Passwort entfern und den API key entfremdet. Wenn du das Beispiel auf deinem Microcontroller nachstellen möchtest so musst du diese Daten vorher anpassen.
Die Ausgabe auf dem seriellen Monitor der Arduino IDE sieht nun wiefolgt aus:
Ausgabe des HTTP Response auf dem seriellen Monitor der Arduino IDE
Nachdem wir also das JSON erhalten haben, müssen wir uns "nurnoch" die für uns nützlichen Daten herausziehen.
parsen des HTTP Response (JSON Objekt)
Für den Zugriff auf die Schlüssel / Wertepaare im JSON Objekt verwende ich nun die Bibliothek von https://arduinojson.org. Des Weiteren erzeuge ich den Quellcode über DEV-C++ IDE welche kostenfrei unter https://sourceforge.net/projects/orwelldevcpp/ heruntergeladen werden kann.
installieren der Bibliothek ArduinoJSON
Die Bibliothek "ArduinoJson" kann man ganz bequem über den Bibliotheksverwalter der Arduino IDE installiert werden. Dazu navigiert man über das Hauptmenü "Sketch" > "Bibliothek einbinden" > "Bibliotheken verwalten..." es öffnet sich nun ein neues Fenster. Nachdem die Daten fertig geladen wurden (je nach Internet & Rechnergeschwindigkeit kann dieses variieren) wird in der Suchleiste (1) nach "ArduinoJSON" gesucht. Der Eintrag lautet "ArduinoJson by Benoit Blanchon". Gemäß der Entwicklerseite soll man die Version 5 auswählen, daher wähle ich hier die Version 5.13.2 (stand 30.09.2018) (2) und betätige danach die Schaltfläche "Installieren" (3).
Installieren der Bibliothek "ArduinoJSON" in der Arduino IDE
Wenn die Bibliothek erfolgreich installiert wurde so steht hinter dem Titel nun die Versionsnummer gefolgt vom Text "INSTALLED".
ArduinoJson erfolgreich installiert
Klasse OpenWeatherMap
Die Klasse "OpenWeatherMap" enthält die geparsten Daten aus dem JSON der Schnittstelle. Um den Parser zu verwenden muss dieser dem Projekt hinzugefügt werden. Dazu werden die Dateien "openWeatherMapParser.h" sowie "OpenWeatherMap.h" parallel zum Arduino Sketch abgelegt.
Ablage der Resourcen
Das gesamte Projekt kann vom GitHub Repository https://github.com/StefanDraeger/Arduino-OpenWeatherMap heruntergeladen werden. Dort findest du alle Resourcen zu diesem Projekt.
Drehspulinstrument für Icons
Für die Darstellung der Icons nutze ich ein Drehspulinstrument. Sicherlich könnte man die Icons auch als XBM convertieren und dann auf dem Display anzeigen jedoch finde ich diese Lösung etwas cooler.
Drehspulinstrument Vorder / Rückseite
Was wird benötigt?
Ich verwende dazu ein Drehspulinstrument mit einer Anzeige für maximal 100µA.
Das Drehspulinstrument habe ich bei Wish.com für je 2,70€ erstanden.
Angebot von Drehspulinstrumente auf wish.com
Auf der Plattform Wish.com gibt es sicherlich einiges an Schrott und der durchgestrichene Preis ist unter Garantie ein Mondpreis aber mit solchen Artikeln habe ich bisher immer glück gehabt. Der Preis von 3€ ist echt unschlagbar auf ebay.de oder anderen Portalen zahlt mal deutlich mehr (Okay die Lieferzeit wird für den ein oder anderen ein K.O. Kriterium sein.).
Des Weiteren wird ein Widerstand von 46kOhm und 30kOhm benötigt und zusätzlich einige Kabel.
Der 46kOhm Widerstand wird für den Test der Schaltung am Arduino UNO und der 30kOhm wird für den Live Betrieb mit dem NoceMCU benötigt.
Wollen wir zuerst ein kleines Projekt starten um zu testen wie sich das Drehspulinstrument am Arduino UNO verhält.
Arduino Sketch
Im nachfolgenden Sketch habe ich eine einfache Schleife welche die Zahlen von 0 bis 255 hochzählt und am digitalen PWM Pin 9 ausgibt.
Dieses bewirkt das die Nadel des Drehspulinstrumentes sich bewegt.
void setup() {
Serial.begin(9600);
}
void loop() {
for(int i = 0;isetFont(ArialMT_Plain_16);
display->drawString(0, 0, openWeatherMap.getName());
Serial.println("1");
}
void windData(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) {
Serial.println("2a");
Wind wind = openWeatherMap.getWind();
Serial.println("2b");
display->setTextAlignment(TEXT_ALIGN_LEFT);
display->setFont(ArialMT_Plain_16);
display->drawString(0, 0, "Wind");
display->setFont(ArialMT_Plain_10);
display->drawString(0, 16, "Speed:");
String windSpeed = String(wind.getSpeed());
windSpeed += " m/sec";
display->drawString(55, 16, windSpeed);
display->drawString(0, 30, "Degree:");
String windDegree = String(wind.getDegree());
windDegree += "°";
display->drawString(55, 30, windDegree);
Serial.println("2");
}
void weather1Data(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) {
Main main = openWeatherMap.getMain();
display->setTextAlignment(TEXT_ALIGN_LEFT);
display->setFont(ArialMT_Plain_16);
display->drawString(0, 0, "Weather");
display->setFont(ArialMT_Plain_10);
display->drawString(0, 16, "Temp.:");
String weatherTemp = String(main.getTemp()-273.15,2);
weatherTemp += "°C";
display->drawString(63, 16, weatherTemp);
String weatherTempMin = String(main.getTempMin()-273.15,2);
weatherTempMin += "°C";
display->drawString(0, 30, "min. Temp.:");
display->drawString(63, 30, weatherTempMin);
String weatherTempMax = String(main.getTempMax()-273.15,2);
weatherTempMax += "°C";
display->drawString(0, 43, "max. Temp.:");
display->drawString(63, 43, weatherTempMax);
Serial.println("3");
}
void weather2Data(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) {
Main main = openWeatherMap.getMain();
display->setTextAlignment(TEXT_ALIGN_LEFT);
display->setFont(ArialMT_Plain_16);
display->drawString(0, 0, "Weather");
display->setFont(ArialMT_Plain_10);
display->drawString(0, 16, "Pressure:");
String weatherPressure = String(main.getPressure());
weatherPressure += " hPa";
display->drawString(55, 16, weatherPressure);
display->drawString(0, 30, "Humidity:");
String weatherHumidity = String(main.getHumidity());
weatherHumidity += " %";
display->drawString(55, 30, weatherHumidity);
Serial.println("4");
}
FrameCallback frames = {mainData, windData, weather1Data, weather2Data};
void setup() {
Serial.begin(115200);
Serial.println();
WiFiMulti.addAP(SSID, PW);
const int frameCount = 4;
ui.setTargetFPS(60);
ui.setFrames(frames, frameCount);
ui.init();
}
void showData(){
Weather weather = openWeatherMap.getWeather();
String icon = weather.getIcon();
setWeatherIcon(icon);
}
void setWeatherIcon(String icon){
int pwmValue = WOLKE;
if(icon == "01d"){ pwmValue = SONNE; }
else if(icon == "02d"){ pwmValue = WOLKE_SONNE; }
else if(icon == "03d"){ pwmValue = WOLKE; }
else if(icon == "04d"){ pwmValue = WOLKE_DUNKEL; }
else if(icon == "09d"){ pwmValue = REGEN; }
else if(icon == "10d"){ pwmValue = SONNE_REGEN; }
else if(icon == "11d"){ pwmValue = GEWITTER; }
else if(icon == "13d"){ pwmValue = SCHNEE; }
else if(icon == "50d"){ pwmValue = WOLKE; }
else if(icon == "01n"){ pwmValue = MOND; }
else if(icon == "02n"){ pwmValue = WOLKE_MOND; }
else if(icon == "03n"){ pwmValue = WOLKE; }
else if(icon == "04n"){ pwmValue = WOLKE_DUNKEL; }
else if(icon == "09n"){ pwmValue = REGEN; }
else if(icon == "10n"){ pwmValue = REGEN_MOND; }
else if(icon == "11n"){ pwmValue = GEWITTER; }
else if(icon == "13n"){ pwmValue = SCHNEE; }
analogWrite(D5,pwmValue);
}
long lastUpdate = -1L;
long updateIntervall = 15000;
void loop() {
if((WiFiMulti.run() == WL_CONNECTED) && ((lastUpdate+updateIntervall) lastUpdate = millis();
HTTPClient http;
Serial.println(" begin...");
http.begin(url);
Serial.println(" GET...");
int httpCode = http.GET();
if(httpCode > 0) {
Serial.printf(" GET... code: %d\n", httpCode);
if(httpCode == HTTP_CODE_OK) {
String response = http.getString();
Serial.println(response);
openWeatherMap = parseCurrentJson(response);
showData();
}
} else {
Serial.printf(" GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
}
http.end();
}
int remainingTimeBudget = ui.update();
}
Wir haben nun die aktuellen Wetterdaten auf dem OLED Display angezeigt, als nächstes können wir nun die letzten 5 Wetterdaten von der Schnittstelle empfangen und anzeigen usw.
Das Parsen der Daten mit der Bibliothek ArduinoJSON kann einfacher nicht sein daher konnte dieses Projekt mit relativ wenig Aufwand bewerkstelligt werden einzig war etwas arbeit nötig um das JSON als Objekt darzustellen.
Als nächstes werde ich nun ein Gehäuse bauen und ein schönes Plätzchen auf meinem Schreibtisch suchen.....
Read the full article