diff --git a/examples/companion_radio/main.cpp b/examples/companion_radio/main.cpp index a689be2d..ff79d273 100644 --- a/examples/companion_radio/main.cpp +++ b/examples/companion_radio/main.cpp @@ -519,7 +519,7 @@ MyMesh the_mesh(radio_driver, fast_rng, rtc_clock, tables, store // Home screen FIRST page: tile taps (virtual coordinate hit test) if (ui_task.isOnHomeScreen() && ui_task.isHomeShowingTiles()) { - const int tileW = 40, tileH = 32, gapX = 1, gapY = 2; + const int tileW = 40, tileH = 28, gapX = 1, gapY = 1; const int gridW = tileW * 3 + gapX * 2; const int gridX = (128 - gridW) / 2; // =3 int gridY = ui_task.getTileGridVY(); @@ -539,13 +539,14 @@ MyMesh the_mesh(radio_driver, fast_rng, rtc_clock, tables, store if (row == 1 && col == 1) { ui_task.gotoNotesScreen(); return 0; } if (row == 1 && col == 2) { ui_task.gotoDiscoveryScreen(); return 0; } } - // Tap outside tiles — cycle home pages - return (char)KEY_NEXT; + // Tap outside tiles — left half backward, right half forward + return (vx < 64) ? (char)KEY_PREV : (char)KEY_NEXT; } - // Home screen (non-tile pages): tap cycles pages + // Home screen (non-tile pages): left half taps backward, right half forward if (ui_task.isOnHomeScreen()) { - return (char)KEY_NEXT; + int vx = (int)(x / 7.5f); + return (vx < 64) ? (char)KEY_PREV : (char)KEY_NEXT; } // Reader (reading mode): tap = next page @@ -586,9 +587,12 @@ MyMesh the_mesh(radio_driver, fast_rng, rtc_clock, tables, store } } - // Home screen: horizontal swipe cycles pages + // Home screen: swipe left = next page, swipe right = previous page if (ui_task.isOnHomeScreen()) { - return (char)KEY_NEXT; + if (horizontal) { + return (dx < 0) ? (char)KEY_NEXT : (char)KEY_PREV; + } + return (char)KEY_NEXT; // vertical swipe = next (default) } // Settings: horizontal swipe → a/d for picker/number editing diff --git a/examples/companion_radio/ui-new/Notesscreen.h b/examples/companion_radio/ui-new/Notesscreen.h index bad3c631..20a63dcc 100644 --- a/examples/companion_radio/ui-new/Notesscreen.h +++ b/examples/companion_radio/ui-new/Notesscreen.h @@ -854,7 +854,7 @@ private: display.setColor(DisplayDriver::YELLOW); display.setCursor(0, footerY); #if defined(LilyGo_T5S3_EPaper_Pro) - display.print("Back:Cancel"); + display.print("Boot:Cancel"); const char* right = "Tap:Confirm"; #else display.print("Q:Cancel"); @@ -893,7 +893,7 @@ private: display.setColor(DisplayDriver::YELLOW); display.setCursor(0, footerY); #if defined(LilyGo_T5S3_EPaper_Pro) - display.print("Back:Cancel"); + display.print("Boot:Cancel"); const char* right = "Tap:Delete"; #else display.print("Q:Cancel"); diff --git a/examples/companion_radio/ui-new/Repeateradminscreen.h b/examples/companion_radio/ui-new/Repeateradminscreen.h index 79b52e6f..67962d07 100644 --- a/examples/companion_radio/ui-new/Repeateradminscreen.h +++ b/examples/companion_radio/ui-new/Repeateradminscreen.h @@ -599,7 +599,7 @@ public: switch (_state) { case STATE_PASSWORD_ENTRY: #if defined(LilyGo_T5S3_EPaper_Pro) - display.print("Back:Exit"); + display.print("Boot:Exit"); renderFooterRight(display, footerY, "Hold:Type"); #else display.print("Sh+Del:Exit"); @@ -610,7 +610,7 @@ public: case STATE_LOGGING_IN: case STATE_COMMAND_PENDING: #if defined(LilyGo_T5S3_EPaper_Pro) - display.print("Back:Cancel"); + display.print("Boot:Cancel"); #else display.print("Sh+Del:Cancel"); #endif @@ -618,7 +618,7 @@ public: case STATE_CATEGORY_MENU: #if defined(LilyGo_T5S3_EPaper_Pro) - display.print("Back:Exit"); + display.print("Boot:Exit"); renderFooterMidRight(display, footerY, "Back:Exit", "Tap:Open", "Swipe:Sel"); #else display.print("Sh+Del:Exit"); @@ -628,7 +628,7 @@ public: case STATE_COMMAND_MENU: #if defined(LilyGo_T5S3_EPaper_Pro) - display.print("Back:Back"); + display.print("Boot:Back"); renderFooterMidRight(display, footerY, "Back:Back", "Tap:Run", "Swipe:Sel"); #else display.print("Sh+Del:Back"); @@ -638,7 +638,7 @@ public: case STATE_PARAM_ENTRY: #if defined(LilyGo_T5S3_EPaper_Pro) - display.print("Back:Cancel"); + display.print("Boot:Cancel"); renderFooterRight(display, footerY, "Tap:Send"); #else display.print("Sh+Del:Cancel"); @@ -648,7 +648,7 @@ public: case STATE_CONFIRM: #if defined(LilyGo_T5S3_EPaper_Pro) - display.print("Back:No"); + display.print("Boot:No"); renderFooterRight(display, footerY, "Tap:Yes"); #else display.print("Sh+Del:No"); @@ -659,7 +659,7 @@ public: case STATE_RESPONSE_VIEW: case STATE_ERROR: #if defined(LilyGo_T5S3_EPaper_Pro) - display.print("Back:Back"); + display.print("Boot:Back"); if (_responseTotalLines > bodyHeight / 9) { renderFooterRight(display, footerY, "Swipe:Scroll"); } @@ -1160,7 +1160,11 @@ private: bool selected, const char* label, bool warn) { if (selected) { display.setColor(DisplayDriver::LIGHT); +#if defined(LilyGo_T5S3_EPaper_Pro) + display.fillRect(0, y, display.width(), lineHeight); +#else display.fillRect(0, y + 5, display.width(), lineHeight); +#endif display.setColor(DisplayDriver::DARK); } else if (warn) { display.setColor(DisplayDriver::YELLOW); diff --git a/examples/companion_radio/ui-new/UITask.cpp b/examples/companion_radio/ui-new/UITask.cpp index a0a097f1..56fb40cc 100644 --- a/examples/companion_radio/ui-new/UITask.cpp +++ b/examples/companion_radio/ui-new/UITask.cpp @@ -26,7 +26,11 @@ #define LONG_PRESS_MILLIS 1200 #ifndef UI_RECENT_LIST_SIZE - #define UI_RECENT_LIST_SIZE 4 + #if defined(LilyGo_T5S3_EPaper_Pro) + #define UI_RECENT_LIST_SIZE 8 + #else + #define UI_RECENT_LIST_SIZE 4 + #endif #endif #if UI_HAS_JOYSTICK @@ -145,9 +149,19 @@ void renderBatteryIndicator(DisplayDriver& display, uint16_t batteryMilliVolts, display.setColor(DisplayDriver::GREEN); - // battery icon dimensions (smaller to match tiny percentage text) + // battery icon dimensions +#if defined(LilyGo_T5S3_EPaper_Pro) + int iconWidth = 10; // Narrower — non-square scaling stretches width +#else int iconWidth = 16; +#endif int iconHeight = 6; + int iconY = 0; +#if defined(LilyGo_T5S3_EPaper_Pro) + int textY = 0; // T5S3: percentage beside icon, same baseline +#else + int textY = iconY - 3; // T-Deck Pro: offset up to center with icon +#endif // measure percentage text width to position icon + text together at right edge display.setTextSize(0); @@ -155,16 +169,9 @@ void renderBatteryIndicator(DisplayDriver& display, uint16_t batteryMilliVolts, sprintf(pctStr, "%d%%", batteryPercentage); uint16_t textWidth = display.getTextWidth(pctStr); - // layout: [icon 16px][cap 2px][gap 2px][text][margin 2px] + // layout: [icon][cap 2px][gap 2px][text][margin 2px] int totalWidth = iconWidth + 2 + 2 + textWidth + 2; int iconX = display.width() - totalWidth; -#if defined(LilyGo_T5S3_EPaper_Pro) - int iconY = 6; // Align with FreeSans12pt text baseline - int textY = 1; // Percentage text — setCursor adds +5 → baseline at (6)*scale_y -#else - int iconY = 0; // vertically align with node name text - int textY = iconY - 3; // offset up to vertically center with icon -#endif if (outIconX) *outIconX = iconX; @@ -178,8 +185,7 @@ void renderBatteryIndicator(DisplayDriver& display, uint16_t batteryMilliVolts, int fillWidth = (batteryPercentage * (iconWidth - 4)) / 100; display.fillRect(iconX + 2, iconY + 2, fillWidth, iconHeight - 4); - // draw percentage text after the battery cap, offset upward to center with icon - // (setCursor adds +5 internally for baseline, so compensate for the tiny font) + // draw percentage text after the battery cap int textX = iconX + iconWidth + 2 + 2; // after cap + gap display.setCursor(textX, textY); display.print(pctStr); @@ -306,7 +312,7 @@ public: } // curr page indicator #if defined(LilyGo_T5S3_EPaper_Pro) - int y = 18; + int y = 14; // Closer to header #else int y = 14; #endif @@ -324,7 +330,7 @@ public: _task->setHomeShowingTiles(true); #endif #if defined(LilyGo_T5S3_EPaper_Pro) - int y = 24; + int y = 18; // Tighter spacing #else int y = 20; #endif @@ -332,23 +338,23 @@ public: display.setTextSize(2); sprintf(tmp, "MSG: %d", _task->getUnreadMsgCount()); display.drawTextCentered(display.width() / 2, y, tmp); - y += 18; + y += 14; // Reduced from 18 #if defined(WIFI_SSID) || defined(MECK_WIFI_COMPANION) IPAddress ip = WiFi.localIP(); if (ip != IPAddress(0,0,0,0)) { snprintf(tmp, sizeof(tmp), "IP: %d.%d.%d.%d:%d", ip[0], ip[1], ip[2], ip[3], TCP_PORT); - display.setTextSize(1); + display.setTextSize(0); // Tiny font for IP display.drawTextCentered(display.width() / 2, y, tmp); - y += 12; + y += 8; } #endif #if defined(BLE_PIN_CODE) || defined(WIFI_SSID) || defined(MECK_WIFI_COMPANION) if (_task->hasConnection()) { display.setColor(DisplayDriver::GREEN); - display.setTextSize(1); + display.setTextSize(0); // Tiny font for Connected display.drawTextCentered(display.width() / 2, y, "< Connected >"); - y += 12; + y += 8; // Reduced from 12 #ifdef BLE_PIN_CODE } else if (_task->isSerialEnabled() && the_mesh.getBLEPin() != 0) { display.setColor(DisplayDriver::RED); @@ -372,9 +378,9 @@ public: }; const int tileW = 40; - const int tileH = 32; + const int tileH = 28; // Reduced from 32 to fit with Connected text const int gapX = 1; - const int gapY = 2; + const int gapY = 1; // Reduced from 2 const int gridW = tileW * 3 + gapX * 2; const int gridX = (display.width() - gridW) / 2; const int gridY = y + 2; @@ -391,11 +397,11 @@ public: // Letter centered in tile (pushed down for vertical centering) display.setTextSize(2); - display.drawTextCentered(tx + tileW / 2, ty + 10, tiles[row][col].letter); + display.drawTextCentered(tx + tileW / 2, ty + 8, tiles[row][col].letter); // Label centered below letter display.setTextSize(0); - display.drawTextCentered(tx + tileW / 2, ty + 20, tiles[row][col].label); + display.drawTextCentered(tx + tileW / 2, ty + 18, tiles[row][col].label); } } diff --git a/src/helpers/ui/FastEPDDisplay.cpp b/src/helpers/ui/FastEPDDisplay.cpp index 90791cee..29b5243c 100644 --- a/src/helpers/ui/FastEPDDisplay.cpp +++ b/src/helpers/ui/FastEPDDisplay.cpp @@ -22,7 +22,7 @@ // CLEAR_FAST, CLEAR_SLOW — full refresh modes // Periodic slow (deep) refresh to clear ghosting -#define FULL_SLOW_PERIOD 20 // every 20 fast-refreshes, do a slow refresh +#define FULL_SLOW_PERIOD 2 // every 2 partial-refreshes, do a full cleanup FastEPDDisplay::~FastEPDDisplay() { delete _canvas; @@ -282,7 +282,6 @@ void FastEPDDisplay::endFrame() { // Copy GFXcanvas1 buffer to FastEPD's current buffer — direct copy. // Both use same polarity: bit 1 = white, bit 0 = black. - // (Meshtastic inverts because OLEDDisplay uses opposite convention — not us.) uint8_t* src = _canvas->getBuffer(); uint8_t* dst = _epd->currentBuffer(); size_t bufSize = ((uint32_t)EPD_WIDTH * EPD_HEIGHT) / 8; @@ -291,9 +290,19 @@ void FastEPDDisplay::endFrame() { memcpy(dst, src, bufSize); - // fullUpdate(true) is the only refresh mode that gives clean transitions - // on the ED047TC1 panel. CLEAR_FAST causes ghosting/mashing on this hardware. - // The brief white flash between frames is inherent to this panel's waveform. - _epd->fullUpdate(true); + // Refresh strategy: + // partialUpdate(true) — no flash, differential, keeps previous buffer + // fullUpdate(false) — brief flash, clears ghosting (CLEAR_FAST) + // fullUpdate(true) — full white flash, cleanest (boot only) + // + // Use partial for most frames. Periodic full refresh every N frames + // to clear accumulated ghosting artifacts. + _fullRefreshCount++; + if (_fullRefreshCount >= FULL_SLOW_PERIOD) { + _fullRefreshCount = 0; + _epd->fullUpdate(true); // Full clean refresh — clears all ghosting + } else { + _epd->partialUpdate(true); // No flash — differential + } _epd->backupPlane(); } \ No newline at end of file