From f6bce4f5ee21ca19fd0a95d2dbdb1ec2ee47c4dd Mon Sep 17 00:00:00 2001 From: pelgraine <140762863+pelgraine@users.noreply.github.com> Date: Thu, 4 Jun 2026 22:29:14 +1000 Subject: [PATCH] Max - fix missing -D HAS_4G_MODEM=1 flags. Revised settingscreen so it uses a scroll bar instead of the heading total counter. New brightness scale in settings to control heart button brightness toggle strength, which also persists after reboot. --- examples/companion_radio/DataStore.cpp | 10 ++++ examples/companion_radio/NodePrefs.h | 5 ++ examples/companion_radio/main.cpp | 15 ++++-- .../companion_radio/ui-new/Settingsscreen.h | 52 ++++++++++++++++--- examples/companion_radio/ui-new/UITask.cpp | 2 +- variants/lilygo_tdeck_max/platformio.ini | 10 ++-- 6 files changed, 80 insertions(+), 14 deletions(-) diff --git a/examples/companion_radio/DataStore.cpp b/examples/companion_radio/DataStore.cpp index 3a802918..67dc86e3 100644 --- a/examples/companion_radio/DataStore.cpp +++ b/examples/companion_radio/DataStore.cpp @@ -197,6 +197,11 @@ void DataStore::loadPrefs(NodePrefs& prefs, double& node_lat, double& node_lon) savePrefs(prefs, node_lat, node_lon); // save to new filename _fs->remove("/node_prefs"); // remove old } + // Fresh device (no prefs file) leaves backlight at memset 0; treat any + // out-of-range value as the default so the heart button has a sane level. + if (prefs.backlight_brightness_pct < 5 || prefs.backlight_brightness_pct > 100) { + prefs.backlight_brightness_pct = 100; + } } void DataStore::loadPrefsInt(const char *filename, NodePrefs& _prefs, double& node_lat, double& node_lon) { @@ -295,6 +300,9 @@ void DataStore::loadPrefsInt(const char *filename, NodePrefs& _prefs, double& no if (file.read((uint8_t *)&_prefs.lora_antenna, sizeof(_prefs.lora_antenna)) != sizeof(_prefs.lora_antenna)) { _prefs.lora_antenna = 0; // default: internal antenna } + if (file.read((uint8_t *)&_prefs.backlight_brightness_pct, sizeof(_prefs.backlight_brightness_pct)) != sizeof(_prefs.backlight_brightness_pct)) { + _prefs.backlight_brightness_pct = 100; // default: full brightness + } // Clamp to valid ranges if (_prefs.dark_mode > 1) _prefs.dark_mode = 0; @@ -309,6 +317,7 @@ void DataStore::loadPrefsInt(const char *filename, NodePrefs& _prefs, double& no if (_prefs.channel_notif[i] > 2) _prefs.channel_notif[i] = 0; } if (_prefs.lora_antenna > 1) _prefs.lora_antenna = 0; + if (_prefs.backlight_brightness_pct < 5 || _prefs.backlight_brightness_pct > 100) _prefs.backlight_brightness_pct = 100; // auto_lock_minutes: only accept known options (0, 2, 5, 10, 15, 30) { uint8_t alm = _prefs.auto_lock_minutes; @@ -370,6 +379,7 @@ void DataStore::savePrefs(const NodePrefs& _prefs, double node_lat, double node_ file.write((uint8_t *)_prefs.default_scope_key, sizeof(_prefs.default_scope_key)); // 137 file.write((uint8_t *)_prefs.channel_notif, sizeof(_prefs.channel_notif)); // 153 file.write((uint8_t *)&_prefs.lora_antenna, sizeof(_prefs.lora_antenna)); // 174 + file.write((uint8_t *)&_prefs.backlight_brightness_pct, sizeof(_prefs.backlight_brightness_pct)); // 175 file.close(); } diff --git a/examples/companion_radio/NodePrefs.h b/examples/companion_radio/NodePrefs.h index 05ebe490..4f4b5e89 100644 --- a/examples/companion_radio/NodePrefs.h +++ b/examples/companion_radio/NodePrefs.h @@ -69,6 +69,11 @@ struct NodePrefs { // persisted to file // Applied at boot in main.cpp and live on toggle in SettingsScreen. uint8_t lora_antenna; // 0 = internal (default), 1 = external + // --- Backlight (e-ink frontlight) brightness, T-Deck Pro MAX only --- + // Percentage (5..100) the heart button toggles the frontlight on to. + // 0 = unset/legacy; treated as the default 100 on load. + uint8_t backlight_brightness_pct; // 5..100, default 100 + // --- Font helpers (inline, no overhead) --- // Returns the DisplayDriver text-size index for "small/body" text. // T-Deck Pro: 0 = built-in 6×8 (or 7pt with custom fonts), 1 = 9pt. diff --git a/examples/companion_radio/main.cpp b/examples/companion_radio/main.cpp index 6271c301..100df87a 100644 --- a/examples/companion_radio/main.cpp +++ b/examples/companion_radio/main.cpp @@ -1237,9 +1237,13 @@ static void lastHeardToggleContact() { Serial.printf("[key] id=%u pressed=%u\n", key_id, pressed ? 1u : 0u); // TEMP: confirm mapping if (!pressed) return; switch (key_id) { - case 0: // heart -- toggle frontlight at full brightness - if (board.isBacklightOn()) board.backlightOff(); - else board.backlightSetBrightness(255); + case 0: // heart -- toggle frontlight at the user-set brightness + if (board.isBacklightOn()) { + board.backlightOff(); + } else { + uint8_t _blPct = the_mesh.getNodePrefs()->backlight_brightness_pct; + board.backlightSetBrightness((uint8_t)((_blPct * 255 + 50) / 100)); + } break; case 1: // speech bubble -- open channel picker (same as 'M') ui_task.gotoChannelPickerScreen(); @@ -4170,7 +4174,10 @@ void loop() { lastTouchAccepted = now; if (smsScr->handleTouch(tx, ty)) { dialerNeedsRefresh = true; - lastDialerRefresh = millis(); + // Don't reset the gate on touch: COMPOSE_REFRESH_INTERVAL is + // measured from the last completed render (set at the render + // site), so the first tap after a pause repaints immediately and + // only rapid successive taps stay throttled (no overlap). } } } else { diff --git a/examples/companion_radio/ui-new/Settingsscreen.h b/examples/companion_radio/ui-new/Settingsscreen.h index cc455fe3..a9916120 100644 --- a/examples/companion_radio/ui-new/Settingsscreen.h +++ b/examples/companion_radio/ui-new/Settingsscreen.h @@ -140,6 +140,7 @@ enum SettingsRowType : uint8_t { ROW_CR, // Coding rate (5-8) ROW_TX_POWER, // TX power (1-20 dBm) ROW_UTC_OFFSET, // UTC offset (-12 to +14) + ROW_BACKLIGHT_BRIGHTNESS, // Backlight brightness % the heart button toggles to (MAX only) ROW_MSG_NOTIFY, // Keyboard flash on new msg toggle ROW_DARK_MODE, // Dark mode toggle (inverted display) ROW_LARGE_FONT, // Font size toggle: 0=tiny (default), 1=larger @@ -490,6 +491,9 @@ private: addRow(ROW_CR); addRow(ROW_TX_POWER); addRow(ROW_UTC_OFFSET); +#if defined(LilyGo_TDeck_Pro_Max) + addRow(ROW_BACKLIGHT_BRIGHTNESS); +#endif addRow(ROW_MSG_NOTIFY); #if HAS_GPS addRow(ROW_GPS_BAUD); @@ -1751,10 +1755,7 @@ public: display.print("Settings"); } - // Right side: row indicator - snprintf(tmp, sizeof(tmp), "%d/%d", _cursor + 1, _numRows); - display.setCursor(display.width() - display.getTextWidth(tmp) - 2, 0); - display.print(tmp); + // (Row indicator is now a scrollbar on the right of the list body.) display.drawRect(0, 11, display.width(), 1); @@ -1771,6 +1772,12 @@ public: _scrollTop = max(0, min(_cursor - maxVisible / 2, _numRows - maxVisible)); int endIdx = min(_numRows, _scrollTop + maxVisible); + // Scrollbar: always shown on the top-level list; on sub-screens only when + // the rows overflow the page. When shown, reserve a few px on the right so + // the selection highlight isn't painted under it. + bool showScrollbar = (_subScreen == SUB_NONE) || (_numRows > maxVisible); + int sbW = showScrollbar ? 6 : 0; + int y = headerH; for (int i = _scrollTop; i < endIdx && y + lineHeight <= maxY; i++) { @@ -1783,9 +1790,9 @@ public: #if defined(LilyGo_T5S3_EPaper_Pro) // FreeSans12pt: baseline at (y+5)*scale_y, ascent ~17px above. // Highlight needs to start above the baseline to cover ascenders. - display.fillRect(0, y, display.width(), lineHeight); + display.fillRect(0, y, display.width() - sbW, lineHeight); #else - display.fillRect(0, y + _prefs->smallHighlightOff(), display.width(), lineHeight); + display.fillRect(0, y + _prefs->smallHighlightOff(), display.width() - sbW, lineHeight); #endif display.setColor(DisplayDriver::DARK); } else { @@ -1877,6 +1884,15 @@ public: display.print(tmp); break; + case ROW_BACKLIGHT_BRIGHTNESS: + if (editing && _editMode == EDIT_NUMBER) { + snprintf(tmp, sizeof(tmp), "Brightness: %d%% " EDIT_ADJ_HINT, _editInt); + } else { + snprintf(tmp, sizeof(tmp), "Backlight Brightness: %d%%", _prefs->backlight_brightness_pct); + } + display.print(tmp); + break; + case ROW_MSG_NOTIFY: snprintf(tmp, sizeof(tmp), "Msg LED Flash: %s", _prefs->kb_flash_notify ? "ON" : "OFF"); @@ -2260,6 +2276,21 @@ public: y += lineHeight; } + // Scrollbar (track + proportional thumb), mirroring the notif-sound picker. + if (showScrollbar) { + int sbX = display.width() - 6; + int sbTop = headerH; + int sbH = maxY - headerH; + display.setColor(DisplayDriver::LIGHT); + display.drawRect(sbX, sbTop, 3, sbH); + int thumbH = max(4, (maxVisible * sbH) / _numRows); + if (thumbH > sbH) thumbH = sbH; + int maxScroll = _numRows - maxVisible; + if (maxScroll < 1) maxScroll = 1; + int thumbY = sbTop + (_scrollTop * (sbH - thumbH)) / maxScroll; + display.fillRect(sbX + 1, thumbY + 1, 1, thumbH - 2); + } + display.setTextSize(1); // === Confirmation overlay === @@ -3384,6 +3415,7 @@ public: case ROW_CR: if (_editInt < 8) _editInt++; break; case ROW_TX_POWER: if (_editInt < MAX_LORA_TX_POWER) _editInt++; break; case ROW_UTC_OFFSET: if (_editInt < 14) _editInt++; break; + case ROW_BACKLIGHT_BRIGHTNESS: if (_editInt < 100) { _editInt += 5; if (_editInt > 100) _editInt = 100; } break; case ROW_PATH_HASH_SIZE: if (_editInt < 3) _editInt++; break; default: break; } @@ -3401,6 +3433,7 @@ public: case ROW_CR: if (_editInt > 5) _editInt--; break; case ROW_TX_POWER: if (_editInt > 1) _editInt--; break; case ROW_UTC_OFFSET: if (_editInt > -12) _editInt--; break; + case ROW_BACKLIGHT_BRIGHTNESS: if (_editInt > 5) { _editInt -= 5; if (_editInt < 5) _editInt = 5; } break; case ROW_PATH_HASH_SIZE: if (_editInt > 1) _editInt--; break; default: break; } @@ -3429,6 +3462,10 @@ public: _prefs->utc_offset_hours = (int8_t)constrain(_editInt, -12, 14); the_mesh.savePrefs(); break; + case ROW_BACKLIGHT_BRIGHTNESS: + _prefs->backlight_brightness_pct = (uint8_t)constrain(_editInt, 5, 100); + the_mesh.savePrefs(); + break; case ROW_PATH_HASH_SIZE: _prefs->path_hash_mode = (uint8_t)constrain(_editInt - 1, 0, 2); // display 1-3, store 0-2 the_mesh.savePrefs(); @@ -3515,6 +3552,9 @@ public: case ROW_UTC_OFFSET: startEditInt(_prefs->utc_offset_hours); break; + case ROW_BACKLIGHT_BRIGHTNESS: + startEditInt(_prefs->backlight_brightness_pct); + break; case ROW_MSG_NOTIFY: _prefs->kb_flash_notify = _prefs->kb_flash_notify ? 0 : 1; the_mesh.savePrefs(); diff --git a/examples/companion_radio/ui-new/UITask.cpp b/examples/companion_radio/ui-new/UITask.cpp index 6317918c..34a9cafb 100644 --- a/examples/companion_radio/ui-new/UITask.cpp +++ b/examples/companion_radio/ui-new/UITask.cpp @@ -717,7 +717,7 @@ public: display.setColor(DisplayDriver::YELLOW); display.drawTextCentered(display.width() / 2, y, "[R] Trace [J] Games "); display.setColor(DisplayDriver::LIGHT); - y += 14; + y += 10; #if defined(HAS_4G_MODEM) && defined(MECK_AUDIO_VARIANT) // Phone on its own line below Trace/Games, centered like the "Press:" header display.drawTextCentered(display.width() / 2, y, "[T] Phone"); diff --git a/variants/lilygo_tdeck_max/platformio.ini b/variants/lilygo_tdeck_max/platformio.ini index 1faa3fd2..16bee4f3 100644 --- a/variants/lilygo_tdeck_max/platformio.ini +++ b/variants/lilygo_tdeck_max/platformio.ini @@ -206,6 +206,7 @@ build_flags = -D OFFLINE_QUEUE_SIZE=256 -D MECK_WEB_READER=1 -D MECK_OTA_UPDATE=1 + -D HAS_4G_MODEM=1 -D FIRMWARE_VERSION='"Meck v1.11.MAX.WiFi"' build_src_filter = ${LilyGo_TDeck_Pro_Max.build_src_filter} + @@ -220,7 +221,7 @@ lib_deps = bitbank2/PNGdec ; MAX standalone (no BLE/WiFi — maximum battery life, LoRa mesh only) -; Contacts in PSRAM (1500 capacity). OTA enabled (WiFi AP on demand). +; Contacts in PSRAM (2000 capacity). OTA enabled (WiFi AP on demand). [env:meck_max_standalone] extends = LilyGo_TDeck_Pro_Max build_flags = @@ -230,6 +231,8 @@ build_flags = -D MAX_GROUP_CHANNELS=40 -D OFFLINE_QUEUE_SIZE=1 -D MECK_OTA_UPDATE=1 + -D HAS_4G_MODEM=1 + -D MECK_WEB_READER=1 -D FIRMWARE_VERSION='"Meck v1.11.MAX.SA"' build_src_filter = ${LilyGo_TDeck_Pro_Max.build_src_filter} + @@ -239,5 +242,6 @@ build_src_filter = ${LilyGo_TDeck_Pro_Max.build_src_filter} + lib_deps = ${LilyGo_TDeck_Pro_Max.lib_deps} - bitbank2/PNGdec - densaugeo/base64 @ ~1.4.0 \ No newline at end of file + densaugeo/base64 @ ~1.4.0 + bitbank2/JPEGDEC + bitbank2/PNGdec \ No newline at end of file