diff --git a/lib/obp60task/OBP60Extensions.cpp b/lib/obp60task/OBP60Extensions.cpp index c589feb..3f47b9d 100644 --- a/lib/obp60task/OBP60Extensions.cpp +++ b/lib/obp60task/OBP60Extensions.cpp @@ -421,32 +421,45 @@ void displayHeader(CommonData &commonData, GwApi::BoatValue *date, GwApi::BoatVa heartbeat = !heartbeat; // Date and time - getdisplay().setTextColor(commonData.fgcolor); + String fmttype = commonData.config->getString(commonData.config->dateFormat); + String timesource = commonData.config->getString(commonData.config->timeSource); + int tz = commonData.config->getInt(commonData.config->timeZone); + getdisplay().setTextColor(textcolor); getdisplay().setFont(&Ubuntu_Bold8pt7b); getdisplay().setCursor(230, 15); - // Show date and time if date present - if(date->valid == true){ - String acttime = formatValue(time, commonData).svalue; - acttime = acttime.substring(0, 5); - String actdate = formatValue(date, commonData).svalue; - getdisplay().print(acttime); + if (timesource == "RTC") { + time_t tv = mktime(&commonData.data.rtcTime) + tz * 3600; + struct tm *local_tm = localtime(&tv); + getdisplay().print(formatTime('m', local_tm->tm_hour, local_tm->tm_min, 0)); getdisplay().print(" "); - getdisplay().print(actdate); + getdisplay().print(formatDate(fmttype, local_tm->tm_year + 1900, local_tm->tm_mon + 1, local_tm->tm_mday)); getdisplay().print(" "); - if(commonData.config->getInt(commonData.config->timeZone) == 0){ - getdisplay().print("UTC"); - } - else{ - getdisplay().print("LOT"); - } } - else{ - if(commonData.config->getBool(commonData.config->useSimuData) == true){ - getdisplay().print("12:00 01.01.2024 LOT"); + else { // timesource == "GPS" + // Show date and time if date present + if(date->valid == true){ + String acttime = formatValue(time, commonData).svalue; + acttime = acttime.substring(0, 5); + String actdate = formatValue(date, commonData).svalue; + getdisplay().print(acttime); + getdisplay().print(" "); + getdisplay().print(actdate); + getdisplay().print(" "); } else{ - getdisplay().print("No GPS data"); + if(commonData.config->getBool(commonData.config->useSimuData) == true){ + getdisplay().print("12:00 01.01.2024 LOT"); + } + else{ + getdisplay().print("No GPS data"); + } } + } // timesource == "GPS" + if (tz == 0) { + getdisplay().print("UTC"); + } + else { + getdisplay().print("LOT"); } } } diff --git a/lib/obp60task/OBP60Formater.cpp b/lib/obp60task/OBP60Formater.cpp index 54e9fb9..5b9745d 100644 --- a/lib/obp60task/OBP60Formater.cpp +++ b/lib/obp60task/OBP60Formater.cpp @@ -8,6 +8,35 @@ // simulation data // hold values by missing data +String formatDate(String fmttype, uint16_t year, uint8_t month, uint8_t day) { + char buffer[12]; + if (fmttype == "GB") { + snprintf(buffer, 12, "%02d/%02d/%04d", day , month, year); + } + else if (fmttype == "US") { + snprintf(buffer, 12, "%02d/%02d/%04d", month, day, year); + } + else if (fmttype == "ISO") { + snprintf(buffer, 12, "%04d-%02d-%02d", year, month, day); + } + else { + snprintf(buffer, 12, "%02d.%02d.%04d", day, month, year); + } + return String(buffer); +} + +String formatTime(char fmttype, uint8_t hour, uint8_t minute, uint8_t second) { + // fmttype: s: with seconds, m: only minutes + char buffer[10]; + if (fmttype == 'm') { + snprintf(buffer, 10, "%02d:%02d", hour , minute); + } + else { + snprintf(buffer, 10, "%02d:%02d:%02d", hour, minute, second); + } + return String(buffer); +} + FormatedData formatValue(GwApi::BoatValue *value, CommonData &commondata){ GwLog *logger = commondata.logger; FormatedData result; diff --git a/lib/obp60task/OBPSensorTask.cpp b/lib/obp60task/OBPSensorTask.cpp index a6d112c..d066e33 100644 --- a/lib/obp60task/OBPSensorTask.cpp +++ b/lib/obp60task/OBPSensorTask.cpp @@ -428,38 +428,40 @@ void sensorTask(void *param){ loopCounter++; } - // If GPS not ready or installed then send RTC time on bus all 500ms - if(millis() > starttime12 + 500){ + // Get current RTC date and time all 500ms + if (millis() > starttime12 + 500) { starttime12 = millis(); - if((rtcOn == "DS1388" && RTC_ready == true && GPS_ready == false) || (rtcOn == "DS1388" && RTC_ready == true && GPS_ready == true && hdop->valid == false)){ - // Convert RTC time to Unix system time - // https://de.wikipedia.org/wiki/Unixzeit - const short daysOfYear[12] = {0,31,59,90,120,151,181,212,243,273,304,334}; - long unixtime = ds1388.now().get(); - uint16_t year = ds1388.now().year(); - uint8_t month = ds1388.now().month(); - uint8_t hour = ds1388.now().hour(); - uint8_t minute = ds1388.now().minute(); - uint8_t second = ds1388.now().second(); - uint8_t day = ds1388.now().day(); - uint16_t switchYear = ((year-1)-1968)/4 - ((year-1)-1900)/100 + ((year-1)-1600)/400; - long daysAt1970 = (year-1970)*365 + switchYear + daysOfYear[month-1] + day-1; - // If switch year then add one day - if ( (month>2) && (year%4==0 && (year%100!=0 || year%400==0)) ){ - daysAt1970 += 1; - } - double sysTime = (hour * 3600) + (minute * 60) + second; - if(!isnan(daysAt1970) && !isnan(sysTime)){ - sensors.rtcYear = year; // Save values in SensorData - sensors.rtcMonth = month; - sensors.rtcDay = day; - sensors.rtcHour = hour; - sensors.rtcMinute = minute; - sensors.rtcSecond = second; - // api->getLogger()->logDebug(GwLog::LOG,"RTC time: %04d/%02d/%02d %02d:%02d:%02d",year, month, day, hour, minute, second); - // api->getLogger()->logDebug(GwLog::LOG,"Send PGN126992: %10d %10d",daysAt1970, (uint16_t)sysTime); - SetN2kPGN126992(N2kMsg,0,daysAt1970,sysTime,N2ktimes_LocalCrystalClock); - api->sendN2kMessage(N2kMsg); + if (rtcOn == "DS1388" && RTC_ready) { + sensors.rtcTime.tm_year = ds1388.now().year() - 1900; // Save values in SensorData + sensors.rtcTime.tm_mon = ds1388.now().month() - 1; + sensors.rtcTime.tm_mday = ds1388.now().day(); + sensors.rtcTime.tm_hour = ds1388.now().hour(); + sensors.rtcTime.tm_min = ds1388.now().minute(); + sensors.rtcTime.tm_sec = ds1388.now().second(); + sensors.rtcTime.tm_isdst = 0; // Not considering daylight saving time + + // If GPS not ready or installed then send RTC time on bus + if ((GPS_ready == false) || (GPS_ready == true && hdop->valid == false)) { + // Convert RTC time to Unix system time + // https://de.wikipedia.org/wiki/Unixzeit + const short daysOfYear[12] = {0,31,59,90,120,151,181,212,243,273,304,334}; + // unused: long unixtime = ds1388.now().get(); + uint16_t switchYear = ((sensors.rtcTime.tm_year-1)-1968)/4 - ((sensors.rtcTime.tm_year-1)-1900)/100 + ((sensors.rtcTime.tm_year-1)-1600)/400; + long daysAt1970 = (sensors.rtcTime.tm_year-1970)*365 + switchYear + daysOfYear[sensors.rtcTime.tm_mon-1] + sensors.rtcTime.tm_mday-1; + // If switch year then add one day + if ((sensors.rtcTime.tm_mon > 2) && (sensors.rtcTime.tm_year % 4 == 0 + && (sensors.rtcTime.tm_year % 100 != 0 || sensors.rtcTime.tm_year % 400 == 0))) { + daysAt1970 += 1; + } + // N2K sysTime ist double in n2klib + double sysTime = (sensors.rtcTime.tm_hour * 3600) + (sensors.rtcTime.tm_min * 60) + sensors.rtcTime.tm_sec; + // WHY? isnan should always fail here + //if(!isnan(daysAt1970) && !isnan(sysTime)){ + // api->getLogger()->logDebug(GwLog::LOG,"RTC time: %04d/%02d/%02d %02d:%02d:%02d",year, month, day, hour, minute, second); + // api->getLogger()->logDebug(GwLog::LOG,"Send PGN126992: %10d %10d",daysAt1970, (uint16_t)sysTime); + SetN2kPGN126992(N2kMsg,0,daysAt1970,sysTime,N2ktimes_LocalCrystalClock); + api->sendN2kMessage(N2kMsg); + // } } } } diff --git a/lib/obp60task/PageClock.cpp b/lib/obp60task/PageClock.cpp index 906956a..3081525 100644 --- a/lib/obp60task/PageClock.cpp +++ b/lib/obp60task/PageClock.cpp @@ -3,10 +3,25 @@ #include "Pagedata.h" #include "OBP60Extensions.h" +/* + * TODO mode: race timer: keys + * - prepare: set countdown to 5min + * reset: abort current countdown and start over with 5min preparation + * - 5min: key press + * - 4min: key press to sync + * - 1min: buzzer signal + * - start: buzzer signal for start + * + */ + class PageClock : public Page { - bool simulation = false; - int simtime; +bool simulation = false; +int simtime; +bool keylock = false; +char source = 'R'; // time source (R)TC | (G)PS +char mode = 'A'; // display mode (A)nalog | (D)igital | race (T)imer +char tz = 'L'; // time zone (L)ocal | (U)TC public: PageClock(CommonData &common){ @@ -18,9 +33,38 @@ class PageClock : public Page // Key functions virtual int handleKey(int key){ - // Code for keylock - if(key == 11){ - commonData->keylock = !commonData->keylock; + // Time source + if (key == 1) { + if (source == 'G') { + source = 'R'; + } else { + source = 'G'; + } + return 0; + } + if (key == 2) { + if (mode == 'A') { + mode = 'D'; + } else if (mode == 'D') { + mode = 'T'; + } else { + mode = 'A'; + } + return 0; + } + // Time zone: Local / UTC + if (key == 5) { + if (tz == 'L') { + tz = 'U'; + } else { + tz = 'L'; + } + return 0; + } + + // Keylock function + if(key == 11){ // Code for keylock + keylock = !keylock; // Toggle keylock return 0; // Commit the key } return key; @@ -47,6 +91,8 @@ class PageClock : public Page // Get config data String lengthformat = config->getString(config->lengthFormat); + String dateformat = config->getString(config->dateFormat); + bool simulation = config->getBool(config->useSimuData); bool holdvalues = config->getBool(config->holdvalues); String flashLED = config->getString(config->flashLED); String backlightMode = config->getString(config->backlight); @@ -100,7 +146,7 @@ class PageClock : public Page // Optical warning by limit violation (unused) if(String(flashLED) == "Limit Violation"){ setBlinkingLED(false); - setFlashLED(false); + setFlashLED(false); } // Logging boat values @@ -115,11 +161,28 @@ class PageClock : public Page getdisplay().setTextColor(commonData->fgcolor); + time_t tv = mktime(&commonData.data.rtcTime) + timezone * 3600; + struct tm *local_tm = localtime(&tv); + // Show values GPS date getdisplay().setFont(&Ubuntu_Bold8pt7b); getdisplay().setCursor(10, 65); - if(holdvalues == false) getdisplay().print(svalue2); // Value - else getdisplay().print(svalue2old); + if (holdvalues == false) { + if (source == 'G') { + // GPS value + getdisplay().print(svalue2); + } else { + // RTC value + if (tz == 'L') { + getdisplay().print(formatDate(dateformat, local_tm->tm_year + 1900, local_tm->tm_mon + 1, local_tm->tm_mday)); + } + else { + getdisplay().print(formatDate(dateformat, commonData.data.rtcTime.tm_year + 1900, commonData.data.rtcTime.tm_mon + 1, commonData.data.rtcTime.tm_mday)); + } + } + } else { + getdisplay().print(svalue2old); + } getdisplay().setFont(&Ubuntu_Bold12pt7b); getdisplay().setCursor(10, 95); getdisplay().print("Date"); // Name @@ -130,8 +193,22 @@ class PageClock : public Page // Show values GPS time getdisplay().setFont(&Ubuntu_Bold8pt7b); getdisplay().setCursor(10, 250); - if(holdvalues == false) getdisplay().print(svalue1); // Value - else getdisplay().print(svalue1old); + if (holdvalues == false) { + if (source == 'G') { + getdisplay().print(svalue1); // Value + } + else { + if (tz == 'L') { + getdisplay().print(formatTime('s', local_tm->tm_hour, local_tm->tm_min, local_tm->tm_sec)); + } + else { + getdisplay().print(formatTime('s', commonData.data.rtcTime.tm_hour, commonData.data.rtcTime.tm_min, commonData.data.rtcTime.tm_sec)); + } + } + } + else { + getdisplay().print(svalue1old); + } getdisplay().setFont(&Ubuntu_Bold12pt7b); getdisplay().setCursor(10, 220); getdisplay().print("Time"); // Name @@ -174,7 +251,7 @@ class PageClock : public Page getdisplay().print("SunS"); // Name //******************************************************************************************* - + // Draw clock int rInstrument = 110; // Radius of clock float pi = 3.141592; @@ -246,24 +323,53 @@ class PageClock : public Page getdisplay().setFont(&Ubuntu_Bold12pt7b); getdisplay().setCursor(175, 110); if(holdvalues == false){ - getdisplay().print(unit2); // Unit + if (tz == 'L') { + getdisplay().print(unit2); // Unit + } else { + getdisplay().print("UTC"); + } } else{ getdisplay().print(unit2old); // Unit } + getdisplay().setFont(&Ubuntu_Bold8pt7b); + getdisplay().setCursor(185, 190); + if (source == 'G') { + getdisplay().print("GPS"); + } else { + getdisplay().print("RTC"); + } + // Clock values double hour = 0; double minute = 0; - value1 = value1 + int(timezone*3600); - if (value1 > 86400) {value1 = value1 - 86400;} - if (value1 < 0) {value1 = value1 + 86400;} - hour = (value1 / 3600.0); - if(hour > 12) hour = hour - 12.0; -// minute = (hour - int(hour)) * 3600.0 / 60.0; // Analog minute pointer smooth moving - minute = int((hour - int(hour)) * 3600.0 / 60.0); // Jumping minute pointer from minute to minute + if (source == 'R') { + if (tz == 'L') { + time_t tv = mktime(&commonData.data.rtcTime) + timezone * 3600; + struct tm *local_tm = localtime(&tv); + minute = local_tm->tm_min; + hour = local_tm->tm_hour; + } else { + minute = commonData.data.rtcTime.tm_min; + hour = commonData.data.rtcTime.tm_hour; + } + hour += minute / 60; + } else { + if (tz == 'L') { + value1 += int(timezone*3600); + } + if (value1 > 86400) {value1 = value1 - 86400;} + if (value1 < 0) {value1 = value1 + 86400;} + hour = (value1 / 3600.0); + // minute = (hour - int(hour)) * 3600.0 / 60.0; // Analog minute pointer smooth moving + minute = int((hour - int(hour)) * 3600.0 / 60.0); // Jumping minute pointer from minute to minute + } + if (hour > 12) { + hour -= 12.0; + } LOG_DEBUG(GwLog::DEBUG,"... PageClock, value1: %f hour: %f minute:%f", value1, hour, minute); - + // Draw hour pointer float startwidth = 8; // Start width of pointer if(valid1 == true || holdvalues == true || simulation == true){ @@ -274,7 +380,7 @@ class PageClock : public Page float xx1 = -startwidth; float xx2 = startwidth; float yy1 = -startwidth; - float yy2 = -(rInstrument * 0.5); + float yy2 = -(rInstrument * 0.5); getdisplay().fillTriangle(200+(int)(cosx*xx1-sinx*yy1),150+(int)(sinx*xx1+cosx*yy1), 200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1), 200+(int)(cosx*0-sinx*yy2),150+(int)(sinx*0+cosx*yy2),commonData->fgcolor); @@ -300,7 +406,7 @@ class PageClock : public Page float xx1 = -startwidth; float xx2 = startwidth; float yy1 = -startwidth; - float yy2 = -(rInstrument - 15); + float yy2 = -(rInstrument - 15); getdisplay().fillTriangle(200+(int)(cosx*xx1-sinx*yy1),150+(int)(sinx*xx1+cosx*yy1), 200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1), 200+(int)(cosx*0-sinx*yy2),150+(int)(sinx*0+cosx*yy2),commonData->fgcolor); @@ -320,6 +426,28 @@ class PageClock : public Page getdisplay().fillCircle(200, 150, startwidth + 6, commonData->bgcolor); getdisplay().fillCircle(200, 150, startwidth + 4, commonData->fgcolor); +//******************************************************************************************* + // Key Layout + getdisplay().setFont(&Ubuntu_Bold8pt7b); + if(keylock == false){ + getdisplay().setCursor(10, 290); + getdisplay().print("[SRC]"); + getdisplay().setCursor(60, 290); + getdisplay().print("[MODE]"); + getdisplay().setCursor(293, 290); + getdisplay().print("[TZ]"); + getdisplay().setCursor(130, 290); + getdisplay().print("[ <<<< " + String(commonData.data.actpage) + "/" + String(commonData.data.maxpage) + " >>>> ]"); + if(String(backlightMode) == "Control by Key"){ // Key for illumination + getdisplay().setCursor(343, 290); + getdisplay().print("[ILUM]"); + } + } + else{ + getdisplay().setCursor(130, 290); + getdisplay().print(" [ Keylock active ]"); + } + // Update display getdisplay().nextPage(); // Partial update (fast) diff --git a/lib/obp60task/Pagedata.h b/lib/obp60task/Pagedata.h index b979b57..0988d01 100644 --- a/lib/obp60task/Pagedata.h +++ b/lib/obp60task/Pagedata.h @@ -43,14 +43,9 @@ typedef struct{ double airHumidity = 0; double airPressure = 0; double onewireTemp[8] = {0,0,0,0,0,0,0,0}; - double rotationAngle = 0; // Rotation angle in radiant - bool validRotAngle = false; // Valid flag magnet present for rotation sensor - int rtcYear = 0; // UTC time - int rtcMonth = 0; - int rtcDay = 0; - int rtcHour = 0; - int rtcMinute = 0; - int rtcSecond = 0; + double rotationAngle = 0; // Rotation angle in radiant + bool validRotAngle = false; // Valid flag magnet present for rotation sensor + struct tm rtcTime; // UTC time from internal RTC int sunsetHour = 0; int sunsetMinute = 0; int sunriseHour = 0; @@ -169,13 +164,16 @@ class PageStruct{ PageDescription *description=NULL; }; -// Structure for formated boat values +// Standard format functions without overhead +String formatDate(String fmttype, uint16_t year, uint8_t month, uint8_t day); +String formatTime(char fmttype, uint8_t hour, uint8_t minute, uint8_t second); + +// Structure for formatted boat values typedef struct{ double value; String svalue; String unit; } FormatedData; - -// Formater for boat values +// Formatter for boat values FormatedData formatValue(GwApi::BoatValue *value, CommonData &commondata); diff --git a/lib/obp60task/config.json b/lib/obp60task/config.json index 14d313a..eaf0be4 100644 --- a/lib/obp60task/config.json +++ b/lib/obp60task/config.json @@ -690,6 +690,21 @@ "obp60":"true" } }, + { + "name": "timeSource", + "label": "Status Time Source", + "type": "list", + "default": "GPS", + "description": "Data source for date and time display in status line [RTC|GPS]", + "list": [ + {"l":"Internal real time clock (RTC)","v":"RTC"}, + {"l":"External time via bus (GPS)","v":"GPS"} + ], + "category": "OBP60 Display", + "capabilities": { + "obp60":"true" + } + }, { "name": "refresh", "label": "Refresh",