diff --git a/Wetterstation.HMI b/Wetterstation.HMI
new file mode 100644
index 0000000000000000000000000000000000000000..a60d230890cfb6a381f23448640624b82e6ac854
Binary files /dev/null and b/Wetterstation.HMI differ
diff --git a/wetterstation/wetterstation.ino b/wetterstation/wetterstation.ino
new file mode 100644
index 0000000000000000000000000000000000000000..369831e8e15dc775d47622d2fef0c980001b2657
--- /dev/null
+++ b/wetterstation/wetterstation.ino
@@ -0,0 +1,640 @@
+/*********************************************************************
+  Weather station with a Nextion display, showing date, time, tempe-
+  rature, humidity, airpressure and weather forecast using
+  openweathermap.org
+
+  Setup used:
+  - NodeMCU (ESP8266)
+  - BME280 sensor
+  - Nextion 7" 800x480 display
+
+**********************************************************************
+  Last updated 20200128 by McUles
+
+  This code is free for personal use, not for commercial purposes.
+  Please leave this header intact.
+
+  contact: dev@itstall.de
+**********************************************************************/
+
+//#define DEBUG
+#define DEBUG_NO_PAGE_FADE
+#define DEBUG_NO_TOUCH
+#define nexSerial Serial1
+
+//
+// include libraries
+//
+#include <Wire.h>
+#include <SPI.h>                                // I2C library
+#include <ArduinoJson.h>                        // https://github.com/bblanchon/ArduinoJson
+#include <ESP8266WiFi.h>                        // WiFi library
+#include <WiFiUdp.h>                            // Udp library
+#include <TimeLib.h>                            // Time library
+
+extern "C" {
+  #include "user_interface.h"
+}
+
+//
+// settings for WiFi connection
+//
+char * ssid     = "Sensors";                    // WiFi SSID
+char * password = "mySensorNetz";               // WiFi password
+
+unsigned int localPort = 2390;                  // local port to listen for UDP packets
+IPAddress timeServerIP;                         // IP address of random server
+const char* ntpServerName = "de.pool.ntp.org";  // server pool
+byte packetBuffer[48];                          // buffer to hold incoming and outgoing packets
+int timeZoneoffsetGMT = 3600;                   // offset from Greenwich Meridan Time
+boolean DST = false;                            // daylight saving time
+WiFiUDP clockUDP;                               // initialize a UDP instance
+
+//
+// settings for the openweathermap connection
+// sign up, get your api key and insert it below
+//
+char * servername = "api.openweathermap.org";         // remote server with weather info
+String APIKEY = "45f6ce019087f08b45d73872319c0573";   // personal api key for retrieving the weather data
+
+const int httpPort = 80;
+String result;
+int cityIDLoop = 0;
+
+// a list of cities you want to display the forecast for
+// get the ID at https://openweathermap.org/
+// type the city, click search and click on the town
+// then check the link, like this: https://openweathermap.org/city/5128581
+// 5128581 is the ID for New York
+String cityIDs[] = {
+  "2836455",  // Schonungen
+  ""          // end the list with an empty string
+};
+
+//
+// settings
+//
+int startupDelay = 1000;                      // startup delay
+int loopDelay = 3000;                         // main loop delay between sensor updates
+
+int timeServerDelay = 1000;                   // delay for the time server to reply
+int timeServerPasses = 4;                     // number of tries to connect to the time server before timing out
+int timeServerResyncNumOfLoops = 3000;        // number of loops before refreshing the time. one loop takes approx. 28 seconds
+int timeServerResyncNumOfLoopsCounter = 0;
+boolean timeServerConnected = false;          // is set to true when the time is read from the server
+
+int maxForecastLoop = 10;                     // number of main loops before the forecast is refreshed, looping through all cities
+int weatherForecastLoop = 0;
+int weatherForecastLoopInc = 1;
+
+int displayStartupDimValue = 30;              // startup display backlight level
+int displayDimValue = 150;                    // main display backlight level
+int displayDimStep = 1;                       // dim step
+int dimStartupDelay = 50;                     // delay for fading in
+int dimPageDelay = 0;                         // delay for fading between pages
+
+//
+// initialize variables
+//
+int page = 0;
+
+float bmeAltitude = 0;
+float bmeHumidity = 0;
+float bmePressure = 0;
+float bmeTemperature = 0;
+
+String command;
+String doubleQuote = "\"\"";
+
+//
+// initialize timer
+//
+os_timer_t secTimer;
+
+void timerDisplayTime(void *pArg) {
+  displayTime();
+}
+
+//
+// setup
+//
+void setup() {
+#ifdef DEBUG
+  Serial.begin(9600);
+#endif
+
+  nexSerial.begin(9600);
+
+  printNextionCommand("dims=" + String(0));         // set initial startup backlight value of the Nextion display to 0
+  printNextionCommand("dim=" + String(0));          // also set the current backlight value to 0
+  printNextionCommand("page 0");
+
+  delay(startupDelay);
+
+  displayFadeIn(0, displayStartupDimValue, dimStartupDelay);
+
+  connectToWifi();
+  clockUDP.begin(localPort);
+  getTimeFromServer();
+
+  if (timeServerConnected) {
+    displayTime();
+  }
+
+  displayDate();
+
+  os_timer_setfn(&secTimer, timerDisplayTime, NULL);
+  if (timeServerConnected) {
+    os_timer_arm(&secTimer, 1000, true);
+  }
+
+  displayFadeIn(displayStartupDimValue, displayDimValue, dimStartupDelay / 2);
+
+#ifdef DEBUG
+  Serial.println("Starting main loop");
+#endif
+}
+
+//
+// main loop
+//
+void loop() {
+  if (!timeServerConnected) {
+    getTimeFromServer();
+    if (timeServerConnected)
+      os_timer_arm(&secTimer, 1000, true);
+  }
+
+  if (page == 0) {
+
+    if (weatherForecastLoop == maxForecastLoop) {
+      timeServerResyncNumOfLoopsCounter += 1;
+      if (timeServerResyncNumOfLoopsCounter == timeServerResyncNumOfLoops) {
+        getTimeFromServer();
+        timeServerResyncNumOfLoopsCounter = 0;
+      }
+
+      page = 1;
+      getWeatherData();
+      weatherForecastLoopInc = -weatherForecastLoopInc;
+      displayDate();
+    }
+
+    delayCheckTouch(loopDelay);
+  }
+
+  else if (page == 1) {
+    if (weatherForecastLoop == 0) {
+      page = 0;
+
+#if !defined (DEBUG_NO_PAGE_FADE)
+      displayFadeOut(displayDimValue, dimPageDelay);
+#endif
+
+      printNextionCommand("page 0");
+
+#if !defined (DEBUG_NO_PAGE_FADE)
+      displayFadeIn(0, displayDimValue, dimPageDelay);
+#endif
+
+      weatherForecastLoopInc = -weatherForecastLoopInc;
+
+      displayTime();
+      displayDate();
+    }
+    else
+      delayCheckTouch(loopDelay);
+  }
+
+  weatherForecastLoop += weatherForecastLoopInc;
+
+#ifdef DEBUG
+  Serial.print(page);
+  Serial.print(" ");
+  Serial.print(weatherForecastLoop);
+  Serial.print(" ");
+  Serial.print(timeServerResyncNumOfLoopsCounter);
+  Serial.print(" ");
+  Serial.print(timeServerResyncNumOfLoops);
+  Serial.println("");
+#endif
+}
+
+void displayDate() {
+  time_t t = now();
+
+  command = "date.txt=\"" + String(day(t)) + " " + monthAsString(month(t)) + "\"";
+  printNextionCommand(command);
+  command = "year.txt=\"" + String(year(t)) + "\"";
+  printNextionCommand(command);
+}
+
+void displayTime() {
+  time_t t = now();
+  char timeString[9];
+
+  snprintf(timeString, sizeof(timeString), "%02d:%02d:%02d", hour(t), minute(t), second(t));
+  command = "time.txt=\"" + String(timeString) + "\"";
+  printNextionCommand(command);
+}
+
+//
+// Nextion commands
+//
+void printNextionCommand (String command) {
+  nexSerial.print(command);
+  endNextionCommand();
+}
+
+void sendToLCD(uint8_t type, String index, String cmd) {
+  if (type == 1 ) {
+    nexSerial.print(index);
+    nexSerial.print(".txt=");
+    nexSerial.print("\"");
+    nexSerial.print(cmd);
+    nexSerial.print("\"");
+  }
+  else if (type == 2) {
+    nexSerial.print(index);
+    nexSerial.print(".val=");
+    nexSerial.print(cmd);
+  }
+  else if (type == 3) {
+    nexSerial.print(index);
+    nexSerial.print(".pic=");
+    nexSerial.print(cmd);
+  }
+  else if (type == 4 ) {
+    nexSerial.print("page ");
+    nexSerial.print(cmd);
+  }
+
+  endNextionCommand();
+}
+
+void endNextionCommand() {
+  nexSerial.write(0xff);
+  nexSerial.write(0xff);
+  nexSerial.write(0xff);
+}
+
+void displayFadeIn(int fromValue, int toValue, int fadeDelay) {
+  for (int i = fromValue; i <= toValue; i += displayDimStep) {
+    if (i > toValue) {
+      i = toValue;
+    }
+    printNextionCommand("dim=" + String(i));
+    delay(fadeDelay);
+  }
+}
+
+void displayFadeOut(int fromValue, int fadeDelay) {
+  for (int i = fromValue; i >= 0; i -= displayDimStep) {
+    if (i < 0) {
+      i = 0;
+    }
+    printNextionCommand("dim=" + String(i));
+    delay(fadeDelay);
+  }
+}
+
+//
+// network functions
+//
+void connectToWifi() {
+  int wifiBlink = 0;
+
+  //  WiFi.enableSTA(true);
+  WiFi.mode(WIFI_STA);
+  WiFi.disconnect();
+
+  WiFi.begin(ssid, password);
+
+  while (WiFi.status() != WL_CONNECTED) {
+    if (wifiBlink == 0) {
+      printNextionCommand("wifi_connect.txt=\"connect...\"");
+      wifiBlink = 1;
+    }
+    else {
+      printNextionCommand("wifi_connect.txt=" + doubleQuote);
+      wifiBlink = 0;
+    }
+    delay(500);
+  }
+
+  printNextionCommand("wifi_connect.txt=" + doubleQuote);
+}
+
+void getTimeFromServer() {
+#ifdef DEBUG
+  Serial.print("Getting time from server...");
+#endif
+
+  int connectStatus = 0, i = 0;
+  unsigned long unixTime;
+
+  while (i < timeServerPasses && !connectStatus) {
+#ifdef DEBUG
+    Serial.print(i);
+    Serial.print("...");
+#endif
+
+    printNextionCommand("time.txt=\"get time..\"");
+    WiFi.hostByName(ntpServerName, timeServerIP);
+    sendNTPpacket(timeServerIP);
+    delay(timeServerDelay / 2);
+    connectStatus = clockUDP.parsePacket();
+    printNextionCommand("time.txt=" + doubleQuote);
+    delay(timeServerDelay / 2);
+    i++;
+  }
+
+  if (connectStatus) {
+#ifdef DEBUG
+    Serial.print(i);
+    Serial.println("...connected");
+#endif
+
+    timeServerConnected = true;
+    clockUDP.read(packetBuffer, 48);
+
+    // the timestamp starts at byte 40 of the received packet and is four bytes, or two words, long.
+    unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
+    unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);
+    // the timestamp is in seconds from 1900, add 70 years to get Unixtime
+    unixTime = (highWord << 16 | lowWord) - 2208988800 + timeZoneoffsetGMT;
+
+    if (DST) {
+      unixTime = unixTime + 3600;
+    }
+
+    setTime(unixTime);
+  }
+  else {
+#ifdef DEBUG
+    Serial.print(i);
+    Serial.println("...failed...");
+#endif
+
+    printNextionCommand("time.txt=\"failed....\"");
+    delay(timeServerDelay);
+    printNextionCommand("time.txt=" + doubleQuote);
+  }
+}
+
+unsigned long sendNTPpacket(IPAddress& address) {
+  memset(packetBuffer, 0, 48);
+  packetBuffer[0] = 0b11100011;     // LI, Version, Mode
+  packetBuffer[1] = 0;              // Stratum, or type of clock
+  packetBuffer[2] = 6;              // Polling Interval
+  packetBuffer[3] = 0xEC;           // Peer Clock Precision
+  // 8 bytes of zero for Root Delay & Root Dispersion
+  packetBuffer[12]  = 49;
+  packetBuffer[13]  = 0x4E;
+  packetBuffer[14]  = 49;
+  packetBuffer[15]  = 52;
+
+  clockUDP.beginPacket(address, 123);    //NTP requests are to port 123
+  clockUDP.write(packetBuffer, 48);
+  clockUDP.endPacket();
+}
+
+//
+// get and display weather data
+//
+void getWeatherData() { //client function to send/receive GET request data.
+  WiFiClient client;
+  if (!client.connect(servername, httpPort)) {
+    return;
+  }
+
+  String cityID = cityIDs[cityIDLoop];
+  cityIDLoop++;
+
+  if (cityIDs[cityIDLoop] == "") {
+    cityIDLoop = 0;
+  }
+
+  String url = "/data/2.5/forecast?id=" + cityID + "&units=metric&cnt=1&APPID=" + APIKEY + "&lang=de";
+  //String url = "/data/2.5/weather?id=" + cityID + "&units=metric&cnt=1&APPID=" + APIKEY;
+  //check weather properties at https://openweathermap.org/current
+
+  // This will send the request to the server
+  client.print(String("GET ") + url + " HTTP/1.1\r\n" + "Host: " + servername + "\r\n" + "Connection: close\r\n\r\n");
+  unsigned long timeout = millis();
+  while (client.available() == 0) {
+    if (millis() - timeout > 5000) {
+      client.stop();
+      return;
+    }
+  }
+
+  result = "";
+  // Read all the lines of the reply from server
+  while (client.available()) {
+    result = client.readStringUntil('\r');
+  }
+
+  result.replace('[', ' ');
+  result.replace(']', ' ');
+
+  char jsonArray [result.length() + 1];
+  result.toCharArray(jsonArray, sizeof(jsonArray));
+  jsonArray[result.length() + 1] = '\0';
+
+  StaticJsonBuffer<1024> json_buf;
+  JsonObject& root = json_buf.parseObject(jsonArray);
+  if (!root.success()) {
+    nexSerial.println("parseObject() failed");
+  }
+
+  //check properties forecasts at https://openweathermap.org/forecast5
+
+  int weatherID = root["list"]["weather"]["id"];
+
+  String tmp0 = root["city"]["name"];
+  String tmp1 = root["list"]["weather"]["main"];
+  String tmp2 = root["list"]["weather"]["description"];
+  float  tmp3 = root["list"]["main"]["temp_min"];
+  float  tmp4 = root["list"]["main"]["temp_max"];
+  float  tmp5 = root["list"]["main"]["humidity"];
+  float  tmp6 = root["list"]["clouds"]["all"];
+  float  tmp7 = root["list"]["rain"]["3h"];
+  float  tmp8 = root["list"]["snow"]["3h"];
+  float  tmp9 = root["list"]["wind"]["speed"];
+  int    tmp10 = root["list"]["wind"]["deg"];
+  float  tmp11 = root["list"]["main"]["pressure"];
+  timeZoneoffsetGMT = root["city"]["timezone"];
+  //String tmp12 = root["list"]["dt_text"]; command = command + tmp12;
+
+#if !defined (DEBUG_NO_PAGE_FADE)
+  displayFadeOut(displayDimValue, dimPageDelay);
+#endif
+
+  printNextionCommand("page 1");
+
+#if !defined (DEBUG_NO_PAGE_FADE)
+  displayFadeIn(0, displayDimValue, dimPageDelay );
+#endif
+
+  setWeatherPicture(weatherID);
+  sendToLCD(1, "city", tmp0);
+  sendToLCD(1, "description", tmp2);
+  sendToLCD(1, "humidity", String(tmp5, 0));
+  sendToLCD(1, "rain", String(tmp7, 1));
+  sendToLCD(1, "wind_dir", getShortWindDirection(tmp10));
+  sendToLCD(1, "wind_speed", String(tmp9, 1));
+  sendToLCD(1, "pressure", String(tmp11, 0));
+  sendToLCD(1, "clouds", String(tmp6, 0));
+  sendToLCD(1, "temp_min", String(tmp3, 1));
+  sendToLCD(1, "temp_max", String(tmp4, 1));
+}
+
+String getWindDirection (int degrees) {
+  int sector = ((degrees + 11) / 22.5 - 1);
+  switch (sector) {
+    case 0: return "Nord";
+    case 1: return "Nord-Nordost";
+    case 2: return "Nordost";
+    case 3: return "Ost-Nordost";
+    case 4: return "Ost";
+    case 5: return "Ost-Südost";
+    case 6: return "Südost";
+    case 7: return "Süd-Südost";
+    case 8: return "Süd";
+    case 9: return "Süd-Südwest";
+    case 10: return "Südwest";
+    case 11: return "West-Nordwest";
+    case 12: return "West";
+    case 13: return "West-Nordwest";
+    case 14: return "Nordwest";
+    case 15: return "Nord-Nordwest";
+  }
+}
+
+String getShortWindDirection (int degrees) {
+  int sector = ((degrees + 11) / 22.5 - 1);
+  switch (sector) {
+    case 0: return "N";
+    case 1: return "NNO";
+    case 2: return "NO";
+    case 3: return "ONO";
+    case 4: return "O";
+    case 5: return "OSO";
+    case 6: return "SO";
+    case 7: return "SSO";
+    case 8: return "S";
+    case 9: return "SSW";
+    case 10: return "SW";
+    case 11: return "WSW";
+    case 12: return "W";
+    case 13: return "WNW";
+    case 14: return "NW";
+    case 15: return "NNW";
+  }
+}
+
+void setWeatherPicture(int weatherID) {
+  switch (weatherID) {
+    case 200:
+    case 201:
+    case 202:
+    case 210: sendToLCD(3, "weatherpic", "26"); break; // tstorm1
+    case 211: sendToLCD(3, "weatherpic", "27"); break; // tstorm2
+    case 212: sendToLCD(3, "weatherpic", "28"); break; // tstorm3
+    case 221:
+    case 230:
+    case 231:
+    case 232: sendToLCD(3, "weatherpic", "27"); break; // tstorm2
+
+    case 300:
+    case 301:
+    case 302:
+    case 310:
+    case 311:
+    case 312:
+    case 313:
+    case 314:
+    case 321: sendToLCD(3, "weatherpic", "15"); break; // rain1
+
+    case 500:
+    case 501: sendToLCD(3, "weatherpic", "15"); break; // rain1
+    case 502:
+    case 503:
+    case 504: sendToLCD(3, "weatherpic", "16"); break; // rain2
+    case 511:
+    case 520:
+    case 521: sendToLCD(3, "weatherpic", "17"); break; // shower1
+    case 522:
+    case 531: sendToLCD(3, "weatherpic", "18"); break; // shower2
+
+    case 600: sendToLCD(3, "weatherpic", "20"); break; // snow1
+    case 601: sendToLCD(3, "weatherpic", "22"); break; // snow3
+    case 602: sendToLCD(3, "weatherpic", "24"); break; // snow5
+    case 611:
+    case 612: sendToLCD(3, "weatherpic", "14"); break; // sleet
+    case 615: sendToLCD(3, "weatherpic", "20"); break; // snow1
+    case 616: sendToLCD(3, "weatherpic", "22"); break; // snow3
+    case 620: sendToLCD(3, "weatherpic", "20"); break; // snow1
+    case 621: sendToLCD(3, "weatherpic", "22"); break; // snow3
+    case 622: sendToLCD(3, "weatherpic", "24"); break; // snow5
+
+    case 701:
+    case 711:
+    case 721: sendToLCD(3, "weatherpic", "13"); break; // mist
+    case 731: sendToLCD(3, "weatherpic", "10"); break; // dunno
+    case 741: sendToLCD(3, "weatherpic", "11"); break; // fog
+    case 751:
+    case 761:
+    case 762:
+    case 771:
+    case 781: sendToLCD(3, "weatherpic", "10"); break; // dunno
+
+    case 800: sendToLCD(3, "weatherpic", "25"); break; // sunny
+    case 801: sendToLCD(3, "weatherpic", "5"); break; // cloud1
+    case 802: sendToLCD(3, "weatherpic", "7"); break; // cloud3
+    case 803: sendToLCD(3, "weatherpic", "8"); break; // cloud4
+    case 804: sendToLCD(3, "weatherpic", "14"); break; // overcast
+
+    default: sendToLCD(3, "weatherpic", "10"); break; // dunno
+  }
+}
+
+void delayCheckTouch (int delayTime) {
+  unsigned long startMillis = millis();
+
+  while (millis() - startMillis < delayTime) {
+    delay(1000);
+  }
+}
+
+String dayAsString(int day) {
+  switch (day) {
+    case 1: return "Sonntag";
+    case 2: return "Montag";
+    case 3: return "Dienstag";
+    case 4: return "Mittwoch";
+    case 5: return "Donnerstag";
+    case 6: return "Freitag";
+    case 7: return "Samstag";
+  }
+  return "" ;
+}
+
+String monthAsString(int month) {
+  switch (month) {
+    case 1:  return "Januar";
+    case 2:  return "Februar";
+    case 3:  return "März";
+    case 4:  return "April";
+    case 5:  return "Mai";
+    case 6:  return "Juni";
+    case 7:  return "Juli";
+    case 8:  return "August";
+    case 9:  return "September";
+    case 10: return "Oktober";
+    case 11: return "November";
+    case 12: return "Dezember";
+  }
+  return "" ;
+}