mirror of
https://github.com/ALLFATHER-BV/wadamesh.git
synced 2026-08-14 09:30:04 +00:00
touch: beta_34 — buffered LoRa receive ON by default (test channel) + core pin core-v1.16.5
Consumes the new core (buffered receive drain task + RX counters, #13 stock payload layout, GPS-date guard). Fork side: - TouchPrefs v33 rx_queue, DEFAULT ON for the test channel (fresh installs + a one-time migrate for upgraders); opt-out toggle 'Buffered receive (experimental)' in Radio & Mesh, applies live. - About live tier shows LoRa RX heard/read/err/late-lost/qdrop, the field evidence for the missed-messages fix. - Spectrum parks/resumes the drain task around raw radio use; MyMesh setParams sequences hold the radio mutex. - TanmatsuLoraRadio: no-op API stubs (P4 keeps its own RX path). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
5d9a184314
commit
4e1ac2ca6b
+2
-2
@@ -178,7 +178,7 @@ lib_deps =
|
||||
; MeshCore core = the meshcomod monorepo built as a lib, pinned by tag.
|
||||
; Touch-app files (TouchPrefsStore, WifiRuntimeStore, transports, ...) are
|
||||
; vendored in this app's src/ and dropped from the lib via -DMC_VENDORED_TOUCH_APP.
|
||||
https://github.com/ALLFATHER-BV/meshcomod.git#core-v1.16.4
|
||||
https://github.com/ALLFATHER-BV/meshcomod.git#core-v1.16.5
|
||||
SPI
|
||||
Wire
|
||||
jgromes/RadioLib @ ^7.6.0
|
||||
@@ -349,7 +349,7 @@ build_src_filter =
|
||||
+<../variants/lilygo_tdeck/*.cpp>
|
||||
|
||||
lib_deps =
|
||||
https://github.com/ALLFATHER-BV/meshcomod.git#core-v1.16.4
|
||||
https://github.com/ALLFATHER-BV/meshcomod.git#core-v1.16.5
|
||||
SPI
|
||||
Wire
|
||||
jgromes/RadioLib @ ^7.6.0
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
# beta_34 (TEST) - buffered receive: the missed-messages fix, ON by default on this test build. On top of beta_33.
|
||||
# One user-facing note per non-blank, non-# line; # lines are section comments.
|
||||
|
||||
# --- New ---
|
||||
This build changes how the device collects packets from the LoRa radio, and the new way is ON by default so the test channel can prove it. Background: the radio chip holds only one received packet at a time, and until now the firmware only collected it when the main loop got around to it, the same loop that draws the screen. If the interface kept the loop busy while two packets arrived back to back, the first one was silently overwritten. That is the "my device missed a message another device right next to it heard" problem some of you reported, and why it affected this firmware but not stock MeshCore, which has no screen to draw. A small dedicated background task now collects every packet within about a millisecond of it arriving, no matter what the interface is doing.
|
||||
Because the receive path is the heart of the device, this ships with a safety hatch: if you see anything odd with receiving, sending, or the Spectrum analyzer, turn off "Buffered receive (experimental)" in Settings > Radio & Mesh (applies instantly, no reboot) and you are back on the old path. Please report it either way, that is exactly what the test channel is for.
|
||||
The About page now shows LoRa receive counters (heard / read / late-lost). "late-lost" counts packets the radio heard but the firmware failed to collect in time. With buffered receive on it should stay at zero. If you turn the toggle off on a busy mesh you may watch it creep up, which is the old bug happening live.
|
||||
|
||||
# --- Fixed ---
|
||||
Messages sent by wadamesh no longer carry a few stray bytes at the end that stock MeshCore devices and some companion apps displayed as garbage characters after the text (issue #13).
|
||||
A GPS module reporting a bogus date can no longer drag the device clock back to the year 1902 (core clock guard).
|
||||
@@ -2539,7 +2539,11 @@ MyMesh::MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMe
|
||||
}
|
||||
|
||||
void MyMesh::applyRadioFromPrefs() {
|
||||
// setParams is a multi-step SPI sequence; hold the radio against the buffered-
|
||||
// receive drain task so it can't re-arm RX between the steps (no-op when off).
|
||||
radio_driver.radioAcquire();
|
||||
radio_driver.setParams(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr);
|
||||
radio_driver.radioRelease();
|
||||
radio_driver.setTxPower(_prefs.tx_power_dbm);
|
||||
#if defined(USE_SX1262) || defined(USE_SX1268)
|
||||
_prefs.rx_boosted_gain = _prefs.rx_boosted_gain ? 1 : 0;
|
||||
@@ -3338,10 +3342,12 @@ void MyMesh::handleCmdFrame(size_t len) {
|
||||
_prefs.client_repeat = repeat;
|
||||
savePrefs();
|
||||
|
||||
radio_driver.radioAcquire(); // hold off the RX drain task mid-sequence (no-op when off)
|
||||
radio_driver.setParams(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr);
|
||||
#if defined(USE_SX1262) || defined(USE_SX1268) || defined(SX126X_RX_BOOSTED_GAIN)
|
||||
radio_driver.setRxBoostedGainMode(_prefs.rx_boosted_gain != 0);
|
||||
#endif
|
||||
radio_driver.radioRelease();
|
||||
MESH_DEBUG_PRINTLN("OK: CMD_SET_RADIO_PARAMS: f=%d, bw=%d, sf=%d, cr=%d", freq, bw, (uint32_t)sf,
|
||||
(uint32_t)cr);
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ static bool s_begun = false;
|
||||
// short read (→ treat as absent → defaults); `ver` lets later builds add fields.
|
||||
static const char* KEY_CFG = "cfg";
|
||||
static const uint16_t TOUCH_CFG_MAGIC = 0x5743; // 'WC' (WadaCfg)
|
||||
static const uint8_t TOUCH_CFG_VER = 32; // v2 sig_probe/poll; v3 tz_zone; v4 hide_node_name; v5 map_night/map_zoom; v6 map text/marker visibility; v7 app_grid_large; v8 ui_scale; v9 tb_keypad; v10 sleep_idle; v11 nav_keys; v12 map_zoom_buttons; v13 nav_dir_keys; v14 home_is_drawer; v15 kbd_nav default ON (one-time migrate); v16 nav_scroll_keys; v17 notify_new_contact; v18 kbd_nav OFF by default (reverses v15; T-Deck/V4 only, Tanmatsu stays on); v19 show_sensors_tab; v20 map_show_links; v21 map_style (0=OSM default, 1=OpenTopoMap); v22 tb_nav; v23 scope_direct (opt-in: scope direct/login floods to the region); v24 tb_nav default OFF (experimental); v25 fem_lna (Heltec V4.3 high-gain FEM LNA, opt-in); v26 msg_flash (flash keyboard backlight + wake screen on a new message, opt-in); v27 flood_adv_hrs + local_adv_min (periodic self-advert intervals, the standard MeshCore flood/local advert on a timer); v28 beta_updates (opt-in to test/beta firmware on the OTA update check + install); v29 ui_scale default -> Large/150% (Tanmatsu; bumps the old 100% default, leaves an explicit Large/Huge choice); v30 boot_advert (opt-in one-shot flood self-advert ~6s after boot, all boards, #76); v31 compact_chat (opt-in IRC-style dense chat rows instead of bubbles); v32 clock_floor (highest epoch handed out — monotonic send-timestamp floor across reboots, #89)
|
||||
static const uint8_t TOUCH_CFG_VER = 33; // v2 sig_probe/poll; v3 tz_zone; v4 hide_node_name; v5 map_night/map_zoom; v6 map text/marker visibility; v7 app_grid_large; v8 ui_scale; v9 tb_keypad; v10 sleep_idle; v11 nav_keys; v12 map_zoom_buttons; v13 nav_dir_keys; v14 home_is_drawer; v15 kbd_nav default ON (one-time migrate); v16 nav_scroll_keys; v17 notify_new_contact; v18 kbd_nav OFF by default (reverses v15; T-Deck/V4 only, Tanmatsu stays on); v19 show_sensors_tab; v20 map_show_links; v21 map_style (0=OSM default, 1=OpenTopoMap); v22 tb_nav; v23 scope_direct (opt-in: scope direct/login floods to the region); v24 tb_nav default OFF (experimental); v25 fem_lna (Heltec V4.3 high-gain FEM LNA, opt-in); v26 msg_flash (flash keyboard backlight + wake screen on a new message, opt-in); v27 flood_adv_hrs + local_adv_min (periodic self-advert intervals, the standard MeshCore flood/local advert on a timer); v28 beta_updates (opt-in to test/beta firmware on the OTA update check + install); v29 ui_scale default -> Large/150% (Tanmatsu; bumps the old 100% default, leaves an explicit Large/Huge choice); v30 boot_advert (opt-in one-shot flood self-advert ~6s after boot, all boards, #76); v31 compact_chat (opt-in IRC-style dense chat rows instead of bubbles); v32 clock_floor (highest epoch handed out — monotonic send-timestamp floor across reboots, #89); v33 rx_queue (buffered LoRa receive: drain task + packet ring, experimental, default OFF)
|
||||
|
||||
// Defaults (kept identical to the historical per-key defaults).
|
||||
static const uint16_t DEFAULT_SCREEN_TIMEOUT_S = 20;
|
||||
@@ -102,6 +102,7 @@ struct __attribute__((packed)) TouchCfg {
|
||||
uint8_t boot_advert; // one-shot flood self-advert ~6s after boot (bool, 0=off) — v30 (trailing) — #76
|
||||
uint8_t compact_chat; // IRC-style dense chat rows instead of bubbles (bool, 0=off) — v31 (trailing)
|
||||
uint32_t clock_floor; // highest epoch this device handed out (ClockFloorRTC persistence) — v32 (trailing)
|
||||
uint8_t rx_queue; // buffered LoRa receive: drain task + packet ring (bool, 0=off, experimental) — v33 (trailing)
|
||||
};
|
||||
|
||||
static TouchCfg s_cfg;
|
||||
@@ -174,6 +175,7 @@ static void cfgSetDefaults(TouchCfg& c) {
|
||||
c.boot_advert = 0; // OFF: no automatic advert on boot — opt-in (#76)
|
||||
c.compact_chat = 0; // OFF: bubble chat layout (opt-in IRC-style dense rows)
|
||||
c.clock_floor = 0; // no persisted send-timestamp floor yet
|
||||
c.rx_queue = 1; // ON: buffered receive (test-channel default; opt-out toggle in Radio & Mesh)
|
||||
c.sleep_idle = 0; // default: idle light-sleep OFF
|
||||
{ const char* d = "ertui"; for (int i = 0; i < 5; i++) c.nav_keys[i] = (uint8_t)d[i]; } // default tab hotkeys E/R/T/U/I
|
||||
c.map_zoom_buttons = 0; // default: map zoom = slider
|
||||
@@ -256,6 +258,7 @@ static void cfgLoadOrMigrate() {
|
||||
if (s_cfg.ver < 30) s_cfg.boot_advert = 0; // #76 new trailing field: advert-on-boot off by default
|
||||
if (s_cfg.ver < 31) s_cfg.compact_chat = 0; // new trailing field: compact chat rows off by default
|
||||
if (s_cfg.ver < 32) s_cfg.clock_floor = 0; // new trailing field: no send-timestamp floor persisted yet (#89)
|
||||
if (s_cfg.ver < 33) s_cfg.rx_queue = 1; // buffered LoRa receive ON for the test channel (opt-out toggle in Radio & Mesh)
|
||||
s_cfg.ver = TOUCH_CFG_VER;
|
||||
s_cfg.magic = TOUCH_CFG_MAGIC;
|
||||
cfgFlush(); // rewrite with new fields defaulted-in
|
||||
@@ -997,6 +1000,16 @@ bool touchPrefsSetBootAdvert(bool on) {
|
||||
return cfgFlush();
|
||||
}
|
||||
|
||||
bool touchPrefsGetRxQueue() {
|
||||
if (!s_begun) touchPrefsBegin();
|
||||
return s_cfg.rx_queue != 0;
|
||||
}
|
||||
bool touchPrefsSetRxQueue(bool on) {
|
||||
if (!s_begun) touchPrefsBegin();
|
||||
s_cfg.rx_queue = on ? 1 : 0;
|
||||
return cfgFlush();
|
||||
}
|
||||
|
||||
bool touchPrefsGetCompactChat() {
|
||||
if (!s_begun) touchPrefsBegin();
|
||||
return s_cfg.compact_chat != 0;
|
||||
|
||||
@@ -157,6 +157,12 @@ bool touchPrefsSetBootAdvert(bool on);
|
||||
* bubbles, so far more history fits on screen. Opt-in, default off (bubbles). */
|
||||
bool touchPrefsGetCompactChat();
|
||||
bool touchPrefsSetCompactChat(bool on);
|
||||
|
||||
/* Buffered LoRa receive (experimental): a high-priority drain task lifts each packet
|
||||
* out of the radio within ~1 ms so UI-thread stalls can't overwrite unread packets
|
||||
* (the missed-messages class). Opt-in, default off = stock receive path. */
|
||||
bool touchPrefsGetRxQueue();
|
||||
bool touchPrefsSetRxQueue(bool on);
|
||||
uint32_t touchPrefsGetClockFloor(); // monotonic send-timestamp floor (#89)
|
||||
bool touchPrefsSetClockFloor(uint32_t epoch); // only ever grows; no-op below current
|
||||
|
||||
|
||||
+103
-26
@@ -3632,7 +3632,8 @@ static lv_obj_t* s_chat_unread_badge = nullptr; // red unread-count badge over
|
||||
static lv_obj_t* s_tab_indicator = nullptr; // thin rounded accent glow bar under the active tab
|
||||
static lv_obj_t* s_update_subtab_badge = nullptr;// red dot over the "About" sub-tab button
|
||||
static lv_obj_t* s_update_about_lbl = nullptr; // status line on the About sub-tab
|
||||
static lv_obj_t* s_sysinfo_lbl = nullptr; // System-info text (refreshed live on the About tab)
|
||||
static lv_obj_t* s_sysinfo_lbl = nullptr; // System-info LIVE tier (uptime/heap, 1 Hz on the About tab)
|
||||
static lv_obj_t* s_sysinfo_rest_lbl = nullptr; // System-info slow tier (re-set only when its text changes)
|
||||
#if defined(HAS_TDECK_GT911)
|
||||
static lv_obj_t* s_sleep_diag_lbl = nullptr; // Idle-sleep instrumentation label (Lock settings, line 1)
|
||||
static lv_obj_t* s_sleep_diag_lbl2 = nullptr; // Idle-sleep instrumentation label (Lock settings, line 2)
|
||||
@@ -7927,6 +7928,15 @@ static void radioFemLnaToggleCb(lv_event_t* e) {
|
||||
board.setFemLnaEnable(on);
|
||||
}
|
||||
#endif
|
||||
// Buffered LoRa receive (experimental): a drain task lifts each packet out of the
|
||||
// radio within ~1 ms so a busy UI thread can't overwrite unread packets (the
|
||||
// missed-messages class). Persists + applies live.
|
||||
static void radioRxQueueToggleCb(lv_event_t* e) {
|
||||
if (lv_event_get_code(e) != LV_EVENT_VALUE_CHANGED) return;
|
||||
const bool on = lv_obj_has_state(lv_event_get_target(e), LV_STATE_CHECKED);
|
||||
touchPrefsSetRxQueue(on);
|
||||
radio_driver.rxQueueEnable(on);
|
||||
}
|
||||
static void radioSigPollSaveCb(lv_event_t* e) {
|
||||
const lv_event_code_t _c = lv_event_get_code(e);
|
||||
if ((_c != LV_EVENT_CLICKED && _c != LV_EVENT_DEFOCUSED) || !s_radio_sig_poll_ta) return;
|
||||
@@ -8202,6 +8212,18 @@ static void buildRadioSettings() {
|
||||
}
|
||||
#endif
|
||||
|
||||
// Buffered receive (experimental): decouple packet pickup from the UI loop.
|
||||
{
|
||||
int rh = settingsRowLabel(body, y, 4, "Buffered receive (experimental)", COLOR_TEXT, &g_font_12, 56);
|
||||
lv_obj_t* sw = lv_switch_create(body);
|
||||
lv_obj_align(sw, LV_ALIGN_TOP_RIGHT, 0, y);
|
||||
if (touchPrefsGetRxQueue()) lv_obj_add_state(sw, LV_STATE_CHECKED);
|
||||
lv_obj_add_event_cb(sw, radioRxQueueToggleCb, LV_EVENT_VALUE_CHANGED, nullptr);
|
||||
y += LV_MAX(34, rh + 10);
|
||||
y += settingsRowLabel(body, y, 0, "Reads packets on a background task so a busy screen can't drop them.",
|
||||
COLOR_SUB, &g_font_12, 0) + 2;
|
||||
}
|
||||
|
||||
// Signal probe — same setting as the home-graph Signal popup. A periodic
|
||||
// discover advert keeps the signal bars / graph fresh. Poll interval in whole
|
||||
// minutes (1..1440); the toggle saves immediately, the field on its Set button.
|
||||
@@ -8558,7 +8580,11 @@ struct StallRec { uint32_t at_s; uint16_t dur_ms; const char* tag; };
|
||||
extern StallRec g_stall_ring[16];
|
||||
extern uint8_t g_stall_cnt, g_stall_w;
|
||||
|
||||
static void sysInfoText(char* buf, size_t cap) {
|
||||
// Live tier of the About text: the two blocks that genuinely change every second
|
||||
// (uptime + heap). Kept in their own small label so the 1 Hz refresh only re-lays
|
||||
// ~7 lines — re-setting the full ~40-line text made LVGL spend ~340 ms per second
|
||||
// rendering while About was open (the "ui:lvgl" stall-ring entries).
|
||||
static void sysInfoTextLive(char* buf, size_t cap) {
|
||||
int p = 0;
|
||||
#if defined(ESP32)
|
||||
const uint32_t up_s = (uint32_t)(esp_timer_get_time() / 1000000ULL);
|
||||
@@ -8570,19 +8596,6 @@ static void sysInfoText(char* buf, size_t cap) {
|
||||
"Uptime\n %ud %02uh %02um %02us\n\n",
|
||||
(unsigned)up_d, (unsigned)up_h, (unsigned)up_m, (unsigned)up_ss);
|
||||
|
||||
esp_chip_info_t chip;
|
||||
esp_chip_info(&chip);
|
||||
const char* model = (chip.model == CHIP_ESP32S3) ? "ESP32-S3"
|
||||
: (chip.model == CHIP_ESP32S2) ? "ESP32-S2"
|
||||
: (chip.model == CHIP_ESP32) ? "ESP32"
|
||||
: "ESP32-?";
|
||||
p += snprintf(buf + p, cap - p,
|
||||
"Chip\n %s rev %d, %d core(s)\n features:%s%s%s\n\n",
|
||||
model, (int)chip.revision, (int)chip.cores,
|
||||
(chip.features & CHIP_FEATURE_BLE) ? " BLE" : "",
|
||||
(chip.features & CHIP_FEATURE_WIFI_BGN) ? " WiFi" : "",
|
||||
(chip.features & CHIP_FEATURE_BT) ? " BT" : "");
|
||||
|
||||
const size_t dram_free = heap_caps_get_free_size(MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
|
||||
const size_t dram_tot = heap_caps_get_total_size(MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
|
||||
const size_t dram_min = heap_caps_get_minimum_free_size(MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
|
||||
@@ -8596,6 +8609,41 @@ static void sysInfoText(char* buf, size_t cap) {
|
||||
(unsigned)(psram_free / 1024),
|
||||
(unsigned)(psram_tot / 1024));
|
||||
|
||||
// LoRa RX health: DIO1 events vs packets actually read out. late-lost > 0 means
|
||||
// packets completed in the radio but were overwritten before the loop read them
|
||||
// (the missed-messages class); "buffered on" should hold it at zero.
|
||||
const uint32_t rx_evt = radio_driver.getRxEvents();
|
||||
const uint32_t rx_ok = radio_driver.getPacketsRecv();
|
||||
const uint32_t rx_err = radio_driver.getPacketsRecvErrors();
|
||||
const uint32_t rx_lost = (rx_evt > rx_ok + rx_err) ? (rx_evt - rx_ok - rx_err) : 0;
|
||||
p += snprintf(buf + p, cap - p,
|
||||
"LoRa RX\n heard: %lu read: %lu err: %lu\n late-lost: %lu qdrop: %lu buffered: %s\n\n",
|
||||
(unsigned long)rx_evt, (unsigned long)rx_ok, (unsigned long)rx_err,
|
||||
(unsigned long)rx_lost, (unsigned long)radio_driver.getRxQueueDrops(),
|
||||
radio_driver.rxQueueEnabled() ? "on" : "off");
|
||||
#endif
|
||||
(void)p; (void)cap;
|
||||
}
|
||||
|
||||
// Slow tier: changes rarely (an SD scan landing, a new stall entry) or never
|
||||
// (chip, flash, reset reason, build). refreshSysInfo() only pushes this to its
|
||||
// label when the generated text actually differs from what is displayed.
|
||||
static void sysInfoTextRest(char* buf, size_t cap) {
|
||||
int p = 0;
|
||||
#if defined(ESP32)
|
||||
esp_chip_info_t chip;
|
||||
esp_chip_info(&chip);
|
||||
const char* model = (chip.model == CHIP_ESP32S3) ? "ESP32-S3"
|
||||
: (chip.model == CHIP_ESP32S2) ? "ESP32-S2"
|
||||
: (chip.model == CHIP_ESP32) ? "ESP32"
|
||||
: "ESP32-?";
|
||||
p += snprintf(buf + p, cap - p,
|
||||
"Chip\n %s rev %d, %d core(s)\n features:%s%s%s\n\n",
|
||||
model, (int)chip.revision, (int)chip.cores,
|
||||
(chip.features & CHIP_FEATURE_BLE) ? " BLE" : "",
|
||||
(chip.features & CHIP_FEATURE_WIFI_BGN) ? " WiFi" : "",
|
||||
(chip.features & CHIP_FEATURE_BT) ? " BT" : "");
|
||||
|
||||
const uint32_t flash_chip_size = ESP.getFlashChipSize();
|
||||
#if defined(HAS_TANMATSU)
|
||||
// ESP.getSketchSize()/getFreeSketchSpace() abort() on the P4: this is an AppFS app (runs from the
|
||||
@@ -8694,17 +8742,24 @@ static void refreshSysInfo(unsigned long now) {
|
||||
next = now + 1000; // not on the About sheet — just rate-limit
|
||||
return;
|
||||
}
|
||||
// Re-setting an ~800-char label re-wraps + repaints it; doing that every second
|
||||
// WHILE the user drags or flings the page stutters the scroll badly. Hold off
|
||||
// while a pointer is pressed or a scroll/throw is in flight, and re-check soon
|
||||
// so the live uptime/heap resumes the instant they let go.
|
||||
// Re-setting a label re-wraps + repaints it; doing that every second WHILE the
|
||||
// user drags or flings the page stutters the scroll badly. Hold off while a
|
||||
// pointer is pressed or a scroll/throw is in flight, and re-check soon so the
|
||||
// live uptime/heap resumes the instant they let go.
|
||||
for (lv_indev_t* in = lv_indev_get_next(nullptr); in; in = lv_indev_get_next(in)) {
|
||||
if (lv_indev_get_scroll_dir(in) != LV_DIR_NONE) { next = now + 120; return; }
|
||||
}
|
||||
next = now + 1000;
|
||||
char buf[800];
|
||||
sysInfoText(buf, sizeof buf);
|
||||
char buf[704];
|
||||
sysInfoTextLive(buf, sizeof buf);
|
||||
lv_label_set_text(s_sysinfo_lbl, buf);
|
||||
// Slow tier: pushing identical text would still invalidate + repaint the whole
|
||||
// ~30-line label (~340 ms of LVGL render per pass) — skip unless it changed.
|
||||
if (s_sysinfo_rest_lbl) {
|
||||
sysInfoTextRest(buf, sizeof buf);
|
||||
if (strcmp(lv_label_get_text(s_sysinfo_rest_lbl), buf) != 0)
|
||||
lv_label_set_text(s_sysinfo_rest_lbl, buf);
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(HAS_TDECK_GT911)
|
||||
@@ -8740,9 +8795,9 @@ static void refreshSleepDiag(unsigned long now) {
|
||||
|
||||
static void buildSystemInfoSettings() {
|
||||
lv_obj_t* body = createSettingsModal("System info", SettingsModalKind::SystemInfo);
|
||||
char buf[800];
|
||||
sysInfoText(buf, sizeof buf);
|
||||
char buf[704];
|
||||
|
||||
// Live tier (uptime + heap): a small constant-height label refreshed at 1 Hz.
|
||||
lv_obj_t* lbl = lv_label_create(body);
|
||||
s_sysinfo_lbl = lbl;
|
||||
lv_label_set_long_mode(lbl, LV_LABEL_LONG_WRAP);
|
||||
@@ -8750,13 +8805,26 @@ static void buildSystemInfoSettings() {
|
||||
lv_obj_set_pos(lbl, 2, 0);
|
||||
lv_obj_set_style_text_font(lbl, &g_font_12, LV_PART_MAIN);
|
||||
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_TEXT), LV_PART_MAIN);
|
||||
sysInfoTextLive(buf, sizeof buf);
|
||||
lv_label_set_text(lbl, buf);
|
||||
|
||||
// "Memory detail" button below the info text (per-region heap breakdown).
|
||||
// Slow tier below it: chip/flash/SD/NVS/stalls/build — re-set only on change.
|
||||
lv_obj_update_layout(lbl);
|
||||
lv_obj_t* rest = lv_label_create(body);
|
||||
s_sysinfo_rest_lbl = rest;
|
||||
lv_label_set_long_mode(rest, LV_LABEL_LONG_WRAP);
|
||||
lv_obj_set_width(rest, lv_pct(100));
|
||||
lv_obj_set_pos(rest, 2, lv_obj_get_y(lbl) + lv_obj_get_height(lbl) + 2);
|
||||
lv_obj_set_style_text_font(rest, &g_font_12, LV_PART_MAIN);
|
||||
lv_obj_set_style_text_color(rest, lv_color_hex(COLOR_TEXT), LV_PART_MAIN);
|
||||
sysInfoTextRest(buf, sizeof buf);
|
||||
lv_label_set_text(rest, buf);
|
||||
|
||||
// "Memory detail" button below the info text (per-region heap breakdown).
|
||||
lv_obj_update_layout(rest);
|
||||
lv_obj_t* membtn = lv_btn_create(body);
|
||||
lv_obj_set_size(membtn, lv_pct(96), SC(34));
|
||||
lv_obj_set_pos(membtn, 2, lv_obj_get_y(lbl) + lv_obj_get_height(lbl) + 8);
|
||||
lv_obj_set_pos(membtn, 2, lv_obj_get_y(rest) + lv_obj_get_height(rest) + 8);
|
||||
styleButton(membtn);
|
||||
lv_obj_add_event_cb(membtn, openMemoryDetailCb, LV_EVENT_CLICKED, nullptr);
|
||||
lv_obj_t* mbl = lv_label_create(membtn);
|
||||
@@ -18215,6 +18283,7 @@ static void spectrumRestoreRadio() {
|
||||
|
||||
static void closeSpectrumPage() {
|
||||
spectrumRestoreRadio(); // ALWAYS restore before tearing the page down
|
||||
radio_driver.rxQueueSuspend(false); // resume the buffered-receive drain task (no-op if off)
|
||||
if (s_spec_timer) { lv_timer_del(s_spec_timer); s_spec_timer = nullptr; }
|
||||
if (s_spec_root) { popupClose(&s_spec_root); }
|
||||
s_spec_chart = nullptr; s_spec_ser = nullptr; s_spec_wf = nullptr;
|
||||
@@ -18239,6 +18308,11 @@ static void spectrumDismissCb(lv_event_t* e) {
|
||||
|
||||
static void openSpectrumPage() {
|
||||
closeSpectrumPage();
|
||||
// Park the buffered-receive drain task while this page drives the raw radio.
|
||||
// The acquire/release pair guarantees no drain is mid-flight when we take over.
|
||||
radio_driver.radioAcquire();
|
||||
radio_driver.rxQueueSuspend(true);
|
||||
radio_driver.radioRelease();
|
||||
const lv_coord_t sw = lv_disp_get_hor_res(nullptr);
|
||||
const lv_coord_t sh = lv_disp_get_ver_res(nullptr);
|
||||
const int H = sh - STATUSBAR_H;
|
||||
@@ -24121,7 +24195,7 @@ static void closeSettingsCategory() {
|
||||
if (!s_settings_sheet && s_settings_open_cat < 0) return;
|
||||
hideKb();
|
||||
if (s_settings_open_cat == CAT_ABOUT) { // null the live-label ptrs (freed with the sheet)
|
||||
s_sysinfo_lbl = nullptr; s_update_about_lbl = nullptr; s_ota_status_lbl = nullptr;
|
||||
s_sysinfo_lbl = nullptr; s_sysinfo_rest_lbl = nullptr; s_update_about_lbl = nullptr; s_ota_status_lbl = nullptr;
|
||||
s_ota_btn = nullptr; s_ota_btn_lbl = nullptr;
|
||||
s_ota_prev_btn = nullptr;
|
||||
g_lv.settings_status = nullptr; g_lv.diag_id_label = nullptr; g_lv.diag_label = nullptr;
|
||||
@@ -35549,6 +35623,9 @@ void UITask::begin(DisplayDriver* display, SensorManager* sensors, NodePrefs* no
|
||||
if (board.femLnaControllable()) board.setFemLnaEnable(touchPrefsGetFemLna());
|
||||
#endif
|
||||
|
||||
// Buffered LoRa receive (experimental, default OFF): apply the saved opt-in.
|
||||
if (touchPrefsGetRxQueue()) radio_driver.rxQueueEnable(true);
|
||||
|
||||
buildUiTree();
|
||||
|
||||
// Force immediate full repaint to replace the TFT "Loading..." banner.
|
||||
|
||||
@@ -36,6 +36,17 @@ public:
|
||||
void idle(); // → standby (RadioLibWrapper::idle)
|
||||
uint32_t getPacketsRecvErrors() const { return _n_recv_errors; }
|
||||
|
||||
// Buffered-receive API parity with RadioLibWrapper — no-op stubs. This bridge
|
||||
// drains the remote radio its own way; the S3 drain-task feature does not map
|
||||
// onto it, but shared UI/mesh code calls these unguarded.
|
||||
void rxQueueEnable(bool) {}
|
||||
bool rxQueueEnabled() const { return false; }
|
||||
void rxQueueSuspend(bool) {}
|
||||
void radioAcquire() {}
|
||||
void radioRelease() {}
|
||||
uint32_t getRxEvents() const { return _n_recv + _n_recv_errors; }
|
||||
uint32_t getRxQueueDrops() const { return 0; }
|
||||
|
||||
// --- mesh::Radio contract (Dispatcher.h) ---
|
||||
void begin() override {} // lifecycle hook; remote radio is RX-ready after init()
|
||||
int recvRaw(uint8_t* bytes, int sz) override; // drain one RX packet, 0 if none
|
||||
|
||||
Reference in New Issue
Block a user