mirror of
https://github.com/MarekZegare4/MeshCore-Solo.git
synced 2026-10-09 11:16:39 +00:00
feat(ui-lvgl): navigation map, clock tools, settings pages, radio
- Home > Map is the navigation map: waypoints (list, add at GPS / by coordinates / long-press spot), live shares, trail recording + tools (save, load, GPX export, track back), live share controls, target bar with ETA; the nodes map opens from Nearby. - Map: smooth panning (no decode during a drag, idle read-ahead, cached coarser stand-ins), overzoom limited to 2 levels, resumable downloads, map credit behind a "©" and in Settings > About; target ring and trail computed in double. - Home in swipeable pages; Clock (alarm, timer, stopwatch, ring card). - Settings rendered from the Core schema (display & power with a brightness slider, messages & contacts, trail / live share / alerts), GPS switch, Radio screen; low-battery shutdown. - WioTrackerL2Display: honour the 0-4 brightness level. - docs: L2 hardware checklist, ui-core notes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
# Wio Tracker L2 (ui-lvgl) — hardware checklist
|
||||
|
||||
Things verified only in the browser simulator so far. Tick on the device;
|
||||
note anything odd next to the item. Branch `wio-tracker-l2`.
|
||||
|
||||
## Feedback round 1 (2026-09-25) — fixed, check on the device
|
||||
Confirmed on the device: live share sends and receives positions.
|
||||
- [ ] Map: ☰ tools moved to the left column (back, pin, ☰); nothing overlaps the crosshair
|
||||
- [ ] Target ring sits exactly on the marker (all markers were drawn ~3 px low)
|
||||
- [ ] A person sharing on a channel, picked as target, is followed as they move (was a fixed point where they were); bar shows the person icon
|
||||
- [ ] Settings: "Trail, live share, alerts" subtitle no longer covers the title (all list rows)
|
||||
- [ ] Map credit is a small "©" (tap: full line in a toast); full text also in Settings > About; long toasts wrap
|
||||
- [ ] Home: Messages, Nearby, Map, Settings — "Map" is the navigation map; the nodes map opens from Nearby's header (map icon), back returns to Nearby
|
||||
- [ ] Home tiles are wider; more apps will go on further swipeable pages (dots appear once there are two)
|
||||
- [ ] Overzoom limited to 2 levels (x4); past that "No map detail here at this zoom" — download more
|
||||
- [ ] Panning: no tile decoding while the finger moves (smooth drag, tiles fill in once you stop); tiles around the view are decoded ahead while idle; newly revealed area shows the coarser tile until the sharp one is ready
|
||||
- [ ] Note how panning feels now: ______ (next step if still slow: decode on the second core / JPEG tiles)
|
||||
|
||||
## Feedback round 2 (2026-09-25) — fixed, check on the device
|
||||
Confirmed: settings rows OK, map pans much faster, ring centred.
|
||||
- [ ] Target ring is wider (30 px) so the green / flag marker shows inside it; a target with no marker on it (map point, message position, stale live share) gets an orange centre dot
|
||||
- [ ] "↓ Resume?" pill sits top right beside +, clear of the zoom pill; tapping it opens the download popup
|
||||
- [ ] Pin button → Waypoints list (distance, tap → Go / Name / Share / Delete) with "Here (GPS)" and "Coordinates"
|
||||
- [ ] Coordinates: digits keyboard, "50.06142, 19.93721 Name" or with a space instead of the comma; bad input keeps the field with a hint; the map centres on the new waypoint
|
||||
- [ ] Holding the map no longer adds a waypoint straight away: "This spot" popup with Add waypoint / Go here
|
||||
|
||||
## Feedback round 3 (2026-09-25) — fixed, check on the device
|
||||
Confirmed: green dot inside the ring, waypoints much better, download works, settings OK.
|
||||
- [ ] Ring exactly on the marker at every zoom (it was computed in float: pixels off at z16+). The trail line had the same error and is fixed too
|
||||
- [ ] Home: swipe left / right anywhere (clock or tiles) turns the page; a swipe starting on a tile doesn't open it; tapping a dot also switches
|
||||
- [ ] Settings > Display & power: Brightness is a slider (5-100 %), changes live while dragging, kept after a reboot and after the screen sleeps / wakes
|
||||
- [ ] After the update: prefs load normally (new field, schema sentinel 0xC0DE002F) — brightness starts from the old level, nothing else reset
|
||||
|
||||
## New: Clock, settings pages, radio (2026-09-25)
|
||||
- [ ] Home has two pages now: swipe left → Clock (dots under the tiles follow; Home remembers the page)
|
||||
- [ ] Clock > Alarm: hour / minute rollers (setting a time arms it), On switch, Repeat (Once / Daily / Weekdays / Weekends); rings at the local time
|
||||
- [ ] Clock > Timer: H / M / S rollers, Start → big countdown, Stop; when it ends a full-screen "Timer done" card with Dismiss appears on any screen (also wakes the display; the user button dismisses too). Silent: no speaker driver yet
|
||||
- [ ] Clock > Stopwatch: Start / Stop / Reset with tenths
|
||||
- [ ] Settings > Display & power: Screen off after; Wake on message
|
||||
- [ ] Settings > Display & power: Battery shutdown (not while on USB), GPS power saving, Time zone (clock + alarm follow)
|
||||
- [ ] Settings > Messages & contacts: Resend direct messages, Contact expiry + "Remove inactive contacts now" (first tap shows the count, second removes; favourites kept), Favourites first
|
||||
- [ ] Settings > Radio: Preset list (built-ins + your saved ones), Frequency (tap, type), SF, Bandwidth, Coding rate, TX power, Auto power; changes apply at once — check messages still flow after switching back to your usual preset
|
||||
- [ ] L1 (ui-new): Settings > Radio TX power / Auto power / presets still apply (now through the shared RadioControl)
|
||||
|
||||
## Basics after the_mesh moved to PSRAM (`MESH_IN_PSRAM`)
|
||||
- [ ] Boots normally, contacts and channels are all there
|
||||
- [ ] Messages arrive and send (channel + DM), delivery ticks work
|
||||
- [ ] Companion app over BLE connects and syncs
|
||||
- [ ] A few hours of uptime without a reboot
|
||||
|
||||
## WiFi map download
|
||||
- [ ] Settings > WiFi: Scan lists networks, pick one, password, Save
|
||||
- [ ] Map > download button: popup shows tile count / size, Download starts
|
||||
- [ ] Pill on the map: "Connecting..." then "↓ n / total"
|
||||
- [ ] Popup while downloading: IP, dBm, heap (internal free / largest block) — note the numbers: ______
|
||||
- [ ] Mesh messages still arrive during a download; BLE stays connected
|
||||
- [ ] Stop → popup shows "Unfinished: z…-…, N tiles" with Resume / trash
|
||||
- [ ] Power off mid-download → power on → map shows "↓ Resume?" (top right; tap it) → Resume continues (count jumps past the tiles already on the card)
|
||||
- [ ] Trash discards the unfinished job
|
||||
- [ ] Download finishes: toast "Map: Done, N new tiles", new tiles appear on the map
|
||||
|
||||
## Overzoom (map past the downloaded detail)
|
||||
- [ ] Zoom in beyond the highest downloaded level: map stays visible (magnified), zoom pill reads e.g. "z17 (map z15)"
|
||||
- [ ] Panning over magnified tiles is smooth enough (note if it stutters)
|
||||
- [ ] From a magnified view, download a higher zoom for that area — the sharp tiles replace the magnified ones
|
||||
|
||||
## GPS
|
||||
- [ ] Settings > Navigation shows a "GPS" switch; toggling it turns the module on/off (and survives a reboot)
|
||||
- [ ] Status bar: GPS icon grey while searching, green with a fix, gone when off
|
||||
- [ ] Map crosshair with GPS off: turns GPS on ("GPS on, waiting for a fix"), centres once a fix arrives
|
||||
- [ ] Bot `!gps on` / `!gps off` works on L2
|
||||
|
||||
## Two maps
|
||||
- [ ] Nodes map (Nearby > map icon): contacts with a position as markers, tap → node detail, back → map
|
||||
- [ ] Map: hold the map → "This spot" → Add waypoint → "WPn" at the finger
|
||||
- [ ] Map: pin button → Waypoints → Here (GPS) → waypoint at the GPS position
|
||||
- [ ] Bar (bottom) → "Navigate to" list: waypoints with distance, Trail start (if a trail exists), people sharing live
|
||||
- [ ] Tap a waypoint → target: ring, dashed line from you, bar with distance / bearing / your course / ETA; view frames you + target
|
||||
- [ ] ✕ on the bar clears the target
|
||||
- [ ] Waypoint menu (pencil): Go / Name (Polish letters, 11-byte limit) / Share / Delete (second tap confirms)
|
||||
- [ ] Share → Messages → pick a conversation → compose holds `[WAY]lat,lon name` → send; a Solo L1 receiving it can save it
|
||||
- [ ] Settings > Trail, live share, alerts > "Show others' positions" on → someone's `[LOC]` share appears on the Map and in the list; tap → navigate to them
|
||||
- [ ] Node detail (a node with a position) has the compass button → Map with that node as target
|
||||
- [ ] Settings > … > "Arrival alert" on + target set → toast "Arrived: …" when you get within the radius
|
||||
|
||||
## Positions in messages
|
||||
- [ ] A received message with `[WAY]lat,lon name`, `[LOC]lat,lon` or plain "lat, lon" shows Go / Save under the bubble
|
||||
- [ ] Save → waypoint named from the [WAY] label (else the sender), Polish letters not cut in half
|
||||
- [ ] Go → Map with that spot as the target
|
||||
|
||||
## Trail & live share (Map > ☰ tools button, left column)
|
||||
- [ ] Record → pill "REC 0 m" at the top; walk: blue trail line follows, distance grows
|
||||
- [ ] Auto-pause (Settings > Trail, live share, alerts): standing still → "PAUSED", walking resumes
|
||||
- [ ] Stop / Save / Load / Reset (second tap confirms) behave; a trail saved on L2 loads (same /trail file as L1)
|
||||
- [ ] Export GPX → toast "Saved trails/trail-YYYYMMDD-HHMM.gpx"; the file opens in a GPX viewer (track + waypoints)
|
||||
- [ ] Live share: pick "Send to" (channel or favourite), Share live → pill "LIVE 1h00"; another node sees the [LOC] updates while you move; stops by itself after the chosen time
|
||||
- [ ] Send once: with sharing on → "Position sent"; with it off → Messages with `[LOC]lat,lon` waiting in the compose field
|
||||
- [ ] Track back (with a recorded trail): bar "Back: n pt" with distance / bearing, ring on the breadcrumb; walking advances it; at the start toast "Back at the trail start"; ✕ stops it
|
||||
- [ ] ☰ > Download this area opens the download popup
|
||||
- [ ] Waypoint averaging 5 s/10 s/30 s: pin → "Averaging GPS… n s" pill (tap cancels) → waypoint saved at the mean
|
||||
|
||||
## Settings > Trail, live share, alerts (schema-driven)
|
||||
- [ ] Every row changes and survives a reboot; "Imperial units" relabels point spacing and distances
|
||||
- [ ] Changing "Stop sharing after" during a session restarts its clock
|
||||
- [ ] Radius / Alert on (arrive, leave, both) change when the arrival toast fires
|
||||
|
||||
## Same data on L1 (ui-new) after the Core changes
|
||||
- [ ] L1: waypoints saved before the update are still listed (Tools › Trail › Waypoints)
|
||||
- [ ] L1: add / rename / delete waypoint, navigate, ETA line still shows
|
||||
- [ ] L1: Home GPS toggle still works
|
||||
- [ ] L1: Tools › Trail Save / Load / Reset still work (now via TrailEngine)
|
||||
- [ ] L1: Mark with averaging (Trail › Settings › Mark avg) still counts down and marks
|
||||
- [ ] L1: Track back still advances along the trail and ends at the start
|
||||
@@ -29,18 +29,18 @@ interleaved. Classified:
|
||||
|
||||
**Models — already UI-free, move as-is**
|
||||
- `MessageHistory.h` — channel (48) / DM (32) rings, unread counters + overflow flags. ✅ moved to `ui-core/`.
|
||||
- `Trail.h` / `TrailStore`, waypoints (`WaypointsView` storage half), `ScopeList.h`.
|
||||
- `Trail.h` / `TrailStore`, `ScopeList.h`. Waypoints ✅ `ui-core/WaypointModel.h` (store + `/waypoints` persistence + "delete clears the Locator target"; ui-new's `UITask::waypoints()` forwards to it).
|
||||
|
||||
**Engines — logic living inside `UITask.cpp` / `UITask.h`**
|
||||
- Unread tracking — DM unread table ✅ (`ui-core/DmUnreadTable.h`), room unread (still in `UITask`).
|
||||
- Notifications ✅ split: the Core decides *what* happened (`MessageArrived` with kind / DM sender / channel slot, `AdvertHeard`); the frontend decides *how* to show it (`showAlert`, `SoundNotifier`, vibration, LED, wake-on-message) — those are platform services.
|
||||
- Live share ✅ `ui-core/LiveShareEngine.h` — session timer, movement/heartbeat gate, send + scope guard, peers' `LiveTrackStore` + expiry.
|
||||
- Locator ✅ `ui-core/LocatorEngine.h` — active target, geofence state machine, proximity beeper.
|
||||
- Trail ✅ `ui-core/TrailEngine.h` — store, sampling, auto-pause, low-battery auto-save.
|
||||
- Trail ✅ `ui-core/TrailEngine.h` — store, sampling, auto-pause, low-battery auto-save, recording actions (start/stop, reset, save/load `/trail`) used by both frontends. GPS averaging for marks ✅ `ui-core/GpsAverager.h`. Track back ✅ `ui-core/TrackBack.h`.
|
||||
- Course over ground + current position ✅ `ui-core/CourseEngine.h`.
|
||||
- Clock tools — alarm / countdown / ring ✅ `ui-core/ClockEngine.h`.
|
||||
- Ping ✅ `ui-core/PingEngine.h` (timeout still decided by `NearbyScreen`).
|
||||
- Device controls — GPS on/off, GPIO (`setGpioMode`, bot GPIO), buzzer mode/volume, brightness, radio apply (`applyTxPower`, `applyApc`, `applyRadioParams`, …).
|
||||
- Device controls — GPS on/off ✅ (`UiCore::setGpsEnabled`, used by ui-new's toggle and ui-lvgl's Settings / map crosshair), GPIO (`setGpioMode`, bot GPIO), buzzer mode/volume, brightness, radio apply (`applyTxPower`, `applyApc`, `applyRadioParams`, …).
|
||||
- Bot hooks — `botSetGPS`, `botBuzz`, `botSetGPIO`, …
|
||||
|
||||
**Screen-embedded logic (harder to extract)**
|
||||
@@ -110,6 +110,8 @@ call the same ones, so e.g. "marking a channel read" has one implementation.
|
||||
|
||||
### 5. Declarative settings schema
|
||||
|
||||
`ui-core/SettingsSchema.h` holds three pages -- navigation (trail, live share, arrival alert, units), display & power (brightness, screen off, wake on message, battery shutdown, GPS power saving, time zone) and messages & contacts (DM resends, contact expiry, favourites first). An entry is a one-byte index by default, or a `values` table over a 1/2/4-byte field (seconds, millivolts, an hour offset, `{1, 0}` for a field stored inverted); side effects run through `UiCore` (`applyGpsInterval()`, `host()->applyDisplayPrefs()`). ui-lvgl renders each page; ui-new still uses its own screens. Radio settings are not schema rows (preset list, typed frequency, radio side effects): `ui-core/RadioControl.h` applies them (`applyParams`, `applyTxPower`, `applyApc`, presets = built-ins + user slots) for both ui-new's Settings > Radio and ui-lvgl's Radio screen.
|
||||
|
||||
Replaces the per-item code in `SettingsScreen`:
|
||||
|
||||
```cpp
|
||||
@@ -147,7 +149,7 @@ consumes keys only.
|
||||
- Theme tokens (colours, spacing, typography) in one place — the first chance to realise the "Amber Trace" direction in colour.
|
||||
- Fonts ✅ Noto Sans generated by `ui-lvgl/fonts/generate.sh` (lv_font_conv): European Latin (Latin-1, Extended-A/B), Greek, Cyrillic + supplement, typographic punctuation, currency, LVGL symbols; 12/14/16/20 px, clock 40 px digits only. On-screen keyboard (`ui-lvgl/Keyboard.h`), phone-style: one layout per script (Latin QWERTY / Cyrillic ЙЦУКЕН / Greek) chosen by the same `keyboard_main_alphabet` / `keyboard_alt_alphabet` prefs as `ui-new` (globe key switches), hold a key for its variants; compose limit counted in UTF-8 bytes. Long-press variants and UTF-8 case mapping are shared with `ui-new` in `ui-core/KeyboardData.h`.
|
||||
- Simulator ✅: `SIM_UI=lvgl variants/sim/build_wasm.sh` builds the same `ui-lvgl` code for the browser (`SimLcdDisplay` in `variants/sim/SimDisplayDriver.h` blits LVGL's RGB565 flush to a 320×240 canvas; the `SIM_PLATFORM` branch of `ui-lvgl/LvglPort.h` feeds the mouse in as touch). LVGL comes from the L2 env's PlatformIO libdeps; its objects are cached in `web/build/obj_lvgl/`. `web/lvgl.html` runs it next to a `ui-new` peer on a direct link (buttons: advert, peer DM, peer → Public, the board button) and exposes `window.sim` (`tap`, `hold`, `touch`, `peerDM`, `peerChannel`) for headless checks.
|
||||
- Map (in progress): offline Web-Mercator raster tiles on the microSD (`/maps/{z}/{x}/{y}.png`, the Meshtastic MUI layout; optional per-column `.pak` from PR #3381's packer), SDMMC 1-bit mount in `LvglPort.h`. `ui-lvgl/map/TileProvider.h` is the swap point: the screen and `map/TileCache.h` (12 decoded RGB565 tiles in PSRAM, LRU) only call `renderTile(z, x, y, rgb565)`, so a vector renderer can replace the PNG provider later. `MapScreen.h`: pan by drag, +/-, re-centre/follow GPS, Nearby nodes with a position as markers (tap = node detail), one tile decoded per loop pass. `maps/attribution.txt` is shown on the map. Planned split (user, 2026-09-25): a Nodes map (every contact with a position) and a separate Navigation map (waypoints + live shares only, for walking to a target). Tiles come from `tools/maps/fetch_tiles.py` (no default server: tile.openstreetmap.org blocks offline use) or on the device: the map's download button fetches the visible area (a few overview levels up to a chosen zoom) over WiFi (`map/TileDownloader.h`; skips tiles on the card, rejects non-PNG and placeholder answers; server from `maps/source.txt`, default OpenTopoMap). The HTTP GET runs on a worker task (`LvglPort.h`, keep-alive TLS); WiFi credentials (Settings > WiFi, with scan) live in NVS, and WiFi is on only during a download. Internal RAM is the constraint (Arduino-ESP32 2.0.17 / IDF 4.4: task stacks and, by default, mbedTLS buffers must be internal): with WiFi + Bluedroid up only ~13 KB was left, so the L2 LVGL env sets `MESH_IN_PSRAM` (`the_mesh`, ~120 KB, placement-new into PSRAM in `main.cpp`) and the fetch task points mbedTLS at PSRAM. The download popup shows IP, RSSI, free/largest internal heap and the last TLS/HTTP error. Arduino-ESP32 3.x (IDF 5: PSRAM stacks, NimBLE) is to be evaluated in the polishing phase. In the simulator the same code downloads through emscripten fetch.
|
||||
- Map (in progress): offline Web-Mercator raster tiles on the microSD (`/maps/{z}/{x}/{y}.png`, the Meshtastic MUI layout; optional per-column `.pak` from PR #3381's packer), SDMMC 1-bit mount in `LvglPort.h`. `ui-lvgl/map/TileProvider.h` is the swap point: the screen and `map/TileCache.h` (24 decoded RGB565 tiles in PSRAM, LRU) only call `renderTile(z, x, y, rgb565)`, so a vector renderer can replace the PNG provider later. `MapScreen.h`: pan by drag, +/-, re-centre/follow GPS, Nearby nodes with a position as markers (tap = node detail), one tile decoded per loop pass, never during a drag (the view fills in once the finger rests; an idle map decodes the ring around the view ahead, and a not-yet-decoded tile shows a cached coarser one meanwhile). `maps/attribution.txt` is behind a "©" on the map and in Settings > About. Two maps over the same tile view: Nearby's header opens the Nodes map (every node Nearby knows a position for), Home > Map the Navigation map (`NavMap.h`): saved waypoints, live shares, the recorded trail and the Locator target with a dashed line to it and a bar with distance / bearing / own course / ETA (`ui-core/EtaTracker.h`, shared with ui-new's NavView). Hold the map to drop a waypoint, the pin marks the GPS position; the bar opens the target list and the waypoint menu (go, rename, share as `[WAY]` into a conversation, delete). Node detail has Navigate (target followed by key); a person sharing on a channel is followed by sender name (Locator target kind 2). Settings > Navigation carries the `track_shared_loc` / `locator_enabled` switches ui-new has on its Live share / Locator screens. Tiles come from `tools/maps/fetch_tiles.py` (no default server: tile.openstreetmap.org blocks offline use) or on the device: the map's download button fetches the visible area (a few overview levels up to a chosen zoom) over WiFi (`map/TileDownloader.h`; skips tiles on the card, rejects non-PNG and placeholder answers; server from `maps/source.txt`, default OpenTopoMap). The HTTP GET runs on a worker task (`LvglPort.h`, keep-alive TLS); WiFi credentials (Settings > WiFi, with scan) live in NVS, and WiFi is on only during a download. The job (area + zoom range) stays in `maps/.job` until it completes, so a download cut short by power-off, lost WiFi or Stop is offered for Resume (tiles already on the card are skipped). Past the downloaded detail the map magnifies the nearest coarser tile it has (up to 2 levels -- more is just big pixels; zoom pill shows e.g. `z17 (map z15)`); known-missing tiles are remembered without a pixel buffer. Internal RAM is the constraint (Arduino-ESP32 2.0.17 / IDF 4.4: task stacks and, by default, mbedTLS buffers must be internal): with WiFi + Bluedroid up only ~13 KB was left, so the L2 LVGL env sets `MESH_IN_PSRAM` (`the_mesh`, ~120 KB, placement-new into PSRAM in `main.cpp`) and the fetch task points mbedTLS at PSRAM. The download popup shows IP, RSSI, free/largest internal heap and the last TLS/HTTP error. Arduino-ESP32 3.x (IDF 5: PSRAM stacks, NimBLE) is to be evaluated in the polishing phase. In the simulator the same code downloads through emscripten fetch.
|
||||
- PR #3381's LVGL UI is a reference for platform glue (flush, GT911, PSRAM pool) only; its screens are not adopted.
|
||||
|
||||
## Migration plan
|
||||
|
||||
Reference in New Issue
Block a user