diff --git a/src/Icons.h b/src/Icons.h index f1b12c2..e50351d 100644 --- a/src/Icons.h +++ b/src/Icons.h @@ -23,6 +23,7 @@ namespace Icons namespace Weather { + // 30x30 const unsigned char wind[] PROGMEM = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -34,6 +35,7 @@ namespace Icons 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + // 30x30 const unsigned char humidity[] PROGMEM = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x80, 0x00, @@ -45,6 +47,41 @@ namespace Icons 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + // 30x30 + const unsigned char sunrise [] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, + 0x01, 0x80, 0x06, 0x00, 0x00, 0x80, 0x04, 0x00, 0x00, 0x0f, 0xc0, 0x00, 0x00, 0x18, 0x60, 0x00, + 0x00, 0x30, 0x30, 0x00, 0x00, 0x20, 0x10, 0x00, 0x00, 0x60, 0x18, 0x00, 0x1e, 0x60, 0x19, 0xe0, + 0x00, 0x60, 0x18, 0x00, 0x00, 0x20, 0x10, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x80, 0x00, + 0x00, 0x7c, 0xfc, 0x00, 0x00, 0x70, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + // 30x30 + const unsigned char sunset [] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, + 0x01, 0x80, 0x06, 0x00, 0x00, 0x80, 0x04, 0x00, 0x00, 0x0f, 0xc0, 0x00, 0x00, 0x1c, 0xe0, 0x00, + 0x00, 0x30, 0x30, 0x00, 0x00, 0x20, 0x10, 0x00, 0x00, 0x60, 0x18, 0x00, 0x1e, 0x60, 0x19, 0xe0, + 0x00, 0x60, 0x18, 0x00, 0x00, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x78, 0x78, 0x00, 0x00, 0x7c, 0xf8, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + const unsigned char noon [] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xe0, 0x00, 0x00, 0x73, 0x38, 0x00, 0x00, 0xc3, 0x0c, 0x00, + 0x01, 0x83, 0x06, 0x00, 0x03, 0x03, 0x03, 0x00, 0x02, 0x03, 0x01, 0x00, 0x06, 0x03, 0x01, 0x80, + 0x06, 0x03, 0x00, 0x80, 0x04, 0x03, 0x00, 0x80, 0x04, 0x00, 0x00, 0x80, 0x04, 0x00, 0x00, 0x80, + 0x04, 0x00, 0x00, 0x80, 0x06, 0x00, 0x01, 0x80, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x03, 0x00, + 0x01, 0x80, 0x06, 0x00, 0x00, 0xc0, 0x0c, 0x00, 0x00, 0x60, 0x18, 0x00, 0x00, 0x3f, 0xf0, 0x00, + 0x00, 0x07, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + // 50x50 const unsigned char day_clear[] PROGMEM = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -96,7 +133,7 @@ namespace Icons 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - const unsigned char night_clear [] PROGMEM = { + const unsigned char night_clear [] PROGMEM = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, diff --git a/src/WatchFacePages/Weather.cpp b/src/WatchFacePages/Weather.cpp index b023be4..bcbf2a4 100644 --- a/src/WatchFacePages/Weather.cpp +++ b/src/WatchFacePages/Weather.cpp @@ -6,13 +6,30 @@ #include #include #include +#include +#include +#include RTC_DATA_ATTR uint64_t WatchFacePages::Weather::m_lastSyncTime = 0; RTC_DATA_ATTR int WatchFacePages::Weather::m_lastCalculatedDay = 0XFFFFFFFF; -RTC_DATA_ATTR float WatchFacePages::Weather::m_locationLat = DEFAULT_LATITUDE; -RTC_DATA_ATTR float WatchFacePages::Weather::m_locationLon = DEFAULT_LONGITUDE; +RTC_DATA_ATTR double WatchFacePages::Weather::m_locationLat = DEFAULT_LATITUDE; +RTC_DATA_ATTR double WatchFacePages::Weather::m_locationLon = DEFAULT_LONGITUDE; RTC_DATA_ATTR char WatchFacePages::Weather::m_locationCity[128]; +RTC_DATA_ATTR uint8_t WatchFacePages::Weather::m_sunriseHour; +RTC_DATA_ATTR uint8_t WatchFacePages::Weather::m_sunriseMinute; +RTC_DATA_ATTR uint8_t WatchFacePages::Weather::m_noonHour; +RTC_DATA_ATTR uint8_t WatchFacePages::Weather::m_noonMinute; +RTC_DATA_ATTR uint8_t WatchFacePages::Weather::m_sunsetHour; +RTC_DATA_ATTR uint8_t WatchFacePages::Weather::m_sunsetMinute; +RTC_DATA_ATTR double WatchFacePages::Weather::m_moonPhase; +RTC_DATA_ATTR uint8_t WatchFacePages::Weather::m_moonBitmap[8 * 64]; + +RTC_DATA_ATTR double WatchFacePages::Weather::m_currentTemperature; +RTC_DATA_ATTR float WatchFacePages::Weather::m_currentWindSpeed; +RTC_DATA_ATTR uint8_t WatchFacePages::Weather::m_currentHumidity; +RTC_DATA_ATTR uint8_t WatchFacePages::Weather::m_currentWeatherIcon; + WatchFacePages::Weather::Weather(WatchyDisplay & display, WatchFeatures::WatchFeatures & features) : m_display(display), m_features(features) { @@ -37,20 +54,10 @@ void WatchFacePages::Weather::DrawPage(bool partialRefresh) m_display.fillScreen(GxEPD_WHITE); m_display.setTextColor(GxEPD_BLACK); - if (m_lastSyncTime == 0) { - m_display.setFont(&FreeSans12pt7b); - int16_t x, y; - uint16_t w, h; - m_display.getTextBounds("Have not synced", 0, 0, &x, &y, &w, &h); - m_display.setCursor(DISPLAY_WIDTH / 2 - w / 2, 110); - m_display.print("Have not synced"); - m_display.display(partialRefresh); - return; - } - bool weatherOutdated = false; - if (m_features.rtc.GetTimestamp() - m_lastSyncTime > 86400) { + // If it hasn't synced in 24 hours, assume weather is outdated + if (m_lastSyncTime == 0 || m_features.rtc.GetTimestamp() - m_lastSyncTime > 86400) { weatherOutdated = true; } @@ -60,42 +67,78 @@ void WatchFacePages::Weather::DrawPage(bool partialRefresh) uint16_t w, h; m_display.drawBitmap(5, 5, Icons::city, 29, 23, GxEPD_BLACK); - m_display.setCursor(39, 22); m_display.print(m_locationCity); // Separator m_display.fillRect(10, 35, DISPLAY_WIDTH - 20, 2, GxEPD_BLACK); + // Moon + m_display.drawBitmap(200 - 64 - 10, 45, m_moonBitmap, 64, 64, GxEPD_BLACK); + m_display.drawCircle(200 - 64 - 10 + 32, 45 + 32, 32, GxEPD_BLACK); + + // Sunrise, solar noon and sunset + m_display.drawBitmap(10, 38, Icons::Weather::sunrise, 30, 30, GxEPD_BLACK); + std::ostringstream sunrise; + sunrise << std::setfill('0') << std::setw(2) << (int)m_sunriseHour << ":" << std::setw(2) << (int)m_sunriseMinute; + m_display.setFont(&FreeSans9pt7b); + m_display.setCursor(45, 56); + m_display.setTextColor(GxEPD_BLACK); + m_display.print(sunrise.str().c_str()); + + m_display.drawBitmap(10, 62, Icons::Weather::noon, 30, 30, GxEPD_BLACK); + std::ostringstream noon; + noon << std::setfill('0') << std::setw(2) << (int)m_noonHour << ":" << std::setw(2) << (int)m_noonMinute; + m_display.setCursor(45, 80); + m_display.print(noon.str().c_str()); + + m_display.drawBitmap(10, 87, Icons::Weather::sunset, 30, 30, GxEPD_BLACK); + std::ostringstream sunset; + sunset << std::setfill('0') << std::setw(2) << (int)m_sunsetHour << ":" << std::setw(2) << (int)m_sunsetMinute; + m_display.setFont(&FreeSans9pt7b); + m_display.setCursor(45, 106); + m_display.print(sunset.str().c_str()); + // Temperature and weather icon - // Separator + // Separator m_display.fillRect(10, 116, DISPLAY_WIDTH - 20, 2, GxEPD_BLACK); - m_display.drawBitmap(5, 125, Icons::Weather::day_clear, 75, 75, GxEPD_BLACK); - m_display.setFont(&FreeSans12pt7b); + if (weatherOutdated) { + m_display.setFont(&FreeSans9pt7b); + m_display.getTextBounds("No Weather Data", 0, 0, &x, &y, &w, &h); + m_display.setCursor(DISPLAY_WIDTH / 2 - w / 2, 162); + m_display.print("No Weather Data"); + } else { + m_display.drawBitmap(5, 125, Icons::Weather::day_clear, 75, 75, GxEPD_BLACK); + m_display.setFont(&FreeSans12pt7b); - std::string temperature = "-20.5 C"; + std::ostringstream temperature; + temperature << (int)m_currentTemperature << " C"; - m_display.getTextBounds(temperature.c_str(), 0, 0, &x, &y, &w, &h); - m_display.setCursor(190 - w, 142); - m_display.print(temperature.c_str()); - // Hacky degree symbol - m_display.fillCircle(190 - 23, 129, 3, GxEPD_BLACK); - m_display.fillCircle(190 - 23, 129, 1, GxEPD_WHITE); + m_display.getTextBounds(temperature.str().c_str(), 0, 0, &x, &y, &w, &h); + m_display.setCursor(190 - w, 142); + m_display.print(temperature.str().c_str()); + // Hacky degree symbol + m_display.fillCircle(190 - 23, 129, 3, GxEPD_BLACK); + m_display.fillCircle(190 - 23, 129, 1, GxEPD_WHITE); - // Wind speed - m_display.drawBitmap(165, 145, Icons::Weather::wind, 30, 30, GxEPD_BLACK); - std::string windSpeed = "99 m/s"; - m_display.setFont(&FreeSans9pt7b); - m_display.getTextBounds(windSpeed.c_str(), 0, 0, &x, &y, &w, &h); - m_display.setCursor(160 - w, 165); - m_display.print(windSpeed.c_str()); + // Wind speed + m_display.drawBitmap(165, 145, Icons::Weather::wind, 30, 30, GxEPD_BLACK); + std::ostringstream windSpeed; + windSpeed << std::fixed << std::setprecision(1) << m_currentWindSpeed << " m/s"; - // Humidity - m_display.drawBitmap(165, 170, Icons::Weather::humidity, 30, 30, GxEPD_BLACK); - std::string humidity = "100"; - m_display.getTextBounds(humidity.c_str(), 0, 0, &x, &y, &w, &h); - m_display.setCursor(160 - w, 190); - m_display.print(humidity.c_str()); + m_display.setFont(&FreeSans9pt7b); + m_display.getTextBounds(windSpeed.str().c_str(), 0, 0, &x, &y, &w, &h); + m_display.setCursor(160 - w, 165); + m_display.print(windSpeed.str().c_str()); + + // Humidity + m_display.drawBitmap(165, 170, Icons::Weather::humidity, 30, 30, GxEPD_BLACK); + std::ostringstream humidity; + humidity << (int)m_currentHumidity; + m_display.getTextBounds(humidity.str().c_str(), 0, 0, &x, &y, &w, &h); + m_display.setCursor(160 - w, 190); + m_display.print(humidity.str().c_str()); + } m_display.display(partialRefresh); } @@ -112,10 +155,11 @@ void WatchFacePages::Weather::Resync() return; } - if (DO_GEOLOCATION) { - HTTPClient client; - client.setConnectTimeout(3000); // 3 second max timeout + HTTPClient client; + client.setConnectTimeout(3000); // 3 second max timeout + // Get geolocation from IP + if (DO_GEOLOCATION) { client.begin("http://ip-api.com/line/"); int httpCode = client.GET(); if (httpCode != 200) { @@ -151,7 +195,167 @@ void WatchFacePages::Weather::Resync() m_locationLon = std::stof(lines[8]); } + // https://wttr.in/60.170833,24.9375?0Q&format=+%x:%t:%h + std::ostringstream url; + url << "http://wttr.in/" << m_locationLat << "," << m_locationLon << "?0Q&format=%x:%t:%h:%w"; + // Grr. wttr.in does a redirect to HTTPS if your agent isn't curl + client.setUserAgent("curl/8.0.1"); + + client.begin(url.str().c_str()); + int httpCode = client.GET(); m_features.wifi.Disconnect(); + if (httpCode != 200) { + return; + } + + String payload = client.getString(); + client.end(); + std::string reponse(payload.c_str()); + // Split on : + std::vector parts; + std::string::size_type pos = 0; + std::string::size_type prev = 0; + char delimiter = ':'; + while ((pos = reponse.find(delimiter, prev)) != std::string::npos) { + parts.push_back(reponse.substr(prev, pos - prev)); + prev = pos + 1; + } + + parts.push_back(reponse.substr(prev)); + + if (parts.size() != 4) { + return; + } + + // Unknown, Cloudy, Fog, Heavy Rain, Heavy Showers, Heavy Snow, Heavy Snow Showers, Light Rain, Light Showers, Light Sleet, Light Sleet Showers, Light Snow, Light Snow Showers, Partly Cloudy, Sunny, Thundery Heavy Rain, Thundery Showers, Thundery Snow Showers, Very Cloudy + std::vector iconLookupTable = {"?", "mm", "=", "///", "//", "**", "/*/", "/", ".", "x", "x/", "*", "*/", "m", "o", "/!/", "!/", "*!*", "mmm"}; + std::string iconName = parts[1]; + for (int i = 0; i < iconLookupTable.size(); i++) { + if (iconName == iconLookupTable[i]) { + m_currentWeatherIcon = i; + } + } + + std::string temperature = parts[2].substr(0, parts[2].length() - 2); + m_currentTemperature = std::stoi(temperature); + + std::string humidity = parts[2].substr(0, parts[2].length() - 1); + m_currentHumidity = std::stoi(humidity); + + std::string windSpeed = parts[3].substr(3, parts[3].length() - 7); + float windSpeedKmh = std::stof(windSpeed); + + // Convert to m/s + m_currentWindSpeed = std::round(windSpeedKmh * 0.277778); + + m_features.wifi.Disconnect(); + Recalc(); m_lastSyncTime = currentTime; +} + +void WatchFacePages::Weather::Recalc() +{ + tmElements_t tm; + m_features.rtc.Get(tm); + + if (m_lastCalculatedDay == tm.Day) { + return; + } + + unsigned int dayOfYear = m_features.rtc.GetDayOfYear(tm); + double denominator = tm.Year / 4 ? 366.0 : 365.0; + double fractionalYear = (2.0 * M_PI / denominator) * (dayOfYear - 1 + ((0 - 12.0) / 24.0)); + + double eqtime = 229.18 * (0.000075 + 0.001868 * cos(fractionalYear) - 0.032077 * sin(fractionalYear) - 0.014615 * cos(2.0 * fractionalYear) - 0.040849 * sin(2.0 * fractionalYear)); + double declination = 0.006918 - 0.399912 * cos(fractionalYear) + 0.070257 * sin(fractionalYear) - 0.006758 * cos(2.0 * fractionalYear) + 0.000907 * sin(2.0 * fractionalYear) - 0.002697 * cos(3.0 * fractionalYear) + 0.00148 * sin(3.0 * fractionalYear); + + const double latitudeRadians = (double)m_locationLat * M_PI / 180.0; + const double longitudeRadians = (double)m_locationLon * M_PI / 180.0; + const double sunriseSunsetAngle = 1.5853349200000815; + + double timeOffset = eqtime + 4.0 * (double)m_locationLon - 60.0; + double trueSolarTime = tm.Hour * 60.0 + tm.Minute + tm.Second / 60.0 + timeOffset; + double hourAngle = acos(cos(sunriseSunsetAngle) / (cos(latitudeRadians) * cos(declination)) - tan(latitudeRadians) * tan(declination)); + hourAngle = hourAngle * 180.0 / M_PI; + double sunrise = 720 - 4.0 * ((double)m_locationLon + hourAngle) - eqtime; + double sunset = 720 - 4.0 * ((double)m_locationLon - hourAngle) - eqtime; + double noon = 720 - 4.0 * ((double)m_locationLon) - eqtime; + + sunrise += m_features.storage.GetTzOffset() / 60; + sunset += m_features.storage.GetTzOffset() / 60; + noon += m_features.storage.GetTzOffset() / 60; + + m_sunriseHour = sunrise / 60; + m_sunriseMinute = sunrise - m_sunriseHour * 60; + m_sunsetHour = sunset / 60; + m_sunsetMinute = sunset - m_sunsetHour * 60; + m_noonHour = noon / 60; + m_noonMinute = noon - m_noonHour * 60; + + tmElements_t newMoon; + newMoon.Year = 2023 - 1970; + newMoon.Month = 5; + newMoon.Day = 19; + + unsigned int daysDifference = m_features.rtc.DaysDifference(newMoon, tm); + m_moonPhase = fmod((double)daysDifference, 29.530588853) / 29.530588853; + + // Draw a 1-bit bitmap with the moon phase + bool pixels[64 * 64]; + for (int i = 0; i < 64 * 64; i++) { + pixels[i] = false; + } + + for (int x = 0; x < 64; x++) { + for (int y = 0; y < 64; y++) { + float distance = sqrt(powf(fabsf(x - 32), 2) + powf(fabsf(y - 32), 2)); + if (distance < 32) { + float inclination = asinf(((float)y - 32.0f) / 32.0f); + float azimuth = acosf((float)(x - 32) / (-32.0f * cosf(inclination))); + + azimuth += M_PI; + + azimuth += (m_moonPhase) * M_PI * 2.0f; + while (azimuth > M_PI * 2.0f) { + azimuth -= M_PI * 2.0f; + } + + while (azimuth < 0) { + azimuth += M_PI * 2.0f; + } + + if (azimuth > M_PI) { + pixels[x + y * 64] = true; + } + } else { + pixels[x + y * 64] = false; + } + } + } + + // Needs to be split into two parts because pio does too aggressive optimisation + for (unsigned int y = 0; y < 32; y++) { + for (unsigned int x = 0; x < 8; x++) { + uint8_t value = 0; + for (unsigned int i = 0; i < 8; i++) { + value |= pixels[(x * 8 + i) + (y * 64)] << (7 - i); + } + + m_moonBitmap[x + y * 8] = value; + } + } + + for (unsigned int y = 32; y < 64; y++) { + for (unsigned int x = 0; x < 8; x++) { + uint8_t value = 0; + for (unsigned int i = 0; i < 8; i++) { + value |= pixels[(x * 8 + i) + (y * 64)] << (7 - i); + } + + m_moonBitmap[x + y * 8] = value; + } + } + + m_lastCalculatedDay = tm.Day; } \ No newline at end of file diff --git a/src/WatchFacePages/Weather.h b/src/WatchFacePages/Weather.h index 29720f6..7c8ebee 100644 --- a/src/WatchFacePages/Weather.h +++ b/src/WatchFacePages/Weather.h @@ -22,11 +22,26 @@ public: void Resync(); private: + void Recalc(); // Offline daily ephemeris like sunrise/sunset WatchyDisplay & m_display; WatchFeatures::WatchFeatures & m_features; static RTC_DATA_ATTR uint64_t m_lastSyncTime; static RTC_DATA_ATTR int m_lastCalculatedDay; static RTC_DATA_ATTR char m_locationCity[128]; - static RTC_DATA_ATTR float m_locationLat, m_locationLon; + static RTC_DATA_ATTR double m_locationLat, m_locationLon; + + static RTC_DATA_ATTR uint8_t m_sunriseHour; + static RTC_DATA_ATTR uint8_t m_sunriseMinute; + static RTC_DATA_ATTR uint8_t m_noonHour; + static RTC_DATA_ATTR uint8_t m_noonMinute; + static RTC_DATA_ATTR uint8_t m_sunsetHour; + static RTC_DATA_ATTR uint8_t m_sunsetMinute; + static RTC_DATA_ATTR double m_moonPhase; + static RTC_DATA_ATTR uint8_t m_moonBitmap[8 * 64]; + + static RTC_DATA_ATTR double m_currentTemperature; + static RTC_DATA_ATTR float m_currentWindSpeed; + static RTC_DATA_ATTR uint8_t m_currentHumidity; + static RTC_DATA_ATTR uint8_t m_currentWeatherIcon; }; \ No newline at end of file diff --git a/src/WatchFeatures/RTC.cpp b/src/WatchFeatures/RTC.cpp index cd0aef3..de5048d 100644 --- a/src/WatchFeatures/RTC.cpp +++ b/src/WatchFeatures/RTC.cpp @@ -106,6 +106,49 @@ void WatchFeatures::RTC::OffsetTime(tmElements_t & tm, int offsetInSeconds) breakTime(t, tm); } +unsigned int WatchFeatures::RTC::GetDayOfYear(tmElements_t & tm) +{ + unsigned int dayOfYear = 1; + for (int i = 1; i < tm.Month; i++) { + switch (i) { + case 2: + dayOfYear += (tm.Year % 4 == 0) ? 29 : 28; + break; + case 4: + case 6: + case 9: + case 11: + dayOfYear += 30; + break; + default: + dayOfYear += 31; + break; + } + } + + dayOfYear += tm.Day; + return dayOfYear; +} + +unsigned int WatchFeatures::RTC::DaysDifference(tmElements_t & tm1, tmElements_t & tm2) +{ + unsigned int dayOfYear1 = GetDayOfYear(tm1); + unsigned int dayOfYear2 = GetDayOfYear(tm2); + unsigned int days = 0; + + if (tm1.Year == tm2.Year) { + days = dayOfYear2 - dayOfYear1; + } else { + days = 365 - dayOfYear1 + dayOfYear2; + for (int i = tm1.Year + 1; i < tm2.Year; i++) { + days += (i % 4 == 0) ? 366 : 365; + } + } + + return days; +} + + // TODO: implement more advanced wakeup logic, i.e. > 255 seconds that are not a multiple of 60 bool WatchFeatures::RTC::CheckWakeup() { diff --git a/src/WatchFeatures/RTC.h b/src/WatchFeatures/RTC.h index 66110dd..a22fa37 100644 --- a/src/WatchFeatures/RTC.h +++ b/src/WatchFeatures/RTC.h @@ -22,6 +22,8 @@ public: void Resync(); // Resync the timer cycle, both initially and after RTC resync static void OffsetTime(tmElements_t & tm, int offsetInSeconds); + static unsigned int GetDayOfYear(tmElements_t & tm); + static unsigned int DaysDifference(tmElements_t & tm1, tmElements_t & tm2); private: Rtc_Pcf8563 m_rtcPcf;