From 36976e002918d136a27482709b2730d85849f293 Mon Sep 17 00:00:00 2001 From: pelgraine <140762863+pelgraine@users.noreply.github.com> Date: Wed, 22 Apr 2026 12:54:16 +1000 Subject: [PATCH] t-echo lite - stripped emoji in contacts and last heard, changed recent adverts list to default 4 --- examples/companion_radio/MyMesh.cpp | 102 ++++++++++++++++++++ examples/companion_radio/ui-new/UITask.cpp | 105 +-------------------- src/helpers/ui/DisplayDriver.h | 13 ++- variants/lilygo_techo_lite/platformio.ini | 2 - 4 files changed, 112 insertions(+), 110 deletions(-) diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index 3fed96af..8cdf1406 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -154,6 +154,10 @@ #define AUTO_ADD_ROOM_SERVER (1 << 3) // 0x08 - auto-add Room Server (ADV_TYPE_ROOM) #define AUTO_ADD_SENSOR (1 << 4) // 0x10 - auto-add Sensor (ADV_TYPE_SENSOR) +// All type bits combined (excludes overwrite flag) +#define AUTO_ADD_ALL_TYPES (AUTO_ADD_CHAT | AUTO_ADD_REPEATER | \ + AUTO_ADD_ROOM_SERVER | AUTO_ADD_SENSOR) + void MyMesh::writeOKFrame() { uint8_t buf[1]; buf[0] = RESP_CODE_OK; @@ -2550,6 +2554,20 @@ void MyMesh::checkCLIRescueCmd() { Serial.printf(" apn: %s\n", modemManager.getAPN()); Serial.printf(" imei: %s\n", modemManager.getIMEI()); #endif + // Contact auto-add + { + const char* mode = (_prefs.manual_add_contacts & 1) == 0 ? "auto" : + (_prefs.autoadd_config & AUTO_ADD_ALL_TYPES) == 0 ? "manual" : "custom"; + Serial.printf(" contacts: %s", mode); + if ((_prefs.manual_add_contacts & 1) != 0 && (_prefs.autoadd_config & AUTO_ADD_ALL_TYPES) != 0) { + Serial.printf(" [%s%s%s%s]", + (_prefs.autoadd_config & AUTO_ADD_CHAT) ? "C" : "", + (_prefs.autoadd_config & AUTO_ADD_REPEATER) ? "R" : "", + (_prefs.autoadd_config & AUTO_ADD_ROOM_SERVER) ? "S" : "", + (_prefs.autoadd_config & AUTO_ADD_SENSOR) ? "N" : ""); + } + Serial.printf(" maxhops:%d\n", _prefs.autoadd_max_hops); + } // Detect current preset bool presetFound = false; for (int i = 0; i < (int)NUM_RADIO_PRESETS; i++) { @@ -2590,6 +2608,30 @@ void MyMesh::checkCLIRescueCmd() { } } if (!chFound) Serial.println(" (none)"); + + // --- Contact auto-add settings --- + } else if (strcmp(key, "contact.mode") == 0) { + if ((_prefs.manual_add_contacts & 1) == 0) { + Serial.println(" > auto"); + } else if ((_prefs.autoadd_config & AUTO_ADD_ALL_TYPES) == 0) { + Serial.println(" > manual"); + } else { + Serial.println(" > custom"); + } + } else if (strcmp(key, "contact.autoadd") == 0) { + Serial.printf(" > chat:%s rptr:%s room:%s sensor:%s overwrite:%s\n", + (_prefs.autoadd_config & AUTO_ADD_CHAT) ? "on" : "off", + (_prefs.autoadd_config & AUTO_ADD_REPEATER) ? "on" : "off", + (_prefs.autoadd_config & AUTO_ADD_ROOM_SERVER) ? "on" : "off", + (_prefs.autoadd_config & AUTO_ADD_SENSOR) ? "on" : "off", + (_prefs.autoadd_config & AUTO_ADD_OVERWRITE_OLDEST) ? "on" : "off"); + } else if (strcmp(key, "contact.maxhops") == 0) { + if (_prefs.autoadd_max_hops == 0) { + Serial.println(" > 0 (no limit)"); + } else { + Serial.printf(" > %d\n", _prefs.autoadd_max_hops); + } + } else { Serial.printf(" Error: unknown key '%s' (try 'help')\n", key); } @@ -3049,6 +3091,60 @@ void MyMesh::checkCLIRescueCmd() { Serial.println(" Error: backlight not available on this device"); #endif + // --- Contact auto-add settings --- + } else if (memcmp(config, "contact.mode ", 13) == 0) { + const char* val = &config[13]; + if (strcmp(val, "auto") == 0) { + _prefs.manual_add_contacts &= ~1; + savePrefs(); + Serial.println(" > auto-add all"); + } else if (strcmp(val, "custom") == 0) { + _prefs.manual_add_contacts |= 1; + if ((_prefs.autoadd_config & AUTO_ADD_ALL_TYPES) == 0) { + _prefs.autoadd_config |= AUTO_ADD_ALL_TYPES; + } + savePrefs(); + Serial.println(" > custom (use contact.autoadd to configure)"); + } else if (strcmp(val, "manual") == 0) { + _prefs.manual_add_contacts |= 1; + _prefs.autoadd_config &= ~AUTO_ADD_ALL_TYPES; + savePrefs(); + Serial.println(" > manual only (no auto-add)"); + } else if (strcmp(val, "repeater-only") == 0) { + _prefs.manual_add_contacts |= 1; + _prefs.autoadd_config = (_prefs.autoadd_config & AUTO_ADD_OVERWRITE_OLDEST) | AUTO_ADD_REPEATER; + savePrefs(); + Serial.println(" > auto-add repeaters only"); + } else { + Serial.println(" Error: auto|custom|manual|repeater-only"); + } + } else if (memcmp(config, "contact.autoadd ", 16) == 0) { + const char* rest = &config[16]; + uint8_t bit = 0; + const char* val = NULL; + if (memcmp(rest, "chat ", 5) == 0) { bit = AUTO_ADD_CHAT; val = &rest[5]; } + else if (memcmp(rest, "repeater ", 9) == 0) { bit = AUTO_ADD_REPEATER; val = &rest[9]; } + else if (memcmp(rest, "room ", 5) == 0) { bit = AUTO_ADD_ROOM_SERVER; val = &rest[5]; } + else if (memcmp(rest, "sensor ", 7) == 0) { bit = AUTO_ADD_SENSOR; val = &rest[7]; } + else if (memcmp(rest, "overwrite ", 10) == 0) { bit = AUTO_ADD_OVERWRITE_OLDEST; val = &rest[10]; } + + if (bit && val) { + if (strcmp(val, "on") == 0) { _prefs.autoadd_config |= bit; savePrefs(); Serial.println(" > OK"); } + else if (strcmp(val, "off") == 0) { _prefs.autoadd_config &= ~bit; savePrefs(); Serial.println(" > OK"); } + else { Serial.println(" Error: on|off"); } + } else { + Serial.println(" Error: chat|repeater|room|sensor|overwrite on|off"); + } + } else if (memcmp(config, "contact.maxhops ", 16) == 0) { + int h = atoi(&config[16]); + if (h >= 0 && h <= 64) { + _prefs.autoadd_max_hops = (uint8_t)h; + savePrefs(); + Serial.printf(" > maxhops = %d%s\n", h, h == 0 ? " (no limit)" : ""); + } else { + Serial.println(" Error: 0-64 (0=no limit)"); + } + } else { Serial.printf(" Error: unknown setting '%s' (try 'help')\n", config); } @@ -3146,6 +3242,12 @@ void MyMesh::checkCLIRescueCmd() { Serial.println(" set region none Clear default region (unscoped)"); Serial.println(" get channel.scope Show scope for channel i"); Serial.println(" set channel.scope "); + Serial.println(""); + Serial.println(" Contact auto-add:"); + Serial.println(" contact.mode auto|custom|manual|repeater-only"); + Serial.println(" contact.autoadd Show type toggles"); + Serial.println(" contact.autoadd on|off chat|repeater|room|sensor|overwrite"); + Serial.println(" contact.maxhops <0-64> Max hops for auto-add (0=no limit)"); #ifdef HAS_4G_MODEM Serial.println(""); Serial.println(" 4G modem:"); diff --git a/examples/companion_radio/ui-new/UITask.cpp b/examples/companion_radio/ui-new/UITask.cpp index a0351bb3..e265cef4 100644 --- a/examples/companion_radio/ui-new/UITask.cpp +++ b/examples/companion_radio/ui-new/UITask.cpp @@ -1100,98 +1100,7 @@ public: } }; -class MsgPreviewScreen : public UIScreen { - UITask* _task; - mesh::RTCClock* _rtc; - - struct MsgEntry { - uint32_t timestamp; - char origin[62]; - char msg[78]; - }; - #define MAX_UNREAD_MSGS 32 - int num_unread; - MsgEntry unread[MAX_UNREAD_MSGS]; - -public: - MsgPreviewScreen(UITask* task, mesh::RTCClock* rtc) : _task(task), _rtc(rtc) { num_unread = 0; } - - void addPreview(uint8_t path_len, const char* from_name, const char* msg) { - if (num_unread >= MAX_UNREAD_MSGS) return; // full - - auto p = &unread[num_unread++]; - p->timestamp = _rtc->getCurrentTime(); - if (path_len == 0xFF) { - sprintf(p->origin, "(D) %s:", from_name); - } else { - sprintf(p->origin, "(%d) %s:", (uint32_t) path_len, from_name); - } - StrHelper::strncpy(p->msg, msg, sizeof(p->msg)); - } - - int render(DisplayDriver& display) override { - char tmp[16]; - display.setCursor(0, 0); - display.setTextSize(1); - display.setColor(DisplayDriver::GREEN); - sprintf(tmp, "Unread: %d", num_unread); - display.print(tmp); - - auto p = &unread[0]; - - int secs = _rtc->getCurrentTime() - p->timestamp; - if (secs < 60) { - sprintf(tmp, "%ds", secs); - } else if (secs < 60*60) { - sprintf(tmp, "%dm", secs / 60); - } else { - sprintf(tmp, "%dh", secs / (60*60)); - } - display.setCursor(display.width() - display.getTextWidth(tmp) - 2, 0); - display.print(tmp); - - display.drawRect(0, 11, display.width(), 1); // horiz line - - display.setCursor(0, 14); - display.setColor(DisplayDriver::YELLOW); - char filtered_origin[sizeof(p->origin)]; - display.translateUTF8ToBlocks(filtered_origin, p->origin, sizeof(filtered_origin)); - display.print(filtered_origin); - - display.setCursor(0, 25); - display.setColor(DisplayDriver::LIGHT); - char filtered_msg[sizeof(p->msg)]; - display.translateUTF8ToBlocks(filtered_msg, p->msg, sizeof(filtered_msg)); - display.printWordWrap(filtered_msg, display.width()); - -#if AUTO_OFF_MILLIS==0 // probably e-ink - return 10000; // 10 s -#else - return 1000; // next render after 1000 ms -#endif - } - - bool handleInput(char c) override { - if (c == KEY_NEXT || c == KEY_RIGHT) { - num_unread--; - if (num_unread == 0) { - _task->gotoHomeScreen(); - } else { - // delete first/curr item from unread queue - for (int i = 0; i < num_unread; i++) { - unread[i] = unread[i + 1]; - } - } - return true; - } - if (c == KEY_ENTER) { - num_unread = 0; // clear unread queue - _task->gotoHomeScreen(); - return true; - } - return false; - } -}; +// MsgPreviewScreen removed — all platforms now use toast alerts for new messages // ========================================================================== // Lock Screen — T5S3 and T-Deck Pro @@ -1338,7 +1247,6 @@ void UITask::begin(DisplayDriver* display, SensorManager* sensors, NodePrefs* no splash = new SplashScreen(this); home = new HomeScreen(this, &rtc_clock, sensors, node_prefs); - msg_preview = new MsgPreviewScreen(this, &rtc_clock); channel_screen = new ChannelScreen(this, &rtc_clock); ((ChannelScreen*)channel_screen)->setDMUnreadPtr(_dmUnread); channel_picker_screen = new ChannelPickerScreen(this); @@ -1460,9 +1368,6 @@ switch(t){ void UITask::msgRead(int msgcount) { _msgcount = msgcount; - if (msgcount == 0 && curr == msg_preview) { - gotoHomeScreen(); - } } void UITask::newMsg(uint8_t path_len, const char* from_name, const char* text, int msgcount, @@ -1487,9 +1392,6 @@ void UITask::newMsg(uint8_t path_len, const char* from_name, const char* text, i _dedup[_dedupIdx].millis = now; _dedupIdx = (_dedupIdx + 1) % MSG_DEDUP_SIZE; - // Add to preview screen (for notifications on non-keyboard devices) - ((MsgPreviewScreen *) msg_preview)->addPreview(path_len, from_name, text); - // Determine channel index by looking up the channel name // For channel messages, from_name is the channel name // For contact messages, from_name is the contact name (channel_idx = 0xFF) @@ -1552,7 +1454,6 @@ void UITask::newMsg(uint8_t path_len, const char* from_name, const char* text, i } } -#if defined(LilyGo_TDeck_Pro) || defined(LilyGo_T5S3_EPaper_Pro) // Don't interrupt user with popup - just show brief notification // Messages are stored in channel history, accessible via tile/key // Suppress toasts for room server messages (bulk sync would spam toasts) @@ -1565,10 +1466,6 @@ void UITask::newMsg(uint8_t path_len, const char* from_name, const char* text, i if (isOnChannelPickerScreen()) { forceRefresh(); } -#else - // Other devices: Show full preview screen (legacy behavior, skip room sync) - if (!isRoomMsg) setCurrScreen(msg_preview); -#endif if (_display != NULL) { if (!_display->isOn() && !hasConnection()) { diff --git a/src/helpers/ui/DisplayDriver.h b/src/helpers/ui/DisplayDriver.h index e43a8d85..9cae4e68 100644 --- a/src/helpers/ui/DisplayDriver.h +++ b/src/helpers/ui/DisplayDriver.h @@ -47,7 +47,12 @@ public: print(str); } - // convert UTF-8 characters to displayable block characters for compatibility + // Strip non-ASCII characters (emoji, etc.) from a UTF-8 string. + // Placeholder glyphs aren't safe across our fonts: GFXfonts (FreeSans) + // silently drop anything outside first..last, while the Adafruit built-in + // 5x7 font remaps c>=0xB0 by +1 so \xDB prints as ▄. Stripping the + // codepoint is the only behaviour that renders consistently across + // FastEPD (T5S3) and GxEPD (T-Deck Pro, T-Echo Lite, etc.) builds. virtual void translateUTF8ToBlocks(char* dest, const char* src, size_t dest_size) { size_t j = 0; for (size_t i = 0; src[i] != 0 && j < dest_size - 1; i++) { @@ -55,9 +60,9 @@ public: if (c >= 32 && c <= 126) { dest[j++] = c; // ASCII printable } else if (c >= 0x80) { - dest[j++] = '\xDB'; // CP437 full block █ - while (src[i+1] && (src[i+1] & 0xC0) == 0x80) - i++; // skip UTF-8 continuation bytes + // Skip the whole UTF-8 codepoint (lead byte + continuation bytes) + while (src[i+1] && (src[i+1] & 0xC0) == 0x80) + i++; } } dest[j] = 0; diff --git a/variants/lilygo_techo_lite/platformio.ini b/variants/lilygo_techo_lite/platformio.ini index b1efb8f0..73e9e35b 100644 --- a/variants/lilygo_techo_lite/platformio.ini +++ b/variants/lilygo_techo_lite/platformio.ini @@ -88,7 +88,6 @@ build_flags = ; -D BLE_DEBUG_LOGGING=1 -D OFFLINE_QUEUE_SIZE=64 -D MECK_CARDKB - -D UI_RECENT_LIST_SIZE=9 -D UI_SENSORS_PAGE=1 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 @@ -118,7 +117,6 @@ build_flags = -D CHANNEL_MSG_HISTORY_SIZE=150 -D OFFLINE_QUEUE_SIZE=1 -D MECK_CARDKB - -D UI_RECENT_LIST_SIZE=9 -D UI_SENSORS_PAGE=1 -D AUTO_SHUTDOWN_MILLIVOLTS=2800 build_src_filter = ${lilygo_techo_lite_meck.build_src_filter}