Skip to content

Feature/lvgl 9.5 migration#37

Open
eleonel wants to merge 12 commits into
devfrom
feature/lvgl-9.5-migration
Open

Feature/lvgl 9.5 migration#37
eleonel wants to merge 12 commits into
devfrom
feature/lvgl-9.5-migration

Conversation

@eleonel

@eleonel eleonel commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

No description provided.

eleonel added 12 commits March 27, 2026 21:19
Stop the OOM-induced reset loops and harden JS app execution:

- Remove both self-inflicted ESP.restart() paths (low-heap check that
  ran PSRAM-arena GC against internal-DRAM pressure, and the 36000-tick
  "maintenance reboot" that fired hourly with 100ms timers).
- Replace device reboots with an in-place JS app restart: teardown of
  timers/widgets/styles/media/MQTT state, engine re-created over the
  same arena, script re-evaluated; safe mode with on-screen error after
  repeated failures. Boot-eval errors now show on the display.
- Elk engine: backport upstream setprop OOM-corruption fix and ++/--
  lvalue guard; re-enable auto-GC safely (gated on !F_CALL, 75%
  threshold); add js_usage/js_total accessors, per-eval statement
  budget (step limit) and C-stack ceiling so runaway scripts become
  recoverable JS errors instead of crashes.
- Memory: LVGL heap now prefers PSRAM (keeps internal DRAM for
  TLS/lwip); fix 214KB PSRAM draw-buffer over-allocation; Elk arena
  size configurable via webscreen.json "js_heap_kb" (64-1024).
- Crash fixes: bounded base64 decode in /upload (stack smash);
  slot-registry handles with validation replace pointer-as-double in
  chart/meter/span bindings; span text copied instead of pointing into
  the GC'd arena; GIF reload leak + mem-fs read underflow; SPI panel
  mutex (loopTask brightness vs JS-task flush race).
- Reliability: WiFi/MQTT actually reconnect now; /load restarts the
  app in place (it never worked before — the reboot discarded the
  selection); new /restart_app and /gc commands; /stats reports JS
  arena usage, min free heap and largest free block; serial receive
  loops time out instead of hanging the loopTask; failed JS start
  falls back to the notification app; power-button pin gets its
  pull-up in the live path.
- New JS API: mem_info(), gc(), timer_delete(), obj_delete(),
  gif_free(), ram_image_free(), mqtt_dropped().
- Structure: split the 3.7k-line lvgl_elk.h monolith into 14 ordered
  ws_*.h fragment headers (token-identical concatenation); remove the
  dead webscreen_main state machine, duplicate network client globals
  and runtime simulation stubs.
Review fixes (22 confirmed findings from multi-agent review):
- elk.c: block auto-GC during F_NOEXEC function-body parsing (GC fired
  mid-expression with unrooted arena temporaries); count for(;;) loop
  iterations against the step budget (empty bodies executed zero
  statements and dodged it); propagate mkscope OOM instead of storing
  an error value as the scope chain.
- obj_delete now releases chart-series/meter-scale/indicator/span
  registry slots owned by the deleted widget subtree (was a
  JS-reachable use-after-free write through stale handles).
- Restart state machine: per-timer error streaks (a healthy timer no
  longer masks a broken one), automatic restarts can't lift safe mode,
  three streak-triggered restarts without a healthy interval park the
  app instead of churning forever, leftover timers from partially
  evaluated scripts are removed before entering safe mode, GC on error
  rate-limited to the first error of a streak.
- /load and /restart_app report an honest error in fallback mode
  instead of printing success and doing nothing; over-long script
  paths rejected before teardown.
- /stats "Min Free Heap" renamed "Heap Low Watermark" — the substring
  collided with WebScreen-Admin's 'Free Heap:' parser and corrupted
  the dashboard memory figure.
- Span text no longer truncated at 255 bytes; ble_init returns false
  on identity mismatch instead of pretending the new UUIDs took
  effect; WiFi reconnect skipped on never-configured (offline)
  devices.

Clarity refactor (behavior-preserving, JS API list verified
byte-identical, flash -4KB):
- 36 of 37 style setters collapsed to X-macro table entries
  (ws_lvgl_styles.h -287 lines, properties still grep-able).
