diff --git a/src/RedirectablePrint.cpp b/src/RedirectablePrint.cpp index 30512044e..9624a4593 100644 --- a/src/RedirectablePrint.cpp +++ b/src/RedirectablePrint.cpp @@ -3,7 +3,6 @@ #include "RTC.h" #include "concurrency/OSThread.h" #include "configuration.h" -#include "graphics/SharedUIDisplay.h" #include "main.h" #include "memGet.h" #include "mesh/generated/meshtastic/mesh.pb.h" @@ -129,8 +128,9 @@ void RedirectablePrint::log_to_serial(const char *logLevel, const char *format, hms = (hms + SEC_PER_DAY) % SEC_PER_DAY; // Tear apart hms into h:m:s - int hour, min, sec; - graphics::decomposeTime(rtc_sec, hour, min, sec); + int hour = hms / SEC_PER_HOUR; + int min = (hms % SEC_PER_HOUR) / SEC_PER_MIN; + int sec = (hms % SEC_PER_HOUR) % SEC_PER_MIN; // or hms % SEC_PER_MIN #ifdef ARCH_PORTDUINO ::printf("%s ", logLevel); if (color) { diff --git a/src/graphics/SharedUIDisplay.cpp b/src/graphics/SharedUIDisplay.cpp index ad465717a..dcaa5d69b 100644 --- a/src/graphics/SharedUIDisplay.cpp +++ b/src/graphics/SharedUIDisplay.cpp @@ -195,8 +195,8 @@ void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const char *ti if (rtc_sec > 0) { // === Build Time String === long hms = (rtc_sec % SEC_PER_DAY + SEC_PER_DAY) % SEC_PER_DAY; - int hour, minute, second; - graphics::decomposeTime(rtc_sec, hour, minute, second); + int hour = hms / SEC_PER_HOUR; + int minute = (hms % SEC_PER_HOUR) / SEC_PER_MIN; snprintf(timeStr, sizeof(timeStr), "%d:%02d", hour, minute); // === Build Date String === @@ -422,17 +422,4 @@ std::string sanitizeString(const std::string &input) return output; } -void decomposeTime(uint32_t rtc_sec, int &hour, int &minute, int &second) -{ - hour = 0; - minute = 0; - second = 0; - if (rtc_sec == 0) - return; - uint32_t hms = (rtc_sec % SEC_PER_DAY + SEC_PER_DAY) % SEC_PER_DAY; - hour = hms / SEC_PER_HOUR; - minute = (hms % SEC_PER_HOUR) / SEC_PER_MIN; - second = hms % SEC_PER_MIN; -} - } // namespace graphics diff --git a/src/graphics/SharedUIDisplay.h b/src/graphics/SharedUIDisplay.h index 4e8c919e5..e1a7c6383 100644 --- a/src/graphics/SharedUIDisplay.h +++ b/src/graphics/SharedUIDisplay.h @@ -58,6 +58,4 @@ bool isAllowedPunctuation(char c); std::string sanitizeString(const std::string &input); -void decomposeTime(uint32_t rtc_sec, int &hour, int &minute, int &second); - } // namespace graphics diff --git a/src/graphics/draw/ClockRenderer.cpp b/src/graphics/draw/ClockRenderer.cpp index 8b900b194..706714ba2 100644 --- a/src/graphics/draw/ClockRenderer.cpp +++ b/src/graphics/draw/ClockRenderer.cpp @@ -1,10 +1,15 @@ #include "configuration.h" #if HAS_SCREEN #include "ClockRenderer.h" +#include "NodeDB.h" +#include "UIRenderer.h" +#include "configuration.h" +#include "gps/GeoCoord.h" #include "gps/RTC.h" #include "graphics/ScreenFonts.h" #include "graphics/SharedUIDisplay.h" #include "graphics/draw/UIRenderer.h" +#include "graphics/emotes.h" #include "graphics/images.h" #include "main.h" @@ -18,31 +23,6 @@ namespace graphics namespace ClockRenderer { -// Segment bitmaps for numerals 0-9 stored in flash to save RAM. -// Each row is a digit, each column is a segment state (1 = on, 0 = off). -// Segment layout reference: -// -// ___1___ -// 6 | | 2 -// |_7___| -// 5 | | 3 -// |___4_| -// -// Segment order: [1, 2, 3, 4, 5, 6, 7] -// -static const uint8_t PROGMEM digitSegments[10][7] = { - {1, 1, 1, 1, 1, 1, 0}, // 0 - {0, 1, 1, 0, 0, 0, 0}, // 1 - {1, 1, 0, 1, 1, 0, 1}, // 2 - {1, 1, 1, 1, 0, 0, 1}, // 3 - {0, 1, 1, 0, 0, 1, 1}, // 4 - {1, 0, 1, 1, 0, 1, 1}, // 5 - {1, 0, 1, 1, 1, 1, 1}, // 6 - {1, 1, 1, 0, 0, 1, 0}, // 7 - {1, 1, 1, 1, 1, 1, 1}, // 8 - {1, 1, 1, 1, 0, 1, 1} // 9 -}; - void drawSegmentedDisplayColon(OLEDDisplay *display, int x, int y, float scale) { uint16_t segmentWidth = SEGMENT_WIDTH * scale; @@ -50,7 +30,7 @@ void drawSegmentedDisplayColon(OLEDDisplay *display, int x, int y, float scale) uint16_t cellHeight = (segmentWidth * 2) + (segmentHeight * 3) + 8; - uint16_t topAndBottomX = x + static_cast(4 * scale); + uint16_t topAndBottomX = x + (4 * scale); uint16_t quarterCellHeight = cellHeight / 4; @@ -63,16 +43,34 @@ void drawSegmentedDisplayColon(OLEDDisplay *display, int x, int y, float scale) void drawSegmentedDisplayCharacter(OLEDDisplay *display, int x, int y, uint8_t number, float scale) { - // Read 7-segment pattern for the digit from flash - uint8_t seg[7]; - for (uint8_t i = 0; i < 7; i++) { - seg[i] = pgm_read_byte(&digitSegments[number][i]); - } + // the numbers 0-9, each expressed as an array of seven boolean (0|1) values encoding the on/off state of + // segment {innerIndex + 1} + // e.g., to display the numeral '0', segments 1-6 are on, and segment 7 is off. + uint8_t numbers[10][7] = { + {1, 1, 1, 1, 1, 1, 0}, // 0 Display segment key + {0, 1, 1, 0, 0, 0, 0}, // 1 1 + {1, 1, 0, 1, 1, 0, 1}, // 2 ___ + {1, 1, 1, 1, 0, 0, 1}, // 3 6 | | 2 + {0, 1, 1, 0, 0, 1, 1}, // 4 |_7̲_| + {1, 0, 1, 1, 0, 1, 1}, // 5 5 | | 3 + {1, 0, 1, 1, 1, 1, 1}, // 6 |___| + {1, 1, 1, 0, 0, 1, 0}, // 7 + {1, 1, 1, 1, 1, 1, 1}, // 8 4 + {1, 1, 1, 1, 0, 1, 1}, // 9 + }; + + // the width and height of each segment's central rectangle: + // _____________________ + // ⋰| (only this part, |⋱ + // ⋰ | not including | ⋱ + // ⋱ | the triangles | ⋰ + // ⋱| on the ends) |⋰ + // ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ uint16_t segmentWidth = SEGMENT_WIDTH * scale; uint16_t segmentHeight = SEGMENT_HEIGHT * scale; - // Precompute segment positions + // segment x and y coordinates uint16_t segmentOneX = x + segmentHeight + 2; uint16_t segmentOneY = y; @@ -94,21 +92,33 @@ void drawSegmentedDisplayCharacter(OLEDDisplay *display, int x, int y, uint8_t n uint16_t segmentSevenX = segmentOneX; uint16_t segmentSevenY = segmentTwoY + segmentWidth + 2; - // Draw only the active segments - if (seg[0]) - drawHorizontalSegment(display, segmentOneX, segmentOneY, segmentWidth, segmentHeight); - if (seg[1]) - drawVerticalSegment(display, segmentTwoX, segmentTwoY, segmentWidth, segmentHeight); - if (seg[2]) - drawVerticalSegment(display, segmentThreeX, segmentThreeY, segmentWidth, segmentHeight); - if (seg[3]) - drawHorizontalSegment(display, segmentFourX, segmentFourY, segmentWidth, segmentHeight); - if (seg[4]) - drawVerticalSegment(display, segmentFiveX, segmentFiveY, segmentWidth, segmentHeight); - if (seg[5]) - drawVerticalSegment(display, segmentSixX, segmentSixY, segmentWidth, segmentHeight); - if (seg[6]) - drawHorizontalSegment(display, segmentSevenX, segmentSevenY, segmentWidth, segmentHeight); + if (numbers[number][0]) { + graphics::ClockRenderer::drawHorizontalSegment(display, segmentOneX, segmentOneY, segmentWidth, segmentHeight); + } + + if (numbers[number][1]) { + graphics::ClockRenderer::drawVerticalSegment(display, segmentTwoX, segmentTwoY, segmentWidth, segmentHeight); + } + + if (numbers[number][2]) { + graphics::ClockRenderer::drawVerticalSegment(display, segmentThreeX, segmentThreeY, segmentWidth, segmentHeight); + } + + if (numbers[number][3]) { + graphics::ClockRenderer::drawHorizontalSegment(display, segmentFourX, segmentFourY, segmentWidth, segmentHeight); + } + + if (numbers[number][4]) { + graphics::ClockRenderer::drawVerticalSegment(display, segmentFiveX, segmentFiveY, segmentWidth, segmentHeight); + } + + if (numbers[number][5]) { + graphics::ClockRenderer::drawVerticalSegment(display, segmentSixX, segmentSixY, segmentWidth, segmentHeight); + } + + if (numbers[number][6]) { + graphics::ClockRenderer::drawHorizontalSegment(display, segmentSevenX, segmentSevenY, segmentWidth, segmentHeight); + } } void drawHorizontalSegment(OLEDDisplay *display, int x, int y, int width, int height) @@ -137,6 +147,42 @@ void drawVerticalSegment(OLEDDisplay *display, int x, int y, int width, int heig display->fillTriangle(x, y + width, x + height - 1, y + width, x + halfHeight, y + width + halfHeight); } +/* +void drawWatchFaceToggleButton(OLEDDisplay *display, int16_t x, int16_t y, bool digitalMode, float scale) +{ + uint16_t segmentWidth = SEGMENT_WIDTH * scale; + uint16_t segmentHeight = SEGMENT_HEIGHT * scale; + + if (digitalMode) { + uint16_t radius = (segmentWidth + (segmentHeight * 2) + 4) / 2; + uint16_t centerX = (x + segmentHeight + 2) + (radius / 2); + uint16_t centerY = (y + segmentHeight + 2) + (radius / 2); + + display->drawCircle(centerX, centerY, radius); + display->drawCircle(centerX, centerY, radius + 1); + display->drawLine(centerX, centerY, centerX, centerY - radius + 3); + display->drawLine(centerX, centerY, centerX + radius - 3, centerY); + } else { + uint16_t segmentOneX = x + segmentHeight + 2; + uint16_t segmentOneY = y; + + uint16_t segmentTwoX = segmentOneX + segmentWidth + 2; + uint16_t segmentTwoY = segmentOneY + segmentHeight + 2; + + uint16_t segmentThreeX = segmentOneX; + uint16_t segmentThreeY = segmentTwoY + segmentWidth + 2; + + uint16_t segmentFourX = x; + uint16_t segmentFourY = y + segmentHeight + 2; + + drawHorizontalSegment(display, segmentOneX, segmentOneY, segmentWidth, segmentHeight); + drawVerticalSegment(display, segmentTwoX, segmentTwoY, segmentWidth, segmentHeight); + drawHorizontalSegment(display, segmentThreeX, segmentThreeY, segmentWidth, segmentHeight); + drawVerticalSegment(display, segmentFourX, segmentFourY, segmentWidth, segmentHeight); + } +} +*/ +// Draw a digital clock void drawDigitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) { display->clear(); @@ -155,8 +201,17 @@ void drawDigitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int1 uint32_t rtc_sec = getValidTime(RTCQuality::RTCQualityDevice, true); // Display local timezone char timeString[16]; - int hour, minute, second; - decomposeTime(rtc_sec, hour, minute, second); + int hour = 0; + int minute = 0; + int second = 0; + if (rtc_sec > 0) { + long hms = rtc_sec % SEC_PER_DAY; + hms = (hms + SEC_PER_DAY) % SEC_PER_DAY; + + hour = hms / SEC_PER_HOUR; + minute = (hms % SEC_PER_HOUR) / SEC_PER_MIN; + second = (hms % SEC_PER_HOUR) % SEC_PER_MIN; // or hms % SEC_PER_MIN + } bool isPM = hour >= 12; // hour = hour > 12 ? hour - 12 : hour; @@ -188,10 +243,9 @@ void drawDigitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int1 uint16_t segmentHeight = SEGMENT_HEIGHT * scale; // calculate hours:minutes string width - size_t len = strlen(timeString); - uint16_t timeStringWidth = len * 5; + uint16_t timeStringWidth = strlen(timeString) * 5; - for (size_t i = 0; i < len; i++) { + for (uint8_t i = 0; i < strlen(timeString); i++) { char character = timeString[i]; if (character == ':') { @@ -208,7 +262,7 @@ void drawDigitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int1 uint16_t hourMinuteTextY = (display->getHeight() / 2) - (((segmentWidth * 2) + (segmentHeight * 3) + 8) / 2); // iterate over characters in hours:minutes string and draw segmented characters - for (uint8_t i = 0; i < len; i++) { + for (uint8_t i = 0; i < strlen(timeString); i++) { char character = timeString[i]; if (character == ':') { @@ -273,7 +327,12 @@ void drawAnalogClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16 int16_t centerY = display->getHeight() / 2; // clock face radius - int16_t radius = (std::min(display->getWidth(), display->getHeight()) / 2) * 0.9; + int16_t radius = 0; + if (display->getHeight() < display->getWidth()) { + radius = (display->getHeight() / 2) * 0.9; + } else { + radius = (display->getWidth() / 2) * 0.9; + } #ifdef T_WATCH_S3 radius = (display->getWidth() / 2) * 0.8; #endif @@ -288,8 +347,17 @@ void drawAnalogClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16 // tick mark outer y coordinate; (first nested circle) int16_t tickMarkOuterNoonY = secondHandNoonY; - double secondsTickMarkInnerNoonY = noonY + (isHighResolution ? 8 : 4); - double hoursTickMarkInnerNoonY = noonY + (isHighResolution ? 16 : 6); + // seconds tick mark inner y coordinate; (second nested circle) + double secondsTickMarkInnerNoonY = (double)noonY + 4; + if (isHighResolution) { + secondsTickMarkInnerNoonY = (double)noonY + 8; + } + + // hours tick mark inner y coordinate; (third nested circle) + double hoursTickMarkInnerNoonY = (double)noonY + 6; + if (isHighResolution) { + hoursTickMarkInnerNoonY = (double)noonY + 16; + } // minute hand y coordinate int16_t minuteHandNoonY = secondsTickMarkInnerNoonY + 4; @@ -309,11 +377,17 @@ void drawAnalogClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16 uint32_t rtc_sec = getValidTime(RTCQuality::RTCQualityDevice, true); // Display local timezone if (rtc_sec > 0) { - int hour, minute, second; - decomposeTime(rtc_sec, hour, minute, second); + long hms = rtc_sec % SEC_PER_DAY; + hms = (hms + SEC_PER_DAY) % SEC_PER_DAY; + // Tear apart hms into h:m:s + int hour = hms / SEC_PER_HOUR; + int minute = (hms % SEC_PER_HOUR) / SEC_PER_MIN; + int second = (hms % SEC_PER_HOUR) % SEC_PER_MIN; // or hms % SEC_PER_MIN + + bool isPM = hour >= 12; if (config.display.use_12h_clock) { - bool isPM = hour >= 12; + isPM = hour >= 12; display->setFont(FONT_SMALL); int yOffset = isHighResolution ? 1 : 0; #ifdef USE_EINK @@ -326,8 +400,8 @@ void drawAnalogClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16 if (hour == 0) hour = 12; - constexpr int16_t degreesPerHour = 30; - constexpr int16_t degreesPerMinuteOrSecond = 6; + int16_t degreesPerHour = 30; + int16_t degreesPerMinuteOrSecond = 6; double hourBaseAngle = hour * degreesPerHour; double hourAngleOffset = ((double)minute / 60) * degreesPerHour; diff --git a/src/graphics/draw/DebugRenderer.cpp b/src/graphics/draw/DebugRenderer.cpp index 4f5bd27b8..fded5ea8e 100644 --- a/src/graphics/draw/DebugRenderer.cpp +++ b/src/graphics/draw/DebugRenderer.cpp @@ -296,8 +296,9 @@ void drawFrameSettings(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t hms = (hms + SEC_PER_DAY) % SEC_PER_DAY; // Tear apart hms into h:m:s - int hour, min, sec; - graphics::decomposeTime(rtc_sec, hour, min, sec); + int hour = hms / SEC_PER_HOUR; + int min = (hms % SEC_PER_HOUR) / SEC_PER_MIN; + int sec = (hms % SEC_PER_HOUR) % SEC_PER_MIN; // or hms % SEC_PER_MIN char timebuf[12]; diff --git a/src/graphics/draw/UIRenderer.cpp b/src/graphics/draw/UIRenderer.cpp index 441a8ca6f..167db50c2 100644 --- a/src/graphics/draw/UIRenderer.cpp +++ b/src/graphics/draw/UIRenderer.cpp @@ -786,8 +786,12 @@ const int daysInMonth[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; // Fills the buffer with a formatted date/time string and returns pixel width int UIRenderer::formatDateTime(char *buf, size_t bufSize, uint32_t rtc_sec, OLEDDisplay *display, bool includeTime) { - int hour, min, sec; - graphics::decomposeTime(rtc_sec, hour, min, sec); + int sec = rtc_sec % 60; + rtc_sec /= 60; + int min = rtc_sec % 60; + rtc_sec /= 60; + int hour = rtc_sec % 24; + rtc_sec /= 24; int year = 1970; while (true) { diff --git a/src/modules/DropzoneModule.cpp b/src/modules/DropzoneModule.cpp index d8535dba0..6c42af98b 100644 --- a/src/modules/DropzoneModule.cpp +++ b/src/modules/DropzoneModule.cpp @@ -55,13 +55,14 @@ meshtastic_MeshPacket *DropzoneModule::sendConditions() 29.25 inHg 72°C */ uint32_t rtc_sec = getValidTime(RTCQuality::RTCQualityDevice, true); - int hour, min, sec; - + int hour = 0, min = 0, sec = 0; if (rtc_sec > 0) { long hms = rtc_sec % SEC_PER_DAY; hms = (hms + SEC_PER_DAY) % SEC_PER_DAY; - graphics::decomposeTime(rtc_sec, hour, min, sec); + hour = hms / SEC_PER_HOUR; + min = (hms % SEC_PER_HOUR) / SEC_PER_MIN; + sec = (hms % SEC_PER_HOUR) % SEC_PER_MIN; } // Check if the dropzone is open or closed by reading the analog pin diff --git a/src/modules/RangeTestModule.cpp b/src/modules/RangeTestModule.cpp index a4fa4d242..3d78d0dc9 100644 --- a/src/modules/RangeTestModule.cpp +++ b/src/modules/RangeTestModule.cpp @@ -258,8 +258,9 @@ bool RangeTestModuleRadio::appendFile(const meshtastic_MeshPacket &mp) hms = (hms + SEC_PER_DAY) % SEC_PER_DAY; // Tear apart hms into h:m:s - int hour, min, sec; - graphics::decomposeTime(rtc_sec, hour, min, sec); + int hour = hms / SEC_PER_HOUR; + int min = (hms % SEC_PER_HOUR) / SEC_PER_MIN; + int sec = (hms % SEC_PER_HOUR) % SEC_PER_MIN; // or hms % SEC_PER_MIN fileToAppend.printf("%02d:%02d:%02d,", hour, min, sec); // Time } else {