From 4dc5c89e25ebfa823262f792fa7ed1c48b61d25d Mon Sep 17 00:00:00 2001 From: "torlando-agent[bot]" <281092095+torlando-agent[bot]@users.noreply.github.com> Date: Fri, 19 Jun 2026 10:53:33 -0400 Subject: [PATCH] fix: scope LVGL lock to the snapshot in T:SCREENSHOT (not the serial dump) LVGL_LOCK() was held for the entire base64 serial dump (~18s for a 320x240 frame), blocking the LVGL render task the whole time and tripping the 5s LVGLLock recursive-take timeout assert in debug builds (crash on every screenshot). lv_snapshot_take() copies the pixels into its own buffer, so the lock is only needed for the snapshot itself -- scope it there, dump the copy unlocked, and re-take the lock only to free the snapshot. Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01UWZuYkHBRqNb6BZHV8sTG5 --- src/main.cpp | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index b1f46c03..8f5b0e80 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2086,15 +2086,23 @@ static void handle_test_hook_command(const String& line) { // all lines between the markers, base64-decodes, and converts // to PNG. FMT carries the byte order (`be` when LV_COLOR_16_SWAP // is on, `le` otherwise) so the decoder doesn't have to guess. - LVGL_LOCK(); - lv_obj_t* scr = lv_scr_act(); - if (!scr) { - Serial.println("T:ERR no active screen"); - return; + // Hold the LVGL lock ONLY for the snapshot: lv_snapshot_take() copies + // the screen pixels into a freshly-allocated buffer, so live LVGL state + // isn't touched during the ~18s base64 serial dump below. Holding the + // recursive mutex across the whole dump blocks the LVGL render task and + // trips the 5s LVGLLock timeout assert (LVGLLock.h) in debug builds. + lv_img_dsc_t* snap = nullptr; + { + LVGL_LOCK(); + lv_obj_t* scr = lv_scr_act(); + if (!scr) { + Serial.println("T:ERR no active screen"); + return; + } + snap = lv_snapshot_take(scr, LV_IMG_CF_TRUE_COLOR); } - lv_img_dsc_t* snap = lv_snapshot_take(scr, LV_IMG_CF_TRUE_COLOR); if (!snap || !snap->data) { - if (snap) lv_snapshot_free(snap); + if (snap) { LVGL_LOCK(); lv_snapshot_free(snap); } Serial.println("T:ERR snapshot failed (PSRAM exhausted?)"); return; } @@ -2150,7 +2158,7 @@ static void handle_test_hook_command(const String& line) { Serial.println(line); } Serial.println("T:SCREENSHOT END"); - lv_snapshot_free(snap); + { LVGL_LOCK(); lv_snapshot_free(snap); } } else if (cmd == "T:CALL_INJECT") { // T:CALL_INJECT [freq_hz] [amp_pct]