- HTTP GET/POST/DELETE share one request scaffold (ws_elk_http.h
  -200 lines; per-verb quirks like GET's retry loop preserved).
- Serial commands dispatch from a single table that also generates
  /help, so help text can't drift from the implementation.
- New shared js_arg_str() helper replaces 10 hand-rolled
  quote-strip sites.

Docs: API.md, SerialCommands.md, CLAUDE.md, RELEASE_NOTES.md
(Unreleased section), webscreen.json.example updated for the new
APIs, commands, telemetry and js_heap_kb config.
…pin fix

Serial console:
- /screenshot (/ss): LVGL snapshot streamed as base64 RGB565, captured at
  the JS task safe point (same handoff pattern as /eval)
- /download (/dl): binary-safe base64 file dump with size header/end marker
- /mkdir; /rm now also removes empty directories
- /factory_reset confirm: delete webscreen.json and reboot to fallback
- /ls [path] [json]: machine-readable one-line listing for host tools;
  text listing gains a "Total: N files, M directories" end marker
- /eval REPL error recording feeds /errors; upload ACK lines
  ([OK] File saved / [ERROR] Upload failed) for host-side verification
- /wget alias renamed download -> fetch (frees /download)

Critical fixes:
- WEBSCREEN_PIN_BUTTON was GPIO 33: on ESP32-S3 with octal PSRAM,
  GPIOs 33-37 ARE the PSRAM bus, so the new pinMode() in setup()
  corrupted every PSRAM access and the device died at the next
  allocation (wedged panic, TG1WDT reset loop, no backtrace over
  USB-CDC). Button is now GPIO 21, the board's real user button;
  deep-sleep ext0 wakeup follows the same define.
- LVGL allocator back to plain malloc: the PSRAM-first
  heap_caps_malloc_prefer routing put LVGL's hot small allocations on
  the slow OPI bus for no measured benefit.
- SD mount restored to proven 400kHz probe + 10MHz remount sequence;
  mounting at high frequency directly wedges the card until power cycle.

Version: 2.2.0-dev. The 0.2.3-dev string regressed WebScreen-Admin's
capability detection (/upload requires reported major >= 2); back on the
2.x scheme matching git tags and main's 2.0.8.

Docs: SerialCommands.md, API.md, README, CLAUDE.md, RELEASE_NOTES updated
to match (byte-exact /help block, stream marker formats, /stats label fix).
…-and-pin-fix

# Conflicts:
#	README.md
#	webscreen/build/esp32.esp32.esp32s3/webscreen.ino.bin
#	webscreen/build/esp32.esp32.esp32s3/webscreen.ino.elf
#	webscreen/build/esp32.esp32.esp32s3/webscreen.ino.map
#	webscreen/build/esp32.esp32.esp32s3/webscreen.ino.merged.bin
#	webscreen/webscreen_hardware.cpp
No code changes: stripped-comment sources and the compiled binary are
byte-identical. Kept one-liners for hardware landmines (GPIO 33-37 =
octal-PSRAM bus, SD 400 kHz-first mount), cross-task handoff invariants,
and stream format contracts; removed history narration and paragraph
rationales.
- New v9-format lv_conf.h (copy to ~/Arduino/libraries/lv_conf.h; the
  Arduino lvgl library folder must contain LVGL 9.5.0)
- Display driver: lv_display_create/set_buffers/set_flush_cb; the panel's
  byte-swapped RGB565 becomes display color format RGB565_SWAPPED
  (LV_COLOR_16_SWAP no longer exists)
- JS lv_meter_* API reimplemented on lv_scale (lv_meter removed upstream):
  needle line/image via lv_scale_set_*_needle_value, arcs as styled lv_arc
  children, scale lines as scale sections; JS signatures unchanged.
  Tick-gradient color and label_gap have no v9 equivalent (ignored)
- lv_chart_set_zoom_x/y and lv_chart_set_axis_tick kept as no-op bindings
  (removed upstream) so LVGL 8 era scripts keep running
- /screenshot: v9 lv_snapshot_take returns lv_draw_buf_t; streams plain
  RGB565 with matching header marker (host tools accept both)
- Assets: boot logo converted to planar RGB565A8 (v9 image header, source
  shrinks 1.7MB -> 0.4MB); fallback GIF descriptor to LV_COLOR_FORMAT_RAW;
  RAM-image descriptors likewise
- lv_timer_t is opaque in v9: timer bridge includes lvgl_private.h
- Misc renames: lv_display_flush_ready, lv_spangroup_delete_span,
  lv_point_precise_t, lv_color_to_u16 (lv_color_t lost .full), typed
  style-setter casts (lv_border_side_t, lv_text_decor_t)

Flash 3,032,515 bytes (96%, +82KB vs LVGL 8); static DRAM 124KB (37%).
Compile-verified only — needs on-device testing (fallback GIF, JS apps,
meters, screenshot).
Config (lv_conf.h):
- Restore dark default theme: the theme flip made the fallback screen
  render white text on a white background
- Re-enable the image cache (LV_CACHE_DEF_SIZE 512KB; was 0 = re-decode
  PNG/JPG on every refresh)
- Disable ARCLABEL and BAR (nothing creates them)

Meter emulation (ws_lvgl_charts.h):
- Resolve layout before computing needle length; a meter built and set in
  the same frame got a 1px needle stub (width read as 0 pre-layout)
- Size extra scales in pixels: lv_scale computes needle geometry from the
  raw style width, so lv_pct(100) placed needles millions of px off-screen
- Arc r_mod: size the arc from the resolved scale size +/- r_mod on both
  signs (positive was dropped, negative shrank the radius twice as much)
- Re-apply all indicators when set_scale_range changes the mapping
- Align image needles so the pivot sits on the scale center (v9 only
  rotates; v8 lv_meter also positioned)

Display buffers:
- Declare draw buffers as bytes (2/px): v9's lv_color_t is 3 bytes, which
  silently grew the DRAM buffer 42KB->64KB and the fallback PSRAM buffer
  by 50%. Static DRAM back to 102.7KB (31%), same as before the migration

Docs: SJPG is not supported by LVGL 9 at all — say so instead of
advertising it (README, API.md, CLAUDE.md, release notes), and fix the
contradictory enabled/disabled widget lists.
…tion

# Conflicts:
#	CLAUDE.md
#	README.md
#	RELEASE_NOTES.md
#	docs/API.md
#	docs/SerialCommands.md
#	lv_conf.h
#	webscreen/fallback.cpp
#	webscreen/webscreen_runtime.cpp
#	webscreen/ws_elk_core.h
#	webscreen/ws_elk_media.h
#	webscreen/ws_lvgl_charts.h
#	webscreen/ws_lvgl_display.h
#	webscreen/ws_lvgl_styles.h
#	webscreen/ws_lvgl_widgets.h
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant