From 575788ae7bef62e8f7104cb89ad64cf83c98ede2 Mon Sep 17 00:00:00 2001 From: Jason P Date: Thu, 30 Jul 2026 23:54:54 -0500 Subject: [PATCH] Implement Fixes for Meshnology W12's Two Color Display (#11288) * Round 1 Fixes * Update GPS icon * Update calculations for revised GPS icon after regression testing * Add BICOLOR_OLED_DISPLAY and actually use it --------- Co-authored-by: Austin --- src/graphics/draw/ClockRenderer.cpp | 7 ++++++- src/graphics/draw/NodeListRenderer.cpp | 15 ++++++++++++++- src/graphics/draw/UIRenderer.cpp | 14 ++++++++------ src/graphics/images.h | 6 +++--- variants/esp32s3/meshnology-w12/variant.h | 1 + 5 files changed, 32 insertions(+), 11 deletions(-) diff --git a/src/graphics/draw/ClockRenderer.cpp b/src/graphics/draw/ClockRenderer.cpp index a3c23c2c4..b0b15b416 100644 --- a/src/graphics/draw/ClockRenderer.cpp +++ b/src/graphics/draw/ClockRenderer.cpp @@ -178,13 +178,15 @@ void drawDigitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int1 snprintf(secondString, sizeof(secondString), "%02d", second); static bool scaleInitialized = false; - static float scale = 0.75f; + static float scale = 0.50f; static float segmentWidth = SEGMENT_WIDTH * 0.75f; static float segmentHeight = SEGMENT_HEIGHT * 0.75f; if (!scaleInitialized) { #ifdef DISPLAY_FORCE_SMALL_FONTS float screenwidth_target_ratio = 0.70f; // Target 70% of display width (adjustable) +#elif defined(BICOLOR_OLED_DISPLAY) + float screenwidth_target_ratio = 0.60f; // Forced for BICOLOR_OLED_DISPLAY due to two color display #else float screenwidth_target_ratio = 0.80f; // Target 80% of display width (adjustable) #endif @@ -315,6 +317,9 @@ void drawAnalogClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16 int16_t radius = (std::min(display->getWidth(), display->getHeight()) / 2) * 0.9; #ifdef T_WATCH_S3 radius = (display->getWidth() / 2) * 0.8; +#elif defined(BICOLOR_OLED_DISPLAY) + centerY += 6; + radius = (display->getHeight() / 2) * 0.7; #endif // noon (0 deg) coordinates (outermost circle) diff --git a/src/graphics/draw/NodeListRenderer.cpp b/src/graphics/draw/NodeListRenderer.cpp index 5b4036a98..de8ff26d5 100644 --- a/src/graphics/draw/NodeListRenderer.cpp +++ b/src/graphics/draw/NodeListRenderer.cpp @@ -28,6 +28,10 @@ namespace graphics namespace NodeListRenderer { +// Y position of the first row (either column) in the current list screen, set by +// drawNodeListScreen(). Used by entry renderers that need to special-case the top row. +static int16_t firstRowY = 0; + // Function moved from Screen.cpp to NodeListRenderer.cpp since it's primarily used here void drawScaledXBitmap16x16(int x, int y, int width, int height, const uint8_t *bitmapXBM, OLEDDisplay *display) { @@ -363,10 +367,18 @@ void drawEntryHopSignal(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int const int gap = 1; const int totalWidth = hopCountWidth + gap + hop_width; const int hopX = barsRightEdge - totalWidth; + +#if defined(BICOLOR_OLED_DISPLAY) + int iconY = y + (FONT_HEIGHT_SMALL - hop_height) / 2; + if (y == firstRowY) { + iconY += 1; // Nudge the hop icon down 1px on the top row to avoid the two color display + } +#else const int iconY = y + (FONT_HEIGHT_SMALL - hop_height) / 2; +#endif display->drawString(hopX, y, hopCount); - display->drawXbm(hopX + hopCountWidth + gap, iconY, hop_width, hop_height, hop); + display->drawXbm(hopX + hopCountWidth + gap, iconY, hop_width, hop_height, imghop); } } @@ -606,6 +618,7 @@ void drawNodeListScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t // Space below header y += COMMON_HEADER_HEIGHT; + firstRowY = y; int totalColumns = 1; // Default to 1 column diff --git a/src/graphics/draw/UIRenderer.cpp b/src/graphics/draw/UIRenderer.cpp index aba2fb632..82baf81d8 100644 --- a/src/graphics/draw/UIRenderer.cpp +++ b/src/graphics/draw/UIRenderer.cpp @@ -40,7 +40,7 @@ static bool gBootSplashBoldPass = false; static inline void drawSatelliteIcon(OLEDDisplay *display, int16_t x, int16_t y) { - int yOffset = (currentResolution == ScreenResolution::High) ? -5 : 1; + int yOffset = (currentResolution == ScreenResolution::High) ? 0 : 1; if (currentResolution == ScreenResolution::High) { NodeListRenderer::drawScaledXBitmap16x16(x, y + yOffset, imgGPS_width, imgGPS_height, imgGPS, display); } else { @@ -521,9 +521,9 @@ void UIRenderer::drawGps(OLEDDisplay *display, int16_t x, int16_t y, const mesht { // Draw satellite image if (currentResolution == ScreenResolution::High) { - NodeListRenderer::drawScaledXBitmap16x16(x, y - 2, imgGPS_width, imgGPS_height, imgGPS, display); + NodeListRenderer::drawScaledXBitmap16x16(x, y + 1, imgGPS_width, imgGPS_height, imgGPS, display); } else { - display->drawXbm(x + 1, y + 1, imgGPS_width, imgGPS_height, imgGPS); + display->drawXbm(x + 1, y + 3, imgGPS_width, imgGPS_height, imgGPS); } char textString[10]; @@ -963,7 +963,7 @@ void UIRenderer::drawFavoriteNode(OLEDDisplay *display, OLEDDisplayUiState *stat curX += display->getStringWidth(hopCount) + 2; const int iconY = yPos + (FONT_HEIGHT_SMALL - hop_height) / 2; - display->drawXbm(curX, iconY, hop_width, hop_height, hop); + display->drawXbm(curX, iconY, hop_width, hop_height, imghop); curX += hop_width + 1; } } @@ -1195,7 +1195,8 @@ void UIRenderer::drawDeviceFocused(OLEDDisplay *display, OLEDDisplayUiState *sta } else { displayLine = config.position.gps_mode == meshtastic_Config_PositionConfig_GpsMode_NOT_PRESENT ? "No GPS" : "GPS off"; } - drawSatelliteIcon(display, x, getTextPositions(display)[line]); + int yOffset = (currentResolution == ScreenResolution::High) ? 0 : 2; + drawSatelliteIcon(display, x, getTextPositions(display)[line] + yOffset); int xOffset = (currentResolution == ScreenResolution::High) ? 6 : 0; display->drawString(x + 11 + xOffset, getTextPositions(display)[line], displayLine); } else { @@ -1586,7 +1587,8 @@ void UIRenderer::drawCompassAndLocationScreen(OLEDDisplay *display, OLEDDisplayU } else { displayLine = config.position.gps_mode == meshtastic_Config_PositionConfig_GpsMode_NOT_PRESENT ? "No GPS" : "GPS off"; } - drawSatelliteIcon(display, x, textPos[line]); + int yOffset = (currentResolution == ScreenResolution::High) ? 1 : 3; + drawSatelliteIcon(display, x, textPos[line] + yOffset); int xOffset = (currentResolution == ScreenResolution::High) ? 6 : 0; display->drawString(x + 11 + xOffset, textPos[line++], displayLine); } else { diff --git a/src/graphics/images.h b/src/graphics/images.h index 3234ff566..046710116 100644 --- a/src/graphics/images.h +++ b/src/graphics/images.h @@ -13,7 +13,7 @@ const uint8_t imgSatellite[] PROGMEM = { }; #define imgGPS_width 8 #define imgGPS_height 8 -const unsigned char imgGPS[] PROGMEM = {0x00, 0x07, 0x39, 0x2D, 0xFF, 0x48, 0x48, 0x60}; +const unsigned char imgGPS[] PROGMEM = {0x03, 0x05, 0x07, 0x38, 0x38, 0xC8, 0x40, 0x60}; const uint8_t imgUSB[] PROGMEM = {0x00, 0xfc, 0xf0, 0xfc, 0x88, 0xff, 0x86, 0xfe, 0x85, 0xfe, 0x89, 0xff, 0xf1, 0xfc, 0x00, 0xfc}; const uint8_t imgUSB_HighResolution[] PROGMEM = {0x00, 0x3e, 0xf8, 0x80, 0x43, 0xf8, 0xc0, 0xc2, 0xff, 0x60, 0x42, 0xfc, @@ -86,8 +86,8 @@ static const unsigned char mail[] PROGMEM = { // Hop icon (9x10) #define hop_width 9 #define hop_height 10 -const uint8_t hop[] PROGMEM = {0x05, 0x00, 0x07, 0x00, 0x05, 0x00, 0x38, 0x00, 0x28, 0x00, - 0x38, 0x00, 0xC0, 0x01, 0x40, 0x01, 0xC0, 0x01, 0x40, 0x00}; +const uint8_t imghop[] PROGMEM = {0x05, 0x00, 0x07, 0x00, 0x05, 0x00, 0x38, 0x00, 0x28, 0x00, + 0x38, 0x00, 0xC0, 0x01, 0x40, 0x01, 0xC0, 0x01, 0x40, 0x00}; // 📬 Mail / Message const uint8_t icon_mail[] PROGMEM = { diff --git a/variants/esp32s3/meshnology-w12/variant.h b/variants/esp32s3/meshnology-w12/variant.h index c412e0222..999c7c45d 100644 --- a/variants/esp32s3/meshnology-w12/variant.h +++ b/variants/esp32s3/meshnology-w12/variant.h @@ -8,6 +8,7 @@ #define USE_SSD1306 #define I2C_SDA 17 #define I2C_SCL 18 +#define BICOLOR_OLED_DISPLAY // Powers the OLED/peripheral rail; active LOW #define VEXT_ENABLE 45