T-Watch: fix discovery screen wrap. Add no alarm tick s3 temp diagnostic build and removed prior faulty temp no pmu diagnostic build. Updated tracescreen.h functionality and UI for t-watch builds.

This commit is contained in:
pelgraine
2026-07-17 19:29:15 +10:00
parent a436beb9d1
commit 619e8f44a5
8 changed files with 128 additions and 29 deletions
+22
View File
@@ -1612,6 +1612,28 @@ static void lastHeardToggleContact() {
return 0;
}
#if defined(MECK_TWATCH)
// Trace screen (watch): tap to select row, tap same to activate. Mode row
// toggles 1/2-byte; Type Path opens the keyboard (handleInput -> wantsKeyboard).
// Guarded to the watch: selectRowAtVY uses the watch's 128-space coords, and
// T5S3/T-Deck keep their existing boot-button/keyboard trace handling.
if (ui_task.isOnTraceScreen()) {
TraceScreen* ts = (TraceScreen*)ui_task.getTraceScreen();
if (ts) {
int result = ts->selectRowAtVY(vy);
if (result == 1) {
ui_task.forceRefresh();
return 0;
}
if (result == 2) {
if (ts->isOnModeRow()) return 'd'; // toggle 1-byte / 2-byte
return KEY_ENTER; // Type Path / Add / Remove / Run / Exit / hop
}
}
return 0;
}
#endif
// Discovery screen: tap to select, tap same to add
if (ui_task.isOnDiscoveryScreen()) {
DiscoveryScreen* ds = (DiscoveryScreen*)ui_task.getDiscoveryScreen();
@@ -104,11 +104,11 @@ public:
display.setCursor(4, 28);
display.print(active ? "Listening for adverts..." : "No nodes found");
#if defined(MECK_TWATCH)
display.setCursor(4, 38);
display.print("Hold: Rescan");
display.setCursor(4, 48);
display.print("Tap: Select");
display.print("Hold: Rescan");
display.setCursor(4, 58);
display.print("Tap: Select");
display.setCursor(4, 68);
display.print("Tap Again: Add");
#else
if (!active) {
+71 -1
View File
@@ -91,6 +91,9 @@ private:
int _resultScroll;
bool _wantExit;
#if defined(MECK_TWATCH)
bool _wantKeyboard; // Watch: Type Path tapped -> UITask opens the on-screen keyboard
#endif
// --- Menu helpers (STATE_BUILD) ---
// Menu layout:
@@ -313,6 +316,56 @@ public:
return _state == STATE_BUILD && menuItemAt(_menuSel) == MENU_TYPE_PATH;
}
// True if the highlighted menu row is the Mode selector (STATE_BUILD only).
bool isOnModeRow() const {
return _state == STATE_BUILD && menuItemAt(_menuSel) == MENU_PATH_SIZE;
}
#if defined(MECK_TWATCH)
bool wantsKeyboard() const { return _wantKeyboard; }
void clearWantKeyboard() { _wantKeyboard = false; }
// Tap-to-select row mapping for the watch (mirrors PathEditor/Discovery).
// vy is in the 128-tall virtual touch space. Returns:
// 0 = miss, 1 = moved the cursor, 2 = tapped the already-selected row.
int selectRowAtVY(int vy) {
if (_state == STATE_BUILD) return selectBuildRowAtVY(vy);
if (_state == STATE_PICK_HOP) return selectPickerRowAtVY(vy);
return 0;
}
int selectBuildRowAtVY(int vy) {
int menuCount = buildMenuCount();
if (menuCount == 0) return 0;
const int headerH = 14, footerH = 14, lineH = 11;
const int bodyTop = headerH; // renderBuild draws row 0 at y=14
if (vy < bodyTop || vy >= 128 - footerH) return 0;
int maxVisible = (128 - headerH - footerH) / lineH;
if (maxVisible < 1) maxVisible = 1;
// Replicate renderBuild's scroll window so a tap maps to the drawn row.
int scrollTop = 0;
if (_menuSel >= scrollTop + maxVisible) scrollTop = _menuSel - maxVisible + 1;
if (_menuSel < scrollTop) scrollTop = _menuSel;
int tappedRow = scrollTop + (vy - bodyTop) / lineH;
if (tappedRow < 0 || tappedRow >= menuCount) return 0;
if (tappedRow == _menuSel) return 2;
_menuSel = tappedRow;
return 1;
}
int selectPickerRowAtVY(int vy) {
if (_repCount == 0) return 0;
const int headerH = 14, footerH = 14, lineH = 11;
const int bodyTop = headerH; // renderPicker draws row 0 at y=14
if (vy < bodyTop || vy >= 128 - footerH) return 0;
int tappedRow = _repScroll + (vy - bodyTop) / lineH;
if (tappedRow < 0 || tappedRow >= _repCount) return 0;
if (tappedRow == _repSel) return 2;
_repSel = tappedRow;
return 1;
}
#endif
// Returns the current path formatted as a comma-separated string, suitable
// for pre-populating an external text editor (e.g. the T5S3 virtual keyboard).
// The returned pointer references an internal buffer and is valid until the
@@ -343,6 +396,9 @@ public:
_repSel = 0;
_repScroll = 0;
_wantExit = false;
#if defined(MECK_TWATCH)
_wantKeyboard = false;
#endif
_resultScroll = 0;
memset(_pathBuf, 0, sizeof(_pathBuf));
memset(&_result, 0, sizeof(_result));
@@ -462,6 +518,8 @@ private:
} else {
#if defined(LilyGo_T5S3_EPaper_Pro)
snprintf(tmp, sizeof(tmp), "%c Type Path: [Long press]", prefix);
#elif defined(MECK_TWATCH)
snprintf(tmp, sizeof(tmp), "%c Type Path: [Tap]", prefix);
#else
snprintf(tmp, sizeof(tmp), "%c Type Path: [Press Enter]", prefix);
#endif
@@ -533,6 +591,8 @@ private:
} else {
#if defined(LilyGo_T5S3_EPaper_Pro)
display.print("Boot:Exit Tap:Sel");
#elif defined(MECK_TWATCH)
display.print("Tap:Sel Hold:Go");
#else
display.print("Sh+Del:Exit W/S:Nav Ent:Sel");
#endif
@@ -596,6 +656,8 @@ private:
display.setCursor(0, footerY);
#if defined(LilyGo_T5S3_EPaper_Pro)
display.print("Boot:Back Tap:Add");
#elif defined(MECK_TWATCH)
display.print("Tap:Sel Hold:Add");
#else
display.print("Sh+Del:Back W/S:Scroll Ent:Add");
#endif
@@ -748,6 +810,8 @@ private:
display.setCursor(0, footerY);
#if defined(LilyGo_T5S3_EPaper_Pro)
display.print("Boot:Back Tap:New Trace");
#elif defined(MECK_TWATCH)
display.print("Hold: New Trace");
#else
display.print("Sh+Del:Back Ent:New Trace");
#endif
@@ -843,9 +907,15 @@ private:
MenuItem item = menuItemAt(_menuSel);
switch (item) {
case MENU_TYPE_PATH:
// Enter edit mode -- pre-fill with current path if any
pathToEditBuf();
#if defined(MECK_TWATCH)
// Watch: no physical keys -- request the on-screen keyboard. The main
// loop polls wantsKeyboard() and opens it, pre-filled with the path.
_wantKeyboard = true;
#else
// Enter inline edit mode -- pre-fill with current path if any
_editing = true;
#endif
return true;
case MENU_ADD_HOP:
+18 -4
View File
@@ -1694,7 +1694,7 @@ void UITask::begin(DisplayDriver* display, SensorManager* sensors, NodePrefs* no
_sensors = sensors;
_auto_off = millis() + AUTO_OFF_MILLIS;
#if (defined(PIN_USER_BTN) || defined(MECK_PMU_BUTTON)) && !defined(MECK_DIAG_NO_PMU)
#if (defined(PIN_USER_BTN) || defined(MECK_PMU_BUTTON))
user_btn.begin();
#endif
#if defined(PIN_USER_BTN_ANA)
@@ -2291,7 +2291,7 @@ void UITask::shutdown(bool restart){
}
bool UITask::isButtonPressed() const {
#if (defined(PIN_USER_BTN) || defined(MECK_PMU_BUTTON)) && !defined(MECK_DIAG_NO_PMU)
#if (defined(PIN_USER_BTN) || defined(MECK_PMU_BUTTON))
return user_btn.isPressed();
#else
return false;
@@ -2314,7 +2314,7 @@ void UITask::loop() {
}
}
#endif
#if (defined(PIN_USER_BTN) || defined(MECK_PMU_BUTTON)) && !defined(MECK_DIAG_NO_PMU)
#if (defined(PIN_USER_BTN) || defined(MECK_PMU_BUTTON))
int ev = user_btn.check();
if (ev == BUTTON_EVENT_CLICK) {
#if defined(LilyGo_T5S3_EPaper_Pro)
@@ -2511,7 +2511,7 @@ void UITask::loop() {
if (buzzer.isPlaying()) buzzer.loop();
#endif
#if defined(LILYGO_TWATCH_S3)
#if defined(LILYGO_TWATCH_S3) && !defined(MECK_DIAG_NO_ALARM)
// Alarms are checked every loop, not from the screen's poll(), so one fires
// with the display off or another screen showing.
if (watch_alarm_screen) {
@@ -2626,6 +2626,12 @@ if (curr) curr->poll();
if (watch_channel_cfg_screen) setCurrScreen(watch_channel_cfg_screen);
else gotoHomeScreen();
if (serr) showAlert(serr, 1200);
} else if (purpose == TWatchKeyboardScreen::TWKB_TRACE_PATH) {
TraceScreen* ts = (TraceScreen*)getTraceScreen();
if (ts) ts->setTypedPath(sendText);
kb->clearOutBuf();
if (trace_screen) setCurrScreen(trace_screen);
else gotoHomeScreen();
} else { // TWKB_ADMIN_PASSWORD or TWKB_ADMIN_CLI
RepeaterAdminScreen* admin = (RepeaterAdminScreen*)getRepeaterAdminScreen();
if (admin) {
@@ -2666,6 +2672,14 @@ if (curr) curr->poll();
gotoSettingsScreen();
}
}
else if (curr == trace_screen && trace_screen != nullptr) {
TraceScreen* ts = (TraceScreen*)trace_screen;
if (ts->wantsKeyboard()) {
ts->clearWantKeyboard();
openTWatchKeyboard(TWatchKeyboardScreen::TWKB_TRACE_PATH, 0);
((TWatchKeyboardScreen*)tw_keyboard)->setInitialText(ts->getCurrentPathAsText());
}
}
else if (curr == settings_screen && settings_screen != nullptr) {
SettingsScreen* ss = (SettingsScreen*)settings_screen;
if (ss->wantsWatchChannels()) {
@@ -492,7 +492,7 @@ class TWatchKeyboardScreen : public UIScreen {
uint8_t _channelIdx;
public:
enum Purpose { TWKB_CHANNEL, TWKB_DM, TWKB_ADMIN_PASSWORD, TWKB_ADMIN_CLI, TWKB_PATH, TWKB_NOTE, TWKB_SCOPE };
enum Purpose { TWKB_CHANNEL, TWKB_DM, TWKB_ADMIN_PASSWORD, TWKB_ADMIN_CLI, TWKB_PATH, TWKB_NOTE, TWKB_SCOPE, TWKB_TRACE_PATH };
private:
Purpose _purpose;
@@ -180,17 +180,11 @@ bool TWatchS3Board::power_init() {
PMU->disableIRQ(XPOWERS_AXP2101_ALL_IRQ);
PMU->clearIrqStatus();
#if !defined(MECK_DIAG_NO_PMU)
// SHORT gives the click; NEGATIVE/POSITIVE are the press/release edges that
// back PMUButton::isPressed().
PMU->enableIRQ(XPOWERS_AXP2101_PKEY_SHORT_IRQ |
XPOWERS_AXP2101_PKEY_NEGATIVE_IRQ |
XPOWERS_AXP2101_PKEY_POSITIVE_IRQ);
#endif
// TEMP DIAGNOSTIC (MECK_DIAG_NO_PMU, power bisect): with the PKEY IRQs left
// disabled above and PMUButton::check() skipped in UITask, the PMU-button
// subsystem is fully inert -- no IRQ config, no 30 ms polling, no dangling
// latch. disableIRQ(ALL)+clearIrqStatus above already left the PMU quiet.
// 150 mA = 0.32C on the 470 mAh cell -- a modest bump over the 125 mA
// vendor default (LilyGoWatchS3.cpp uses 125), well under the 0.5C rule.
+12 -13
View File
@@ -138,20 +138,19 @@ lib_deps =
lib_ignore =
AsyncTCP
ESPAsyncWebServer
; ---------------------------------------------------------------------------
; TEMP DIAGNOSTIC BUILD -- power-drain bisect (remove when concluded).
; The S3 standalone firmware with the PMU-button subsystem fully disabled,
; gated by MECK_DIAG_NO_PMU: the PKEY IRQ enable in power_init is skipped and
; PMUButton::check()/begin()/isPressed() are compiled out of UITask, so there
; is no IRQ config and no 30 ms I2C polling of the PMU (and no dangling latch).
; Run the same wear test as the standalone S3 build; a materially slower drain
; implicates the PMU-button subsystem in the S3-only excess. The PWR key does
; nothing on this build (no click, no wake, no path-editor open) by design.
; Revert: delete this env and grep MECK_DIAG_NO_PMU (one block in
; TWatchS3Board.cpp, three in UITask.cpp).
; Flash: pio run -e meck_twatch_s3_diag_nopmu -t upload
; The S3 standalone firmware with the per-loop WatchAlarmScreen tick() disabled,
; gated by MECK_DIAG_NO_ALARM: the alarm-check block in UITask::loop is compiled
; out, so localNow() (an RTC read) and the slot scan no longer run every loop.
; The alarm screen is still created and menu-reachable, but no alarm can fire on
; this build by design. Run the same wear test as the standalone S3 build; a
; materially slower drain implicates the alarm tick() in the S3-only excess.
; Revert: delete this env and grep MECK_DIAG_NO_ALARM (one block in UITask.cpp).
; Flash: pio run -e meck_twatch_s3_diag_noalarm -t upload
; ---------------------------------------------------------------------------
[env:meck_twatch_s3_diag_nopmu]
[env:meck_twatch_s3_diag_noalarm]
extends = LilyGo_TWatchS3
build_flags =
${LilyGo_TWatchS3.build_flags}
@@ -160,8 +159,8 @@ build_flags =
-D MAX_GROUP_CHANNELS=40
-D OFFLINE_QUEUE_SIZE=1
-D ESP32_CPU_FREQ=80
-D MECK_DIAG_NO_PMU
-D FIRMWARE_VERSION='"Meck TWatch S3 DiagNoPMU v0.1"'
-D MECK_DIAG_NO_ALARM
-D FIRMWARE_VERSION='"Meck TWatch S3 DiagNoAlarm v0.1"'
build_src_filter = ${LilyGo_TWatchS3.build_src_filter}
+<helpers/esp32/*.cpp>
-<helpers/esp32/SerialBLEInterface.cpp>
@@ -492,7 +492,7 @@ class TWatchKeyboardScreen : public UIScreen {
uint8_t _channelIdx;
public:
enum Purpose { TWKB_CHANNEL, TWKB_DM, TWKB_ADMIN_PASSWORD, TWKB_ADMIN_CLI, TWKB_PATH, TWKB_NOTE, TWKB_SCOPE };
enum Purpose { TWKB_CHANNEL, TWKB_DM, TWKB_ADMIN_PASSWORD, TWKB_ADMIN_CLI, TWKB_PATH, TWKB_NOTE, TWKB_SCOPE, TWKB_TRACE_PATH };
private:
Purpose _purpose;