diff --git a/data/weatherCurrent.json b/data/weatherCurrent.json index d1e2eff..ae94d50 100644 --- a/data/weatherCurrent.json +++ b/data/weatherCurrent.json @@ -1 +1 @@ -{"coord":{"lon":13.04,"lat":47.8},"weather":[{"id":803,"main":"Clouds","description":"Überwiegend bewölkt","icon":"04d"}],"base":"stations","main":{"temp":282.55,"feels_like":276.58,"temp_min":279.26,"temp_max":285.15,"pressure":1000,"humidity":61},"visibility":10000,"wind":{"speed":6.2,"deg":140,"gust":11.8},"clouds":{"all":75},"dt":1582986835,"sys":{"type":1,"id":6877,"country":"AT","sunrise":1582955413,"sunset":1582995046},"timezone":3600,"id":2766824,"name":"Salzburg","cod":200} \ No newline at end of file +{"coord":{"lon":13.04,"lat":47.8},"weather":[{"id":520,"main":"Rain","description":"Leichter Regenschauer","icon":"09d"}],"base":"stations","main":{"temp":3.72,"feels_like":-0.89,"temp_min":2.22,"temp_max":5.56,"pressure":1019,"humidity":86},"visibility":10000,"wind":{"speed":4.1,"deg":290},"clouds":{"all":75},"dt":1583595650,"sys":{"type":1,"id":6877,"country":"AT","sunrise":1583559395,"sunset":1583600476},"timezone":3600,"id":2766824,"name":"Salzburg","cod":200} \ No newline at end of file diff --git a/data/weatherForecast.json b/data/weatherForecast.json index 03b1c73..1aeb7ac 100644 --- a/data/weatherForecast.json +++ b/data/weatherForecast.json @@ -1 +1 @@ -{"cod":"200","message":0,"cnt":3,"list":[{"dt":1582988400,"main":{"temp":281.77,"feels_like":277.27,"temp_min":281.77,"temp_max":282.14,"pressure":1004,"sea_level":1004,"grnd_level":944,"humidity":61,"temp_kf":-0.37},"weather":[{"id":804,"main":"Clouds","description":"Bedeckt","icon":"04d"}],"clouds":{"all":100},"wind":{"speed":3.93,"deg":154},"sys":{"pod":"d"},"dt_txt":"2020-02-29 15:00:00"},{"dt":1582999200,"main":{"temp":277.8,"feels_like":274.11,"temp_min":277.8,"temp_max":278.08,"pressure":1005,"sea_level":1005,"grnd_level":946,"humidity":80,"temp_kf":-0.28},"weather":[{"id":804,"main":"Clouds","description":"Bedeckt","icon":"04n"}],"clouds":{"all":100},"wind":{"speed":2.77,"deg":164},"sys":{"pod":"n"},"dt_txt":"2020-02-29 18:00:00"},{"dt":1583010000,"main":{"temp":275.73,"feels_like":271.41,"temp_min":275.73,"temp_max":275.91,"pressure":1006,"sea_level":1006,"grnd_level":946,"humidity":66,"temp_kf":-0.18},"weather":[{"id":804,"main":"Clouds","description":"Bedeckt","icon":"04n"}],"clouds":{"all":100},"wind":{"speed":2.74,"deg":174},"sys":{"pod":"n"},"dt_txt":"2020-02-29 21:00:00"}],"city":{"id":2766824,"name":"Salzburg","coord":{"lat":47.7994,"lon":13.044},"country":"AT","timezone":3600,"sunrise":1582955412,"sunset":1582995045}} \ No newline at end of file +{"city":{"id":2766824,"name":"Salzburg","coord":{"lon":13.044,"lat":47.7994},"country":"AT","population":0,"timezone":3600},"cod":"200","message":0.0756077,"cnt":3,"list":[{"dt":1583578800,"sunrise":1583559394,"sunset":1583600475,"temp":{"day":276.7,"min":273.5,"max":276.7,"night":273.5,"eve":274.74,"morn":276.7},"feels_like":{"day":271.93,"night":269.99,"eve":271.06,"morn":271.93},"pressure":1018,"humidity":75,"weather":[{"id":600,"main":"Snow","description":"Mäßiger Schnee","icon":"13d"}],"speed":3.89,"deg":274,"clouds":100,"rain":2.38,"snow":0.31},{"dt":1583665200,"sunrise":1583645675,"sunset":1583686964,"temp":{"day":281.7,"min":272.09,"max":281.7,"night":275.81,"eve":276.57,"morn":272.09},"feels_like":{"day":279.03,"night":272.32,"eve":273.21,"morn":268.74},"pressure":1021,"humidity":53,"weather":[{"id":801,"main":"Clouds","description":"Ein paar Wolken","icon":"02d"}],"speed":0.88,"deg":259,"clouds":24},{"dt":1583751600,"sunrise":1583731955,"sunset":1583773453,"temp":{"day":276.82,"min":274.09,"max":277.7,"night":274.09,"eve":276.3,"morn":275.27},"feels_like":{"day":272.12,"night":270.38,"eve":272.51,"morn":272.04},"pressure":1016,"humidity":97,"weather":[{"id":501,"main":"Rain","description":"Mäßiger Regen","icon":"10d"}],"speed":4.63,"deg":250,"clouds":100,"rain":5.57}]} \ No newline at end of file diff --git a/include/faceWeather.h b/include/faceWeather.h index 2dd5620..1056ef9 100644 --- a/include/faceWeather.h +++ b/include/faceWeather.h @@ -1,26 +1,41 @@ #ifndef FACE_WEATHER_H #define FACE_WEATHER_H -// TODO struct faceWeatherData { - //char hostname[64]; - //int port; + // global + char location[20]; + // current condition char current_icon[4]; - char current_description[20]; + int current_temp; + int current_min; + int current_max; - char location[20]; + // forecast +1 (tomorrow) + char forecast_1_icon[4]; + int forecast_1_min; + int forecast_1_max; - // current - int current_temp; - int today_min; - int today_max; + // forecast +2 + char forecast_2_icon[4]; + int forecast_2_min; + int forecast_2_max; + + // forecast +3 + char forecast_3_icon[4]; + int forecast_3_min; + int forecast_3_max; - // forecast + // forecast +4 + char forecast_4_icon[4]; + int forecast_4_min; + int forecast_4_max; }; void setupFaceWeather(); void loopFaceWeather(); +bool updateWeatherData(); + #endif \ No newline at end of file diff --git a/src/faceWeather.cpp b/src/faceWeather.cpp index 5e89808..f1b823b 100644 --- a/src/faceWeather.cpp +++ b/src/faceWeather.cpp @@ -16,11 +16,11 @@ faceWeatherData weatherData; void render_current(); void render_forecast(); -bool downloadWeatherData(); -void readWeatherData(); +bool readWeatherData(); void setupFaceWeather() { + readWeatherData(); } void loopFaceWeather() @@ -40,38 +40,39 @@ void loopFaceWeather() void render_current() { + // name + display.setFont(&FreeSansBold18pt7b); + display.setTextSize(1); + display.setCursor(20, 220); + display.println(weatherData.location); + // temperature display.setFont(&FreeSansBold24pt7b); display.setTextSize(2); display.setCursor(50, 120); - display.println("3°"); + display.println(weatherData.current_temp); // icon - const uint *icon = getIconById("02n", 192); // 192 + const unsigned char *icon = getIconById(weatherData.current_icon, 256); if (icon) { - //display.drawInvertedBitmap(224, 30, myIcon, 192, 192, GxEPD_WHITE); - display.drawBitmap(224, 30, (uint8_t *)icon + 4, icon[0], icon[1], GxEPD_WHITE); + display.drawInvertedBitmap(192, 0, icon, 256, 256, GxEPD_WHITE); } - // text - display.setTextSize(1); - display.setCursor(400, 300); - //display.println("Ein paar Wolken"); - + // 250 height // high display.setTextSize(1); - display.setCursor(500, 30); - display.println("5°"); + display.setCursor(500, 100); + display.println(weatherData.current_max); // low - display.setCursor(500, 60); - display.println("-3°"); + display.setCursor(500, 180); + display.println(weatherData.current_min); } void render_forecast() { - const uint *icon; + const unsigned char *icon; // line forecast display.drawRect(0, 250, 640, 2, GxEPD_WHITE); @@ -90,12 +91,12 @@ void render_forecast() uint16_t tempRangeY = 260 + 64 + 40; // day +1 - icon = getIconById("03d", 64); + icon = getIconById(weatherData.forecast_1_icon, 64); if (icon) { - display.drawBitmap(0 + 48, 260, (uint8_t *)icon + 4, icon[0], icon[1], GxEPD_WHITE); + display.drawInvertedBitmap(0 + 48, 260, icon, 64, 64, GxEPD_WHITE); - sprintf(label, "%2d ... %2d", -3, 11); + sprintf(label, "%2d ... %2d", weatherData.forecast_1_min, weatherData.forecast_1_max); display.getTextBounds(label, 0, 0, &tbx, &tby, &tbw, &tbh); x = ((160 - tbw) / 2) - tbx; display.setCursor(x, tempRangeY); @@ -103,12 +104,12 @@ void render_forecast() } // day +2 - icon = getIconById("09d", 64); + icon = getIconById(weatherData.forecast_2_icon, 64); if (icon) { - display.drawBitmap(160 + 48, 260, (uint8_t *)icon + 4, icon[0], icon[1], GxEPD_WHITE); + display.drawInvertedBitmap(160 + 48, 260, icon, 64, 64, GxEPD_WHITE); - sprintf(label, "%2d ... %2d", 0, 7); + sprintf(label, "%2d ... %2d", weatherData.forecast_2_min, weatherData.forecast_2_max); display.getTextBounds(label, 0, 0, &tbx, &tby, &tbw, &tbh); x = ((160 - tbw) / 2) - tbx; display.setCursor(160 + x, tempRangeY); @@ -116,12 +117,12 @@ void render_forecast() } // day +3 - icon = getIconById("13d", 64); + icon = getIconById(weatherData.forecast_3_icon, 64); if (icon) { - display.drawBitmap(320 + 48, 260, (uint8_t *)icon + 4, icon[0], icon[1], GxEPD_WHITE); + display.drawInvertedBitmap(320 + 48, 260, icon, 64, 64, GxEPD_WHITE); - sprintf(label, "%2d ... %2d", 1, 21); + sprintf(label, "%2d ... %2d", weatherData.forecast_3_min, weatherData.forecast_3_max); display.getTextBounds(label, 0, 0, &tbx, &tby, &tbw, &tbh); x = ((160 - tbw) / 2) - tbx; display.setCursor(320 + x, tempRangeY); @@ -129,12 +130,12 @@ void render_forecast() } // day +4 - icon = getIconById("13d", 64); + icon = getIconById(weatherData.forecast_4_icon, 64); if (icon) { - display.drawBitmap(480 + 48, 260, (uint8_t *)icon + 4, icon[0], icon[1], GxEPD_WHITE); + display.drawInvertedBitmap(480 + 48, 260, icon, 64, 64, GxEPD_WHITE); - sprintf(label, "%2d ... %2d", 19, 35); + sprintf(label, "%2d ... %2d", weatherData.forecast_4_min, weatherData.forecast_4_max); display.getTextBounds(label, 0, 0, &tbx, &tby, &tbw, &tbh); x = ((160 - tbw) / 2) - tbx; display.setCursor(480 + x, tempRangeY); @@ -162,10 +163,11 @@ bool downloadWeatherData() } // https://openweathermap.org/forecast5 - url = "http://api.openweathermap.org/data/2.5/forecast?"; + // http://api.openweathermap.org/data/2.5/forecast/daily?id=2766824&APPID=883b3c87223430d6f3a399645f8ba12b&lang=de&cnt=3 + url = "http://api.openweathermap.org/data/2.5/forecast/daily?"; url += "APPID=883b3c87223430d6f3a399645f8ba12b"; // api key url += "&id=2766824"; // location - url += "&lang=de&cnt=3&units=metric"; // settings + url += "&lang=de&cnt=4&units=metric"; // settings if (!downloadFile(url, faceWeatherForecast)) { return false; @@ -174,41 +176,106 @@ bool downloadWeatherData() return true; } -void readWeatherData() +/** + * download and update weather data + */ +bool updateWeatherData() { + if (downloadWeatherData()) + { + readWeatherData(); + + return true; + } + + return false; +} + +/** + * read weather data from json file + */ +bool readWeatherData() +{ + Serial.println(" readWeatherData"); File file; DeserializationError error; // current weather file = SPIFFS.open(faceWeatherCurrent); + if (!file) + { + Serial.print("Failed to open file: "); + Serial.println(faceWeatherCurrent); + + return false; + } + StaticJsonDocument<976> docCurrent; // Use arduinojson.org/v6/assistant to compute the capacity. error = deserializeJson(docCurrent, file); + file.close(); if (error) { - Serial.println(F("Failed to read file, using default configuration")); + Serial.print("Failed to read file: "); + Serial.println(faceWeatherCurrent); + + Serial.println(error.c_str()); + return false; } - // TODO Copy values from the JsonDocument to the Config - weatherData.current_temp = 12; + //serializeJsonPretty(docCurrent, Serial); + //Serial.println(docCurrent["weather"][0]["icon"].as()); + //Serial.println(docCurrent["weather"][0]["main"].as()); + //Serial.println(docCurrent["sys"]["country"].as()); + //Serial.println(docCurrent["main"]["temp"].as()); - /* - config.port = doc["port"] | 2731; - strlcpy(config.hostname, // <- destination - doc["hostname"] | "example.com", // <- source - sizeof(config.hostname)); // <- destination's capacity - */ - file.close(); + // copy required values + + strlcpy(weatherData.location, docCurrent["name"] | "?", sizeof(weatherData.location)); + + weatherData.current_temp = round(docCurrent["main"]["temp"].as()); + weatherData.current_min = floor(docCurrent["main"]["temp_min"].as()); + weatherData.current_max = ceil(docCurrent["main"]["temp_max"].as()); + strlcpy(weatherData.current_icon, docCurrent["weather"][0]["icon"] | "50n", sizeof(weatherData.current_icon)); // forecast file = SPIFFS.open(faceWeatherForecast); - StaticJsonDocument<2180> docForecast; // Use arduinojson.org/v6/assistant to compute the capacity. + if (!file) + { + Serial.print("Failed to open file: "); + Serial.println(faceWeatherForecast); + + return false; + } + + StaticJsonDocument<3000> docForecast; // Use arduinojson.org/v6/assistant to compute the capacity. error = deserializeJson(docForecast, file); + file.close(); if (error) { - Serial.println(F("Failed to read file, using default configuration")); + Serial.print("Failed to read file: "); + Serial.println(faceWeatherForecast); + + Serial.println(error.c_str()); + + return false; } - // TODO get values + // copy required values + weatherData.forecast_1_min = round(docForecast["list"][0]["temp"]["min"].as()); + weatherData.forecast_1_max = round(docForecast["list"][0]["temp"]["max"].as()); + strlcpy(weatherData.forecast_1_icon, docForecast["list"][0]["weather"][0]["icon"] | "50n", sizeof(weatherData.forecast_1_icon)); - file.close(); + weatherData.forecast_2_min = round(docForecast["list"][1]["temp"]["min"].as()); + weatherData.forecast_2_max = round(docForecast["list"][1]["temp"]["max"].as()); + strlcpy(weatherData.forecast_2_icon, docForecast["list"][1]["weather"][0]["icon"] | "50n", sizeof(weatherData.forecast_2_icon)); + + weatherData.forecast_3_min = round(docForecast["list"][2]["temp"]["min"].as()); + weatherData.forecast_3_max = round(docForecast["list"][2]["temp"]["max"].as()); + strlcpy(weatherData.forecast_3_icon, docForecast["list"][2]["weather"][0]["icon"] | "50n", sizeof(weatherData.forecast_3_icon)); + + weatherData.forecast_4_min = round(docForecast["list"][3]["temp"]["min"].as()); + weatherData.forecast_4_max = round(docForecast["list"][3]["temp"]["max"].as()); + strlcpy(weatherData.forecast_4_icon, docForecast["list"][3]["weather"][0]["icon"] | "50n", sizeof(weatherData.forecast_4_icon)); + + return true; } \ No newline at end of file