#include "Meteo.h" #include "Ecran.h" #include "common.h" #include "common_functions.h" #include "Ctrl.h" Meteo::Meteo(Ecran * _ecran, Connect * _connect, Timer * _timer, String _apikey, String _server, String _City, String _Country) { ecran = _ecran; connect = _connect; timer = _timer; apikey = _apikey; server = _server; City = _City; Country = _Country; } bool Meteo::loadData() { byte Attempts = 1; bool RxWeather = false, RxForecast = false; while ((RxWeather == false || RxForecast == false) && Attempts <= 2) { // Try up-to 2 time for Weather and Forecast data if (RxWeather == false) RxWeather = getWeather(connect->client, "weather"); if (RxForecast == false) RxForecast = getWeather(connect->client, "forecast"); Attempts++; } return RxWeather && RxForecast; } //######################################################################################### // Symbols are drawn on a relative 10x10grid and 1 scale unit = 1 drawing unit void Meteo::addcloud(int x, int y, int scale, int linesize) { //Draw cloud outer ecran->_display->fillCircle(x - scale * 3, y, scale, GxEPD_BLACK); // Left most circle ecran->_display->fillCircle(x + scale * 3, y, scale, GxEPD_BLACK); // Right most circle ecran->_display->fillCircle(x - scale, y - scale, scale * 1.4, GxEPD_BLACK); // left middle upper circle ecran->_display->fillCircle(x + scale * 1.5, y - scale * 1.3, scale * 1.75, GxEPD_BLACK); // Right middle upper circle ecran->_display->fillRect(x - scale * 3 - 1, y - scale, scale * 6, scale * 2 + 1, GxEPD_BLACK); // Upper and lower lines //Clear cloud inner ecran->_display->fillCircle(x - scale * 3, y, scale - linesize, GxEPD_WHITE); // Clear left most circle ecran->_display->fillCircle(x + scale * 3, y, scale - linesize, GxEPD_WHITE); // Clear right most circle ecran->_display->fillCircle(x - scale, y - scale, scale * 1.4 - linesize, GxEPD_WHITE); // left middle upper circle ecran->_display->fillCircle(x + scale * 1.5, y - scale * 1.3, scale * 1.75 - linesize, GxEPD_WHITE); // Right middle upper circle ecran->_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 Meteo::addraindrop(int x, int y, int scale) { ecran->_display->fillCircle(x, y, scale / 2, GxEPD_BLACK); ecran->_display->fillTriangle(x - scale / 2, y, x, y - scale * 1.2, x + scale / 2, y , GxEPD_BLACK); x = x + scale * 1.6; y = y + scale / 3; ecran->_display->fillCircle(x, y, scale / 2, GxEPD_BLACK); ecran->_display->fillTriangle(x - scale / 2, y, x, y - scale * 1.2, x + scale / 2, y , GxEPD_BLACK); } //######################################################################################### void Meteo::addrain(int x, int y, int scale, bool IconSize) { if (IconSize == SmallIcon) scale *= 1.34; for (int d = 0; d < 4; d++) { addraindrop( x + scale * (7.8 - d * 1.95) - scale * 5.2, y + scale * 2.1 - scale / 6, scale / 1.6); } } //######################################################################################### void Meteo::addsnow(int x, int y, int scale, bool IconSize) { int dxo, dyo, dxi, dyi; for (int flakes = 0; flakes < 5; flakes++) { for (int i = 0; i < 360; i = i + 45) { 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; ecran->_display->drawLine(dxo + x + flakes * 1.5 * scale - scale * 3, dyo + y + scale * 2, dxi + x + 0 + flakes * 1.5 * scale - scale * 3, dyi + y + scale * 2, GxEPD_BLACK); } } } //######################################################################################### void Meteo::addtstorm(int x, int y, int scale) { y = y + scale / 2; for (int i = 0; i < 5; i++) { ecran->_display->drawLine(x - scale * 4 + scale * i * 1.5 + 0, y + scale * 1.5, x - scale * 3.5 + scale * i * 1.5 + 0, y + scale, GxEPD_BLACK); if (scale != Small) { ecran->_display->drawLine(x - scale * 4 + scale * i * 1.5 + 1, y + scale * 1.5, x - scale * 3.5 + scale * i * 1.5 + 1, y + scale, GxEPD_BLACK); ecran->_display->drawLine(x - scale * 4 + scale * i * 1.5 + 2, y + scale * 1.5, x - scale * 3.5 + scale * i * 1.5 + 2, y + scale, GxEPD_BLACK); } ecran->_display->drawLine(x - scale * 4 + scale * i * 1.5, y + scale * 1.5 + 0, x - scale * 3 + scale * i * 1.5 + 0, y + scale * 1.5 + 0, GxEPD_BLACK); if (scale != Small) { ecran->_display->drawLine(x - scale * 4 + scale * i * 1.5, y + scale * 1.5 + 1, x - scale * 3 + scale * i * 1.5 + 0, y + scale * 1.5 + 1, GxEPD_BLACK); ecran->_display->drawLine(x - scale * 4 + scale * i * 1.5, y + scale * 1.5 + 2, x - scale * 3 + scale * i * 1.5 + 0, y + scale * 1.5 + 2, GxEPD_BLACK); } ecran->_display->drawLine(x - scale * 3.5 + scale * i * 1.4 + 0, y + scale * 2.5, x - scale * 3 + scale * i * 1.5 + 0, y + scale * 1.5, GxEPD_BLACK); if (scale != Small) { ecran->_display->drawLine(x - scale * 3.5 + scale * i * 1.4 + 1, y + scale * 2.5, x - scale * 3 + scale * i * 1.5 + 1, y + scale * 1.5, GxEPD_BLACK); ecran->_display->drawLine(x - scale * 3.5 + scale * i * 1.4 + 2, y + scale * 2.5, x - scale * 3 + scale * i * 1.5 + 2, y + scale * 1.5, GxEPD_BLACK); } } } //######################################################################################### void Meteo::addsun(int x, int y, int scale, bool IconSize) { int linesize = 3; if (IconSize == SmallIcon) linesize = 1; ecran->_display->fillRect(x - scale * 2, y, scale * 4, linesize, GxEPD_BLACK); ecran->_display->fillRect(x, y - scale * 2, linesize, scale * 4, GxEPD_BLACK); ecran->_display->drawLine(x - scale * 1.3, y - scale * 1.3, x + scale * 1.3, y + scale * 1.3, GxEPD_BLACK); ecran->_display->drawLine(x - scale * 1.3, y + scale * 1.3, x + scale * 1.3, y - scale * 1.3, GxEPD_BLACK); if (IconSize == LargeIcon) { ecran->_display->drawLine(1 + x - scale * 1.3, y - scale * 1.3, 1 + x + scale * 1.3, y + scale * 1.3, GxEPD_BLACK); ecran->_display->drawLine(2 + x - scale * 1.3, y - scale * 1.3, 2 + x + scale * 1.3, y + scale * 1.3, GxEPD_BLACK); ecran->_display->drawLine(3 + x - scale * 1.3, y - scale * 1.3, 3 + x + scale * 1.3, y + scale * 1.3, GxEPD_BLACK); ecran->_display->drawLine(1 + x - scale * 1.3, y + scale * 1.3, 1 + x + scale * 1.3, y - scale * 1.3, GxEPD_BLACK); ecran->_display->drawLine(2 + x - scale * 1.3, y + scale * 1.3, 2 + x + scale * 1.3, y - scale * 1.3, GxEPD_BLACK); ecran->_display->drawLine(3 + x - scale * 1.3, y + scale * 1.3, 3 + x + scale * 1.3, y - scale * 1.3, GxEPD_BLACK); } ecran->_display->fillCircle(x, y, scale * 1.3, GxEPD_WHITE); ecran->_display->fillCircle(x, y, scale, GxEPD_BLACK); ecran->_display->fillCircle(x, y, scale - linesize, GxEPD_WHITE); } //######################################################################################### void Meteo::addfog(int x, int y, int scale, int linesize, bool IconSize) { if (IconSize == SmallIcon) { y -= 10; linesize = 1; } for (int i = 0; i < 6; i++) { ecran->_display->fillRect(x - scale * 3, y + scale * 1.5, scale * 6, linesize, GxEPD_BLACK); ecran->_display->fillRect(x - scale * 3, y + scale * 2.0, scale * 6, linesize, GxEPD_BLACK); ecran->_display->fillRect(x - scale * 3, y + scale * 2.5, scale * 6, linesize, GxEPD_BLACK); } } //######################################################################################### void Meteo::Sunny(int x, int y, bool IconSize, String IconName) { int scale = Small; if (IconSize == LargeIcon) scale = Large; else y = y - 3; // Shift up small sun icon if (IconName.endsWith("n")) addMoon(x, y + 3, scale, IconSize); scale = scale * 1.6; addsun(x, y, scale, IconSize); } //######################################################################################### void Meteo::MostlySunny(int x, int y, bool IconSize, String IconName) { int scale = Small, linesize = 3, offset = 5; if (IconSize == LargeIcon) { scale = Large; offset = 10; } if (scale == Small) linesize = 1; if (IconName.endsWith("n")) addMoon(x, y + offset, scale, IconSize); addcloud(x, y + offset, scale, linesize); addsun(x - scale * 1.8, y - scale * 1.8 + offset, scale, IconSize); } //######################################################################################### void Meteo::MostlyCloudy(int x, int y, bool IconSize, String IconName) { int scale = Small, linesize = 3; if (IconSize == LargeIcon) { scale = Large; linesize = 1; } if (IconName.endsWith("n")) addMoon(x, y, scale, IconSize); addcloud(x, y, scale, linesize); addsun(x - scale * 1.8, y - scale * 1.8, scale, IconSize); addcloud(x, y, scale, linesize); } //######################################################################################### void Meteo::Cloudy(int x, int y, bool IconSize, String IconName) { int scale = Large, linesize = 3; if (IconSize == SmallIcon) { scale = Small; if (IconName.endsWith("n")) addMoon(x, y, scale, IconSize); linesize = 1; addcloud(x, y, scale, linesize); } else { y += 10; if (IconName.endsWith("n")) addMoon(x, y, scale, IconSize); addcloud(x + 30, y - 45, 5, linesize); // Cloud top right addcloud(x - 20, y - 30, 7, linesize); // Cloud top left addcloud(x, y, scale, linesize); // Main cloud } } //######################################################################################### void Meteo::Rain(int x, int y, bool IconSize, String IconName) { int scale = Large, linesize = 3; if (IconSize == SmallIcon) { scale = Small; linesize = 1; } if (IconName.endsWith("n")) addMoon(x, y, scale, IconSize); addcloud(x, y, scale, linesize); addrain(x, y, scale, IconSize); } //######################################################################################### void Meteo::ExpectRain(int x, int y, bool IconSize, String IconName) { int scale = Large, linesize = 3; if (IconSize == SmallIcon) { scale = Small; linesize = 1; } if (IconName.endsWith("n")) addMoon(x, y, scale, IconSize); addsun(x - scale * 1.8, y - scale * 1.8, scale, IconSize); addcloud(x, y, scale, linesize); addrain(x, y, scale, IconSize); } //######################################################################################### void Meteo::ChanceRain(int x, int y, bool IconSize, String IconName) { int scale = Large, linesize = 3; if (IconSize == SmallIcon) { scale = Small; linesize = 1; } if (IconName.endsWith("n")) addMoon(x, y, scale, IconSize); addsun(x - scale * 1.8, y - scale * 1.8, scale, IconSize); addcloud(x, y, scale, linesize); addrain(x, y, scale, IconSize); } //######################################################################################### void Meteo::Tstorms(int x, int y, bool IconSize, String IconName) { int scale = Large, linesize = 3; if (IconSize == SmallIcon) { scale = Small; linesize = 1; } if (IconName.endsWith("n")) addMoon(x, y, scale, IconSize); addcloud(x, y, scale, linesize); addtstorm(x, y, scale); } //######################################################################################### void Meteo::Snow(int x, int y, bool IconSize, String IconName) { int scale = Large, linesize = 3; if (IconSize == SmallIcon) { scale = Small; linesize = 1; } if (IconName.endsWith("n")) addMoon(x, y, scale, IconSize); addcloud(x, y, scale, linesize); addsnow(x, y, scale, IconSize); } //######################################################################################### void Meteo::Fog(int x, int y, bool IconSize, String IconName) { int linesize = 3, scale = Large; if (IconSize == SmallIcon) { scale = Small; linesize = 1; } if (IconName.endsWith("n")) addMoon(x, y, scale, IconSize); addcloud(x, y - 5, scale, linesize); addfog(x, y - 5, scale, linesize, IconSize); } //######################################################################################### void Meteo::Haze(int x, int y, bool IconSize, String IconName) { int linesize = 3, scale = Large; if (IconSize == SmallIcon) { scale = Small; linesize = 1; } if (IconName.endsWith("n")) addMoon(x, y, scale, IconSize); addsun(x, y - 5, scale * 1.4, IconSize); addfog(x, y - 5, scale * 1.4, linesize, IconSize); } //######################################################################################### void Meteo::CloudCover(int x, int y, int CCover) { // addcloud(x - 9, y - 3, Small * 0.5, 2); // Cloud top left // addcloud(x + 3, y - 3, Small * 0.5, 2); // Cloud top right // addcloud(x, y, Small * 0.5, 2); // Main cloud ecran->fonts.setFont(u8g2_font_helvB12_tf); ecran->drawString(x, y, String(CCover) + "%", LEFT); } //######################################################################################### void Meteo::Visibility(int x, int y, String Visi) { y = y - 3; // float start_angle = 0.52, end_angle = 2.61; int r = 10; for (float i = start_angle; i < end_angle; i = i + 0.05) { ecran->_display->drawPixel(x + r * cos(i), y - r / 2 + r * sin(i), GxEPD_BLACK); ecran->_display->drawPixel(x + r * cos(i), 1 + y - r / 2 + r * sin(i), GxEPD_BLACK); } start_angle = 3.61; end_angle = 5.78; for (float i = start_angle; i < end_angle; i = i + 0.05) { ecran->_display->drawPixel(x + r * cos(i), y + r / 2 + r * sin(i), GxEPD_BLACK); ecran->_display->drawPixel(x + r * cos(i), 1 + y + r / 2 + r * sin(i), GxEPD_BLACK); } ecran->_display->fillCircle(x, y, r / 4, GxEPD_BLACK); ecran->fonts.setFont(FONT_08); ecran->drawString(x + 12, y - 3, Visi, LEFT); } //######################################################################################### void Meteo::Convert_Readings_to_Imperial() { conditions[0].Pressure = hPa_to_inHg(conditions[0].Pressure); previsions[1].Rainfall = mm_to_inches(previsions[1].Rainfall); previsions[1].Snowfall = mm_to_inches(previsions[1].Snowfall); } //######################################################################################### // Problems with stucturing JSON decodes, see here: https://arduinojson.org/assistant/ bool Meteo::DecodeWeather(WiFiClient& json, String Type) { if (debug) Serial.print(F("\nCreating object...and ")); // allocate the JsonDocument DynamicJsonDocument doc(35 * 1024); // Deserialize the JSON document DeserializationError error = deserializeJson(doc, json); // Test if parsing succeeds. if (error) { if (debug) Serial.print(F("deserializeJson() failed: ")); if (debug) Serial.println(error.c_str()); return false; } // convert it to a JsonObject JsonObject root = doc.as(); if (debug) Serial.println(" Decoding " + Type + " data"); if (Type == "weather") { // All if (debug) Serial.println statements are for diagnostic purposes and not required, remove if not needed conditions[0].lon = root["coord"]["lon"].as(); if (debug) Serial.println(" Lon: "+String(conditions[0].lon)); conditions[0].lat = root["coord"]["lat"].as(); if (debug) Serial.println(" Lat: "+String(conditions[0].lat)); conditions[0].Main0 = root["weather"][0]["main"].as(); if (debug) Serial.println("Main: "+String(conditions[0].Main0)); conditions[0].Forecast0 = root["weather"][0]["description"].as(); if (debug) Serial.println("For0: "+String(conditions[0].Forecast0)); conditions[0].Forecast1 = root["weather"][1]["description"].as(); if (debug) Serial.println("For1: "+String(conditions[0].Forecast1)); conditions[0].Forecast2 = root["weather"][2]["description"].as(); if (debug) Serial.println("For2: "+String(conditions[0].Forecast2)); conditions[0].Icon = root["weather"][0]["icon"].as(); if (debug) Serial.println("Icon: "+String(conditions[0].Icon)); conditions[0].Temperature = root["main"]["temp"].as(); if (debug) Serial.println("Temp: "+String(conditions[0].Temperature)); conditions[0].Pressure = root["main"]["pressure"].as(); if (debug) Serial.println("Pres: "+String(conditions[0].Pressure)); conditions[0].Humidity = root["main"]["humidity"].as(); if (debug) Serial.println("Humi: "+String(conditions[0].Humidity)); conditions[0].Low = root["main"]["temp_min"].as(); if (debug) Serial.println("TLow: "+String(conditions[0].Low)); conditions[0].High = root["main"]["temp_max"].as(); if (debug) Serial.println("THig: "+String(conditions[0].High)); conditions[0].Windspeed = root["wind"]["speed"].as(); if (debug) Serial.println("WSpd: "+String(conditions[0].Windspeed)); conditions[0].Winddir = root["wind"]["deg"].as(); if (debug) Serial.println("WDir: "+String(conditions[0].Winddir)); conditions[0].Cloudcover = root["clouds"]["all"].as(); if (debug) Serial.println("CCov: "+String(conditions[0].Cloudcover)); // in % of cloud cover conditions[0].Visibility = root["visibility"].as(); if (debug) Serial.println("Visi: "+String(conditions[0].Visibility)); // in metres conditions[0].Rainfall = root["rain"]["1h"].as(); if (debug) Serial.println("Rain: "+String(conditions[0].Rainfall)); conditions[0].Snowfall = root["snow"]["1h"].as(); if (debug) Serial.println("Snow: "+String(conditions[0].Snowfall)); conditions[0].Country = root["sys"]["country"].as(); if (debug) Serial.println("Ctry: "+String(conditions[0].Country)); conditions[0].Sunrise = root["sys"]["sunrise"].as(); if (debug) Serial.println("SRis: "+String(conditions[0].Sunrise)); conditions[0].Sunset = root["sys"]["sunset"].as(); if (debug) Serial.println("SSet: "+String(conditions[0].Sunset)); conditions[0].Timezone = root["timezone"].as(); if (debug) Serial.println("TZon: "+String(conditions[0].Timezone)); } if (Type == "forecast") { //if (debug) Serial.println(json); if (debug) Serial.print(F("\nReceiving Forecast period - ")); //------------------------------------------------ JsonArray list = root["list"]; for (byte r = 0; r < max_readings; r++) { // if (debug) Serial.println("\nPeriod-" + String(r) + "--------------"); previsions[r].Dt = list[r]["dt"].as(); if (debug) Serial.println("DTim: "+String(previsions[r].Dt)); previsions[r].Temperature = list[r]["main"]["temp"].as(); if (debug) Serial.println("Temp: "+String(previsions[r].Temperature)); previsions[r].Low = list[r]["main"]["temp_min"].as(); if (debug) Serial.println("TLow: "+String(previsions[r].Low)); previsions[r].High = list[r]["main"]["temp_max"].as(); if (debug) Serial.println("THig: "+String(previsions[r].High)); previsions[r].Pressure = list[r]["main"]["pressure"].as(); if (debug) Serial.println("Pres: "+String(previsions[r].Pressure)); previsions[r].Humidity = list[r]["main"]["humidity"].as(); if (debug) Serial.println("Humi: "+String(previsions[r].Humidity)); previsions[r].Forecast0 = list[r]["weather"][0]["main"].as(); if (debug) Serial.println("For0: "+String(previsions[r].Forecast0)); previsions[r].Forecast1 = list[r]["weather"][1]["main"].as(); if (debug) Serial.println("For1: "+String(previsions[r].Forecast1)); previsions[r].Forecast2 = list[r]["weather"][2]["main"].as(); if (debug) Serial.println("For2: "+String(previsions[r].Forecast2)); previsions[r].Icon = list[r]["weather"][0]["icon"].as(); if (debug) Serial.println("Icon: "+String(previsions[r].Icon)); previsions[r].Description = list[r]["weather"][0]["description"].as(); if (debug) Serial.println("Desc: "+String(previsions[r].Description)); previsions[r].Cloudcover = list[r]["clouds"]["all"].as(); if (debug) Serial.println("CCov: "+String(previsions[r].Cloudcover)); // in % of cloud cover previsions[r].Windspeed = list[r]["wind"]["speed"].as(); if (debug) Serial.println("WSpd: "+String(previsions[r].Windspeed)); previsions[r].Winddir = list[r]["wind"]["deg"].as(); if (debug) Serial.println("WDir: "+String(previsions[r].Winddir)); previsions[r].Rainfall = list[r]["rain"]["3h"].as(); if (debug) Serial.println("Rain: "+String(previsions[r].Rainfall)); previsions[r].Snowfall = list[r]["snow"]["3h"].as(); if (debug) Serial.println("Snow: "+String(previsions[r].Snowfall)); previsions[r].Pop = list[r]["pop"].as(); if (debug) Serial.println("Pop: "+String(previsions[r].Pop)); previsions[r].Period = list[r]["dt_txt"].as(); if (debug) Serial.println("Peri: "+String(previsions[r].Period)); } //------------------------------------------ float pressure_trend = previsions[2].Pressure - previsions[0].Pressure; // Measure pressure slope between ~now and later pressure_trend = ((int)(pressure_trend * 10)) / 10.0; // Remove any small variations less than 0.1 conditions[0].Trend = "0"; if (pressure_trend > 0) conditions[0].Trend = "+"; if (pressure_trend < 0) conditions[0].Trend = "-"; if (pressure_trend == 0) conditions[0].Trend = "0"; if (Units == "I") Convert_Readings_to_Imperial(); } return true; } //######################################################################################### String Meteo::ConvertUnixTime(int unix_time) { // Returns either '21:12 ' or ' 09:12pm' depending on Units mode time_t tm = unix_time; struct tm *now_tm = gmtime(&tm); char output[40]; if (Units == "M") { strftime(output, sizeof(output), "%H:%M %d/%m/%y", now_tm); } else { strftime(output, sizeof(output), "%I:%M%P %m/%d/%y", now_tm); } return output; } //######################################################################################### //WiFiClient client; // wifi client object bool Meteo::getWeather(WiFiClient& client, const String& RequestType) { if (debug) Serial.println("B - getWeather"); const String units = (Units == "M" ? "metric" : "imperial"); client.stop(); // close connection before sending a new request HTTPClient http; String uri = "/data/2.5/" + RequestType + "?q=" + City + "," + Country + "&APPID=" + apikey + "&mode=json&units=" + units + "&lang=" + Language; if(RequestType != "weather") { uri += "&cnt=" + String(max_readings); } //http.begin(uri,test_root_ca); //HTTPS example connection http.begin(client, server.c_str(), 80, uri); int httpCode = http.GET(); if(httpCode == HTTP_CODE_OK) { if (!DecodeWeather(http.getStream(), RequestType)) return false; client.stop(); http.end(); return true; } else { if (debug) Serial.printf("connection failed, error: %s", http.errorToString(httpCode).c_str()); client.stop(); http.end(); return false; } http.end(); if (debug) Serial.println("E - getWeather"); return true; } //######################################################################################### void Meteo::drawMoon(int x, int y) { if (debug) Serial.println("B - draw moon"); double Phase = normalizedTodayPhase(); if (debug) Serial.print(" "); if (debug) Serial.println(hemisphere); if (hemisphere == "south") Phase = 1 - Phase; // Draw dark part of moon ecran->_display->fillCircle(x + MOON_DIAMETER - 1, y + MOON_DIAMETER, MOON_DIAMETER / 2 + 1, GxEPD_BLACK); if (debug) Serial.println("circle done"); const int number_of_lines = 90; for (double Ypos = 0; Ypos <= number_of_lines / 2; Ypos++) { double Xpos = sqrt(number_of_lines / 2 * number_of_lines / 2 - Ypos * Ypos); // Determine the edges of the lighted part of the moon double Rpos = 2 * Xpos; double Xpos1, Xpos2; if (Phase < 0.5) { Xpos1 = -Xpos; Xpos2 = Rpos - 2 * Phase * Rpos - Xpos; } else { Xpos1 = Xpos; Xpos2 = Xpos - 2 * Phase * Rpos + Rpos; } // Draw light part of moon double pW1x = (Xpos1 + number_of_lines) / number_of_lines * MOON_DIAMETER + x; double pW1y = (number_of_lines - Ypos) / number_of_lines * MOON_DIAMETER + y; double pW2x = (Xpos2 + number_of_lines) / number_of_lines * MOON_DIAMETER + x; double pW2y = (number_of_lines - Ypos) / number_of_lines * MOON_DIAMETER + y; double pW3x = (Xpos1 + number_of_lines) / number_of_lines * MOON_DIAMETER + x; double pW3y = (Ypos + number_of_lines) / number_of_lines * MOON_DIAMETER + y; double pW4x = (Xpos2 + number_of_lines) / number_of_lines * MOON_DIAMETER + x; double pW4y = (Ypos + number_of_lines) / number_of_lines * MOON_DIAMETER + y; ecran->_display->drawLine(pW1x, pW1y, pW2x, pW2y, GxEPD_WHITE); ecran->_display->drawLine(pW3x, pW3y, pW4x, pW4y, GxEPD_WHITE); } ecran->_display->drawCircle(x + MOON_DIAMETER - 1, y + MOON_DIAMETER, MOON_DIAMETER / 2, GxEPD_BLACK); if (debug) Serial.println("E - draw moon"); } //######################################################################################### String Meteo::calculateTodayPhase() { if (debug) Serial.println("B - moon phase"); time_t now = time(NULL); struct tm * now_utc = gmtime(&now); // Panel * panel = Panel::getSingletonInstance(); int d = timer->day; //now_utc->tm_mday; int m = timer->month; // now_utc->tm_mon + 1; int y = timer->year; // now_utc->tm_year + 1900; int c, e; double jd; int b; if (m < 3) { y--; m += 12; } ++m; c = 365.25 * y; e = 30.6 * m; jd = c + e + d - 694039.09; /* jd is total days elapsed */ jd /= 29.53059; /* divide by the moon cycle (29.53 days) */ b = jd; /* int(jd) -> b, take integer part of jd */ jd -= b; /* subtract integer part to leave fractional part of original jd */ b = jd * 8 + 0.5; /* scale fraction from 0-8 and round by adding 0.5 */ b = b & 7; /* 0 and 8 are the same phase so modulo 8 for 0 */ if (hemisphere == "south") b = 7 - b; if (debug) Serial.print("hemisphere="); if (debug) Serial.println(b); phase = b; if (debug) Serial.println("E - moon phase"); if (b == 0) return TXT_MOON_NEW; // New; 0% illuminated if (b == 1) return TXT_MOON_WAXING_CRESCENT; // Waxing crescent; 25% illuminated if (b == 2) return TXT_MOON_FIRST_QUARTER; // First quarter; 50% illuminated if (b == 3) return TXT_MOON_WAXING_GIBBOUS; // Waxing gibbous; 75% illuminated if (b == 4) return TXT_MOON_FULL; // Full; 100% illuminated if (b == 5) return TXT_MOON_WANING_GIBBOUS; // Waning gibbous; 75% illuminated if (b == 6) return TXT_MOON_THIRD_QUARTER; // Third quarter; 50% illuminated if (b == 7) return TXT_MOON_WANING_CRESCENT; // Waning crescent; 25% illuminated return ""; } double Meteo::normalizedTodayPhase() { if (debug) Serial.println("B - normalizedTodayPhase"); time_t now = time(NULL); struct tm * now_utc = gmtime(&now); int d = timer->day; //now_utc->tm_mday; int m = timer->month; // now_utc->tm_mon + 1; int y = timer->year; // now_utc->tm_year + 1900; if (debug) Serial.print(d); if (debug) Serial.print(" "); if (debug) Serial.print(m); if (debug) Serial.print(" "); if (debug) Serial.println(y); //Panel * panel = Panel::getSingletonInstance(); int j = JulianDate(d, m, y); //Calculate the approximate phase of the moon double Phase = (j + 4.867) / 29.53059; if (debug) Serial.print("E - normalizedTodayPhase "); return (Phase - (int) Phase); } //######################################################################################### void Meteo::addMoon(int x, int y, int scale, bool largeIcon) { if (largeIcon) { if (!DRAW_MOON_IN_CONDITION) { ecran->_display->fillCircle(x - 62, y - 68, scale, GxEPD_BLACK); ecran->_display->fillCircle(x - 43, y - 68, scale * 1.6, GxEPD_WHITE); } } else { ecran->_display->fillCircle(x - 25, y - 15, scale, GxEPD_BLACK); ecran->_display->fillCircle(x - 18, y - 15, scale * 1.6, GxEPD_WHITE); } } void Meteo::drawTodayMoon(int x, int y) { String phase_txt = calculateTodayPhase(); drawMoon(x, y); // ecran->drawString(x + 4, y + 64, phase_txt, LEFT); }