From 74b24f1222805d1ed06308049569bc5bc41677f3 Mon Sep 17 00:00:00 2001 From: pelgraine <140762863+pelgraine@users.noreply.github.com> Date: Thu, 12 Mar 2026 17:34:22 +1100 Subject: [PATCH] t5s3 wifi companion --- examples/companion_radio/main.cpp | 4 ++ .../companion_radio/ui-new/Settingsscreen.h | 67 +++++++++++++++++++ examples/companion_radio/ui-new/UITask.cpp | 29 ++++++++ .../companion_radio/ui-new/virtualkeyboard.h | 3 +- .../lilygo_t5s3_epaper_pro/platformio.ini | 4 +- 5 files changed, 105 insertions(+), 2 deletions(-) diff --git a/examples/companion_radio/main.cpp b/examples/companion_radio/main.cpp index 9015bd80..ba2548ec 100644 --- a/examples/companion_radio/main.cpp +++ b/examples/companion_radio/main.cpp @@ -537,7 +537,11 @@ MyMesh the_mesh(radio_driver, fast_rng, rtc_clock, tables, store if (row == 0 && col == 2) { ui_task.gotoSettingsScreen(); return 0; } if (row == 1 && col == 0) { ui_task.gotoTextReader(); return 0; } if (row == 1 && col == 1) { ui_task.gotoNotesScreen(); return 0; } +#ifdef MECK_WEB_READER + if (row == 1 && col == 2) { ui_task.gotoWebReader(); return 0; } +#else if (row == 1 && col == 2) { ui_task.gotoDiscoveryScreen(); return 0; } +#endif } // Tap outside tiles — left half backward, right half forward return (vx < 64) ? (char)KEY_PREV : (char)KEY_NEXT; diff --git a/examples/companion_radio/ui-new/Settingsscreen.h b/examples/companion_radio/ui-new/Settingsscreen.h index 5cc2f387..51c325d7 100644 --- a/examples/companion_radio/ui-new/Settingsscreen.h +++ b/examples/companion_radio/ui-new/Settingsscreen.h @@ -173,6 +173,9 @@ private: char _wifiPassBuf[64]; int _wifiPassLen; unsigned long _wifiFormLastChar; // For brief password reveal +#if defined(LilyGo_T5S3_EPaper_Pro) + bool _wifiNeedsVKB; // T5S3: signal UITask to open VKB for password +#endif #endif // --------------------------------------------------------------------------- @@ -447,6 +450,9 @@ public: _wifiPassLen = 0; memset(_wifiPassBuf, 0, sizeof(_wifiPassBuf)); _wifiFormLastChar = 0; + #if defined(LilyGo_T5S3_EPaper_Pro) + _wifiNeedsVKB = false; + #endif #endif rebuildRows(); } @@ -519,6 +525,60 @@ public: digitalWrite(SDCARD_CS, HIGH); } } + +#if defined(LilyGo_T5S3_EPaper_Pro) + // T5S3 VKB integration — UITask polls this to open the virtual keyboard + // when settings enters WiFi password phase (no physical keyboard available). + bool needsWifiVKB() const { return _wifiNeedsVKB; } + void clearWifiNeedsVKB() { _wifiNeedsVKB = false; } + + // Called by UITask::onVKBSubmit with the password text from VKB. + // Fills the password buffer and triggers the WiFi connect sequence. + void submitWifiPassword(const char* pass) { + _wifiNeedsVKB = false; + int len = strlen(pass); + if (len > 63) len = 63; + memcpy(_wifiPassBuf, pass, len); + _wifiPassBuf[len] = '\0'; + _wifiPassLen = len; + + // Trigger the same connect sequence as pressing Enter in password phase + _wifiPhase = WIFI_PHASE_CONNECTING; + + // Save credentials to SD (so web reader can reuse them) + if (SD.exists("/web") || SD.mkdir("/web")) { + File f = SD.open("/web/wifi.cfg", FILE_WRITE); + if (f) { + f.println(_wifiSSIDs[_wifiSSIDSelected]); + f.println(_wifiPassBuf); + f.close(); + } + digitalWrite(SDCARD_CS, HIGH); + } + + WiFi.disconnect(false); + WiFi.begin(_wifiSSIDs[_wifiSSIDSelected].c_str(), _wifiPassBuf); + + // Brief blocking wait — fine for e-ink + unsigned long timeout = millis() + 8000; + while (WiFi.status() != WL_CONNECTED && millis() < timeout) { + delay(100); + } + + if (WiFi.status() == WL_CONNECTED) { + Serial.printf("Settings VKB: WiFi connected to %s, IP: %s\n", + _wifiSSIDs[_wifiSSIDSelected].c_str(), + WiFi.localIP().toString().c_str()); + _editMode = EDIT_NONE; + _wifiPhase = WIFI_PHASE_IDLE; + if (_onboarding) _onboarding = false; + } else { + Serial.println("Settings VKB: WiFi connection failed"); + _wifiPhase = WIFI_PHASE_SELECT; // Back to SSID list to retry + } + } +#endif + #endif // --------------------------------------------------------------------------- @@ -955,7 +1015,11 @@ public: bool sel = (wi == _wifiSSIDSelected); if (sel) { display.setColor(DisplayDriver::LIGHT); +#if defined(LilyGo_T5S3_EPaper_Pro) + display.fillRect(bx + 2, wy, bw - 4, 8); +#else display.fillRect(bx + 2, wy + 5, bw - 4, 8); +#endif display.setColor(DisplayDriver::DARK); } else { display.setColor(DisplayDriver::LIGHT); @@ -1136,6 +1200,9 @@ public: _wifiPassLen = 0; memset(_wifiPassBuf, 0, sizeof(_wifiPassBuf)); _wifiFormLastChar = 0; +#if defined(LilyGo_T5S3_EPaper_Pro) + _wifiNeedsVKB = true; // Signal UITask to open virtual keyboard +#endif return true; } if (c == 'q' || c == 'Q') { diff --git a/examples/companion_radio/ui-new/UITask.cpp b/examples/companion_radio/ui-new/UITask.cpp index 7a756280..e42ed2ed 100644 --- a/examples/companion_radio/ui-new/UITask.cpp +++ b/examples/companion_radio/ui-new/UITask.cpp @@ -377,7 +377,11 @@ public: struct Tile { const uint8_t* icon; const char* label; }; const Tile tiles[2][3] = { { {icon_envelope, "Messages"}, {icon_people, "Contacts"}, {icon_gear, "Settings"} }, +#ifdef MECK_WEB_READER + { {icon_book, "Reader"}, {icon_notepad, "Notes"}, {icon_search, "Browser"} } +#else { {icon_book, "Reader"}, {icon_notepad, "Notes"}, {icon_search, "Discover"} } +#endif }; const int tileW = 40; @@ -1527,6 +1531,18 @@ if (curr) curr->poll(); } } else { int delay_millis = curr->render(*_display); + + // Check if settings screen needs VKB for WiFi password entry +#ifdef MECK_WIFI_COMPANION + if (isOnSettingsScreen() && !_vkbActive) { + SettingsScreen* ss = (SettingsScreen*)settings_screen; + if (ss->needsWifiVKB()) { + ss->clearWifiNeedsVKB(); + showVirtualKeyboard(VKB_WIFI_PASSWORD, "WiFi Password", "", 63); + } + } +#endif + if (millis() < _alert_expiry) { _display->setTextSize(1); int y = _display->height() / 3; @@ -1785,6 +1801,19 @@ void UITask::onVKBSubmit() { if (_screenBeforeVKB) setCurrScreen(_screenBeforeVKB); break; } +#ifdef MECK_WIFI_COMPANION + case VKB_WIFI_PASSWORD: { + SettingsScreen* ss = (SettingsScreen*)settings_screen; + ss->submitWifiPassword(text); + if (WiFi.status() == WL_CONNECTED) { + showAlert("WiFi connected!", 2000); + } else { + showAlert("WiFi failed", 2000); + } + if (_screenBeforeVKB) setCurrScreen(_screenBeforeVKB); + break; + } +#endif } _screenBeforeVKB = nullptr; _next_refresh = 0; diff --git a/examples/companion_radio/ui-new/virtualkeyboard.h b/examples/companion_radio/ui-new/virtualkeyboard.h index 57b6a3f4..405a23ac 100644 --- a/examples/companion_radio/ui-new/virtualkeyboard.h +++ b/examples/companion_radio/ui-new/virtualkeyboard.h @@ -28,7 +28,8 @@ enum VKBPurpose { VKB_ADMIN_PASSWORD, // Repeater admin login VKB_ADMIN_CLI, // Repeater admin CLI command VKB_NOTES, // Insert text into notes - VKB_SETTINGS_NAME // Edit node name + VKB_SETTINGS_NAME, // Edit node name + VKB_WIFI_PASSWORD // WiFi password entry (settings screen) }; class VirtualKeyboard { diff --git a/variants/lilygo_t5s3_epaper_pro/platformio.ini b/variants/lilygo_t5s3_epaper_pro/platformio.ini index ece7ff1d..a36efe85 100644 --- a/variants/lilygo_t5s3_epaper_pro/platformio.ini +++ b/variants/lilygo_t5s3_epaper_pro/platformio.ini @@ -124,8 +124,9 @@ lib_deps = https://github.com/lewisxhe/SensorLib/archive/refs/tags/v0.3.4.zip ; --------------------------------------------------------------------------- -; T5S3 WiFi companion — touch UI, WiFi phone bridging +; T5S3 WiFi companion — touch UI, WiFi phone bridging, web browser ; Connect via MeshCore web app or meshcore.js over local network (TCP:5000) +; MECK_WEB_READER: shares WiFi companion connection — no extra setup needed ; Flash: pio run -e meck_t5s3_wifi -t upload ; --------------------------------------------------------------------------- [env:meck_t5s3_wifi] @@ -136,6 +137,7 @@ build_flags = -D MAX_CONTACTS=1500 -D MAX_GROUP_CHANNELS=20 -D MECK_WIFI_COMPANION=1 + -D MECK_WEB_READER=1 -D TCP_PORT=5000 -D OFFLINE_QUEUE_SIZE=256 -D DISPLAY_CLASS=FastEPDDisplay