diff --git a/src/RedirectablePrint.cpp b/src/RedirectablePrint.cpp index 9624a4593..f5efe3c03 100644 --- a/src/RedirectablePrint.cpp +++ b/src/RedirectablePrint.cpp @@ -3,6 +3,7 @@ #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" @@ -128,9 +129,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 = 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 + int hour, min, sec; + graphics::decomposeTime(rtc_sec, hour, min, sec); + #ifdef ARCH_PORTDUINO ::printf("%s ", logLevel); if (color) { diff --git a/src/graphics/SharedUIDisplay.cpp b/src/graphics/SharedUIDisplay.cpp index dcaa5d69b..e9182e70f 100644 --- a/src/graphics/SharedUIDisplay.cpp +++ b/src/graphics/SharedUIDisplay.cpp @@ -27,6 +27,19 @@ void determineResolution(int16_t screenheight, int16_t screenwidth) } } +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; +} + // === Shared External State === bool hasUnreadMessage = false; bool isMuted = false; @@ -195,8 +208,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 = hms / SEC_PER_HOUR; - int minute = (hms % SEC_PER_HOUR) / SEC_PER_MIN; + int hour, minute, second; + graphics::decomposeTime(rtc_sec, hour, minute, second); snprintf(timeStr, sizeof(timeStr), "%d:%02d", hour, minute); // === Build Date String === diff --git a/src/graphics/SharedUIDisplay.h b/src/graphics/SharedUIDisplay.h index e1a7c6383..bb8879c4f 100644 --- a/src/graphics/SharedUIDisplay.h +++ b/src/graphics/SharedUIDisplay.h @@ -45,6 +45,8 @@ extern bool isMuted; extern bool isHighResolution; void determineResolution(int16_t screenheight, int16_t screenwidth); +void decomposeTime(uint32_t rtc_sec, int &hour, int &minute, int &second); + // Rounded highlight (used for inverted headers) void drawRoundedHighlight(OLEDDisplay *display, int16_t x, int16_t y, int16_t w, int16_t h, int16_t r); diff --git a/src/graphics/draw/ClockRenderer.cpp b/src/graphics/draw/ClockRenderer.cpp index 706714ba2..81b7a1ccf 100644 --- a/src/graphics/draw/ClockRenderer.cpp +++ b/src/graphics/draw/ClockRenderer.cpp @@ -1,15 +1,10 @@ #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" @@ -23,6 +18,31 @@ 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; @@ -30,7 +50,7 @@ void drawSegmentedDisplayColon(OLEDDisplay *display, int x, int y, float scale) uint16_t cellHeight = (segmentWidth * 2) + (segmentHeight * 3) + 8; - uint16_t topAndBottomX = x + (4 * scale); + uint16_t topAndBottomX = x + static_cast(4 * scale); uint16_t quarterCellHeight = cellHeight / 4; @@ -43,34 +63,16 @@ void drawSegmentedDisplayColon(OLEDDisplay *display, int x, int y, float scale) void drawSegmentedDisplayCharacter(OLEDDisplay *display, int x, int y, uint8_t number, float scale) { - // 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) |⋰ - // ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ + // 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]); + } uint16_t segmentWidth = SEGMENT_WIDTH * scale; uint16_t segmentHeight = SEGMENT_HEIGHT * scale; - // segment x and y coordinates + // Precompute segment positions uint16_t segmentOneX = x + segmentHeight + 2; uint16_t segmentOneY = y; @@ -92,33 +94,21 @@ void drawSegmentedDisplayCharacter(OLEDDisplay *display, int x, int y, uint8_t n uint16_t segmentSevenX = segmentOneX; uint16_t segmentSevenY = segmentTwoY + segmentWidth + 2; - 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); - } + // 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); } void drawHorizontalSegment(OLEDDisplay *display, int x, int y, int width, int height) @@ -147,42 +137,6 @@ 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(); @@ -201,17 +155,8 @@ void drawDigitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int1 uint32_t rtc_sec = getValidTime(RTCQuality::RTCQualityDevice, true); // Display local timezone char timeString[16]; - 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 - } + int hour, minute, second; + decomposeTime(rtc_sec, hour, minute, second); bool isPM = hour >= 12; // hour = hour > 12 ? hour - 12 : hour; @@ -243,9 +188,10 @@ void drawDigitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int1 uint16_t segmentHeight = SEGMENT_HEIGHT * scale; // calculate hours:minutes string width - uint16_t timeStringWidth = strlen(timeString) * 5; + size_t len = strlen(timeString); + uint16_t timeStringWidth = len * 5; - for (uint8_t i = 0; i < strlen(timeString); i++) { + for (size_t i = 0; i < len; i++) { char character = timeString[i]; if (character == ':') { @@ -262,7 +208,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 < strlen(timeString); i++) { + for (uint8_t i = 0; i < len; i++) { char character = timeString[i]; if (character == ':') { @@ -327,12 +273,7 @@ void drawAnalogClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16 int16_t centerY = display->getHeight() / 2; // clock face radius - int16_t radius = 0; - if (display->getHeight() < display->getWidth()) { - radius = (display->getHeight() / 2) * 0.9; - } else { - radius = (display->getWidth() / 2) * 0.9; - } + int16_t radius = (std::min(display->getWidth(), display->getHeight()) / 2) * 0.9; #ifdef T_WATCH_S3 radius = (display->getWidth() / 2) * 0.8; #endif @@ -347,17 +288,8 @@ void drawAnalogClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16 // tick mark outer y coordinate; (first nested circle) int16_t tickMarkOuterNoonY = secondHandNoonY; - // 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; - } + double secondsTickMarkInnerNoonY = noonY + (isHighResolution ? 8 : 4); + double hoursTickMarkInnerNoonY = noonY + (isHighResolution ? 16 : 6); // minute hand y coordinate int16_t minuteHandNoonY = secondsTickMarkInnerNoonY + 4; @@ -377,17 +309,11 @@ void drawAnalogClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16 uint32_t rtc_sec = getValidTime(RTCQuality::RTCQualityDevice, true); // Display local timezone if (rtc_sec > 0) { - long hms = rtc_sec % SEC_PER_DAY; - hms = (hms + SEC_PER_DAY) % SEC_PER_DAY; + int hour, minute, second; + decomposeTime(rtc_sec, hour, minute, second); - // 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) { - isPM = hour >= 12; + bool isPM = hour >= 12; display->setFont(FONT_SMALL); int yOffset = isHighResolution ? 1 : 0; #ifdef USE_EINK diff --git a/src/graphics/draw/DebugRenderer.cpp b/src/graphics/draw/DebugRenderer.cpp index fded5ea8e..4f5bd27b8 100644 --- a/src/graphics/draw/DebugRenderer.cpp +++ b/src/graphics/draw/DebugRenderer.cpp @@ -296,9 +296,8 @@ 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 = 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 + int hour, min, sec; + graphics::decomposeTime(rtc_sec, hour, min, sec); char timebuf[12]; diff --git a/src/modules/DropzoneModule.cpp b/src/modules/DropzoneModule.cpp index 6c42af98b..4b8f2fec8 100644 --- a/src/modules/DropzoneModule.cpp +++ b/src/modules/DropzoneModule.cpp @@ -60,9 +60,7 @@ meshtastic_MeshPacket *DropzoneModule::sendConditions() long hms = rtc_sec % SEC_PER_DAY; hms = (hms + SEC_PER_DAY) % SEC_PER_DAY; - hour = hms / SEC_PER_HOUR; - min = (hms % SEC_PER_HOUR) / SEC_PER_MIN; - sec = (hms % SEC_PER_HOUR) % SEC_PER_MIN; + graphics::decomposeTime(rtc_sec, hour, min, sec); } // 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 3d78d0dc9..a4fa4d242 100644 --- a/src/modules/RangeTestModule.cpp +++ b/src/modules/RangeTestModule.cpp @@ -258,9 +258,8 @@ bool RangeTestModuleRadio::appendFile(const meshtastic_MeshPacket &mp) hms = (hms + SEC_PER_DAY) % SEC_PER_DAY; // Tear apart hms into h:m:s - 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 + int hour, min, sec; + graphics::decomposeTime(rtc_sec, hour, min, sec); fileToAppend.printf("%02d:%02d:%02d,", hour, min, sec); // Time } else {