mirror of
https://github.com/thooge/esp32-nmea2000-obp60.git
synced 2026-02-11 15:13:06 +01:00
Change design for PageClock
This commit is contained in:
@@ -35,17 +35,17 @@ class PageClock : public Page
|
||||
int simtime;
|
||||
bool keylock = false;
|
||||
#ifdef BOARD_OBP60S3
|
||||
char source = 'G'; // time source (R)TC | (G)PS | (N)TP
|
||||
char source = 'G'; // Time source (R)TC | (G)PS | (N)TP
|
||||
#endif
|
||||
#ifdef BOARD_OBP40S3
|
||||
char source = 'R'; // time source (R)TC | (G)PS | (N)TP
|
||||
#endif
|
||||
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
|
||||
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
|
||||
bool homevalid = false; // Homelat and homelon are valid
|
||||
|
||||
// Timer state (static so it survives page switches)
|
||||
static bool timerInitialized;
|
||||
@@ -53,9 +53,13 @@ class PageClock : public Page
|
||||
static int timerHours;
|
||||
static int timerMinutes;
|
||||
static int timerSeconds;
|
||||
// Initial timer setting at start (so we can restore it)
|
||||
static int timerStartHours;
|
||||
static int timerStartMinutes;
|
||||
static int timerStartSeconds;
|
||||
static int selectedField; // 0 = hours, 1 = minutes, 2 = seconds
|
||||
static bool showSelectionMarker;
|
||||
static time_t timerEndEpoch; // absolute end time based on RTC
|
||||
static time_t timerEndEpoch; // Absolute end time based on RTC
|
||||
|
||||
void setupTimerDefaults()
|
||||
{
|
||||
@@ -65,16 +69,20 @@ class PageClock : public Page
|
||||
timerHours = 0;
|
||||
timerMinutes = 0;
|
||||
timerSeconds = 0;
|
||||
timerStartHours = 0;
|
||||
timerStartMinutes = 0;
|
||||
timerStartSeconds = 0;
|
||||
selectedField = 0;
|
||||
showSelectionMarker = true;
|
||||
timerEndEpoch = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Limiter for overrun settings values
|
||||
static int clamp(int value, int minVal, int maxVal)
|
||||
{
|
||||
if (value < minVal) return minVal;
|
||||
if (value > maxVal) return maxVal;
|
||||
if (value < minVal) return maxVal;
|
||||
if (value > maxVal) return minVal;
|
||||
return value;
|
||||
}
|
||||
|
||||
@@ -189,6 +197,11 @@ public:
|
||||
// Start timer if a non-zero duration is set
|
||||
int total = totalTimerSeconds();
|
||||
if (total > 0 && commonData->data.rtcValid) {
|
||||
// Remember initial timer setting at start
|
||||
timerStartHours = timerHours;
|
||||
timerStartMinutes = timerMinutes;
|
||||
timerStartSeconds = timerSeconds;
|
||||
|
||||
struct tm rtcCopy = commonData->data.rtcTime;
|
||||
time_t nowEpoch = mktime(&rtcCopy);
|
||||
timerEndEpoch = nowEpoch + total;
|
||||
@@ -196,18 +209,10 @@ public:
|
||||
showSelectionMarker = false;
|
||||
}
|
||||
} else {
|
||||
// Stop timer: compute remaining time and keep as new setting
|
||||
if (commonData->data.rtcValid) {
|
||||
struct tm rtcCopy = commonData->data.rtcTime;
|
||||
time_t nowEpoch = mktime(&rtcCopy);
|
||||
time_t remaining = timerEndEpoch - nowEpoch;
|
||||
if (remaining < 0) remaining = 0;
|
||||
int rem = static_cast<int>(remaining);
|
||||
timerHours = rem / 3600;
|
||||
rem -= timerHours * 3600;
|
||||
timerMinutes = rem / 60;
|
||||
timerSeconds = rem % 60;
|
||||
}
|
||||
// Stop timer: restore initial start setting
|
||||
timerHours = timerStartHours;
|
||||
timerMinutes = timerStartMinutes;
|
||||
timerSeconds = timerStartSeconds;
|
||||
timerRunning = false;
|
||||
// marker will become visible again only after POS press
|
||||
}
|
||||
@@ -261,7 +266,7 @@ public:
|
||||
GwLog* logger = commonData->logger;
|
||||
|
||||
setupTimerDefaults();
|
||||
setupKeys(); // ensure correct key labels for current mode
|
||||
setupKeys(); // Ensure correct key labels for current mode
|
||||
|
||||
static String svalue1old = "";
|
||||
static String unit1old = "";
|
||||
@@ -306,7 +311,7 @@ public:
|
||||
String name2 = bvalue2->getName().c_str(); // Value name
|
||||
name2 = name2.substring(0, 6); // String length limit for value name
|
||||
value2 = bvalue2->value; // Value as double in SI unit
|
||||
bool valid2 = bvalue2->valid; // Valid information
|
||||
bool valid2 = bvalue2->valid; // Valid informationgetdisplay().print("RTC");
|
||||
String svalue2 = formatValue(bvalue2, *commonData).svalue; // Formatted value
|
||||
String unit2 = formatValue(bvalue2, *commonData).unit; // Unit of value
|
||||
if (valid2 == true) {
|
||||
@@ -348,6 +353,174 @@ public:
|
||||
time_t tv = mktime(&commonData->data.rtcTime) + timezone * 3600;
|
||||
struct tm* local_tm = localtime(&tv);
|
||||
|
||||
if (mode == 'T') {
|
||||
// TIMER MODE: countdown timer HH:MM:SS in the center with 7-segment font
|
||||
//************************************************************************
|
||||
|
||||
int dispH = timerHours;
|
||||
int dispM = timerMinutes;
|
||||
int dispS = timerSeconds;
|
||||
|
||||
// Update remaining time if timer is running (based on RTC)
|
||||
if (timerRunning && commonData->data.rtcValid) {
|
||||
struct tm rtcCopy = commonData->data.rtcTime;
|
||||
time_t nowEpoch = mktime(&rtcCopy);
|
||||
time_t remaining = timerEndEpoch - nowEpoch;
|
||||
if(remaining <= 5){
|
||||
// Short buzzer alarm (100% power)
|
||||
setBuzzerPower(100);
|
||||
buzzer(TONE4, 75);
|
||||
setBuzzerPower(config->getInt(config->buzzerPower));
|
||||
}
|
||||
if (remaining <= 0) {
|
||||
remaining = 0;
|
||||
timerRunning = false;
|
||||
commonData->keydata[4].label = "RUN";
|
||||
// Buzzer alarm (100% power)
|
||||
setBuzzerPower(100);
|
||||
buzzer(TONE4, 800);
|
||||
setBuzzerPower(config->getInt(config->buzzerPower));
|
||||
|
||||
// When countdown is finished, restore the initial start time
|
||||
timerHours = timerStartHours;
|
||||
timerMinutes = timerStartMinutes;
|
||||
timerSeconds = timerStartSeconds;
|
||||
}
|
||||
else{
|
||||
commonData->keydata[4].label = "STOP";
|
||||
}
|
||||
int rem = static_cast<int>(remaining);
|
||||
dispH = rem / 3600;
|
||||
rem -= dispH * 3600;
|
||||
dispM = rem / 60;
|
||||
dispS = rem % 60;
|
||||
}
|
||||
|
||||
char buf[9]; // "HH:MM:SS"
|
||||
snprintf(buf, sizeof(buf), "%02d:%02d:%02d", dispH, dispM, dispS);
|
||||
String timeStr = String(buf);
|
||||
|
||||
// Clear central area and draw large digital time
|
||||
getdisplay().fillRect(0, 110, getdisplay().width(), 80, commonData->bgcolor);
|
||||
|
||||
getdisplay().setFont(&DSEG7Classic_BoldItalic30pt7b);
|
||||
|
||||
// Determine widths for digits and colon to position selection underline exactly
|
||||
int16_t x0, y0;
|
||||
uint16_t wDigit, hDigit;
|
||||
uint16_t wColon, hColon;
|
||||
|
||||
getdisplay().getTextBounds("00", 0, 0, &x0, &y0, &wDigit, &hDigit);
|
||||
getdisplay().getTextBounds(":", 0, 0, &x0, &y0, &wColon, &hColon);
|
||||
|
||||
uint16_t totalWidth = 3 * wDigit + 2 * wColon;
|
||||
|
||||
int16_t baseX = (static_cast<int16_t>(getdisplay().width()) - static_cast<int16_t>(totalWidth)) / 2;
|
||||
int16_t centerY = 150;
|
||||
|
||||
// Draw time string centered
|
||||
int16_t x1b, y1b;
|
||||
uint16_t wb, hb;
|
||||
getdisplay().getTextBounds(timeStr, 0, 0, &x1b, &y1b, &wb, &hb);
|
||||
int16_t textX = (static_cast<int16_t>(getdisplay().width()) - static_cast<int16_t>(wb)) / 2;
|
||||
int16_t textY = centerY + hb / 2;
|
||||
|
||||
getdisplay().setCursor(textX, textY);
|
||||
getdisplay().print(timeStr);
|
||||
|
||||
// Selection marker (only visible when not running and POS pressed)
|
||||
if (!timerRunning && showSelectionMarker) {
|
||||
int16_t selX = baseX - 8; // Hours start
|
||||
if (selectedField == 1) {
|
||||
selX = baseX + wDigit + wColon; // Minutes start
|
||||
} else if (selectedField == 2) {
|
||||
selX = baseX + 2 * wDigit + 2 * wColon + 12; // Seconds start
|
||||
}
|
||||
|
||||
int16_t underlineY = centerY + hb / 2 + 5;
|
||||
//getdisplay().fillRect(selX, underlineY, wDigit, 6, commonData->fgcolor);
|
||||
getdisplay().fillRoundRect(selX, underlineY, wDigit, 6, 2, commonData->fgcolor);
|
||||
}
|
||||
|
||||
// Page label
|
||||
getdisplay().setFont(&Ubuntu_Bold16pt8b);
|
||||
getdisplay().setCursor(80, 70);
|
||||
getdisplay().print("Count Dow Timer");
|
||||
|
||||
} else if (mode == 'D') {
|
||||
// DIGITAL CLOCK MODE: large 7-segment time based on GPS/RTC
|
||||
//**********************************************************
|
||||
|
||||
int hour24 = 0;
|
||||
int minute24 = 0;
|
||||
int second24 = 0;
|
||||
|
||||
if (source == 'R' && commonData->data.rtcValid) {
|
||||
time_t tv2 = mktime(&commonData->data.rtcTime);
|
||||
if (tz == 'L') {
|
||||
tv2 += static_cast<time_t>(timezone * 3600);
|
||||
}
|
||||
struct tm* tm2 = localtime(&tv2);
|
||||
hour24 = tm2->tm_hour;
|
||||
minute24 = tm2->tm_min;
|
||||
second24 = tm2->tm_sec;
|
||||
} else {
|
||||
double t = value1;
|
||||
if (tz == 'L') {
|
||||
t += timezone * 3600;
|
||||
}
|
||||
if (t >= 86400) t -= 86400;
|
||||
if (t < 0) t += 86400;
|
||||
hour24 = static_cast<int>(t / 3600.0);
|
||||
int rest = static_cast<int>(t) - hour24 * 3600;
|
||||
minute24 = rest / 60;
|
||||
second24 = rest % 60;
|
||||
}
|
||||
|
||||
char buf[9]; // "HH:MM:SS"
|
||||
snprintf(buf, sizeof(buf), "%02d:%02d:%02d", hour24, minute24, second24);
|
||||
String timeStr = String(buf);
|
||||
|
||||
getdisplay().fillRect(0, 110, getdisplay().width(), 80, commonData->bgcolor);
|
||||
|
||||
getdisplay().setFont(&DSEG7Classic_BoldItalic30pt7b);
|
||||
|
||||
int16_t x1b, y1b;
|
||||
uint16_t wb, hb;
|
||||
getdisplay().getTextBounds(timeStr, 0, 0, &x1b, &y1b, &wb, &hb);
|
||||
|
||||
int16_t x = (static_cast<int16_t>(getdisplay().width()) - static_cast<int16_t>(wb)) / 2;
|
||||
int16_t y = 150 + hb / 2;
|
||||
|
||||
getdisplay().setCursor(x, y);
|
||||
getdisplay().print(timeStr); // Display actual time
|
||||
|
||||
// Small indicators: timezone and source
|
||||
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
||||
|
||||
getdisplay().setCursor(x, 110);
|
||||
if (source == 'G') {
|
||||
getdisplay().print("GPS");
|
||||
} else {
|
||||
getdisplay().print("RTC");
|
||||
}
|
||||
|
||||
getdisplay().setCursor(x + 40, 110);
|
||||
if (holdvalues == false) {
|
||||
getdisplay().print(tz == 'L' ? "LOT" : "UTC");
|
||||
} else {
|
||||
getdisplay().print(unit2old); // date unit
|
||||
}
|
||||
|
||||
// Page label
|
||||
getdisplay().setFont(&Ubuntu_Bold16pt8b);
|
||||
getdisplay().setCursor(100, 70);
|
||||
getdisplay().print("Digital Clock");
|
||||
|
||||
} else {
|
||||
// ANALOG CLOCK MODE (mode == 'A')
|
||||
//********************************
|
||||
|
||||
// Show values GPS date
|
||||
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
||||
getdisplay().setCursor(10, 65);
|
||||
@@ -434,157 +607,6 @@ public:
|
||||
getdisplay().setCursor(335, 220);
|
||||
getdisplay().print("SunS"); // Name
|
||||
|
||||
//*******************************************************************************************
|
||||
|
||||
if (mode == 'T') {
|
||||
// TIMER MODE: countdown timer HH:MM:SS in the center with 7-segment font
|
||||
|
||||
int dispH = timerHours;
|
||||
int dispM = timerMinutes;
|
||||
int dispS = timerSeconds;
|
||||
|
||||
// Update remaining time if timer is running (based on RTC)
|
||||
if (timerRunning && commonData->data.rtcValid) {
|
||||
struct tm rtcCopy = commonData->data.rtcTime;
|
||||
time_t nowEpoch = mktime(&rtcCopy);
|
||||
time_t remaining = timerEndEpoch - nowEpoch;
|
||||
if (remaining <= 0) {
|
||||
remaining = 0;
|
||||
timerRunning = false;
|
||||
}
|
||||
int rem = static_cast<int>(remaining);
|
||||
dispH = rem / 3600;
|
||||
rem -= dispH * 3600;
|
||||
dispM = rem / 60;
|
||||
dispS = rem % 60;
|
||||
}
|
||||
|
||||
char buf[9]; // "HH:MM:SS"
|
||||
snprintf(buf, sizeof(buf), "%02d:%02d:%02d", dispH, dispM, dispS);
|
||||
String timeStr = String(buf);
|
||||
|
||||
// Clear central area and draw large digital time
|
||||
getdisplay().fillRect(0, 110, getdisplay().width(), 80, commonData->bgcolor);
|
||||
|
||||
getdisplay().setFont(&DSEG7Classic_BoldItalic30pt7b);
|
||||
|
||||
// Determine widths for digits and colon to position selection underline exactly
|
||||
int16_t x0, y0;
|
||||
uint16_t wDigit, hDigit;
|
||||
uint16_t wColon, hColon;
|
||||
|
||||
getdisplay().getTextBounds("00", 0, 0, &x0, &y0, &wDigit, &hDigit);
|
||||
getdisplay().getTextBounds(":", 0, 0, &x0, &y0, &wColon, &hColon);
|
||||
|
||||
uint16_t totalWidth = 3 * wDigit + 2 * wColon;
|
||||
|
||||
int16_t baseX = (static_cast<int16_t>(getdisplay().width()) - static_cast<int16_t>(totalWidth)) / 2;
|
||||
int16_t centerY = 150;
|
||||
|
||||
// Draw time string centered
|
||||
int16_t x1b, y1b;
|
||||
uint16_t wb, hb;
|
||||
getdisplay().getTextBounds(timeStr, 0, 0, &x1b, &y1b, &wb, &hb);
|
||||
int16_t textX = (static_cast<int16_t>(getdisplay().width()) - static_cast<int16_t>(wb)) / 2;
|
||||
int16_t textY = centerY + hb / 2;
|
||||
|
||||
getdisplay().setCursor(textX, textY);
|
||||
getdisplay().print(timeStr);
|
||||
|
||||
// Selection marker (only visible when not running and POS pressed)
|
||||
if (!timerRunning && showSelectionMarker) {
|
||||
int16_t selX = baseX;
|
||||
if (selectedField == 1) {
|
||||
selX = baseX + wDigit + wColon; // minutes start
|
||||
} else if (selectedField == 2) {
|
||||
selX = baseX + 2 * wDigit + 2 * wColon; // seconds start
|
||||
}
|
||||
|
||||
int16_t underlineY = centerY + hb / 2 + 5;
|
||||
getdisplay().fillRect(selX, underlineY, wDigit, 6, commonData->fgcolor);
|
||||
}
|
||||
|
||||
// Small indicators: timezone and source
|
||||
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
||||
getdisplay().setCursor(185, 110);
|
||||
if (holdvalues == false) {
|
||||
getdisplay().print(tz == 'L' ? "LOT" : "UTC");
|
||||
} else {
|
||||
getdisplay().print(unit2old); // date unit
|
||||
}
|
||||
|
||||
getdisplay().setCursor(185, 210);
|
||||
if (source == 'G') {
|
||||
getdisplay().print("GPS");
|
||||
} else {
|
||||
getdisplay().print("RTC");
|
||||
}
|
||||
|
||||
} else if (mode == 'D') {
|
||||
// DIGITAL CLOCK MODE: large 7-segment time based on GPS/RTC
|
||||
|
||||
int hour24 = 0;
|
||||
int minute24 = 0;
|
||||
int second24 = 0;
|
||||
|
||||
if (source == 'R' && commonData->data.rtcValid) {
|
||||
time_t tv2 = mktime(&commonData->data.rtcTime);
|
||||
if (tz == 'L') {
|
||||
tv2 += static_cast<time_t>(timezone * 3600);
|
||||
}
|
||||
struct tm* tm2 = localtime(&tv2);
|
||||
hour24 = tm2->tm_hour;
|
||||
minute24 = tm2->tm_min;
|
||||
second24 = tm2->tm_sec;
|
||||
} else {
|
||||
double t = value1;
|
||||
if (tz == 'L') {
|
||||
t += timezone * 3600;
|
||||
}
|
||||
if (t >= 86400) t -= 86400;
|
||||
if (t < 0) t += 86400;
|
||||
hour24 = static_cast<int>(t / 3600.0);
|
||||
int rest = static_cast<int>(t) - hour24 * 3600;
|
||||
minute24 = rest / 60;
|
||||
second24 = rest % 60;
|
||||
}
|
||||
|
||||
char buf[9]; // "HH:MM:SS"
|
||||
snprintf(buf, sizeof(buf), "%02d:%02d:%02d", hour24, minute24, second24);
|
||||
String timeStr = String(buf);
|
||||
|
||||
getdisplay().fillRect(0, 110, getdisplay().width(), 80, commonData->bgcolor);
|
||||
|
||||
getdisplay().setFont(&DSEG7Classic_BoldItalic30pt7b);
|
||||
|
||||
int16_t x1b, y1b;
|
||||
uint16_t wb, hb;
|
||||
getdisplay().getTextBounds(timeStr, 0, 0, &x1b, &y1b, &wb, &hb);
|
||||
|
||||
int16_t x = (static_cast<int16_t>(getdisplay().width()) - static_cast<int16_t>(wb)) / 2;
|
||||
int16_t y = 150 + hb / 2;
|
||||
|
||||
getdisplay().setCursor(x, y);
|
||||
getdisplay().print(timeStr);
|
||||
|
||||
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
||||
getdisplay().setCursor(185, 110);
|
||||
if (holdvalues == false) {
|
||||
getdisplay().print(tz == 'L' ? "LOT" : "UTC");
|
||||
} else {
|
||||
getdisplay().print(unit2old); // date unit
|
||||
}
|
||||
|
||||
getdisplay().setCursor(185, 210);
|
||||
if (source == 'G') {
|
||||
getdisplay().print("GPS");
|
||||
} else {
|
||||
getdisplay().print("RTC");
|
||||
}
|
||||
|
||||
} else {
|
||||
// ANALOG CLOCK MODE (mode == 'A')
|
||||
|
||||
int rInstrument = 110; // Radius of clock
|
||||
float pi = 3.141592;
|
||||
|
||||
@@ -756,6 +778,9 @@ bool PageClock::timerRunning = false;
|
||||
int PageClock::timerHours = 0;
|
||||
int PageClock::timerMinutes = 0;
|
||||
int PageClock::timerSeconds = 0;
|
||||
int PageClock::timerStartHours = 0;
|
||||
int PageClock::timerStartMinutes = 0;
|
||||
int PageClock::timerStartSeconds = 0;
|
||||
int PageClock::selectedField = 0;
|
||||
bool PageClock::showSelectionMarker = true;
|
||||
time_t PageClock::timerEndEpoch = 0;
|
||||
|
||||
Reference in New Issue
Block a user