1
0
mirror of https://github.com/thooge/esp32-nmea2000-obp60.git synced 2026-02-11 07:03:07 +01:00
Files
esp32-nmea2000-obp60/lib/obp60task/PageClock3.new
2026-02-05 16:32:15 +01:00

778 lines
31 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h"
#include "OBP60Extensions.h"
/*
* PageClock3: Clock page with
* - Analog mode (mode == 'A')
* - Digital mode (mode == 'D')
* - Countdown timer mode (mode == 'T')
*
* Timer mode:
* - Format HH:MM:SS (24h, leading zeros)
* - Keys in timer mode:
* K1: MODE (A/D/T)
* K2: POS (select field: HH / MM / SS)
* K3: + (increment selected field)
* K4: - (decrement selected field)
* K5: RUN (start/stop countdown)
* - Selection marker: line under active field (width 2px, not wider than digits)
* - Editing only possible when timer is not running
* - When page is left, running timer continues in background using RTC time
* (on re-entry, remaining time is recalculated from RTC)
*/
class PageClock3 : public Page
{
bool simulation = false;
int simtime;
bool keylock = false;
#ifdef BOARD_OBP60S3
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
double homelat;
double homelon;
bool homevalid = false; // homelat and homelon are valid
// Timer state (static so it survives page switches)
static bool timerInitialized;
static bool timerRunning;
static int timerHours;
static int timerMinutes;
static int timerSeconds;
static int selectedField; // 0 = hours, 1 = minutes, 2 = seconds
static bool showSelectionMarker;
static time_t timerEndEpoch; // absolute end time based on RTC
void setupTimerDefaults()
{
if (!timerInitialized) {
timerInitialized = true;
timerRunning = false;
timerHours = 0;
timerMinutes = 0;
timerSeconds = 0;
selectedField = 0;
showSelectionMarker = true;
timerEndEpoch = 0;
}
}
static int clamp(int value, int minVal, int maxVal)
{
if (value < minVal) return minVal;
if (value > maxVal) return maxVal;
return value;
}
void incrementSelected()
{
if (selectedField == 0) {
timerHours = clamp(timerHours + 1, 0, 23);
} else if (selectedField == 1) {
timerMinutes = clamp(timerMinutes + 1, 0, 59);
} else {
timerSeconds = clamp(timerSeconds + 1, 0, 59);
}
}
void decrementSelected()
{
if (selectedField == 0) {
timerHours = clamp(timerHours - 1, 0, 23);
} else if (selectedField == 1) {
timerMinutes = clamp(timerMinutes - 1, 0, 59);
} else {
timerSeconds = clamp(timerSeconds - 1, 0, 59);
}
}
int totalTimerSeconds() const
{
return timerHours * 3600 + timerMinutes * 60 + timerSeconds;
}
public:
PageClock3(CommonData& common)
{
commonData = &common;
common.logger->logDebug(GwLog::LOG, "Instantiate PageClock3");
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
setupTimerDefaults();
}
virtual void setupKeys()
{
Page::setupKeys();
if (mode == 'T') {
// Timer mode: MODE, POS, +, -, RUN
commonData->keydata[0].label = "MODE";
commonData->keydata[1].label = "POS";
commonData->keydata[2].label = "+";
commonData->keydata[3].label = "-";
commonData->keydata[4].label = "RUN";
} else {
// Clock modes: like original
commonData->keydata[0].label = "SRC";
commonData->keydata[1].label = "MODE";
commonData->keydata[4].label = "TZ";
}
}
// Key functions
virtual int handleKey(int key)
{
setupTimerDefaults();
// Keylock function
if (key == 11) { // Code for keylock
keylock = !keylock; // Toggle keylock
return 0; // Commit the key
}
if (mode == 'T') {
// Timer mode key handling
// MODE (K1): cycle display mode A/D/T
if (key == 1) {
switch (mode) {
case 'A': mode = 'D'; break;
case 'D': mode = 'T'; break;
case 'T': mode = 'A'; break;
default: mode = 'A'; break;
}
setupKeys();
return 0;
}
// POS (K2): select field HH / MM / SS (only if timer not running)
if (key == 2 && !timerRunning) {
selectedField = (selectedField + 1) % 3;
showSelectionMarker = true;
return 0;
}
// + (K3): increment selected field (only if timer not running)
if (key == 3 && !timerRunning) {
incrementSelected();
return 0;
}
// - (K4): decrement selected field (only if timer not running)
if (key == 4 && !timerRunning) {
decrementSelected();
return 0;
}
// RUN (K5): start/stop timer
if (key == 5) {
if (!timerRunning) {
// Start timer if a non-zero duration is set
int total = totalTimerSeconds();
if (total > 0 && commonData->data.rtcValid) {
struct tm rtcCopy = commonData->data.rtcTime;
time_t nowEpoch = mktime(&rtcCopy);
timerEndEpoch = nowEpoch + total;
timerRunning = true;
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;
}
timerRunning = false;
// marker will become visible again only after POS press
}
return 0;
}
// In timer mode, other keys are passed through
return key;
}
// Clock (A/D) modes key handling like original PageClock
// Time source (K1)
if (key == 1) {
switch (source) {
case 'G': source = 'R'; break;
case 'R': source = 'G'; break;
default: source = 'G'; break;
}
return 0;
}
// MODE (K2)
if (key == 2) {
switch (mode) {
case 'A': mode = 'D'; break;
case 'D': mode = 'T'; break;
case 'T': mode = 'A'; break;
default: mode = 'A'; break;
}
setupKeys();
return 0;
}
// Time zone: Local / UTC (K5)
if (key == 5) {
switch (tz) {
case 'L': tz = 'U'; break;
case 'U': tz = 'L'; break;
default: tz = 'L'; break;
}
return 0;
}
return key;
}
int displayPage(PageData& pageData)
{
GwConfigHandler* config = commonData->config;
GwLog* logger = commonData->logger;
setupTimerDefaults();
setupKeys(); // ensure correct key labels for current mode
static String svalue1old = "";
static String unit1old = "";
static String svalue2old = "";
static String unit2old = "";
static String svalue3old = "";
static String unit3old = "";
static String svalue5old = "";
static String svalue6old = "";
double value1 = 0;
double value2 = 0;
double value3 = 0;
// Get config data
String lengthformat = config->getString(config->lengthFormat);
String dateformat = config->getString(config->dateFormat);
bool holdvalues = config->getBool(config->holdvalues);
String flashLED = config->getString(config->flashLED);
String backlightMode = config->getString(config->backlight);
// Get boat values for GPS time
GwApi::BoatValue* bvalue1 = pageData.values[0]; // First element in list
String name1 = bvalue1->getName().c_str(); // Value name
name1 = name1.substring(0, 6); // String length limit for value name
if (simulation == false) {
value1 = bvalue1->value; // Value as double in SI unit
} else {
value1 = simtime++; // Simulation data for time value 11:36 in seconds
} // Other simulation data see OBP60Formatter.cpp
bool valid1 = bvalue1->valid; // Valid information
String svalue1 = formatValue(bvalue1, *commonData).svalue; // Formatted value
String unit1 = formatValue(bvalue1, *commonData).unit; // Unit of value
if (valid1 == true) {
svalue1old = svalue1; // Save old value
unit1old = unit1; // Save old unit
}
// Get boat values for GPS date
GwApi::BoatValue* bvalue2 = pageData.values[1]; // Second element in list
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
String svalue2 = formatValue(bvalue2, *commonData).svalue; // Formatted value
String unit2 = formatValue(bvalue2, *commonData).unit; // Unit of value
if (valid2 == true) {
svalue2old = svalue2; // Save old value
unit2old = unit2; // Save old unit
}
// Get boat values for HDOP
GwApi::BoatValue* bvalue3 = pageData.values[2]; // Third element in list
String name3 = bvalue3->getName().c_str(); // Value name
name3 = name3.substring(0, 6); // String length limit for value name
value3 = bvalue3->value; // Value as double in SI unit
bool valid3 = bvalue3->valid; // Valid information
String svalue3 = formatValue(bvalue3, *commonData).svalue; // Formatted value
String unit3 = formatValue(bvalue3, *commonData).unit; // Unit of value
if (valid3 == true) {
svalue3old = svalue3; // Save old value
unit3old = unit3; // Save old unit
}
// Optical warning by limit violation (unused)
if (String(flashLED) == "Limit Violation") {
setBlinkingLED(false);
setFlashLED(false);
}
// Logging boat values
if (bvalue1 == NULL) return PAGE_OK;
LOG_DEBUG(GwLog::LOG, "Drawing at PageClock3, %s:%f, %s:%f", name1.c_str(), value1, name2.c_str(), value2);
// Draw page
//***********************************************************
// Set display in partial refresh mode
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
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_Bold8pt8b);
getdisplay().setCursor(10, 65);
if (holdvalues == false) {
if (source == 'G') {
// GPS value
getdisplay().print(svalue2);
} else if (commonData->data.rtcValid) {
// 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("---");
}
} else {
getdisplay().print(svalue2old);
}
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(10, 95);
getdisplay().print("Date"); // Name
// Horizontal separator left
getdisplay().fillRect(0, 149, 60, 3, commonData->fgcolor);
// Show values GPS time (small text bottom left)
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(10, 250);
if (holdvalues == false) {
if (source == 'G') {
getdisplay().print(svalue1); // Value
} else if (commonData->data.rtcValid) {
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("---");
}
} else {
getdisplay().print(svalue1old);
}
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(10, 220);
getdisplay().print("Time"); // Name
// Show values sunrise
String sunrise = "---";
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) {
sunrise = String("06:42");
}
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(335, 65);
if (holdvalues == false) getdisplay().print(sunrise); // Value
else getdisplay().print(svalue5old);
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(335, 95);
getdisplay().print("SunR"); // Name
// Horizontal separator right
getdisplay().fillRect(340, 149, 80, 3, commonData->fgcolor);
// Show values sunset
String sunset = "---";
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) {
sunset = String("21:03");
}
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(335, 250);
if (holdvalues == false) getdisplay().print(sunset); // Value
else getdisplay().print(svalue6old);
getdisplay().setFont(&Ubuntu_Bold12pt8b);
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_BoldItalic60pt7b);
// 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 + 2;
getdisplay().fillRect(selX, underlineY, wDigit, 2, commonData->fgcolor);
}
// Small indicators: timezone and source
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(180, 110);
if (holdvalues == false) {
getdisplay().print(tz == 'L' ? "LOT" : "UTC");
} else {
getdisplay().print(unit2old); // date unit
}
getdisplay().setCursor(185, 190);
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_BoldItalic60pt7b);
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(180, 110);
if (holdvalues == false) {
getdisplay().print(tz == 'L' ? "LOT" : "UTC");
} else {
getdisplay().print(unit2old); // date unit
}
getdisplay().setCursor(185, 190);
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;
getdisplay().fillCircle(200, 150, rInstrument + 10, commonData->fgcolor); // Outer circle
getdisplay().fillCircle(200, 150, rInstrument + 7, commonData->bgcolor); // Outer circle
for (int i = 0; i < 360; i = i + 1)
{
// Scaling values
float x = 200 + (rInstrument - 30) * sin(i / 180.0 * pi); // x-coordinate dots
float y = 150 - (rInstrument - 30) * cos(i / 180.0 * pi); // y-coordinate dots
const char* ii = "";
switch (i)
{
case 0: ii = "12"; break;
case 90: ii = "3"; break;
case 180: ii = "6"; break;
case 270: ii = "9"; break;
default: break;
}
// Print text centered on position x, y
int16_t x1c, y1c; // Return values of getTextBounds
uint16_t wc, hc; // Return values of getTextBounds
getdisplay().getTextBounds(ii, int(x), int(y), &x1c, &y1c, &wc, &hc); // Calc width of new string
getdisplay().setCursor(x - wc / 2, y + hc / 2);
if (i % 90 == 0) {
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().print(ii);
}
// Draw sub scale with dots
float sinx = 0;
float cosx = 0;
if (i % 6 == 0) {
float x1d = 200 + rInstrument * sin(i / 180.0 * pi);
float y1d = 150 - rInstrument * cos(i / 180.0 * pi);
getdisplay().fillCircle((int)x1d, (int)y1d, 2, commonData->fgcolor);
sinx = sin(i / 180.0 * pi);
cosx = cos(i / 180.0 * pi);
}
// Draw sub scale with lines (two triangles)
if (i % 30 == 0) {
float dx = 2; // Line thickness = 2*dx+1
float xx1 = -dx;
float xx2 = +dx;
float yy1 = -(rInstrument - 10);
float yy2 = -(rInstrument + 10);
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 * xx1 - sinx * yy2), 150 + (int)(sinx * xx1 + cosx * yy2), commonData->fgcolor);
getdisplay().fillTriangle(200 + (int)(cosx * xx2 - sinx * yy1), 150 + (int)(sinx * xx2 + cosx * yy1),
200 + (int)(cosx * xx1 - sinx * yy2), 150 + (int)(sinx * xx1 + cosx * yy2),
200 + (int)(cosx * xx2 - sinx * yy2), 150 + (int)(sinx * xx2 + cosx * yy2), commonData->fgcolor);
}
}
// Print Unit in clock
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(175, 110);
if (holdvalues == false) {
getdisplay().print(tz == 'L' ? "LOT" : "UTC");
} else {
getdisplay().print(unit2old); // date unit
}
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(185, 190);
if (source == 'G') {
getdisplay().print("GPS");
} else {
getdisplay().print("RTC");
}
// Clock values
double hour = 0;
double minute = 0;
if (source == 'R') {
if (tz == 'L') {
time_t tv2 = mktime(&commonData->data.rtcTime) + timezone * 3600;
struct tm* local_tm2 = localtime(&tv2);
minute = local_tm2->tm_min;
hour = local_tm2->tm_hour;
} else {
minute = commonData->data.rtcTime.tm_min;
hour = commonData->data.rtcTime.tm_hour;
}
hour += minute / 60;
} else {
if (tz == 'L') {
value1 += timezone * 3600;
}
if (value1 > 86400) { value1 -= 86400; }
if (value1 < 0) { 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, "... PageClock3, value1: %f hour: %f minute:%f", value1, hour, minute);
// Draw hour pointer
float startwidth = 8; // Start width of pointer
if (valid1 == true || (source == 'R' && commonData->data.rtcValid) || holdvalues == true || simulation == true) {
float sinx = sin(hour * 30.0 * pi / 180); // Hour
float cosx = cos(hour * 30.0 * pi / 180);
// Normal pointer
// Pointer as triangle with center base 2*width
float xx1 = -startwidth;
float xx2 = startwidth;
float yy1 = -startwidth;
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);
// Inverted pointer
// Pointer as triangle with center base 2*width
float endwidth = 2; // End width of pointer
float ix1 = endwidth;
float ix2 = -endwidth;
float iy1 = -(rInstrument * 0.5);
float iy2 = -endwidth;
getdisplay().fillTriangle(200 + (int)(cosx * ix1 - sinx * iy1), 150 + (int)(sinx * ix1 + cosx * iy1),
200 + (int)(cosx * ix2 - sinx * iy1), 150 + (int)(sinx * ix2 + cosx * iy1),
200 + (int)(cosx * 0 - sinx * iy2), 150 + (int)(sinx * 0 + cosx * iy2), commonData->fgcolor);
}
// Draw minute pointer
startwidth = 8; // Start width of pointer
if (valid1 == true || (source == 'R' && commonData->data.rtcValid) || holdvalues == true || simulation == true) {
float sinx = sin(minute * 6.0 * pi / 180); // Minute
float cosx = cos(minute * 6.0 * pi / 180);
// Normal pointer
// Pointer as triangle with center base 2*width
float xx1 = -startwidth;
float xx2 = startwidth;
float yy1 = -startwidth;
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);
// Inverted pointer
// Pointer as triangle with center base 2*width
float endwidth = 2; // End width of pointer
float ix1 = endwidth;
float ix2 = -endwidth;
float iy1 = -(rInstrument - 15);
float iy2 = -endwidth;
getdisplay().fillTriangle(200 + (int)(cosx * ix1 - sinx * iy1), 150 + (int)(sinx * ix1 + cosx * iy1),
200 + (int)(cosx * ix2 - sinx * iy1), 150 + (int)(sinx * ix2 + cosx * iy1),
200 + (int)(cosx * 0 - sinx * iy2), 150 + (int)(sinx * 0 + cosx * iy2), commonData->fgcolor);
}
// Center circle
getdisplay().fillCircle(200, 150, startwidth + 6, commonData->bgcolor);
getdisplay().fillCircle(200, 150, startwidth + 4, commonData->fgcolor);
}
return PAGE_UPDATE;
};
};
// Static member definitions
bool PageClock3::timerInitialized = false;
bool PageClock3::timerRunning = false;
int PageClock3::timerHours = 0;
int PageClock3::timerMinutes = 0;
int PageClock3::timerSeconds = 0;
int PageClock3::selectedField = 0;
bool PageClock3::showSelectionMarker = true;
time_t PageClock3::timerEndEpoch = 0;
static Page* createPage(CommonData& common)
{
return new PageClock3(common);
}
/**
* with the code below we make this page known to the PageTask
* we give it a type (name) that can be selected in the config
* we define which function is to be called
* we provide the number of user parameters we expect (0 here)
* and we provide the names of the fixed values we need
*/
PageDescription registerPageClock3(
"Clock3", // Page name
createPage, // Action
0, // Number of bus values depends on selection in Web configuration
{"GPST", "GPSD", "HDOP"}, // Bus values we need in the page
true // Show display header on/off
);
#endif