mirror of
https://github.com/MarekZegare4/MeshCore-Solo.git
synced 2026-10-09 03:06:39 +00:00
fix(ui-lvgl): 16 KB UI stack (screenshots reset the device); USB popup after the splash
The UI runs on Arduino's loop task, whose stock 8 KB stack had 340 B left at its deepest point; a screenshot's file write went past it and reset the device. The loop stack is now 16 KB (measured peak ~7.8 KB) and the screenshot's line buffer is static. Diagnostics names the row "UI stack free (lowest)". With a cable in at boot, the USB popup waited for nothing and showed over the splash; it now comes once the splash has faded. Adds docs/development/plan-next.md, the plan up to the merge into dev. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
# Plan: from here to the merge into dev (2026-09-28)
|
||||
|
||||
Work in order; each item is implemented, checked in the sim, flashed, and
|
||||
committed only after it's accepted. Tick items off as they land. Working
|
||||
file: goes in the repo clean-up (stage I).
|
||||
|
||||
## A. Bugs
|
||||
|
||||
- [x] **A1. Restart on a screenshot + "340 B stack free".** Likely one cause.
|
||||
Diagnostics shows the UI loop task's lowest-ever stack headroom
|
||||
(`uxTaskGetStackHighWaterMark`); the Arduino loop stack is 8 KB, so
|
||||
340 B left is nearly an overflow. `takeScreenshot()` keeps a 960 B line
|
||||
buffer on that stack, plus the FAT write.
|
||||
1. Confirm with Diagnostics > Last crash after a screenshot restart.
|
||||
2. Move the line buffer off the stack.
|
||||
3. Raise the loop stack (internal RAM).
|
||||
4. Measure the headroom on heavy screens (map, history, OTA).
|
||||
5. Label the Diagnostics row with the task it measures.
|
||||
Done 2026-09-28: loop stack 16 KB (SET_LOOP_TASK_STACK_SIZE), line
|
||||
buffer static, row "UI stack free (lowest)". Measured 8532 B free
|
||||
after map, history and screenshots, so the UI peaks at ~7.8 KB --
|
||||
the old 8 KB left 340 B. Could go down to 12 KB if RAM gets tight.
|
||||
- [x] **A2. USB popup before the splash screen is gone.** `usbPoll()` waits
|
||||
for the splash to finish; a cable plugged in at boot shows the popup
|
||||
right after it.
|
||||
|
||||
## B. Map
|
||||
|
||||
- [x] **B1. Map menu order.** List the current entries, propose groups and
|
||||
an order (view, overlays, areas & downloads, trails, test), agree on
|
||||
it before coding. Done: TRAIL / LIVE SHARE / ARRIVAL ALERT (each with
|
||||
its own options page, one section of PG_NAV), OFFLINE MAPS (Map areas,
|
||||
Live tiles; "Download an area" dropped, a running / unfinished
|
||||
download shows at the top of Map areas), LAYERS.
|
||||
- [x] **B2. Downloaded areas.** Selecting an area previews it at once (no
|
||||
Show button); leaving the menu hides it and restores the previous map
|
||||
view. Fixes the area staying on the map after Show.
|
||||
|
||||
## C. Diagnostics: the noise tests (to think over)
|
||||
|
||||
Diagnostics stays as it is. Only the Noise tab is in question: the tests that
|
||||
found the interference source (noise floor with the board's parts off one by
|
||||
one, the 850-930 MHz and mesh-channel sweeps, the spike hunt, the 15 s states
|
||||
for a second radio). They are L2-specific and block the loop for ~90 s.
|
||||
|
||||
- [ ] Decide: remove them, or keep one universal tool (for example a noise
|
||||
floor sweep round the mesh frequency that works on any board) and drop
|
||||
the L2 part toggling and spike hunt.
|
||||
|
||||
## D. Quiet hours in the core
|
||||
|
||||
- [ ] A start and end time when the device is silent; in ui-core, so L1, L2
|
||||
and other boards get it. New NodePrefs fields go at the end of the
|
||||
stored layout, so an L1 keeps its settings after the update.
|
||||
To decide: the Clock alarm rings anyway (proposed yes); whether a
|
||||
message wakes the screen during quiet hours; with the clock not set,
|
||||
quiet hours are off (proposed).
|
||||
|
||||
## E. OTA end-to-end test
|
||||
|
||||
- [ ] After the feature changes: a real release with an L2 asset (the v3 env
|
||||
must be the `*_solo_lvgl` one before tagging), install over WiFi.
|
||||
Publishing the release needs confirmation first.
|
||||
|
||||
## F. The big review
|
||||
|
||||
- [ ] Dead code; similar elements written several times, merged into one;
|
||||
places to speed things up or save RAM, flash and battery.
|
||||
First a list of findings to accept, then fixes in batches, each checked
|
||||
in the sim and on the device.
|
||||
|
||||
## G. Core parity with L1 SOLO
|
||||
|
||||
- [ ] A feature table: L1 SOLO / ui-core / L2 (ui-lvgl). What's missing goes
|
||||
into ui-core, not into each UI separately.
|
||||
|
||||
## H. Before the merge into dev
|
||||
|
||||
- [ ] The pull request about the lock screen.
|
||||
- [ ] The new issues.
|
||||
- [ ] Merge into dev.
|
||||
- [ ] A last review.
|
||||
|
||||
## I. Repo, documentation, website, firmware tiers (its own detailed plan)
|
||||
|
||||
- [ ] Repo: remove working files (plans etc.), keep only the project's code;
|
||||
README down to the minimum; add Buy Me a Coffee.
|
||||
- [ ] Documentation written from scratch: short, describing the firmware's
|
||||
features, easy to browse; sections marking where devices differ; no
|
||||
screenshots of every screen and device. It stays in the repo, its
|
||||
official entry is the simulator website.
|
||||
- [ ] Website rebuilt and polished; the simulator lets you pick which
|
||||
firmware to simulate.
|
||||
- [ ] Firmware in three tiers, one shared core, extras (map tiles etc.) by
|
||||
what the hardware can do, making full use of each device:
|
||||
- minimal -- nRF52, a limited UI, small e-ink and OLED;
|
||||
- standard -- mostly ESP32, large e-ink and LCD, higher resolutions,
|
||||
a somewhat richer UI;
|
||||
- color -- colour touch screens.
|
||||
|
||||
Proposed: write a short tier spec (which boards, which features, what's in
|
||||
the core) as a document before stage F, so the review consolidates towards
|
||||
it; implement the tiers, docs and website after the merge.
|
||||
|
||||
## Open decisions
|
||||
|
||||
- C: remove the noise tests or keep a universal version.
|
||||
- D: alarm during quiet hours; messages waking the screen.
|
||||
- I: tier spec before stage F, or after the merge.
|
||||
@@ -66,7 +66,7 @@ static const Name NAMES[] = {
|
||||
{ "Noise floor", SEC_RADIO, "Noise floor", false }, { "RSSI/SNR", SEC_RADIO, "Last packet", false },
|
||||
{ "Queue", SEC_RADIO, "Send queue", false }, { "Errors", SEC_RADIO, "Errors", false },
|
||||
{ "RXPS wd s/h", SEC_RADIO, "RX watchdog soft / hard", false },
|
||||
{ "Heap free", SEC_MEMORY, "Heap free", false }, { "Stack free", SEC_MEMORY, "Stack free", false },
|
||||
{ "Heap free", SEC_MEMORY, "Heap free", false }, { "Stack free", SEC_MEMORY, "UI stack free (lowest)", false },
|
||||
{ "Pool free", SEC_MEMORY, "Packet pool free", false },
|
||||
};
|
||||
static const Name* nameOf(const char* core) {
|
||||
|
||||
@@ -92,6 +92,7 @@ static void dismiss() {
|
||||
anim::run(s_root, anim::setOpa, LV_OPA_COVER, LV_OPA_TRANSP, 300, freeAll);
|
||||
}
|
||||
static void onTimer(lv_timer_t* t) { (void)t; dismiss(); }
|
||||
static bool up() { return s_root != nullptr; } // until its fade-out has finished
|
||||
static void onTap(lv_event_t* e) { (void)e; dismiss(); }
|
||||
|
||||
// One character of the lettering: its width, and whether (x, y) is inked.
|
||||
|
||||
@@ -504,10 +504,13 @@ static void usbPanel(const char* title, const char* text, bool buttons) {
|
||||
}
|
||||
}
|
||||
|
||||
namespace splash { static bool up(); } // Splash.h
|
||||
|
||||
void UITask::usbPoll() {
|
||||
using namespace usbview;
|
||||
static uint32_t next = 0;
|
||||
if ((int32_t)(millis() - next) < 0) return;
|
||||
if (splash::up()) return; // a cable in at boot: the popup comes once the splash is gone
|
||||
next = millis() + 250;
|
||||
if (lvport::usbDriveOn()) {
|
||||
if (!lvport::usbDriveDone()) return;
|
||||
|
||||
@@ -4,6 +4,11 @@
|
||||
#include "target.h"
|
||||
#if defined(ESP32)
|
||||
#include <esp_heap_caps.h>
|
||||
// The UI runs on Arduino's loop task: LVGL's renderer, FAT on the card and
|
||||
// the map's decoders all nest on its stack, and the stock 8 KB left a few
|
||||
// hundred bytes at the deepest point (Diagnostics > Stack free, the lowest
|
||||
// ever) -- a screenshot's file write went past it and reset the device.
|
||||
SET_LOOP_TASK_STACK_SIZE(16 * 1024);
|
||||
#endif
|
||||
#include <new>
|
||||
#include <stdarg.h>
|
||||
@@ -1496,7 +1501,7 @@ void UITask::takeScreenshot() {
|
||||
put32(2, size); put32(10, 54); put32(14, 40); put32(18, W); put32(22, H);
|
||||
hdr[26] = 1; hdr[28] = 24; put32(34, row * H);
|
||||
ok = fwrite(hdr, 1, 54, f) == 54;
|
||||
uint8_t line[320 * 3];
|
||||
static uint8_t line[320 * 3]; // not on the loop task's stack
|
||||
for (int32_t y = H - 1; ok && y >= 0; y--) { // bottom-up, BGR
|
||||
const uint16_t* s = px + y * W;
|
||||
for (int32_t x = 0; x < W && x < 320; x++) {
|
||||
|
||||
Reference in New Issue
Block a user