From 00a8aff8ca15765ee7cbd78b263074e4234bf6c7 Mon Sep 17 00:00:00 2001 From: Thomas Hooge Date: Mon, 10 Feb 2025 13:09:43 +0100 Subject: [PATCH] Fix OBP60 build and add sunrise/sunset feature for stationary OBP40 --- lib/obp60task/OBP60Extensions.cpp | 37 +++++++++++++++++++++++++---- lib/obp60task/OBP60Extensions.h | 3 ++- lib/obp60task/PageClock.cpp | 10 ++++++-- lib/obp60task/config.json | 39 +++++++++++++++++++++++++++++++ lib/obp60task/config_obp40.json | 28 ++++++++++++++++++++++ lib/obp60task/obp60task.cpp | 16 +++++++++++-- lib/obp60task/platformio.ini | 5 ++-- 7 files changed, 127 insertions(+), 11 deletions(-) diff --git a/lib/obp60task/OBP60Extensions.cpp b/lib/obp60task/OBP60Extensions.cpp index d8598ec..83e9ed6 100644 --- a/lib/obp60task/OBP60Extensions.cpp +++ b/lib/obp60task/OBP60Extensions.cpp @@ -545,8 +545,7 @@ void displayFooter(CommonData &commonData) { } // Sunset und sunrise calculation -SunData calcSunsetSunrise(GwApi *api, double time, double date, double latitude, double longitude, double timezone){ - GwLog *logger=api->getLogger(); +SunData calcSunsetSunrise(double time, double date, double latitude, double longitude, float timezone){ SunData returnset; SunRise sr; int secPerHour = 3600; @@ -560,8 +559,7 @@ SunData calcSunsetSunrise(GwApi *api, double time, double date, double latitude, if (!isnan(time) && !isnan(date) && !isnan(latitude) && !isnan(longitude) && !isnan(timezone)) { // Calculate local epoch - t = (date * secPerYear) + time; - // api->getLogger()->logDebug(GwLog::DEBUG,"... calcSun: Lat %f, Lon %f, at: %d ", latitude, longitude, t); + t = (date * secPerYear) + time; sr.calculate(latitude, longitude, t); // LAT, LON, EPOCH // Sunrise if (sr.hasRise) { @@ -584,6 +582,37 @@ SunData calcSunsetSunrise(GwApi *api, double time, double date, double latitude, return returnset; } +SunData calcSunsetSunriseRTC(struct tm *rtctime, double latitude, double longitude, float timezone) { + SunData returnset; + SunRise sr; + const int secPerHour = 3600; + const int secPerYear = 86400; + sr.hasRise = false; + sr.hasSet = false; + time_t t = mktime(rtctime) + timezone * 3600;; + time_t sunR = 0; + time_t sunS = 0; + + sr.calculate(latitude, longitude, t); // LAT, LON, EPOCH + // Sunrise + if (sr.hasRise) { + sunR = (sr.riseTime + int(timezone * secPerHour) + 30) % secPerYear; // add 30 seconds: round to minutes + returnset.sunriseHour = int (sunR / secPerHour); + returnset.sunriseMinute = int((sunR - returnset.sunriseHour * secPerHour) / 60); + } + // Sunset + if (sr.hasSet) { + sunS = (sr.setTime + int(timezone * secPerHour) + 30) % secPerYear; // add 30 seconds: round to minutes + returnset.sunsetHour = int (sunS / secPerHour); + returnset.sunsetMinute = int((sunS - returnset.sunsetHour * secPerHour) / 60); + } + // Sun control (return value by sun on sky = false, sun down = true) + if ((t >= sr.riseTime) && (t <= sr.setTime)) + returnset.sunDown = false; + else returnset.sunDown = true; + return returnset; +} + // Battery graphic with fill level void batteryGraphic(uint x, uint y, float percent, int pcolor, int bcolor){ // Show battery diff --git a/lib/obp60task/OBP60Extensions.h b/lib/obp60task/OBP60Extensions.h index 4118623..b878d00 100644 --- a/lib/obp60task/OBP60Extensions.h +++ b/lib/obp60task/OBP60Extensions.h @@ -97,7 +97,8 @@ void displayTrendLow(int16_t x, int16_t y, uint16_t size, uint16_t color); void displayHeader(CommonData &commonData, GwApi::BoatValue *date, GwApi::BoatValue *time, GwApi::BoatValue *hdop); // Draw display header void displayFooter(CommonData &commonData); -SunData calcSunsetSunrise(GwApi *api, double time, double date, double latitude, double longitude, double timezone); // Calulate sunset and sunrise +SunData calcSunsetSunrise(double time, double date, double latitude, double longitude, float timezone); // Calulate sunset and sunrise +SunData calcSunsetSunriseRTC(struct tm *rtctime, double latitude, double longitude, float timezone); void batteryGraphic(uint x, uint y, float percent, int pcolor, int bcolor); // Battery graphic with fill level void solarGraphic(uint x, uint y, int pcolor, int bcolor); // Solar graphic diff --git a/lib/obp60task/PageClock.cpp b/lib/obp60task/PageClock.cpp index fcdace1..b3cea6b 100644 --- a/lib/obp60task/PageClock.cpp +++ b/lib/obp60task/PageClock.cpp @@ -23,6 +23,9 @@ char source = 'R'; // time source (R)TC | (G)PS | (N)TP char mode = 'A'; // display mode (A)nalog | (D)igital | race (T)imer char tz = 'L'; // time zone (L)ocal | (U)TC double timezone = 0; // there are timezones with non int offsets, e.g. 5.5 or 5.75 +double homelat; +double homelon; +bool homevalid = false; // homelat and homelon are valid public: PageClock(CommonData &common){ @@ -30,6 +33,9 @@ double timezone = 0; // there are timezones with non int offsets, e.g. 5.5 or 5. common.logger->logDebug(GwLog::LOG,"Instantiate PageClock"); simulation = common.config->getBool(common.config->useSimuData); timezone = common.config->getString(common.config->timeZone).toDouble(); + homelat = common.config->getString(common.config->homeLAT).toDouble(); + homelon = common.config->getString(common.config->homeLON).toDouble(); + homevalid = homelat >= -180.0 and homelat <= 180 and homelon >= -90.0 and homelon <= 90.0; simtime = 38160; // time value 11:36 } @@ -225,7 +231,7 @@ double timezone = 0; // there are timezones with non int offsets, e.g. 5.5 or 5. // Show values sunrise String sunrise = "---"; - if(valid1 == true && valid2 == true && valid3 == true){ + if ((valid1 and valid2 and valid3 == true) or (homevalid and commonData->data.rtcValid)) { sunrise = String(commonData->sundata.sunriseHour) + ":" + String(commonData->sundata.sunriseMinute + 100).substring(1); svalue5old = sunrise; } else if (simulation) { @@ -245,7 +251,7 @@ double timezone = 0; // there are timezones with non int offsets, e.g. 5.5 or 5. // Show values sunset String sunset = "---"; - if(valid1 == true && valid2 == true && valid3 == true){ + if ((valid1 and valid2 and valid3 == true) or (homevalid and commonData->data.rtcValid)) { sunset = String(commonData->sundata.sunsetHour) + ":" + String(commonData->sundata.sunsetMinute + 100).substring(1); svalue6old = sunset; } else if (simulation) { diff --git a/lib/obp60task/config.json b/lib/obp60task/config.json index 62a10d2..fe50dea 100644 --- a/lib/obp60task/config.json +++ b/lib/obp60task/config.json @@ -8,6 +8,17 @@ "description": "system name, used for the access point and for services", "category": "system" }, + { + "name": "timeServer", + "label": "time server", + "type": "string", + "default": "pool.ntp.org", + "description": "NTP time server. Use only one hostname or IP address", + "category": "wifi client", + "capabilities": { + "obp40": "true" + } + }, { "name": "timeZone", "label": "Time Zone", @@ -22,6 +33,34 @@ "obp60":"true" } }, + { + "name": "homeLAT", + "label": "Home latitude", + "type": "number", + "default": "", + "check": "checkMinMax", + "min": -90.0, + "max": 90.0, + "description": "Latitude of boat home location [-90.0...+90.0]", + "category": "OBP60 Settings", + "capabilities": { + "obp60":"true" + } + }, + { + "name": "homeLON", + "label": "Home longitude", + "type": "number", + "default": "", + "check": "checkMinMax", + "min": -180.0, + "max": 180.0, + "description": "Longitude of boat home location [-90.0...+90.0]", + "category": "OBP60 Settings", + "capabilities": { + "obp60":"true" + } + }, { "name": "draft", "label": "Boat Draft [m]", diff --git a/lib/obp60task/config_obp40.json b/lib/obp60task/config_obp40.json index 9ba6da1..bdb48b3 100644 --- a/lib/obp60task/config_obp40.json +++ b/lib/obp60task/config_obp40.json @@ -33,6 +33,34 @@ "obp40": "true" } }, + { + "name": "homeLAT", + "label": "Home latitude", + "type": "number", + "default": "0.00000", + "check": "checkMinMax", + "min": -90.0, + "max": 90.0, + "description": "Latitude of boat home location [-90.0...+90.0]", + "category": "OBP40 Settings", + "capabilities": { + "obp40": "true" + } + }, + { + "name": "homeLON", + "label": "Home longitude", + "type": "number", + "default": "0.00000", + "check": "checkMinMax", + "min": -180.0, + "max": 180.0, + "description": "Longitude of boat home location [-90.0...+90.0]", + "category": "OBP40 Settings", + "capabilities": { + "obp40": "true" + } + }, { "name": "draft", "label": "Boat Draft [m]", diff --git a/lib/obp60task/obp60task.cpp b/lib/obp60task/obp60task.cpp index d92454b..8a65aac 100644 --- a/lib/obp60task/obp60task.cpp +++ b/lib/obp60task/obp60task.cpp @@ -548,7 +548,7 @@ void OBP60Task(GwApi *api){ // Configuration values for main loop String gpsFix = api->getConfig()->getConfigItem(api->getConfig()->flashLED,true)->asString(); String gpsOn=api->getConfig()->getConfigItem(api->getConfig()->useGPS,true)->asString(); - String tz = api->getConfig()->getConfigItem(api->getConfig()->timeZone,true)->asString(); + float tz = api->getConfig()->getConfigItem(api->getConfig()->timeZone,true)->asFloat(); commonData.backlight.mode = backlightMapping(config->getConfigItem(config->backlight,true)->asString()); commonData.backlight.color = colorMapping(config->getConfigItem(config->blColor,true)->asString()); @@ -559,6 +559,15 @@ void OBP60Task(GwApi *api){ String cpuspeed = api->getConfig()->getConfigItem(api->getConfig()->cpuSpeed,true)->asString(); uint hdopAccuracy = uint(api->getConfig()->getConfigItem(api->getConfig()->hdopAccuracy,true)->asInt()); + double homelat = commonData.config->getString(commonData.config->homeLAT).toDouble(); + double homelon = commonData.config->getString(commonData.config->homeLON).toDouble(); + bool homevalid = homelat >= -180.0 and homelat <= 180 and homelon >= -90.0 and homelon <= 90.0; + if (homevalid) { + LOG_DEBUG(GwLog::LOG, "Home location set to %f : %f", homelat, homelon); + } else { + LOG_DEBUG(GwLog::LOG, "No valid home location found"); + } + // refreshmode defined in init section // Boat values for main loop @@ -708,7 +717,7 @@ void OBP60Task(GwApi *api){ starttime5 = millis(); if(time->valid == true && date->valid == true && lat->valid == true && lon->valid == true){ // Provide sundata to all pages - commonData.sundata = calcSunsetSunrise(api, time->value , date->value, lat->value, lon->value, tz.toDouble()); + commonData.sundata = calcSunsetSunrise(time->value , date->value, lat->value, lon->value, tz); // Backlight with sun control if (commonData.backlight.mode == BacklightMode::SUN) { // if(String(backlight) == "Control by Sun"){ @@ -719,6 +728,9 @@ void OBP60Task(GwApi *api){ setBacklightLED(0, COLOR_BLUE); // Backlight LEDs off (blue without britghness) } } + } else if (homevalid and commonData.data.rtcValid) { + // No gps fix but valid home location and time configured + commonData.sundata = calcSunsetSunriseRTC(&commonData.data.rtcTime, homelat, homelon, tz); } } diff --git a/lib/obp60task/platformio.ini b/lib/obp60task/platformio.ini index 5ea19a0..4acd552 100644 --- a/lib/obp60task/platformio.ini +++ b/lib/obp60task/platformio.ini @@ -21,6 +21,7 @@ lib_deps = ${basedeps.lib_deps} Wire SPI + ESP32time esphome/AsyncTCP-esphome@2.0.1 robtillaart/PCF8574@0.3.9 adafruit/Adafruit Unified Sensor @ 1.1.13 @@ -95,8 +96,8 @@ build_flags= -D DISABLE_DIAGNOSTIC_OUTPUT #Disable diagnostic output for GxEPD2 lib -D BOARD_OBP40S3 #Board OBP40 V1.0 with ESP32S3 SKU:DIE07300S (CrowPanel 4.2) -D DISPLAY_GDEY042T81 #new E-Ink display from Waveshare, R10 2.2 ohm - -D LIPO_ACCU_1200 #Hardware extension, LiPo accu 3,7V 1200mAh - -D VOLTAGE_SENSOR #Hardware extension, LiPo voltage sensor with two resistors + #-D LIPO_ACCU_1200 #Hardware extension, LiPo accu 3,7V 1200mAh + #-D VOLTAGE_SENSOR #Hardware extension, LiPo voltage sensor with two resistors ${env.build_flags} upload_port = /dev/ttyUSB0 #OBP40 download via external USB/Serail converter upload_protocol = esptool #firmware upload via USB OTG seriell, by first upload need to set the ESP32-S3 in the upload mode with shortcut GND to Pin27