From: Roman Bazalevskiy <rvb@rvb.name>
Date: Thu, 14 Jan 2021 06:48:32 +0000 (+0300)
Subject: ESP32 e-Paper info screen (client part).
X-Git-Url: https://git.rvb.name/weathermon.git/commitdiff_plain/414a39ee4b909dbd7c891ce7a3bff90b75db866c?ds=sidebyside

ESP32 e-Paper info screen (client part).
---

diff --git a/weather_screen/fonts.h b/weather_screen/fonts.h
new file mode 100644
index 0000000..e1269c7
--- /dev/null
+++ b/weather_screen/fonts.h
@@ -0,0 +1,40 @@
+typedef struct {
+  int maxSize;
+  const uint8_t* u8g2;
+} FontInfo;
+
+#define CyrFonts 4
+
+const FontInfo CyrFontArray[CyrFonts] = {
+  { 5, u8g2_font_4x6_t_cyrillic },
+  { 9, u8g2_font_6x13_t_cyrillic },
+  { 12, u8g2_font_9x15_t_cyrillic },
+  { 24, u8g2_font_inr24_t_cyrillic },
+};
+
+#define NumFonts 8
+
+const FontInfo NumFontArray[NumFonts] = {
+  { 5, u8g2_font_4x6_t_cyrillic },
+  { 9, u8g2_font_6x13_t_cyrillic },
+  { 12, u8g2_font_9x15_t_cyrillic },
+  { 16, u8g2_font_logisoso16_tf },
+  { 24, u8g2_font_logisoso24_tf },
+  { 32, u8g2_font_logisoso32_tf },
+  { 50, u8g2_font_logisoso50_tf },
+  { 92, u8g2_font_logisoso92_tn }
+};
+
+void SetCyrFont(byte size) {
+  for (byte i=0; i<CyrFonts; i++) {
+    if (CyrFontArray[i].maxSize<size) u8g2Fonts.setFont(CyrFontArray[i].u8g2);
+  }
+};
+
+void SetNumFont(byte size) {
+  for (byte i=0; i<NumFonts; i++) {
+    if (NumFontArray[i].maxSize<size) u8g2Fonts.setFont(NumFontArray[i].u8g2);
+  }
+};
+
+
diff --git a/weather_screen/weather_screen.ino b/weather_screen/weather_screen.ino
new file mode 100644
index 0000000..75448cd
--- /dev/null
+++ b/weather_screen/weather_screen.ino
@@ -0,0 +1,992 @@
+#include <FS.h>                   //this needs to be first, or it all crashes and burns...
+#include "SPIFFS.h"
+#include <WiFi.h>          //https://github.com/esp8266/Arduino
+//needed for library
+#include <DNSServer.h>
+#include <WebServer.h>
+#include <WiFiManager.h>          //https://github.com/tzapu/WiFiManager
+
+#include <ArduinoJson.h>          //https://github.com/bblanchon/ArduinoJson
+
+#include <time.h>                     // Built-in
+#include <SPI.h>                      // Built-in 
+
+#include <HTTPClient.h>
+
+#include <GxEPD2_BW.h>
+#include <U8g2_for_Adafruit_GFX.h>
+
+#include <time.h>
+#include <sys/time.h>
+
+#include "weather_types.h"
+
+#define SCREEN_WIDTH  800             // Set for landscape mode
+#define SCREEN_HEIGHT 480
+
+enum alignment {LEFT, RIGHT, CENTER};
+
+// Connections for e.g. Waveshare ESP32 e-Paper Driver Board
+static const uint8_t EPD_BUSY = 25;
+static const uint8_t EPD_CS   = 15;
+static const uint8_t EPD_RST  = 26; 
+static const uint8_t EPD_DC   = 27; 
+static const uint8_t EPD_SCK  = 13;
+static const uint8_t EPD_MISO = 12; // Master-In Slave-Out not used, as no data from display
+static const uint8_t EPD_MOSI = 14;
+
+GxEPD2_BW<GxEPD2_750_T7, GxEPD2_750_T7::HEIGHT> display(GxEPD2_750_T7(/*CS=*/ EPD_CS, /*DC=*/ EPD_DC, /*RST=*/ EPD_RST, /*BUSY=*/ EPD_BUSY));   // B/W display
+//GxEPD2_3C<GxEPD2_750c, GxEPD2_750c::HEIGHT> display(GxEPD2_750(/*CS=*/ EPD_CS, /*DC=*/ EPD_DC, /*RST=*/ EPD_RST, /*BUSY=*/ EPD_BUSY)); // 3-colour displays
+// use GxEPD_BLACK or GxEPD_WHITE or GxEPD_RED or GxEPD_YELLOW depending on display type
+
+U8G2_FOR_ADAFRUIT_GFX u8g2Fonts;
+
+#include "fonts.h"
+
+//define your default values here, if there are different values in config.json, they are overwritten.
+char api_key[40]      = "";                      // See: https://openweathermap.org/  // It's free to get an API key, but don't take more than 60 readings/minute!
+char latitude[8]      = "55.5";
+char longitude[8]     = "37.5";
+char language[4]      = "RU";                    // NOTE: Only the weather description is translated by OWM
+                                                 // Examples: Arabic (AR) Czech (CZ) English (EN) Greek (EL) Persian(Farsi) (FA) Galician (GL) Hungarian (HU) Japanese (JA)
+                                                 // Korean (KR) Latvian (LA) Lithuanian (LT) Macedonian (MK) Slovak (SK) Slovenian (SL) Vietnamese (VI)
+
+char current_weather_server[255] = "";
+char current_weather_uri[255] = "";
+char current_weather_pressure[40] = "PRESSURE";
+char current_weather_temperature[40] = "TEMPERATURE_C";
+char current_weather_humidity[40] = "HUMIDITY";
+char current_weather_windspeed[40] = "";
+char current_weather_lux[40] = "LUX";
+
+const char server[] = "api.openweathermap.org";
+
+int SleepDuration = 10;
+
+//flag for saving data
+bool shouldSaveConfig = false;
+
+//callback notifying us of the need to save config
+void saveConfigCallback () {
+  Serial.println("Should save config");
+  shouldSaveConfig = true;
+}
+
+void DisplaySetup() {
+  display.init(115200, true, 2); // init(uint32_t serial_diag_bitrate, bool initial, uint16_t reset_duration, bool pulldown_rst_mode)
+  SPI.end();
+  SPI.begin(EPD_SCK, EPD_MISO, EPD_MOSI, EPD_CS);
+  u8g2Fonts.begin(display); // connect u8g2 procedures to Adafruit GFX
+  u8g2Fonts.setFontMode(1);                  // use u8g2 transparent mode (this is default)
+  u8g2Fonts.setFontDirection(0);             // left to right (this is default)
+  u8g2Fonts.setForegroundColor(GxEPD_BLACK); // apply Adafruit GFX color
+  u8g2Fonts.setBackgroundColor(GxEPD_WHITE); // apply Adafruit GFX color
+  SetCyrFont(10); 
+  display.fillScreen(GxEPD_WHITE);
+  display.setFullWindow();
+}
+
+void WiFiSetup() {
+
+  // The extra parameters to be configured (can be either global or just in the setup)
+  // After connecting, parameter.getValue() will get you the configured value
+  // id/name placeholder/prompt default length
+  WiFiManagerParameter custom_api_key("api_key", "OWN API key", api_key, 40);
+  WiFiManagerParameter custom_latitude("latitide", "Latitude", latitude, 8);
+  WiFiManagerParameter custom_longitude("longitude", "Longitude", longitude, 8);
+  WiFiManagerParameter custom_language("language", "Language", language, 4);
+  WiFiManagerParameter custom_current_weather_server("current_weather_server", "Weather server", current_weather_server, 255);
+  WiFiManagerParameter custom_current_weather_uri("current_weather_uri", "Weather URI", current_weather_uri, 255);
+  WiFiManagerParameter custom_current_weather_pressure("current_weather_pressure", "Pressure key", current_weather_pressure, 40);
+  WiFiManagerParameter custom_current_weather_temperature("current_weather_temperature", "Temperature key", current_weather_temperature, 40);
+  WiFiManagerParameter custom_current_weather_humidity("current_weather_humidity", "Humidity key", current_weather_humidity, 40);
+  WiFiManagerParameter custom_current_weather_windspeed("current_weather_windspeed", "Windspeed key", current_weather_windspeed, 40);
+  WiFiManagerParameter custom_current_weather_lux("current_weather_lux", "Lux key", current_weather_lux, 40);
+ 
+  //WiFiManager
+  //Local intialization. Once its business is done, there is no need to keep it around
+  WiFiManager wifiManager;
+
+  //set config save notify callback
+  wifiManager.setSaveConfigCallback(saveConfigCallback);
+
+  //add all your parameters here
+  wifiManager.addParameter(&custom_api_key);
+  wifiManager.addParameter(&custom_latitude);
+  wifiManager.addParameter(&custom_longitude);
+  wifiManager.addParameter(&custom_language);
+  wifiManager.addParameter(&custom_current_weather_server);
+  wifiManager.addParameter(&custom_current_weather_uri);
+  wifiManager.addParameter(&custom_current_weather_pressure);
+  wifiManager.addParameter(&custom_current_weather_temperature);
+  wifiManager.addParameter(&custom_current_weather_humidity);
+  wifiManager.addParameter(&custom_current_weather_windspeed);
+  wifiManager.addParameter(&custom_current_weather_lux);
+
+  wifiManager.setTimeout(300);
+
+  if (!wifiManager.startConfigPortal()) {
+    Serial.println("failed to connect and hit timeout");
+    delay(3000);
+    //reset and try again, or maybe put it to deep sleep
+    ESP.restart();
+  }
+
+  //if you get here you have connected to the WiFi
+  Serial.println("connected...");
+
+  //read updated parameters
+  strcpy(api_key, custom_api_key.getValue());
+  strcpy(latitude, custom_latitude.getValue());
+  strcpy(longitude, custom_longitude.getValue());
+  strcpy(language, custom_language.getValue());
+  strcpy(current_weather_server, custom_current_weather_server.getValue());
+  strcpy(current_weather_uri, custom_current_weather_uri.getValue());
+  strcpy(current_weather_pressure, custom_current_weather_pressure.getValue());
+  strcpy(current_weather_temperature, custom_current_weather_temperature.getValue());
+  strcpy(current_weather_humidity, custom_current_weather_humidity.getValue());
+  strcpy(current_weather_windspeed, custom_current_weather_windspeed.getValue());
+  strcpy(current_weather_lux, custom_current_weather_lux.getValue());
+
+  //save the custom parameters to FS
+  if (shouldSaveConfig) {
+    Serial.println("saving config");
+    DynamicJsonDocument json(1024);
+    json["api_key"] = api_key;
+    json["latitude"] = latitude;
+    json["longitude"] = longitude;
+    json["language"] = language;
+    json["current_weather_server"] = current_weather_server;
+    json["current_weather_uri"] = current_weather_uri;
+    json["current_weather_pressure"] = current_weather_pressure;
+    json["current_weather_temperature"] = current_weather_temperature;
+    json["current_weather_humidity"] = current_weather_humidity;
+    json["current_weather_windspeed"] = current_weather_windspeed;
+    json["current_weather_lux"] = current_weather_lux;
+
+    File configFile = SPIFFS.open("/config.json", "w");
+    if (!configFile) {
+      Serial.println("failed to open config file for writing");
+    }
+
+    serializeJson(json, Serial);
+    serializeJson(json, configFile);
+    configFile.close();
+    //end save
+  }
+ 
+}
+
+void setup() {
+  byte need_setup = false;
+  // put your setup code here, to run once:
+  Serial.begin(115200);
+  Serial.println();
+
+  //read configuration from FS json
+  Serial.println("mounting FS...");
+
+  if (SPIFFS.begin(true)) {
+    Serial.println("mounted file system");
+    if (SPIFFS.exists("/config.json")) {
+      //file exists, reading and loading
+      Serial.println("reading config file");
+      File configFile = SPIFFS.open("/config.json", "r");
+      if (configFile) {
+        Serial.println("opened config file");
+        size_t size = configFile.size();
+        // Allocate a buffer to store contents of the file.
+        std::unique_ptr<char[]> buf(new char[size]);
+
+        configFile.readBytes(buf.get(), size);
+
+        DynamicJsonDocument json(1024);
+        auto deserializeError = deserializeJson(json, buf.get());
+        serializeJson(json, Serial);
+        if ( ! deserializeError ) {
+          Serial.println("\nparsed json");
+          if (json.containsKey("api_key")) { strcpy(api_key, json["api_key"]); }
+          if (json.containsKey("latitude")) { strcpy(latitude, json["latitude"]); }
+          if (json.containsKey("longitude")) { strcpy(longitude, json["longitude"]); }
+          if (json.containsKey("language")) { strcpy(language, json["language"]); }
+          if (json.containsKey("current_weather_server")) { strcpy(current_weather_server, json["current_weather_server"]); }
+          if (json.containsKey("current_weather_uri")) { strcpy(current_weather_uri, json["current_weather_uri"]); }
+          if (json.containsKey("current_weather_pressure")) { strcpy(current_weather_pressure, json["current_weather_pressure"]); }
+          if (json.containsKey("current_weather_temperature")) { strcpy(current_weather_temperature, json["current_weather_temperature"]); }
+          if (json.containsKey("current_weather_humidity")) { strcpy(current_weather_humidity, json["current_weather_humidity"]); }
+          if (json.containsKey("current_weather_windspeed")) { strcpy(current_weather_windspeed, json["current_weather_windspeed"]); }
+          if (json.containsKey("current_weather_lux")) { strcpy(current_weather_lux, json["current_weather_lux"]); }
+        } else {
+          Serial.println("failed to load json config");
+        }
+        configFile.close();
+      } else {
+        need_setup = true;
+      }
+    } else {
+      need_setup = true;
+    }
+  } else {
+    Serial.println("failed to mount FS");
+  }
+  //end read
+
+  if (need_setup) {
+    WiFiSetup(); 
+  } else {
+    WiFiManager wifiManager;
+    wifiManager.setTimeout(10);
+    if(!wifiManager.autoConnect()) {
+      Serial.println("failed to connect and hit timeout");
+      delay(3000);
+      ESP.restart();
+    }  
+  }
+
+  Serial.println("\nlocal ip");
+  Serial.println(WiFi.localIP());
+
+  DisplaySetup();
+  
+}
+
+int TimeZoneOffset;
+int SunRise, SunSet;
+
+int HourlyDT[MaxHourlyFC];
+float HourlyTemp[MaxHourlyFC];
+float HourlyFeelsLike[MaxHourlyFC];
+float HourlyPressure[MaxHourlyFC];
+float HourlyHumidity[MaxHourlyFC];
+float HourlyClouds[MaxHourlyFC];
+float HourlyWindSpeed[MaxHourlyFC];
+float HourlyRain[MaxHourlyFC];
+float HourlySnow[MaxHourlyFC];
+float HourlyPrecip[MaxHourlyFC];
+
+int DailyDT[MaxDailyFC];
+float DailyTempMin[MaxDailyFC];
+float DailyTempMax[MaxDailyFC];
+float DailyFeelsLikeMin[MaxDailyFC];
+float DailyFeelsLikeMax[MaxDailyFC];
+float DailyPressure[MaxDailyFC];
+float DailyHumidity[MaxDailyFC];
+float DailyClouds[MaxDailyFC];
+float DailyWindSpeed[MaxDailyFC];
+float DailyRain[MaxDailyFC];
+float DailySnow[MaxDailyFC];
+float DailyPrecip[MaxDailyFC];
+
+String UnixTime(int unix_time) {
+  time_t tm = unix_time;
+  struct tm *now_tm = localtime(&tm);
+  char output[40];
+  strftime(output, sizeof(output), "%H:%M %d/%m/%y", now_tm);
+  return output;
+}
+
+String UnixTimeOnly(int unix_time) {
+  time_t tm = unix_time;
+  struct tm *now_tm = localtime(&tm);
+  char output[40];
+  strftime(output, sizeof(output), "%H:%M", now_tm);
+  return output;
+}
+
+String UnixDate(int unix_time) {
+  time_t tm = unix_time;
+  struct tm *now_tm = localtime(&tm);
+  char output[40];
+  strftime(output, sizeof(output), "%d/%m/%y", now_tm);
+  return output;
+}
+
+bool DecodeWeather(WiFiClient& json) {
+  DynamicJsonDocument doc(35 * 1024);
+  // Deserialize the JSON document
+  DeserializationError error = deserializeJson(doc, json);
+  // Test if parsing succeeds.
+  if (error) {
+    Serial.print(F("deserializeJson() failed: "));
+    Serial.println(error.c_str());
+    return false;
+  }
+  // convert it to a JsonObject
+  JsonObject root = doc.as<JsonObject>();
+
+  TimeZoneOffset = root["timezone_offset"].as<int>();
+  
+  SunRise = root["current"]["sunrise"].as<int>();
+  SunSet  = root["current"]["sunset"].as<int>();
+
+  CurrentWeather.dt           = root["current"]["dt"].as<int>();
+  CurrentWeather.temp         = root["current"]["temp"].as<float>();
+  CurrentWeather.feels_like   = root["current"]["feels_like"].as<float>();
+  CurrentWeather.pressure     = 0.75 * root["current"]["pressure"].as<float>();
+  CurrentWeather.humidity     = root["current"]["humidity"].as<float>();
+  CurrentWeather.clouds       = root["current"]["clouds"].as<float>();
+  CurrentWeather.wind_speed   = root["current"]["wind_speed"].as<float>();
+  CurrentWeather.wind_deg     = root["current"]["wind_deg"].as<float>();
+  CurrentWeather.rain         = root["current"]["rain"]["1h"].as<float>();
+  CurrentWeather.snow         = root["current"]["snow"]["1h"].as<float>();
+  CurrentWeather.description  = root["current"]["weather"][0]["description"].as<char*>();
+  CurrentWeather.icon         = root["current"]["weather"][0]["icon"].as<char*>();
+
+  struct timeval now = { .tv_sec =  CurrentWeather.dt };
+  settimeofday(&now, NULL);
+
+  JsonArray hourly = root["hourly"];
+  for (byte i=0; i<MaxHourlyFC; i++) {
+    HourlyDT[i] = HourlyForecasts[i].dt                  = hourly[i]["dt"].as<int>();
+    HourlyTemp[i] = HourlyForecasts[i].temp              = hourly[i]["temp"].as<float>();
+    HourlyFeelsLike[i] = HourlyForecasts[i].feels_like   = hourly[i]["feels_like"].as<float>();
+    HourlyPressure[i] = HourlyForecasts[i].pressure      = 0.75 * hourly[i]["pressure"].as<float>();
+    HourlyHumidity[i] = HourlyForecasts[i].humidity      = hourly[i]["humidity"].as<float>();
+    HourlyClouds[i] = HourlyForecasts[i].clouds          = hourly[i]["clouds"].as<float>();
+    HourlyWindSpeed[i] = HourlyForecasts[i].wind_speed   = hourly[i]["wind_speed"].as<float>();
+    HourlyForecasts[i].wind_deg                          = hourly[i]["wind_deg"].as<float>();
+    HourlyRain[i] = HourlyForecasts[i].rain              = hourly[i]["rain"]["1h"].as<float>();
+    HourlySnow[i] = HourlyForecasts[i].snow              = hourly[i]["snow"]["1h"].as<float>();
+    HourlyPrecip[i] = HourlyRain[i] + HourlySnow[i];
+    HourlyForecasts[i].description                       = hourly[i]["weather"][0]["description"].as<char*>();
+    HourlyForecasts[i].icon                              = hourly[i]["weather"][0]["icon"].as<char*>();
+  }
+
+  JsonArray daily = root["daily"];
+  for (byte i=0; i<MaxDailyFC; i++) {
+    DailyDT[i] = DailyForecasts[i].dt                        = daily[i]["dt"].as<int>();
+    DailyTempMin[i] = DailyForecasts[i].temp_min             = daily[i]["temp"]["min"].as<float>();
+    DailyFeelsLikeMin[i] = DailyForecasts[i].feels_like_min  = daily[i]["feels_like"]["min"].as<float>();
+    DailyTempMax[i] = DailyForecasts[i].temp_max             = daily[i]["temp"]["max"].as<float>();
+    DailyFeelsLikeMax[i] = DailyForecasts[i].feels_like_max  = daily[i]["feels_like"]["max"].as<float>();
+    DailyPressure[i] = DailyForecasts[i].pressure            = 0.75 * daily[i]["pressure"].as<float>();
+    DailyHumidity[i] = DailyForecasts[i].humidity            = daily[i]["humidity"].as<float>();
+    DailyClouds[i] = DailyForecasts[i].clouds                = daily[i]["clouds"].as<float>();
+    DailyWindSpeed[i] = DailyForecasts[i].wind_speed         = daily[i]["wind_speed"].as<float>();
+    DailyForecasts[i].wind_deg                               = daily[i]["wind_deg"].as<float>();
+    DailyRain[i] = DailyForecasts[i].rain                    = daily[i]["rain"].as<float>();
+    DailySnow[i] = DailyForecasts[i].snow                    = daily[i]["snow"].as<float>();
+    DailyPrecip[i] = DailyRain[i] + DailySnow[i];
+    DailyForecasts[i].description                            = daily[i]["weather"][0]["description"].as<char*>();
+    DailyForecasts[i].icon                                   = daily[i]["weather"][0]["icon"].as<char*>();
+  }
+  
+  return true;
+}
+
+bool GetWeather(WiFiClient& client) {
+  client.stop(); // close connection before sending a new request
+  HTTPClient http;
+  char uri[128];
+  sprintf(uri,"/data/2.5/onecall?lat=%s&lon=%s&appid=%s&lang=%s&units=metric",latitude,longitude,api_key,language);
+  http.useHTTP10(true);
+  Serial.println("Connecting...");
+  http.begin(client, server, 80, uri);
+  int httpCode = http.GET();
+  if(httpCode == HTTP_CODE_OK) {
+    if (!DecodeWeather(http.getStream())) return false;
+    client.stop();
+    http.end();
+    return true;
+  } else {
+    Serial.printf("connection failed, error: %s\n", http.errorToString(httpCode).c_str());
+    client.stop();
+    http.end();
+    return false;
+  }
+  http.end();
+  return true;
+}
+
+bool DecodeCurrentWeather(WiFiClient& json) {
+  DynamicJsonDocument doc(35 * 1024);
+  // Deserialize the JSON document
+  DeserializationError error = deserializeJson(doc, json);
+  // Test if parsing succeeds.
+  if (error) {
+    Serial.print(F("deserializeJson() failed: "));
+    Serial.println(error.c_str());
+    return false;
+  }
+  // convert it to a JsonObject
+  JsonObject root = doc.as<JsonObject>();
+
+  if (current_weather_pressure[0] && root.containsKey(current_weather_pressure)) {
+    CurrentWeather.pressure     = root[current_weather_pressure].as<float>();
+  }
+  if (current_weather_temperature[0] && root.containsKey(current_weather_temperature)) {
+    CurrentWeather.temp     = root[current_weather_temperature].as<float>();
+  }
+  if (current_weather_humidity[0] && root.containsKey(current_weather_humidity)) {
+    CurrentWeather.humidity     = root[current_weather_humidity].as<float>();
+  }
+  if (current_weather_windspeed[0] && root.containsKey(current_weather_windspeed)) {
+    CurrentWeather.wind_speed     = root[current_weather_windspeed].as<float>();
+  }
+
+  float Ro = (CurrentWeather.humidity/100) * 6.105 * pow(2.712828, 17.27 * CurrentWeather.temp/(237.7+CurrentWeather.temp));
+  CurrentWeather.feels_like = CurrentWeather.temp + 0.348*Ro - 0.70*CurrentWeather.wind_speed - 4.25;
+  if (current_weather_lux[0] && root.containsKey(current_weather_lux)) {
+    float wm2 = root[current_weather_lux].as<float>()/685;
+    CurrentWeather.feels_like = CurrentWeather.feels_like + 0.70*wm2/(CurrentWeather.wind_speed + 10);
+  }
+  Serial.print("Calculated feels like: ");
+  Serial.println(CurrentWeather.feels_like);
+  
+  return true;
+}
+
+
+bool GetCurrentWeather(WiFiClient& client) {
+  if (current_weather_server) {
+    client.stop(); // close connection before sending a new request
+    HTTPClient http;
+    http.useHTTP10(true);
+    Serial.println("Connecting...");
+    http.begin(client, current_weather_server, 80, current_weather_uri);
+    int httpCode = http.GET();
+    if(httpCode == HTTP_CODE_OK) {
+      if (!DecodeCurrentWeather(http.getStream())) return false;
+      client.stop();
+      http.end();
+      return true;
+    } else {
+      Serial.printf("connection failed, error: %s\n", http.errorToString(httpCode).c_str());
+      client.stop();
+      http.end();
+      return false;
+    }
+    http.end();
+  }
+  return true;
+}
+
+void arrow(int x, int y, int asize, float aangle, int pwidth, int plength) {
+  float dx = (asize) * cos((aangle - 90) * PI / 180) + x; // calculate X position
+  float dy = (asize) * sin((aangle - 90) * PI / 180) + y; // calculate Y position
+  float x1 = 0;         float y1 = plength;
+  float x2 = pwidth / 2;  float y2 = pwidth / 2;
+  float x3 = -pwidth / 2; float y3 = pwidth / 2;
+  float angle = aangle * PI / 180 - 135;
+  float xx1 = x1 * cos(angle) - y1 * sin(angle) + dx;
+  float yy1 = y1 * cos(angle) + x1 * sin(angle) + dy;
+  float xx2 = x2 * cos(angle) - y2 * sin(angle) + dx;
+  float yy2 = y2 * cos(angle) + x2 * sin(angle) + dy;
+  float xx3 = x3 * cos(angle) - y3 * sin(angle) + dx;
+  float yy3 = y3 * cos(angle) + x3 * sin(angle) + dy;
+  display.fillTriangle(xx1, yy1, xx3, yy3, xx2, yy2, GxEPD_BLACK);
+}
+
+void drawString(int x, int y, String text, alignment align) {
+  uint16_t w, h;
+  display.setTextWrap(false);
+  w = u8g2Fonts.getUTF8Width(text.c_str());
+  h = u8g2Fonts.getFontAscent();
+  if (align == RIGHT)  x = x - w;
+  if (align == CENTER) x = x - w / 2;
+  u8g2Fonts.setCursor(x, y + h / 2);
+  u8g2Fonts.print(text);
+}
+
+String WindDegToDirection(float winddirection) {
+  if (winddirection >= 337.5 || winddirection < 22.5)  return "С";
+  if (winddirection >=  22.5 && winddirection < 67.5)  return "СВ";
+  if (winddirection >=  67.5 && winddirection < 112.5) return "В";
+  if (winddirection >= 112.5 && winddirection < 157.5) return "ЮВ";
+  if (winddirection >= 157.5 && winddirection < 202.5) return "Ю";
+  if (winddirection >= 202.5 && winddirection < 247.5) return "ЮЗ";
+  if (winddirection >= 247.5 && winddirection < 292.5) return "З";
+  if (winddirection >= 292.5 && winddirection < 337.5) return "СЗ";
+  return "?";
+}
+
+void DrawBlock(int x1, int x2, int y1, int y2) {
+  display.fillRect(x1+3, y1+3, x2-x1, y2-y1, GxEPD_BLACK);
+  display.fillRect(x1, y1, x2-x1, y2-y1, GxEPD_WHITE);
+  display.drawRect(x1, y1, x2-x1, y2-y1, GxEPD_BLACK);
+}
+
+void DisplayCurrent(int x1, int x2, int y1, int y2, WeatherRecord weather) {
+
+  DrawBlock(x1,x2,y1,y2);
+
+  int x = (x1 + x2)/2;
+  int y = (y1 + y2)/2;
+  int h = (y2 - y1)/3;
+  int w = (x2 - x1)/4;
+  if (h<w) { w = h; }
+  SetNumFont(w);
+  drawString(x, y1+w*2/3, String(weather.temp,1) + "°", CENTER);
+  SetCyrFont(w/4);
+  drawString(x, y1+w*3/2, "ощущается как", CENTER);
+  SetNumFont(w*2/3);
+  drawString(x, y1+w*2, String(weather.feels_like,1) + "°", CENTER);
+  SetCyrFont(w/2);
+  drawString(x, y2-w/3, String(weather.humidity,0) + "% " + String(weather.pressure,0) + "мм", CENTER);
+}
+
+void DisplayWind(int x1, int x2, int y1, int y2, WeatherRecord weather) {
+
+  DrawBlock(x1,x2,y1,y2);
+
+  int r, dxo, dyo, dxi, dyi;
+  int x = (x1 + x2)/2;
+  int y = (y1 + y2)/2;
+  int r1 = (x2 - x1)/2;
+  int r2 = (y2 - y1)/2;
+  if (r1>r2) { r = r2; } else { r = r1; }
+  int w = r/5;
+  if (w>40) { w = 40; }
+  r = r - w;
+
+  String speedstr = String(weather.wind_speed,1);
+  speedstr.trim();
+
+  arrow (x, y, r-w, weather.wind_deg, w/2, w*7/8);
+
+  display.drawCircle(x, y, r, GxEPD_BLACK);     // Draw compass circle
+  display.drawCircle(x, y, r + 1, GxEPD_BLACK); // Draw compass circle
+  display.drawCircle(x, y, r - w, GxEPD_BLACK); // Draw compass inner circle
+  display.drawCircle(x, y, r - w + 1, GxEPD_BLACK); // Draw compass inner circle
+  for (float a = 0; a < 360; a = a + 30) {
+    dxo = r * cos((a - 90) * PI / 180);
+    dyo = r * sin((a - 90) * PI / 180);
+    dxi = dxo * 0.9;
+    dyi = dyo * 0.9;
+    display.drawLine(dxo + x, dyo + y, dxi + x, dyi + y, GxEPD_BLACK);
+  }
+
+  SetCyrFont(w*2/3);
+  drawString(x, y - r - w/2, "С", CENTER);
+  drawString(x, y + r + w/2, "Ю", CENTER);
+  drawString(x - r - w/2, y, "З", CENTER);
+  drawString(x + r + w/2, y, "В", CENTER);
+  drawString(x - (r + w/2)*10/14, y - (r + w/2)*10/14, "сз", CENTER);
+  drawString(x - (r + w/2)*10/14, y + (r + w/2)*10/14, "юз", CENTER);
+  drawString(x + (r + w/2)*10/14, y + (r + w/2)*10/14, "юв", CENTER);
+  drawString(x + (r + w/2)*10/14, y - (r + w/2)*10/14, "св", CENTER);
+  SetNumFont(r*5/8);
+  drawString(x, y-r/8, speedstr, CENTER);
+  SetCyrFont(w);
+  drawString(x, y+r*3/8, "м/с", CENTER);
+
+}
+
+void drawsun(int x, int y, int scale, int linesize) {
+  display.fillRect(x - scale * 2, y, scale * 4, linesize, GxEPD_BLACK);
+  display.fillRect(x, y - scale * 2, linesize, scale * 4, GxEPD_BLACK);
+  display.drawLine(x - scale * 1.3, y - scale * 1.3, x + scale * 1.3, y + scale * 1.3, GxEPD_BLACK);
+  display.drawLine(x - scale * 1.3, y + scale * 1.3, x + scale * 1.3, y - scale * 1.3, GxEPD_BLACK);
+  if (linesize>1) {
+    display.drawLine(1 + x - scale * 1.3, y - scale * 1.3, 1 + x + scale * 1.3, y + scale * 1.3, GxEPD_BLACK);
+    display.drawLine(2 + x - scale * 1.3, y - scale * 1.3, 2 + x + scale * 1.3, y + scale * 1.3, GxEPD_BLACK);
+    display.drawLine(3 + x - scale * 1.3, y - scale * 1.3, 3 + x + scale * 1.3, y + scale * 1.3, GxEPD_BLACK);
+    display.drawLine(1 + x - scale * 1.3, y + scale * 1.3, 1 + x + scale * 1.3, y - scale * 1.3, GxEPD_BLACK);
+    display.drawLine(2 + x - scale * 1.3, y + scale * 1.3, 2 + x + scale * 1.3, y - scale * 1.3, GxEPD_BLACK);
+    display.drawLine(3 + x - scale * 1.3, y + scale * 1.3, 3 + x + scale * 1.3, y - scale * 1.3, GxEPD_BLACK);
+  }
+  display.fillCircle(x, y, scale * 1.3, GxEPD_WHITE);
+  display.fillCircle(x, y, scale, GxEPD_BLACK);
+  display.fillCircle(x, y, scale - linesize, GxEPD_WHITE);
+}
+
+void drawcloud(int x, int y, int scale, int linesize) {
+  //Draw cloud outer
+  display.fillCircle(x - scale * 3, y, scale, GxEPD_BLACK);                // Left most circle
+  display.fillCircle(x + scale * 3, y, scale, GxEPD_BLACK);                // Right most circle
+  display.fillCircle(x - scale, y - scale, scale * 1.4, GxEPD_BLACK);    // left middle upper circle
+  display.fillCircle(x + scale * 1.5, y - scale * 1.3, scale * 1.75, GxEPD_BLACK); // Right middle upper circle
+  display.fillRect(x - scale * 3 - 1, y - scale, scale * 6, scale * 2 + 1, GxEPD_BLACK); // Upper and lower lines
+  //Clear cloud inner
+  display.fillCircle(x - scale * 3, y, scale - linesize, GxEPD_WHITE);            // Clear left most circle
+  display.fillCircle(x + scale * 3, y, scale - linesize, GxEPD_WHITE);            // Clear right most circle
+  display.fillCircle(x - scale, y - scale, scale * 1.4 - linesize, GxEPD_WHITE);  // left middle upper circle
+  display.fillCircle(x + scale * 1.5, y - scale * 1.3, scale * 1.75 - linesize, GxEPD_WHITE); // Right middle upper circle
+  display.fillRect(x - scale * 3 + 2, y - scale + linesize - 1, scale * 5.9, scale * 2 - linesize * 2 + 2, GxEPD_WHITE); // Upper and lower lines
+}
+
+void drawraindrop(int x, int y, int scale) {
+  display.fillCircle(x, y, scale / 4, GxEPD_BLACK);
+  display.fillTriangle(x - scale / 4, y, x, y - scale, x + scale / 4, y , GxEPD_BLACK);
+  x = x + scale * 1.5; y = y + scale / 2;
+  display.fillCircle(x, y, scale / 4, GxEPD_BLACK);
+  display.fillTriangle(x - scale / 4, y, x, y - scale, x + scale / 4, y , GxEPD_BLACK);
+}
+
+void drawrain(int x, int y, int scale) {
+  for (int d = 0; d < 4; d++) {
+    drawraindrop(x + scale * (7.8 - d * 1.85) - scale * 5.2, y + scale * 2 - scale / 6, scale / 1.6);
+  }
+}
+
+void drawsnow(int x, int y, int scale) {
+  int dxo, dyo, dxi, dyi;
+  int delta = -scale/5;
+  for (int flakes = 0; flakes < 5; flakes++) {
+    for (int i = 0; i < 360; i = i + 60) {
+      dxo = 0.5 * scale * cos((i - 90) * 3.14 / 180); dxi = dxo * 0.1;
+      dyo = 0.5 * scale * sin((i - 90) * 3.14 / 180); dyi = dyo * 0.1;
+      display.drawLine(dxo + x + flakes * 1.5 * scale - scale * 3, dyo + y + delta + scale * 1.8, dxi + x + 0 + flakes * 1.5 * scale - scale * 3, dyi + y + delta + scale * 1.8, GxEPD_BLACK);
+    }
+    delta = - delta;
+  }
+}
+
+void drawtstorm(int x, int y, int scale) {
+  y = y + scale / 2;
+  int delta = -scale/5;
+  for (int i = 0; i < 4; i++) {
+    delta = - delta;
+    display.drawLine(x - scale * 3 + scale * i * 1.5 + 0, y + delta + scale * 1.4, x - scale * 2.5 + scale * i * 1.5 + 0, y + delta + scale * 0.9, GxEPD_BLACK);
+    if (scale > 15) {
+      display.drawLine(x - scale * 3 + scale * i * 1.5 + 1, y + delta + scale * 1.4, x - scale * 2.5 + scale * i * 1.5 + 1, y + delta + scale * 0.9, GxEPD_BLACK);
+      display.drawLine(x - scale * 3 + scale * i * 1.5 + 2, y + delta + scale * 1.4, x - scale * 2.5 + scale * i * 1.5 + 2, y + delta + scale * 0.9, GxEPD_BLACK);
+    }
+    display.drawLine(x - scale * 3 + scale * i * 1.5, y + delta + scale * 1.4 + 0, x - scale * 2 + scale * i * 1.5 + 0, y + delta + scale * 1.4 + 0, GxEPD_BLACK);
+    if (scale > 15) {
+      display.drawLine(x - scale * 3 + scale * i * 1.5, y + delta + scale * 1.4 + 1, x - scale * 2 + scale * i * 1.5 + 0, y + delta + scale * 1.4 + 1, GxEPD_BLACK);
+      display.drawLine(x - scale * 3 + scale * i * 1.5, y + delta + scale * 1.4 + 2, x - scale * 2 + scale * i * 1.5 + 0, y + delta + scale * 1.4 + 2, GxEPD_BLACK);
+    }
+    display.drawLine(x - scale * 2.5 + scale * i * 1.4 + 0, y + delta + scale * 2.4, x - scale * 2 + scale * i * 1.5 + 0, y + delta + scale * 1.4, GxEPD_BLACK);
+    if (scale > 15) {
+      display.drawLine(x - scale * 2.5 + scale * i * 1.4 + 1, y + delta + scale * 2.4, x - scale * 2 + scale * i * 1.5 + 1, y + delta + scale * 1.4, GxEPD_BLACK);
+      display.drawLine(x - scale * 2.5 + scale * i * 1.4 + 2, y + delta + scale * 2.4, x - scale * 2 + scale * i * 1.5 + 2, y + delta + scale * 1.4, GxEPD_BLACK);
+    }
+  }
+}
+
+void drawfog(int x, int y, int scale, int linesize) {
+  if (scale < 15) {
+    y -= 10;
+    linesize = 1;
+  }
+  for (int i = 0; i < 6; i++) {
+    display.fillRect(x - scale * 3, y + scale * 1.5, scale * 6, linesize, GxEPD_BLACK);
+    display.fillRect(x - scale * 3, y + scale * 2.0, scale * 6, linesize, GxEPD_BLACK);
+    display.fillRect(x - scale * 3, y + scale * 2.5, scale * 6, linesize, GxEPD_BLACK);
+  }
+}
+
+void Sunny(int x, int y, int scale) {
+  int linesize = 1;
+  if (scale>15) linesize=3;
+  drawsun(x, y, scale*6/5, linesize);
+}
+
+void MostlySunny(int x, int y, int scale) {
+  int linesize = 1, offset = 5;
+  if (scale>15) {
+    linesize = 3;
+    offset = 15;
+  }
+  drawcloud(x, y + offset, scale, linesize);
+  drawsun(x - scale * 1.8, y - scale * 1.8 + offset, scale, linesize);
+}
+
+void MostlyCloudy(int x, int y, int scale) {
+  int linesize = 1, offset = 5;
+  if (scale>15) {
+    linesize = 3;
+    offset = 15;
+  }
+  drawsun(x - scale * 1.8, y - scale * 1.8 + offset, scale, linesize);
+  drawcloud(x, y + offset, scale, linesize);
+}
+
+void Cloudy(int x, int y, int scale) {
+  int linesize = 1;
+  if (scale<=15) {
+    drawcloud(x, y+5, scale, linesize);
+  }
+  else {
+    linesize = 5;
+    y += scale/2;
+    drawcloud(x + scale * 2.5, y - scale * 1.8, scale/1.6, linesize); // Cloud top right
+    drawcloud(x - scale * 2, y - scale * 2, scale/2, linesize); // Cloud top left
+    drawcloud(x, y, scale, linesize);       // Main cloud
+  }
+}
+
+void Rain(int x, int y, int scale) {
+  int linesize = 3;
+  if (scale<15) {
+    linesize = 1;
+  }
+  drawcloud(x, y-scale/3, scale, linesize);
+  drawrain(x, y-scale/3, scale);
+}
+
+void ExpectRain(int x, int y, int scale) {
+  int linesize = 3;
+  if (scale<15) {
+    linesize = 1;
+  }
+  drawsun(x - scale * 1.8, y - scale * 1.6, scale, linesize);
+  drawcloud(x, y+scale/5, scale, linesize);
+  drawrain(x, y, scale);
+}
+
+void Tstorms(int x, int y, int scale) {
+  int linesize = 3;
+  if (scale<15) {
+    linesize = 1;
+  }
+  drawcloud(x, y, scale, linesize);
+  drawtstorm(x, y, scale);
+}
+
+void Snow(int x, int y, int scale) {
+  int linesize = 3;
+  if (scale<15) {
+    linesize = 1;
+  }
+  drawcloud(x, y, scale, linesize);
+  drawsnow(x, y, scale);
+}
+
+void Fog(int x, int y, int scale) {
+  int linesize = 3;
+  if (scale<15) {
+    linesize = 1;
+  }
+  drawcloud(x, y - 5, scale, linesize);
+  drawfog(x, y - 5, scale, linesize);
+}
+
+void Haze(int x, int y, int scale) {
+  int linesize = 3;
+  if (scale<15) {
+    linesize = 1;
+  }
+  drawsun(x, y - 5, scale*6/5, linesize);
+  drawfog(x, y - 5, scale, linesize);
+}
+
+void DisplayIcon(int x1, int x2, int y1, int y2, WeatherRecord weather) {
+
+  DrawBlock(x1,x2,y1,y2);
+
+  int x = (x1 + x2)/2;
+  int y = (y1 + y2)/2;
+  int h = (y2 - y1);
+  int w = (x2 - x1);
+
+  int scale = w/14;
+  int fscale = w/12;
+
+  String icon = weather.icon;
+  String description = weather.description;
+
+  if      (icon == "01d" || icon == "01n")  
+    Sunny(x, y, scale);
+  else if (icon == "02d" || icon == "02n")  
+    MostlySunny(x, y, scale);
+  else if (icon == "03d" || icon == "03n")  
+    MostlyCloudy(x, y, scale);
+  else if (icon == "04d" || icon == "04n")  
+    Cloudy(x, y, scale);
+  else if (icon == "09d" || icon == "09n")  
+    Rain(x, y, scale);
+  else if (icon == "10d" || icon == "10n")  
+    ExpectRain(x, y, scale);
+  else if (icon == "11d" || icon == "11n")  
+    Tstorms(x, y, scale);
+  else if (icon == "13d" || icon == "13n")  
+    Snow(x, y, scale);
+  else if (icon == "50d")                   
+    Haze(x, y, scale);
+  else if (icon == "50n")                   
+    Fog(x, y, scale);
+
+  SetCyrFont(fscale);
+  drawString(x, y2-fscale*2/3, description, CENTER); 
+
+}
+
+void DisplayUpdate(int x1, int x2, int y1, int y2, WeatherRecord weather) {
+
+  DrawBlock(x1,x2,y1,y2);
+
+  int x = (x1 + x2)/2;
+  int y = (y1 + y2)/2;
+  int h = (y2 - y1)/2;
+  int w = (x2 - x1)/16;
+  if (h<w) { w = h; }
+  SetCyrFont(w);
+  drawString(x, y1+w, "Прогноз от " + UnixTime(weather.dt + TimeZoneOffset), CENTER);
+  
+}
+
+void DisplayForecast(int x1, int x2, int y1, int y2, WeatherRecord weather) {
+
+  DrawBlock(x1,x2,y1,y2);
+
+  int x = (x1 + x2)/2;
+  int y = (y1 + y2)/2;
+  int h = (y2 - y1);
+  int w = (x2 - x1);
+
+  int scale = w/12;
+  int fscale = w/6;
+  if (fscale<6) fscale = 6;
+
+  String icon = weather.icon;
+  String temperature = String(weather.temp,1);
+
+  if      (icon == "01d" || icon == "01n")  
+    Sunny(x, y, scale);
+  else if (icon == "02d" || icon == "02n")  
+    MostlySunny(x, y, scale);
+  else if (icon == "03d" || icon == "03n")  
+    MostlyCloudy(x, y, scale);
+  else if (icon == "04d" || icon == "04n")  
+    Cloudy(x, y, scale);
+  else if (icon == "09d" || icon == "09n")  
+    Rain(x, y, scale);
+  else if (icon == "10d" || icon == "10n")  
+    ExpectRain(x, y, scale);
+  else if (icon == "11d" || icon == "11n")  
+    Tstorms(x, y, scale);
+  else if (icon == "13d" || icon == "13n")  
+    Snow(x, y, scale);
+  else if (icon == "50d")                   
+    Haze(x, y, scale);
+  else if (icon == "50n")                   
+    Fog(x, y, scale);
+
+  SetCyrFont(fscale);
+  drawString(x, y1+fscale*2/3, UnixTimeOnly(weather.dt+ TimeZoneOffset), CENTER); 
+  drawString(x, y2-fscale*2/3, temperature, CENTER); 
+
+}
+
+void DisplayGraph(int x1, int x2, int y1, int y2, int TimeArray[], float DataArray[], int readings, boolean barchart_mode, String label) {
+
+  DrawBlock(x1,x2,y1,y2);
+
+  float maxY = -10000;
+  float minY =  10000;
+
+  int last_x, last_y;
+
+  int scale = (y2 - y1)/10;
+
+  SetCyrFont(scale);
+  drawString((x1+x2)/2, y1+scale*2/3, label, CENTER); 
+  
+  int x_pos = x1 + 3 + scale*2;
+  int y_pos = y2 - 3;
+
+  int h = y2 - y1 - 6 - scale;
+  int w = x2 - x1 - 8 - scale*2;
+
+  float x_new, y_new;
+  
+  for (int i = 1; i < readings; i++ ) {
+    if (DataArray[i] >= maxY) maxY = DataArray[i];
+    if (DataArray[i] <= minY) minY = DataArray[i];
+  }
+  maxY = round(maxY + 0.5);
+  minY = round(minY);
+
+  last_x = x_pos + 1;
+  last_y = y_pos - (constrain(DataArray[0], minY, maxY) - minY) / (maxY - minY) * h -1;
+  
+  display.drawRect(x_pos, y_pos-h, w, h, GxEPD_BLACK);
+
+  for (int gx = 1; gx < readings; gx++) {
+    x_new = x_pos + gx * w / (readings - 1) - 1 ;
+    y_new = y_pos - (constrain(DataArray[gx], minY, maxY) - minY) / (maxY - minY) * h -1;
+    if (barchart_mode) {
+      if (DataArray[gx]!=0) {
+        display.fillRect(x_new, y_new, (w / readings) - 1, y_pos - y_new, GxEPD_BLACK);
+      }
+    } else {
+      display.drawLine(last_x, last_y, x_new, y_new, GxEPD_BLACK);
+    }
+    last_x = x_new;
+    last_y = y_new;
+  }
+
+#define y_minor_axis 5
+#define number_of_dashes 20
+  SetNumFont(scale/1.3);
+  for (int spacing = 1; spacing < y_minor_axis; spacing++) {
+    for (int j = 0; j < number_of_dashes; j++) {
+      if (spacing < y_minor_axis) display.drawFastHLine((x_pos + 3 + j * w / number_of_dashes), y_pos - (h * spacing / y_minor_axis), w / (2 * number_of_dashes), GxEPD_BLACK);
+    }
+    drawString(x_pos - 1, y_pos - h * spacing / y_minor_axis, String((minY + (float)(maxY - minY) / y_minor_axis * spacing + 0.01), 1), RIGHT);
+  }
+}
+
+void DisplayWeather() {
+  Serial.println("Displaying...");
+  DisplayWind(20,235,20,225, CurrentWeather);
+  DisplayCurrent(240,515,20,225,CurrentWeather);
+  DisplayIcon(520,775,60,225,CurrentWeather);
+  DisplayUpdate(520,775,20,55,CurrentWeather);
+  int x=20;
+  for (int i=0; i<MaxHourlyFC; i+=3) {
+    DisplayForecast(x,x+90,235,305,HourlyForecasts[i]);
+    x = x + 95;
+    if (x+95>780) break;
+  }
+  DisplayGraph(20,268,315,460,HourlyDT,HourlyTemp,MaxHourlyFC,false,"Температура");
+  DisplayGraph(273,522,315,460,HourlyDT,HourlyPressure,MaxHourlyFC,false,"Давление");
+  DisplayGraph(527,775,315,460,HourlyDT,HourlyPrecip,MaxHourlyFC,true,"Осадки");
+  display.display();
+}
+
+void loop() {
+
+  delay(2000);
+  WiFiManager wifiManager;
+  wifiManager.setTimeout(10);
+  if(!wifiManager.autoConnect()) {
+
+    Serial.println("failed to connect and hit timeout");
+  
+  } else {
+
+    if (Serial.available() && Serial.read() == 'c') {
+      WiFiSetup();
+    }
+  
+    // put your main code here, to run repeatedly:
+    byte Attempts = 1;
+    WiFiClient client;   // wifi client object
+    for (byte i=1; i<=3; i++) {
+      if (GetWeather(client)) {
+        GetCurrentWeather(client);
+        WiFi.mode(WIFI_OFF);
+        btStop();
+        DisplayWeather();
+
+        int CurrentMin, CurrentSec;
+        struct tm timeinfo;
+        getLocalTime(&timeinfo, 10000);
+        CurrentMin  = timeinfo.tm_min;
+        CurrentSec  = timeinfo.tm_sec;
+        long SleepTimer = (SleepDuration * 60 - ((CurrentMin % SleepDuration) * 60 + CurrentSec));
+        Serial.print("Going to long sleep for ");
+        Serial.print(SleepTimer);
+        Serial.println(" seconds now");
+        delay(1000);
+        Serial.flush(); 
+
+        esp_sleep_enable_timer_wakeup((SleepTimer+5)*1000*1000);
+        esp_deep_sleep_start();
+      }
+    }
+
+  }  
+
+  Serial.println("Going to short sleep for 120 seconds now ");
+  delay(1000);
+  Serial.flush(); 
+  esp_sleep_enable_timer_wakeup(120*1000*1000);
+  esp_deep_sleep_start();
+
+}
+
+
diff --git a/weather_screen/weather_types.h b/weather_screen/weather_types.h
new file mode 100644
index 0000000..6a49dff
--- /dev/null
+++ b/weather_screen/weather_types.h
@@ -0,0 +1,26 @@
+typedef struct { // For current Day and Day 1, 2, 3, etc
+  int      dt;
+  float    temp;
+  float    temp_min;
+  float    temp_max;
+  float    feels_like;
+  float    feels_like_min;
+  float    feels_like_max;
+  float    pressure;
+  float    humidity;
+  float    clouds;
+  float    wind_speed;
+  float    wind_deg;
+  float    rain;
+  float    snow;
+  String   description;
+  String   icon;
+} WeatherRecord;
+
+WeatherRecord CurrentWeather;
+
+#define MaxHourlyFC 48
+#define MaxDailyFC 7
+WeatherRecord HourlyForecasts[MaxHourlyFC];
+WeatherRecord DailyForecasts[MaxDailyFC];
+