From 4f5357031f90871ee277ac61e89c716b86abf0a3 Mon Sep 17 00:00:00 2001 From: Jakub <106778416+MarekZegare4@users.noreply.github.com> Date: Sat, 26 Sep 2026 13:51:08 +0200 Subject: [PATCH] feat(l2): Arduino 3.x as the published env, limits sized for the PSRAM, stage 7 findings - Wio_Tracker_L2_companion_solo_lvgl now builds on Arduino-ESP32 3.3.12 (pioarduino); the 2.0.17 build stays as _solo_lvgl_arduino2 (not published) - Per-board limits (defaults unchanged, L1 byte-for-byte the same): TRAIL_CAPACITY, WAYPOINT_CAPACITY, HIST_CH_MAX / HIST_DM_MAX, NEARBY_MAX; the L2 sets 4096 / 64 / 256 / 128 / 64 (in PSRAM with UiCore; internal heap 154 KB free after). ui-lvgl: 32 trail segments, contact list 256, rooms 32, 50 bubbles per conversation, pickers 128, WiFi scan 20 - Map trail drawing drops points under 2 px apart - "Nearby" is "Nodes"; advert options "Zero hop" / "Flood" - Roadmap stage 7: LVGL on other displays -- e-ink stays ui-new, an OLED spike after this roadmap, Cardputer as a colour candidate Co-Authored-By: Claude Opus 5.5 --- docs/development/l2-hardware-checklist.md | 11 ++- docs/development/l2-roadmap.md | 76 +++++++++++++++++-- examples/companion_radio/Trail.h | 8 +- examples/companion_radio/Waypoint.h | 6 +- .../companion_radio/ui-core/MessageHistory.h | 13 +++- .../companion_radio/ui-core/NearbyModel.h | 5 +- .../companion_radio/ui-lvgl/AdminScreen.h | 2 +- .../companion_radio/ui-lvgl/DeviceScreen.h | 2 +- examples/companion_radio/ui-lvgl/LvglPort.h | 2 +- examples/companion_radio/ui-lvgl/MapScreen.h | 3 +- examples/companion_radio/ui-lvgl/NavMap.h | 29 ++++--- .../companion_radio/ui-lvgl/QuickScreen.h | 6 +- examples/companion_radio/ui-lvgl/UITask.cpp | 20 +++-- solo/wio-tracker-l2/platformio.ini | 35 +++++---- variants/sim/build_wasm.sh | 4 +- 15 files changed, 172 insertions(+), 50 deletions(-) diff --git a/docs/development/l2-hardware-checklist.md b/docs/development/l2-hardware-checklist.md index 3685fdbf..0367fa3c 100644 --- a/docs/development/l2-hardware-checklist.md +++ b/docs/development/l2-hardware-checklist.md @@ -67,7 +67,7 @@ Confirmed: green dot inside the ring, waypoints much better, download works, set - [ ] L1 (ui-new): Tools > Admin works as before (login, saved password, typed values, confirm reboot); it now also skips the login for the node you just used ## Roadmap stage 2: Arduino-ESP32 3.3.12 / IDF 5.5 (2026-09-26) -Env `Wio_Tracker_L2_companion_solo_lvgl_v3` (flash with `L2_ENV=Wio_Tracker_L2_companion_solo_lvgl_v3`). Bluetooth runs on NimBLE now, the speaker on the new I2S driver. Internal heap at runtime: 147.9 KB free on 3.x vs 147.8 KB on 2.0.17 (large UI buffers moved to PSRAM; static RAM 109 → 71 KB). +Env `Wio_Tracker_L2_companion_solo_lvgl` (Arduino 3.x since 2026-09-26; the 2.0.17 build is `_arduino2`). Bluetooth runs on NimBLE now, the speaker on the new I2S driver. Internal heap at runtime: 147.9 KB free on 3.x vs 147.8 KB on 2.0.17 (large UI buffers moved to PSRAM; static RAM 109 → 71 KB). - [ ] Boots, screen and touch as before; WAKE and USER buttons - [ ] Bluetooth: the app pairs with the PIN (asks for it: the link is encrypted), connects, syncs contacts and messages, sends; reconnects after the app is closed / reopened; the Bluetooth switch still works - [ ] Sound: startup chime, message sounds, melody editor, volume; no new knocks @@ -292,3 +292,12 @@ The speaker plays through the ES8311 codec (I2S MCLK 10 / BCK 11 / WS 12 / DOUT - [ ] Map download popup: no WiFi button, fits the screen (scrolls if the unfinished-job row is shown); no network saved → toast pointing to Settings > WiFi - [ ] Unlock (slider or right PIN): the lock fades away while the screen underneath drifts up into place, no instant jump - [ ] Lock slider: knob snaps to the end near it but only unlocks on letting go; let go earlier → it springs back smoothly + +## Roadmap stage 2: limits for the PSRAM (2026-09-26) +- [ ] Boot with `-D UI_HEAP_REPORT`: internal heap free still ~145 KB+ (the bigger rings go to PSRAM) +- [ ] Long walk / drive with the trail on: well past 512 points, the map stays smooth zoomed out, saving and GPX export work +- [ ] Waypoints: more than 16 can be added; the full message says (64) +- [ ] Busy channels: a conversation shows up to 50 bubbles, older ones are kept longer than before +- [ ] Nearby with many nodes: up to 64 rows; contact list "All" shows beyond 64 contacts +- [ ] Home tile and screen title read "Nodes" (was "Nearby") +- [ ] Nodes > advert: "Zero hop" / "Flood" (were "Nearby only" / "Everyone"), toasts "Zero-hop advert sent" / "Flood advert sent" diff --git a/docs/development/l2-roadmap.md b/docs/development/l2-roadmap.md index 9aa1f910..01319ce2 100644 --- a/docs/development/l2-roadmap.md +++ b/docs/development/l2-roadmap.md @@ -35,7 +35,22 @@ Decisions already made: runtime: 2.0.17 147.8 KB; 3.x 128.2 KB before, 147.9 KB after (static RAM 109 → 71 KB). Measured with `-D UI_HEAP_REPORT`. History and a larger trail come to PSRAM with stage 3. -- [ ] Full hardware checklist, then make the new env the default. +- [x] The 3.x env is the default (2026-09-26, after stages 5 and 6 were + tested on it): `Wio_Tracker_L2_companion_solo_lvgl` is Arduino 3.3.12 + and is the one published; 2.0.17 stays as + `Wio_Tracker_L2_companion_solo_lvgl_arduino2` (not published, can't + self-update). +- [x] Limits sized for the PSRAM, overridable per board so the nRF52 + defaults stay (the L1 build is byte-for-byte unchanged): trail 512 → + 4096 points (`TRAIL_CAPACITY`; the map drops points under 2 px apart + when drawing, so a long trail doesn't slow redraws; 8 → 32 trail + segments), waypoints 16 → 64, message history 48 → 256 channel and + 32 → 128 DM entries (`HIST_CH_MAX` / `HIST_DM_MAX`; stage 3 moves it + to SD), Nearby 32 → 64; in ui-lvgl: contact list 64 → 256 rows, + rooms 16 → 32, conversation 30 → 50 bubbles, pickers 64 → 128, WiFi + scan 12 → 20. The sim builds with the same limits. + Measured after: internal heap 154.1 KB free (was 147.9), PSRAM + 5.67 MB free. ## 3. Data on the SD card @@ -43,8 +58,7 @@ Postponed (2026-09-26): the user may extend this stage first. - [ ] Message history on SD, kept across reboots (~100 per conversation; the newest ones cached in PSRAM). -- [ ] Trail sized to the device: thousands of points in PSRAM (now 512), - saved to SD. +- [ ] Trail saved to SD (it holds 4096 points in PSRAM since stage 2). ## 4. UI layout @@ -77,8 +91,9 @@ L1 splits its tools into many small screens because of the joystick and the streamed over TLS verified with the framework's CA bundle into the idle slot of `default_16MB.csv` (two 6.25 MB app slots, no layout change), chip id checked, then restart. Arduino 3.x builds only. -- [ ] Before the first release with L2: make the 3.x env the - `*_solo_lvgl` one (stage 2's last item), then test an update end to end. +- [x] The 3.x env is the `*_solo_lvgl` one (stage 2). +- [ ] With the first release that has an L2 asset: test an update end to + end. ## 6. Theme @@ -111,10 +126,59 @@ L1 splits its tools into many small screens because of the joystick and the ## 7. Research: LVGL for the other displays -- [ ] Find out whether LVGL on every display variant gives consistency and a +- [x] Find out whether LVGL on every display variant gives consistency and a nicer look more easily. Adopt only if the result is better and every feature is kept (L1: 128×64 OLED, nRF52, RAM already 68% used). +Findings (2026-09-26). Solo builds, static RAM / flash used: + +| Board | MCU | Display | RAM | Flash | +|---|---|---|---|---| +| Wio Tracker L1 | nRF52840 | 128×64 OLED | 68% (74 KB free) | 70% (214 KB free) | +| Wio Tracker L1 e-ink | nRF52840 | e-ink | 70% | 71% | +| T-Echo Lite | nRF52840 | e-ink | 69% | 64% | +| GAT562 Mesh Watch13 | nRF52840 | 128×64 OLED | 68% | 92% (55 KB free) | +| ProMicro, GAT562 30S | nRF52840 | 128×64 OLED | (as L1) | | +| Heltec V3 / V4 | ESP32-S3 | 128×64 OLED | 56% | 44% of 3.2 MB | +| Cardputer ADV | ESP32-S3, no PSRAM | 240×135 colour TFT, keyboard | 56% | 43% of 3.2 MB | + +What LVGL costs on the L2: the library ~310 KB of code (full config, PNG +decoder and all widgets; a minimal one is ~120-150 KB), the ui-lvgl screens +~225 KB, fonts 360 KB (uncompressed, European + Cyrillic, 12-40 px). ui-new +on the L1 is ~137 KB. + +- **128×64 OLED (L1, Heltec, ProMicro, GAT562): to be tried.** Colour and + anti-aliasing don't matter there, but the v2 UI goals do: motion (Home + carousel slide, loading-dot wave, animated splash, a bottom drawer, radar + sweep), soft corners, graphic indicators instead of text, a bigger font + to try out, dithering for large elements. LVGL has the animation engine, + shapes, TTF fonts at any size and self-laying-out lists for that; rendered + in greyscale and converted to 1 bit in the flush, a Bayer threshold there + would turn every fade / translucent fill into dithering while 1-bpp text + and icons stay crisp. Against it: a rewrite of ui-new's screens (~15.6k + lines; `ui-core/` carries over), I2C caps a full frame at ~23 ms either + way, and nRF52 memory -- estimated ~20-30 KB RAM (heap + an 8 KB L8 + buffer) and ~120-150 KB flash for a trimmed LVGL, against 74 KB RAM / + 214 KB flash free on the L1 (ui-new's drawing code would go) and only + 55 KB flash on the Watch13. **Next step, after this roadmap:** a spike on + the L1 -- Home carousel with the slide and the bottom drawer, a message + list with soft bubbles, the dot wave, dithering in the flush -- measured + (RAM, flash, fps) and shown in the sim next to ui-new, then decide. +- **E-ink (L1 e-ink, T-Echo Lite): no.** Slow full refreshes rule out + motion; LVGL's small dirty areas fit partial refresh poorly and would need + batching. Same RAM limits as above. +- **Cardputer ADV: the one candidate.** A 240×135 colour screen now shows + ui-new's 128×64 picture scaled up; ESP32-S3 with room to spare (flash + 43%). LVGL would use the real resolution and colour, reuse `Theme.h`, + `Anim.h`, the fonts and much of the ui-lvgl screen code. Open points: no + PSRAM (the L2's PSRAM buffers -- tiles, lists, keyboard maps -- need + smaller internal ones or dropping, no raster map), no touch (keyboard + focus navigation, LVGL groups, instead of taps), a 135 px-high layout. + Worth a separate pilot if the Cardputer matters; otherwise skip. + +So: e-ink stays on ui-new; the OLED boards get an LVGL spike (above) once +this roadmap is done; the Cardputer follows whatever the spike shows. + ## Backlog (found along the way) - [ ] USB power detection: `WioTrackerL2Board::isExternalPowered()` reads diff --git a/examples/companion_radio/Trail.h b/examples/companion_radio/Trail.h index 2333d700..ca0452f3 100644 --- a/examples/companion_radio/Trail.h +++ b/examples/companion_radio/Trail.h @@ -23,6 +23,12 @@ // turn still gets a vertex roughly every min-delta of deviation — so CAPACITY // covers a much longer route than CAPACITY × min-delta would suggest. +// Boards with PSRAM raise it (the L2: -D TRAIL_CAPACITY=4096, 64 KB, since +// UiCore is heap-allocated there, in PSRAM); the default stays for the nRF52. +#ifndef TRAIL_CAPACITY +#define TRAIL_CAPACITY 512 +#endif + struct TrailPoint { int32_t lat_1e6; int32_t lon_1e6; @@ -34,7 +40,7 @@ static const uint8_t TRAIL_FLAG_SEG_START = 0x01; class TrailStore { public: - static const int CAPACITY = 512; + static const int CAPACITY = TRAIL_CAPACITY; // _count is serialised as uint16_t in the save header — fail the build loudly // if CAPACITY is ever grown past what that can hold, rather than truncating. static_assert(CAPACITY <= 0xFFFF, "TrailStore::CAPACITY must fit in the uint16_t save-header count"); diff --git a/examples/companion_radio/Waypoint.h b/examples/companion_radio/Waypoint.h index 5a7259b4..7bd205ee 100644 --- a/examples/companion_radio/Waypoint.h +++ b/examples/companion_radio/Waypoint.h @@ -16,9 +16,13 @@ struct Waypoint { char label[WAYPOINT_LABEL_LEN]; }; +#ifndef WAYPOINT_CAPACITY +#define WAYPOINT_CAPACITY 16 // the L2 raises it (-D WAYPOINT_CAPACITY=64) +#endif + class WaypointStore { public: - static const int CAPACITY = 16; + static const int CAPACITY = WAYPOINT_CAPACITY; int count() const { return _count; } bool full() const { return _count >= CAPACITY; } diff --git a/examples/companion_radio/ui-core/MessageHistory.h b/examples/companion_radio/ui-core/MessageHistory.h index c9067fcf..37f27535 100644 --- a/examples/companion_radio/ui-core/MessageHistory.h +++ b/examples/companion_radio/ui-core/MessageHistory.h @@ -80,13 +80,22 @@ struct DmHistEntry { uint8_t path[MAX_HIST_PATH_BYTES]; }; +// Boards with PSRAM raise these (the L2: -D HIST_CH_MAX=256 -D HIST_DM_MAX=128, +// ~80 KB in PSRAM with UiCore); the defaults are sized for the nRF52's heap. +#ifndef HIST_CH_MAX +#define HIST_CH_MAX 48 +#endif +#ifndef HIST_DM_MAX +#define HIST_DM_MAX 32 +#endif + class MessageHistory { public: // Shared ring for all channels combined; each slot carries a full MSG_TEXT_BUF, // so this ring dominates RAM — kept modest to leave heap headroom (see // DM_HIST_MAX). The DM ring is likewise bounded. - static const int CH_HIST_MAX = 48; - static const int DM_HIST_MAX = 32; + static const int CH_HIST_MAX = HIST_CH_MAX; + static const int DM_HIST_MAX = HIST_DM_MAX; MessageHistory() : _hist_head(0), _hist_count(0), _dm_hist_head(0), _dm_hist_count(0) { diff --git a/examples/companion_radio/ui-core/NearbyModel.h b/examples/companion_radio/ui-core/NearbyModel.h index 525df1d3..ce730b3f 100644 --- a/examples/companion_radio/ui-core/NearbyModel.h +++ b/examples/companion_radio/ui-core/NearbyModel.h @@ -47,7 +47,10 @@ public: bool fav; }; - static const int MAX_NEARBY = 32; +#ifndef NEARBY_MAX +#define NEARBY_MAX 32 // the L2 raises it (-D NEARBY_MAX=64) +#endif + static const int MAX_NEARBY = NEARBY_MAX; static const char* const* filterLabels() { static const char* const L[F_COUNT] = { "All", "Fav", "Comp", "Rpt", "Room", "Snsr" }; diff --git a/examples/companion_radio/ui-lvgl/AdminScreen.h b/examples/companion_radio/ui-lvgl/AdminScreen.h index 6f9b7d14..e90c7ca1 100644 --- a/examples/companion_radio/ui-lvgl/AdminScreen.h +++ b/examples/companion_radio/ui-lvgl/AdminScreen.h @@ -66,7 +66,7 @@ static void onOpenAdminPick(lv_event_t* e) { (void)e; s_ui->showAdminPick(); } static void onAdminPickRow(lv_event_t* e) { s_ui->adminPick((int)(uintptr_t)lv_event_get_user_data(e)); } namespace adminview { -static const int PICK_MAX = 64; +static const int PICK_MAX = 128; static uint8_t (*s_pick)[PUB_KEY_SIZE] = psramBuf(PICK_MAX); } diff --git a/examples/companion_radio/ui-lvgl/DeviceScreen.h b/examples/companion_radio/ui-lvgl/DeviceScreen.h index 3d20cfa6..fd812ce8 100644 --- a/examples/companion_radio/ui-lvgl/DeviceScreen.h +++ b/examples/companion_radio/ui-lvgl/DeviceScreen.h @@ -30,7 +30,7 @@ static const char* const PIN_MAP[] = { "1", "2", "3", "\n", "4", "5", "6", "\n", LV_SYMBOL_BACKSPACE, "0", LV_SYMBOL_OK, "" }; static int s_fav_slot = -1; // slot the hold / pick popup is about -static const int PICK_MAX = 64; +static const int PICK_MAX = 128; static uint8_t (*s_pick_keys)[PUB_KEY_SIZE] = psramBuf(PICK_MAX); enum : int { PICK_CONTACT = 100 }; // pick codes: channel index, or PICK_CONTACT + row diff --git a/examples/companion_radio/ui-lvgl/LvglPort.h b/examples/companion_radio/ui-lvgl/LvglPort.h index 924bcab5..fa599982 100644 --- a/examples/companion_radio/ui-lvgl/LvglPort.h +++ b/examples/companion_radio/ui-lvgl/LvglPort.h @@ -22,7 +22,7 @@ namespace lvport { // Network for map downloads: state of the link, and one HTTP GET at a time. enum NetState { NET_OFF, NET_CONNECTING, NET_UP, NET_FAILED }; -static const int WIFI_SCAN_MAX = 12; +static const int WIFI_SCAN_MAX = 20; #if defined(SEEED_WIO_TRACKER_L2) // LovyanGFX device (NV3031B QSPI panel + GT911 touch) from WioTrackerL2Display. diff --git a/examples/companion_radio/ui-lvgl/MapScreen.h b/examples/companion_radio/ui-lvgl/MapScreen.h index 86f68004..30fbfd30 100644 --- a/examples/companion_radio/ui-lvgl/MapScreen.h +++ b/examples/companion_radio/ui-lvgl/MapScreen.h @@ -44,7 +44,8 @@ static double latToTileY(double lat, int z) { // (MK_NODE), the waypoint index (MK_WAYPOINT) or the live-share slot (MK_LIVE). enum : uint8_t { MK_NODE, MK_WAYPOINT, MK_LIVE }; struct Mark { int32_t lat_e6, lon_e6; int idx; uint8_t kind; lv_obj_t* obj; lv_obj_t* dot; }; -static const int MAX_MARKS = 48; // >= MAX_NEARBY, and >= waypoints + live shares +static const int MAX_MARKS = NearbyModel::MAX_NEARBY > WaypointStore::CAPACITY + LiveTrackStore::CAPACITY + ? NearbyModel::MAX_NEARBY : WaypointStore::CAPACITY + LiveTrackStore::CAPACITY; // Nearby map, or waypoints + live shares static Mark* s_marks = psramBuf(MAX_MARKS); static int s_mark_count = 0; static int s_drag = 0; // px moved in the current press: a drag isn't a tap diff --git a/examples/companion_radio/ui-lvgl/NavMap.h b/examples/companion_radio/ui-lvgl/NavMap.h index c5673e67..797b18fe 100644 --- a/examples/companion_radio/ui-lvgl/NavMap.h +++ b/examples/companion_radio/ui-lvgl/NavMap.h @@ -21,7 +21,7 @@ enum : uint8_t { WP_NAV, WP_RENAME, WP_SHARE, WP_DELETE }; // Trail: normalised Web-Mercator coords (0..1) cached when the trail is // re-read, so a pan only scales and offsets them. One lv_line per recorded // segment (a pause starts a new one); extra segments join the last. -static const int TRAIL_SEGS = 8; +static const int TRAIL_SEGS = 32; // pauses / saves split the trail; beyond this, segments join // Stored as float offsets from the first point (s_ox/s_oy): a float holds a // small offset exactly enough, but not a whole 0..1 coordinate at street zoom // (2^18 * 256 px across -- a float is off by pixels there). @@ -219,12 +219,23 @@ void UITask::layoutNav() { for (int s = 0; s < navmap::TRAIL_SEGS; s++) { lv_obj_t* l = navmap::s_trail[s]; if (s >= navmap::s_segs || navmap::s_seg_len[s] < 2) { lv_obj_add_flag(l, LV_OBJ_FLAG_HIDDEN); continue; } - int f = navmap::s_seg_first[s], len = navmap::s_seg_len[s]; + // Points closer than 2 px to the last kept one are dropped (the last + // point always stays): zoomed out, thousands of points shrink to the few + // hundred that show, so a long trail doesn't slow every map redraw. + int f = navmap::s_seg_first[s], len = navmap::s_seg_len[s], k = 0; for (int i = f; i < f + len; i++) { - navmap::s_pts[i].x = (lv_value_precise_t)lround((navmap::s_ox + navmap::s_nx[i]) * scale - mapview::s_left); - navmap::s_pts[i].y = (lv_value_precise_t)lround((navmap::s_oy + navmap::s_ny[i]) * scale - mapview::s_top); + lv_value_precise_t x = (lv_value_precise_t)lround((navmap::s_ox + navmap::s_nx[i]) * scale - mapview::s_left); + lv_value_precise_t y = (lv_value_precise_t)lround((navmap::s_oy + navmap::s_ny[i]) * scale - mapview::s_top); + if (k > 0 && i < f + len - 1) { + lv_point_precise_t& last = navmap::s_pts[f + k - 1]; + if (fabsf((float)(x - last.x)) < 2 && fabsf((float)(y - last.y)) < 2) continue; + } + navmap::s_pts[f + k].x = x; + navmap::s_pts[f + k].y = y; + k++; } - lv_line_set_points(l, &navmap::s_pts[f], len); + if (k < 2) { lv_obj_add_flag(l, LV_OBJ_FLAG_HIDDEN); continue; } + lv_line_set_points(l, &navmap::s_pts[f], k); lv_obj_remove_flag(l, LV_OBJ_FLAG_HIDDEN); } @@ -683,7 +694,7 @@ void UITask::navRenameDone(bool ok) { // ── Adding waypoints ────────────────────────────────────────────────────────── void UITask::navAddWaypoint(int32_t lat, int32_t lon) { - if (_core->waypoints.full()) { showToast("Waypoints full (16)"); return; } + if (_core->waypoints.full()) { showToast(waypointsFull()); return; } if (!_core->waypoints.add(lat, lon, rtc_clock.getCurrentTime(), "")) return; int i = _core->waypoints.count() - 1; char t[48]; @@ -698,7 +709,7 @@ void UITask::navMarkHere() { if (!ensureGps() || !_core->course.currentLocation(lat, lon)) return; uint16_t secs = NodePrefs::gpsAvgSecs(_prefs ? _prefs->gps_avg_idx : 0); if (secs == 0) { navAddWaypoint(lat, lon); return; } - if (_core->waypoints.full()) { showToast("Waypoints full (16)"); return; } + if (_core->waypoints.full()) { showToast(waypointsFull()); return; } navmap::s_avg.start(secs, lat, lon); navPollAveraging(); } @@ -1115,7 +1126,7 @@ void UITask::navWaypointsPopup() { // "50.06142, 19.93721 Name" (decimal degrees; comma or space between them; the // name is optional) -> a new waypoint. void UITask::navCoordsPopup() { - if (_core->waypoints.full()) { showToast("Waypoints full (16)"); return; } + if (_core->waypoints.full()) { showToast(waypointsFull()); return; } lv_obj_t* panel = navPopupPanel("Add by coordinates", false); lv_obj_align(panel, LV_ALIGN_TOP_MID, 0, theme::STATUS_H + 4); // above the keyboard _nav_ta = textField(panel); @@ -1152,7 +1163,7 @@ void UITask::navCoordsDone(bool ok) { return; // keep the field for a fix } navClosePopup(); - if (!_core->waypoints.add(lat, lon, rtc_clock.getCurrentTime(), name)) { showToast("Waypoints full (16)"); return; } + if (!_core->waypoints.add(lat, lon, rtc_clock.getCurrentTime(), name)) { showToast(waypointsFull()); return; } const Waypoint& w = _core->waypoints.at(_core->waypoints.count() - 1); char t[40]; snprintf(t, sizeof(t), "Saved %s", w.label); diff --git a/examples/companion_radio/ui-lvgl/QuickScreen.h b/examples/companion_radio/ui-lvgl/QuickScreen.h index 65d980a7..fbb58fb7 100644 --- a/examples/companion_radio/ui-lvgl/QuickScreen.h +++ b/examples/companion_radio/ui-lvgl/QuickScreen.h @@ -194,8 +194,8 @@ void UITask::advertPopup() { lv_obj_set_flex_flow(list, LV_FLEX_FLOW_COLUMN); lv_obj_set_style_pad_row(list, theme::GAP, 0); sectionTitle(list, "SEND NOW"); - listRow(list, "Nearby only", "Direct range only", onAdvertGo, (void*)(uintptr_t)0); - listRow(list, "Everyone", "Across the mesh", onAdvertGo, (void*)(uintptr_t)1); + listRow(list, "Zero hop", "Direct range only", onAdvertGo, (void*)(uintptr_t)0); + listRow(list, "Flood", "Across the mesh", onAdvertGo, (void*)(uintptr_t)1); schemaRows(list, settings::PG_ADVERT); } @@ -203,7 +203,7 @@ void UITask::advertSend(bool flood) { navClosePopup(); bool ok = flood ? the_mesh.advertFlood() : the_mesh.advert(); if (ok) notify(UIEventType::ack); - showToast(!ok ? "Advert failed" : flood ? "Advert sent across the mesh" : "Advert sent to nodes in range"); + showToast(!ok ? "Advert failed" : flood ? "Flood advert sent" : "Zero-hop advert sent"); } // ── Settings > Bluetooth ────────────────────────────────────────────────────── diff --git a/examples/companion_radio/ui-lvgl/UITask.cpp b/examples/companion_radio/ui-lvgl/UITask.cpp index 017d19e2..c3204400 100644 --- a/examples/companion_radio/ui-lvgl/UITask.cpp +++ b/examples/companion_radio/ui-lvgl/UITask.cpp @@ -37,6 +37,12 @@ template static T* psramBuf(size_t n) { #include "../ui-core/KeyboardData.h" #include "Keyboard.h" +static const char* waypointsFull() { + static char t[24]; + snprintf(t, sizeof(t), "Waypoints full (%d)", WaypointStore::CAPACITY); + return t; +} + // ui-lvgl skeleton (docs/development/ui-core.md, step 5): status bar, home, // conversation list, contact picker, conversation view with compose. Every // piece of state it shows comes from the UI Core; this file only draws it. @@ -837,7 +843,7 @@ namespace home { struct App { const char* icon; const char* text; lv_event_cb_t cb; bool unread; }; static const App APPS[] = { { LV_SYMBOL_ENVELOPE, "Messages", onOpenChats, true }, - { UI_SYMBOL_USERS, "Nearby", onOpenNearby, false }, + { UI_SYMBOL_USERS, "Nodes", onOpenNearby, false }, { UI_SYMBOL_MAP, "Map", onOpenNav, false }, { LV_SYMBOL_SETTINGS, "Settings", onOpenSettings, false }, // Page 2: tools @@ -1003,7 +1009,7 @@ static void onOpenChannel(lv_event_t* e) { // DM rows carry a 4-byte prefix; kept in a static table the rows point into. static uint8_t s_dm_rows[MessageHistory::DM_HIST_MAX][4]; -static const int CONTACT_ROWS_MAX = 64; +static const int CONTACT_ROWS_MAX = 256; // "All" with a full contact table stays usable static uint8_t (*s_contact_rows)[PUB_KEY_SIZE] = psramBuf(CONTACT_ROWS_MAX); static void onOpenDMRow(lv_event_t* e) { @@ -1016,7 +1022,7 @@ static void onNewChat(lv_event_t* e) { (void)e; s_ui->showContacts(); } static void onChanRowHold(lv_event_t* e); // ChannelScreen.h static void onChanAdd(lv_event_t* e); static void onChanThreadMenu(lv_event_t* e); -static const int ROOM_ROWS_MAX = 16; +static const int ROOM_ROWS_MAX = 32; static uint8_t (*s_room_rows)[PUB_KEY_SIZE] = psramBuf(ROOM_ROWS_MAX); static void onDMRowHold(lv_event_t* e); // ConversationScreen.h static void onRoomRow(lv_event_t* e); @@ -1261,7 +1267,7 @@ void UITask::showNearby() { } void UITask::buildNearby() { - lv_obj_t* body = newScreen("Nearby", true); + lv_obj_t* body = newScreen("Nodes", true); lv_obj_set_style_pad_row(body, 4, 0); if (_header) { headerButton(_header, LV_SYMBOL_REFRESH, onNearbyScan, 4, NULL); // scan @@ -1675,7 +1681,7 @@ void UITask::nodeAction(uint8_t action) { openAdmin(e.pub_key); break; case NODE_WAYPOINT: { - if (_core->waypoints.full()) { showToast("Waypoints full (16)"); break; } + if (_core->waypoints.full()) { showToast(waypointsFull()); break; } char t[48]; if (_core->waypoints.add(e.lat_e6, e.lon_e6, rtc_clock.getCurrentTime(), e.name[0] ? e.name : "Node")) { snprintf(t, sizeof(t), "Saved %s", _core->waypoints.at(_core->waypoints.count() - 1).label); @@ -1878,7 +1884,7 @@ uint32_t UITask::threadSignature() const { // Positions found in the shown messages ([WAY] / [LOC] / plain "lat,lon"), // for the Go / Save buttons under such a bubble. struct MsgLoc { int32_t lat, lon; char label[WAYPOINT_LABEL_LEN * 2]; }; -static const int THREAD_MAX_SHOWN = 30; +static const int THREAD_MAX_SHOWN = 50; // newest bubbles built per conversation static MsgLoc* s_msg_locs = psramBuf(THREAD_MAX_SHOWN); static int s_msg_loc_n = 0; @@ -2008,7 +2014,7 @@ void UITask::messageLocationAction(int idx, bool save) { if (idx < 0 || idx >= s_msg_loc_n) return; const MsgLoc& m = s_msg_locs[idx]; if (save) { - if (_core->waypoints.full()) { showToast("Waypoints full (16)"); return; } + if (_core->waypoints.full()) { showToast(waypointsFull()); return; } if (_core->waypoints.add(m.lat, m.lon, rtc_clock.getCurrentTime(), m.label)) { char t[48]; snprintf(t, sizeof(t), "Saved %s", _core->waypoints.at(_core->waypoints.count() - 1).label); diff --git a/solo/wio-tracker-l2/platformio.ini b/solo/wio-tracker-l2/platformio.ini index bcd8c11c..726ee666 100644 --- a/solo/wio-tracker-l2/platformio.ini +++ b/solo/wio-tracker-l2/platformio.ini @@ -37,12 +37,12 @@ lib_deps = densaugeo/base64 @ ~1.4.0 ; ui-lvgl: the real L2 interface -- LVGL 9 frontend over the shared UI Core -; (docs/development/ui-core.md, step 5). *_solo_lvgl envs are published by -; build-solo-firmwares.yml (merged image + the -ota.bin that Settings > Firmware -; update installs). Make the Arduino 3.x env below this one before tagging a -; release: only 3.x builds can update themselves (CA bundle for the TLS check). +; (docs/development/ui-core.md, step 5), on Arduino-ESP32 3.3.12 / ESP-IDF 5.5 +; (pioarduino). *_solo_lvgl envs are published by build-solo-firmwares.yml +; (merged image + the -ota.bin that Settings > Firmware update installs). [env:Wio_Tracker_L2_companion_solo_lvgl] extends = Wio_Tracker_L2 +platform = https://github.com/pioarduino/platform-espressif32/releases/download/55.03.312-1/platform-espressif32.zip build_flags = ${Wio_Tracker_L2.build_flags} -I examples/companion_radio/ui-lvgl @@ -60,7 +60,14 @@ build_flags = -D MESH_IN_PSRAM=1 ; frees ~120 KB internal RAM for WiFi/TLS map downloads -D PIN_BUZZER=-1 ; no piezo pin: the speaker plays through the codec -D BUZZER_I2S - -D OTA_ASSET_SUFFIX='"-Wio-Tracker-L2-ota.bin"' ; Settings > Firmware update (Arduino 3.x builds) + -D OTA_ASSET_SUFFIX='"-Wio-Tracker-L2-ota.bin"' ; Settings > Firmware update + ; Sized for the PSRAM (UiCore is heap-allocated, so these land there); + ; the defaults in the headers are the nRF52 boards'. + -D TRAIL_CAPACITY=4096 ; 64 KB (+64 KB of map drawing buffers) + -D WAYPOINT_CAPACITY=64 + -D HIST_CH_MAX=256 ; all channels together, ~50 KB + -D HIST_DM_MAX=128 ; all DMs together, ~27 KB + -D NEARBY_MAX=64 build_src_filter = ${Wio_Tracker_L2.build_src_filter} + + @@ -75,17 +82,18 @@ lib_deps = lvgl/lvgl @ 9.2.2 ; pinned: bump deliberately, re-test on hardware SD_MMC ; map tiles on the microSD (framework libraries from here on) FS - WiFi ; on-device map download + WiFi ; on-device map download, live tiles, OTA HTTPClient - WiFiClientSecure + NetworkClientSecure ; 3.x name of WiFiClientSecure (the header is still there) Preferences + Update ; Settings > Firmware update -; The same firmware on Arduino-ESP32 3.x (ESP-IDF 5.5, pioarduino). Runs beside -; the 2.0.17 env above until it passes docs/development/l2-hardware-checklist.md -; (roadmap stage 2), then replaces it. -[env:Wio_Tracker_L2_companion_solo_lvgl_v3] +; The same firmware on the old Arduino-ESP32 2.0.17 (ESP-IDF 4.4), kept for +; comparison while the 3.x one settles. Not published (the name doesn't end in +; _solo_lvgl) and can't update itself (no CA bundle for the TLS check). +[env:Wio_Tracker_L2_companion_solo_lvgl_arduino2] extends = env:Wio_Tracker_L2_companion_solo_lvgl -platform = https://github.com/pioarduino/platform-espressif32/releases/download/55.03.312-1/platform-espressif32.zip +platform = ${esp32_base.platform} lib_deps = ${Wio_Tracker_L2.lib_deps} densaugeo/base64 @ ~1.4.0 @@ -94,6 +102,5 @@ lib_deps = FS WiFi HTTPClient - NetworkClientSecure ; 3.x name of WiFiClientSecure (the header is still there) + WiFiClientSecure Preferences - Update ; Settings > Firmware update diff --git a/variants/sim/build_wasm.sh b/variants/sim/build_wasm.sh index abb35372..a5c93f0e 100755 --- a/variants/sim/build_wasm.sh +++ b/variants/sim/build_wasm.sh @@ -182,7 +182,9 @@ if [ "$SIM_UI" = "lvgl" ]; then for d in "${DEFINES[@]}"; do case "$d" in -DDISPLAY_CLASS=*|-DMAX_GROUP_CHANNELS=*) ;; *) kept+=("$d") ;; esac done - DEFINES=("${kept[@]}" -DDISPLAY_CLASS=SimLcdDisplay -DMAX_GROUP_CHANNELS=40 -DUI_ZOOM=1 -DLV_CONF_INCLUDE_SIMPLE) + # The L2's PSRAM-sized limits (solo/wio-tracker-l2/platformio.ini). + DEFINES=("${kept[@]}" -DDISPLAY_CLASS=SimLcdDisplay -DMAX_GROUP_CHANNELS=40 -DUI_ZOOM=1 -DLV_CONF_INCLUDE_SIMPLE + -DTRAIL_CAPACITY=4096 -DWAYPOINT_CAPACITY=64 -DHIST_CH_MAX=256 -DHIST_DM_MAX=128 -DNEARBY_MAX=64) INCLUDES+=("-I$LVGL_DIR") LINK_EXTRA=(-sFETCH=1) # map downloads (LvglPort.h) go through emscripten_fetch fi