Commit Graph
72 Commits
Author SHA1 Message Date
JakubandClaude Opus 5.5 57cf58424d feat: a node map and a calendar on ui-new, a banner switch on the L2
- Nearby › Map (ui-new): the positioned nodes and you on one view, scaled
  to fit, with north, a scale bar and the selected node's name and
  distance; Left / Right picks a node, Up / Down zooms on it, Enter opens it.
- Clock tools › Calendar (ui-new): the month with today and the alarm's
  days; a tight grid on small screens, Left / Right pages the months.
- L2 Settings: a Message banner switch (on / off) in place of ui-new's
  three-way alert setting.
- Simulator: sim_nodes() puts eight positioned contacts around Kraków.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 09:38:29 +02:00
JakubandClaude Opus 5.5 d901a58424 refactor: one place each for "in view" and the keyboard's backspace
ui-new's messagesInView() and ui-lvgl's threadInView() hold the
lit-and-unlocked test the viewing checks repeated; the keyboard's three
copies of the backspace call deleteBeforeCursor(). A stale comment on the
cursor-mode hint goes, onDisplayOn() sits after the view helpers it calls.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 23:58:52 +02:00
JakubandClaude Opus 5.5 e09a90b325 fix: messages that arrive while asleep or locked stay unread
A conversation left open under the lock screen or a dark display no longer
counts as read: the counters and the lock screen's tally go up, and on
waking (L2: on unlocking) the conversation reopens on its first unread
under the "New" line, a typed draft kept.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 23:41:40 +02:00
JakubandClaude Opus 5.5 aaf35351d7 feat: open a conversation on its first unread message, under a "New" line
Both UIs: a channel, DM or room with unread messages opens on the oldest of
them (MessageHistory::firstUnreadChannel / firstUnreadDm, own messages not
counted), a "New" line above it, so it reads down to the newest. Nothing
unread: the newest, as before.

L1: the first unread is selected and scrolled up top with the line in view;
the channel's unread count now counts down as you read towards the newest.
L2: the page holding it, scrolled to the line; an incoming message scrolls
down only when the list is already at its end. Sim hooks: sim_unread (L1),
sim_open("unread") (L2).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 22:20:04 +02:00
JakubandClaude Opus 5.5 38925dde65 fix(l2): a dot per DM send, lists that notice every change, the LED lit only on TX
- A pending DM shows a dot per send, as on the L1. The thread is read from
  the SD archive, which a resend never updated: it does now, and the
  thread's signature and patch state count the attempt.
- Nearby redraws when a node goes live, changes type or the units change;
  the scan popup when a name or the remote SNR arrives.
- The user LED is active high: it was lit all the time and went dark
  while transmitting.
- Sim hooks "dm" and "send" for testing a DM thread.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 14:32:54 +02:00
JakubandClaude Opus 5.5 6c9186dc54 feat: a message's bytes left, counted as it will be sent, on every screen
The limit is what the send leaves of MAX_TEXT_LEN: a channel post carries
"<node name>: ", a direct message resent more than three times keeps two
bytes spare, and placeholders count as they expand (msgtext::limit/sentLen).
Bytes, not characters. The path and packet header sit outside the payload;
static_asserts keep a full message within the radio frame on the longest path.

From 40 bytes left the count shows: on the L2 as a tag on the field's top
edge, on OLED/e-ink at the right of the preview's last line with the text
wrapped short of it. Input stops at the limit; a send still over (a
placeholder grew) is held. Sim hooks to open a compose with text.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 22:58:01 +02:00
Claude 2ea0ac4416 refactor(ui-lvgl): the perf tools move out of UITask.cpp into PerfTest.h
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VeBrxKyHHxWsmttVkUdBrj
2026-10-05 07:27:54 +00:00
Claude 8df1abd79b refactor(ui-lvgl): quick-message editor uses navTextEntry (bare mode)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VeBrxKyHHxWsmttVkUdBrj
2026-10-05 07:26:39 +00:00
Claude 84fd5013b5 refactor: shared text-entry popup (ui-lvgl) and chat bubble row (ui-new); GPS search clock keeps a found fix
- ui-lvgl: navTextEntry()/TextEntry replace eight copies of popup + field +
  keyboard wiring; one byte-capped insert callback instead of two.
- ui-new: drawHeadedBubble()/BubbleMark replace the duplicated DM and channel
  bubble drawing in MessagesScreen.
- GpsSky::halt(): stopping the receiver ends an unfinished search but keeps
  the first-fix time; keyboard cursor blink restarts after a key.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VeBrxKyHHxWsmttVkUdBrj
2026-10-05 07:23:56 +00:00
JakubandClaude Opus 5.5 b5e4845dae feat(ui-lvgl): quick panel from the status bar; L2 fixes from rc1
The quick panel, as a phone's: pulled down from the status bar (or a tap
on it). Bluetooth, WiFi, GPS, sound, trail and live share switch at a
tap; Advert opens the advert popup (moved here from the Nodes header);
Lock locks the screen; a brightness slider. Holding a tile opens its
settings. Lock and sun glyphs added to the fonts.

Fixes from rc1 testing on the L2:
- GPS status icon: none when GPS is off (the module kept its last fix
  valid, so it stayed green); grey on, green with a fix. ensureGps()
  turns GPS on before trusting that stale fix.
- Screen wake: the panel wakes dark, a frame is drawn, then the
  backlight fades in -- no white flash. Off fades out first.
- Power off / flat battery: the chip wakes every 30 s to look for USB
  power and starts when plugged in (it slept with no wake source before,
  until the reset button). Plugged in when switched off: stays off until
  re-plugged.
- Message ages in a conversation refresh every 2 s, as on ui-new; the
  message popup keeps its age, repeaters heard and delivery up to date.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 09:10:06 +02:00
JakubandClaude Opus 5.5 9597e1e2a4 feat(ui-lvgl): e-ink graphics in colour on the L2
Node detail: a card with a compass rose and the hop chain over the info;
the bar keeps Message / Admin / Add, Ping and Navigate, the rest goes to a
"More" group under the info (favourite toggled in place, delete by a second
tap). Nodes: an arrow by each distance. A trail's height profile in Map
tools and a saved trail's popup. Diagnostics > History: battery, noise,
SNR and traffic charts. Clock > Calendar: today, the weekend and alarm days.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 23:26:29 +02:00
JakubandClaude Opus 5.5 61c75946f6 feat(l2,core): battery curve editor and an L2 feedback round
- Battery curve: NodePrefs batt_curve_mv[11] (voltage at 0..100 %, all
  zeros = built-in LiPo curve, sentinel 0xC0DE0032). ui-core/Battery.h
  uses it for every percentage. L1: Settings > System > Batt curve list
  (Left/Right 10 mV, Reset); L2: Settings > Power > Battery curve chart
  with draggable points and a line at the cell's voltage.
- L2: the Nodes map is the full navigation map plus the nodes, Back
  returns to Nodes; the favourite button is lit while it is one; pinning
  to the Home dial uses a thumbtack (fonts regenerated); minimap dots
  follow the accent; arranging tiles shows where they land while dragging;
  emoji row in Diagnostics > Font.
- Sim hooks: sim_open nodesmap / accent@N / homeedit / battcurve.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 11:25:58 +02:00
JakubandClaude Opus 5.5 c8e1f4a604 perf(l2): long lists build only the rows on screen
New message, Nodes (and its Scan popup), Admin and Messages show their
items through one virtual list: a screenful of rows, handed to the next
item as one scrolls out, the scroll extent from the items' heights. 256
contacts: 522 objects and ~950 ms of filling in (frames up to 96 ms)
before, 19 objects at once now; a scrolled frame 28.5 -> 11.7 ms. Nodes
257 -> 49 objects. A new message rewrites Messages' rows in place
(46 -> 20 ms) instead of building the screen again; it's rebuilt only
when "Read all" comes or goes. A held row keeps what it shows until let
go. Perf tools: -D PERF_LISTS, -D PERF_LIST_MUL.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 20:56:14 +02:00
JakubandClaude Opus 5.5 1125e9a13e perf(l2): choice popups open smoothly, switches never cut short by a rebuild
A popup over a still screen is frozen and dimmed as it opens, so the
panel's rise redraws only the panel: 6-8 frames of 19-25 ms instead of
3-4 of 45-65 ms. Picker options are one label each (time zone list
97 -> 52 ms to build). The bottom popup no longer animates a dim of 0.

Repeater updates its hint in place (130 ms rebuild gone); Auto power,
Imperial units and Relay on rebuild the screen after the knob has
moved (rebuildSoon). -D PERF_CONTROLS times every control on the
settings screens.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 11:27:48 +02:00
JakubandClaude Opus 5.5 910a7c0197 fix(l2): map download no longer stalls on busy tile servers
A 404/503 answer (OpenTopoMap renders on demand) went to an immediate
2 s retry and counted as network loss, so each tile then waited up to
60 s. Answered errors now go straight to the retry list; long pauses
only when there is no answer at all. Three retry rounds (20/60/120 s)
for the tiles still missing, transfers cut after 30 s, failures logged
on serial.

Download popup tidied: title + floating close, percentage instead of
tile counts (also on the map pill), no dead zoom row, no finish toast
after Stop or while the popup shows the result.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 10:54:09 +02:00
JakubandClaude Opus 5.5 039485bf34 perf(l2): heavy screens build in pieces over several loop passes
The list fill becomes a small job queue (fill/then/fillStart, fillTick with a
25 ms budget per pass). Settings pages build section by section in
schemaRows, with the trailing extras (melodies, prune, quick messages) queued
after them so the order stays; Radio and Repeater build by group, Admin's
repeater pick like the contact list, Diag's Live tab by section. Rebuilds
that restore the scroll flush the queue first. Screens went from 220-360 ms
to 80-175 ms (build + first frame).

Diag's crash line read the core dump in flash every second (~44 ms each);
it is read once now. The perf run times every page and dismisses the USB
popup, which had covered all earlier measurements.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 08:53:15 +02:00
JakubandClaude Opus 5.5 34f739c45b perf(l2): long lists fill in over several loop passes
A widget costs ~1 ms on the L2, so Nodes with 48 rows held the screen for
~600 ms and a full contact list for over a second. fillStart() builds the
first screenful at once and fillTick() adds the rest ~25 ms a pass.

- Nodes: 6 rows first; the 2 s refresh rewrites built rows in place
- New message: contacts filled the same way
- Settings: a group at a time (all at once when coming back scrolled)
- Nodes 671 -> ~200 ms, Settings 376 -> ~190 ms longest UI stall

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 00:24:57 +02:00
JakubandClaude Opus 5.5 247fab08d0 perf(l2): map tiles decode on the other core
- TileCache: request() hands z/x/y to a task on core 0, which renders into
  a spare buffer; poll() swaps it into the LRU slot (stale results dropped
  after drop()/invalidate()); the sim runs the job at once
- MapScreen / Home map place finished tiles as they arrive; the longest
  UI stall with the map opening goes from ~340 to ~170 ms
- LVGL allocates from the PSRAM heap (lv_mem_psram.h), safe from both
  tasks; LVGL's own pool is gone
- lv_conf: notes why LV_OS_FREERTOS with two draw units stays off
  (measured slower: drawing is bound by PSRAM bandwidth)
- UI_PERF_TEST: the map (tile read/decode, pan, redraw), build time and
  the longest loop pass per screen

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 00:24:57 +02:00
JakubandClaude Opus 5.5 b3b77a1f6d feat(l2): long trails with a card copy; map follow and redraw fixes
Trail on the L2: 32768 points every 5 m, no simplification, no point
spacing setting; copied to the card every minute and restored at power-on.
The line keeps up with the position dot (incremental sync, pending point).

Map: a tap's finger jitter no longer turns follow off; the GPS button
shows when the map follows. The trail is drawn as one polyline with
breaks and off-view chunks skipped (an lv_line queued a draw task per
point).

Fewer rebuilds: conversation bubbles patched (new messages, delivery
marks), home mini-map dots reused, Nearby rows rewritten in place,
history read in runs and each file opened once.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 22:02:49 +02:00
JakubandClaude Opus 5.5 9938ae7a2d chore(ui-lvgl): comments name the real Settings pages
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 13:27:34 +02:00
JakubandClaude Opus 5.5 e4d24650c5 feat(l2): screenshots over USB (RGB565); USB tools moved to the Solo site
- CMD_GET_SCREENSHOT on colour screens: display type 2 (RGB565 LE),
  32-bit payload size; ui-lvgl captureFrame() shared with the SD BMP
- ENABLE_SCREENSHOT in the L2 build; tools/screenshot.py decodes type 2
- docs and READMEs point to solo.marekzegarek.com/#pc-tools
- USB popup: "Keep card" instead of "Charge only" (only the card is lent;
  charging and USB serial work either way)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 12:27:59 +02:00
JakubandClaude Opus 5.5 4ab9d949ec perf(ui-lvgl): lower clock when idle, fewer redraws, wake on BLE frames
- CPU at 80 MHz with the screen on after 2 s without touch or animation;
  a touch restores the full clock before its frame is drawn
- setText(): periodic refreshes no longer redraw unchanged labels
  (status bar, Home, lock, Diag, GPS, compass, clock, map, nav, nearby)
- map markers kept across the 3 s rebuild when unchanged
- app connected: the loop blocks until a BLE frame arrives
  (serialFrameArrived / hasPendingFrames) instead of polling every 2 ms
- PSRAM: boot frame sprite and the HTTP response buffer released

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 11:51:18 +02:00
JakubandClaude Opus 5.5 e822242660 docs: stage I3 -- documentation rewritten under docs/solo
Short pages by feature (getting started, messages, contacts, navigation,
tools, settings, lock, hardware) instead of one page per OLED screen, with
Wio Tracker L2 differences as notes; facts checked against the code
(keyboard scripts vs. hold-for-accents, {batt}/{dist}, L2 limits and
settings layout). Developer guides and build flags under docs/solo/developer.
Old docs/solo_features and its ~50 screenshots removed; a hero banner and
three simulator screenshots added; links updated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 08:41:10 +02:00
JakubandClaude Opus 5.5 3adf0b0436 chore: stage I1 -- repo cleanup, minimal README
Working plans (docs/development, docs/design redesign notes) removed; the
two developer guides moved to docs/developer/. README cut to devices,
flashing, links and building (Wio Tracker L2 added); its feature list and
doc tables moved to docs/solo_features/README.md. RELEASE.md describes the
Solo release flow. Upstream's GitHub Pages workflow (docs.meshcore.io)
dropped, CLAUDE.md untracked, Buy Me a Coffee back in FUNDING.yml.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 07:36:15 +02:00
JakubandClaude Opus 5.5 358f92222a Merge PR #37: lock screen PIN (3urobeat), adapted
The contributor's feature -- a PIN asked before the screen unlocks, on
boot, and when the magnet cover opens, stored as a salted SHA-256 -- on
top of this branch, with:

- ui-core/ScreenLock.h: set / check / isSet and the tries limit, shared by
  L1 and L2. isSet looks at every byte (the PR tested only the first, so one
  PIN in 256 silently disabled the lock). 5 misses pause entry for 30 s.
- NodePrefs: the fields at the tail after quiet hours, sentinel 0xC0DE0031,
  sizeof 2880; an older file's bytes there are cleared. A sentinel bump no
  longer resets display_brightness_pct from a file that already has it.
- DataStore: no AGPL header (the project is MIT), no whitespace churn.
- Keyboard: the number pad is a widget flag (pin_mode) with a prompt in the
  preview ("New PIN", "Again", "PIN"), not keyboard_type = 2 written into the
  prefs and restored after.
- L1 Settings: "Lock PIN" right after the schema's "Lock screen" row, typed
  twice, at least 4 characters, saved at once (a power-off right after
  setting it kept no PIN). Home is told about a boot-time lock once it
  exists (also fixes booting with the cover closed).
- L2: the screen PIN moves from plain text in NVS to the same hash in
  NodePrefs; the old one is migrated on first boot and removed. The pad
  unlocks as soon as the hash matches from 4 digits on.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 00:06:01 +02:00
JakubandClaude Opus 5.5 b355922d32 feat(companion): core parity -- L1 sends via the core, shared telemetry, schema settings on L1
- L1's MessagesScreen sends through UiCore::sendDirectText / sendChannelText
  (its own copy of send + file + ACK tracking gone).
- ui-core/Telemetry.h: the dashboard fields (numbers, names, formats) and
  sensor readings as text, each frontend passing its Style; L2 Home, L1's
  clock page, lock screen, sensors page and GPS altitude use it.
- L2: the side button held in the first 8 s is the CLI rescue, as on L1.
- L1's repeater radio goes through rptctl.
- L1 Settings are built from SettingsSchema: short labels for small
  screens, SCHEMA_* placeholders expand to the schema's sections, one
  generic row (switch ON/OFF, bars for brightness / volume). 20 hand-written
  rows and their tables gone; L1 implements applyDisplayPrefs /
  applySoundPrefs. settings::text / step / indexOf shared with L2.
- Schema: auto-off gains 5 s; the advert sound scope reads "Direct".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 23:19:47 +02:00
JakubandClaude Opus 5.5 ece849db3b refactor(ui-lvgl): review -- shared button bar, confirms, notes, local time
- Dead code gone: anim::fadeHide, radioctl::presetCount, the L2 board's
  setSdPower / setGrovePower / expanderOK (awRead, gnssReset now private).
- buttonBar() / barButton() replace 7 copies of the popup button row.
- confirmBody(): one red-button confirm for reboot / power off, reset
  counters, admin commands, preset and scope delete.
- tapConfirmed(): the 11 "tap again" buttons share one mechanism, 3 s
  everywhere, the label restored when left alone.
- noteLabel() for ~30 wrapped notes.
- localTm() in NodePrefs.h and one MONTHS table: lvgl, MsgExpand, the L1
  clock and lock screen.
- The sim keeps UI settings in an in-memory nvs::, so one set of load /
  save helpers serves both.
- Asleep, the loop wakes every 50 ms (the button / touch poll), not 20.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 22:35:26 +02:00
JakubandClaude Opus 5.5 2e9654fd1c refactor(ui-lvgl): Diagnostics > Noise is one measurement for any board
The Noise tab held the L2's interference hunt: parts of the board off one
by one, a slow cycle for a second radio, a spike hunt and three sweeps
(~90 s, L2 only). It is now one Measure: the noise floor on the mesh
frequency (median and 10th percentile of 5 s) and a +-1.1 MHz sweep round
it, ~12 s, with a short legend for reading it. Works wherever the variant
defines the radio.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 21:41:16 +02:00
JakubandClaude Opus 5.5 e47738ce01 feat(ui-lvgl): map tools regrouped; a map area previews when picked
Map tools: TRAIL, LIVE SHARE and ARRIVAL ALERT each with their own options
page (that section of Map options; back returns to Map tools), the arrival
alert switch where its state is shown, OFFLINE MAPS (Map areas, Live
tiles; the separate Download an area is gone -- a running or unfinished
download shows at the top of Map areas) and LAYERS.

Map areas: picking one frames it on the map above a small sheet (no Show
button, the map above isn't dimmed); closing the sheet, however it's
closed, puts the map back where it was and drops the outline -- it used
to stay on the map. New popup kind POP_BOTTOM.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 21:30:46 +02:00
JakubandClaude Opus 5.5 7a21e58c54 feat(ui-lvgl): arrange Home's apps -- drag to move, tap to hide
Hold an app on Home (or Settings > Home apps) to arrange them: every app
is shown, hidden ones dimmed with a +; a drag drops the app into the slot
under the finger, held at a screen edge it goes to that page; a tap hides
or shows it. Settings stays shown. The order is kept in NVS (letters per
app, lower case = hidden), saved on Done or on leaving Home.

Roadmap: note the speaker-knock experiment (DAC ramp didn't help).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 18:29:00 +02:00
JakubandClaude Opus 5.5 51530ac451 feat(ui-lvgl): SD card as a USB drive, message banner, card format, restart screen
- USB through TinyUSB (ARDUINO_USB_MODE=0) instead of USB-Serial/JTAG: the
  serial port stays, and plugged into a computer (enumerated, the bus not
  suspended -- on battery an unplug only shows as a suspend) the device asks
  "Charge only / USB drive". As a drive the firmware lets go of the card and
  the computer gets its sectors; ejected or unplugged, the device restarts.
- Incoming messages: a banner slides down from under the status bar (icon,
  sender or channel, two lines of the text from history); a tap opens the
  conversation (not while locked), a swipe up dismisses it, not shown for
  the conversation already open. Toasts moved up there too, under the banner
  when both are up; the ones only echoing a switch or a choice are gone.
- Storage > Format card: a popup saying what goes, then Format tapped twice;
  one FAT32 volume over the whole card (readable or not), then a restart.
- A deliberate restart shows a full "Restarting..." screen with the reason
  instead of the screen just freezing.
- The screenshot flash is a thin frame round the edges: a full-screen fade
  redrew all 320x240 each step and tore.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 17:17:38 +02:00
JakubandClaude Opus 5.5 e82cedaf23 feat(ui-lvgl): several saved WiFi networks, joined by signal; Save checks the password
- Settings > WiFi keeps up to 8 networks (NVS "s0".."s7" / "p0".."p7"; the
  single one older builds saved moves over). A tap loads one to change its
  password, Forget asks with a second tap, a scan marks the known ones.
- Map downloads, live tiles and updates join the strongest saved network in
  range (a short scan first when more than one is saved), else the newest
  one -- which also covers a hidden network.
- Save joins the network once: "password OK", turned away (forgotten again,
  the form keeps it to fix) or not in range (kept, checked later).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 16:24:10 +02:00
JakubandClaude Opus 5.5 2765ac6d8b feat(ui-lvgl): screenshots (side + top button), lock screen always times out
- Side button held + top button saves the screen, popups and toasts included,
  to /sdcard/screenshots/scr_NNNN.bmp (24-bit BMP). The flush callbacks copy
  one forced full re-render into a PSRAM buffer. A white flash and a toast
  confirm it. The side button's mute now fires on release, so holding it a
  little long before the top button doesn't also mute. In the sim, the page's
  Screenshot button downloads the file.
- The lock screen goes dark after 30 s even with auto-off at Never (a boot
  PIN, a pocket wake-up, a message), and sooner if auto-off is shorter.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 16:03:48 +02:00
JakubandClaude Opus 5.5 782f8aa567 feat(ui-lvgl): Diagnostics > Noise tab for RF noise hunting
The LoRa noise floor with the board's parts off one at a time, sweeps of
850-930 MHz, the mesh channel and a 7.8 kHz zoom on its loudest point; a
slow cycle of the same states for a second radio beside the device; and
a spike hunt over 864-876 MHz listing the lines over the floor and their
spacing (how the I2S MCLK comb was found).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 13:54:25 +02:00
JakubandClaude Opus 5.5 002f478231 feat(ui-lvgl): vector region packs, PBF input, region list
One .vpk file per region in /vmap (header, sorted index of tiles and
points, then the data), read in place with the index in PSRAM; loose
{dz}/{x}/{y}.vt/.vp files still work. osm_vector.py writes packs
(--pack/--name), reads OSM PBF extracts for a box (--pbf/--bbox, via
pyosmium) and picks label languages (--lang). Map tools > Vector regions
lists the packs on the card: show one on the map, delete it. No
on-device download: packs are made with the tool.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 13:54:25 +02:00
JakubandClaude Opus 5.5 1973361244 feat(ui-lvgl): vector map spike (own VT2 format from OSM)
tools/maps/osm_vector.py turns Overpass JSON into VT2 tiles (data zooms
10/12/14, per-feature bbox, simplified); VectorTileProvider draws them
into 256 px tiles (scanline polygons, thick lines with round joins) with
a small PSRAM cache of data files. Behind Map tools > Vector map (test),
raster where there is no vector data.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 10:31:33 +02:00
JakubandClaude Opus 5.5 c90cf474fe feat(ui-lvgl): pick download areas with a frame, list of map areas
- Download an area: a frame with four corner handles on the map (kept in
  world coordinates, the map pans / zooms under it); a bar gives its size,
  zoom range, tile count and size, the trails switch and Get; areas on the
  card are outlined
- Map tools > Map areas: the downloaded areas (map/AreaStore.h,
  /sdcard/maps/areas.txt) -- show (framed, outlined), rename, fill gaps,
  refresh (force re-download), trails on / off, delete (in steps, keeping
  tiles another area covers)
- TileDownloader: force and per-job trails options

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 09:51:29 +02:00
JakubandClaude Opus 5.5 ad890a49f6 feat(ui-lvgl): hiking trails overlay on the map
Waymarked Trails' transparent hiking layer, kept in /sdcard/maps-trails and
blended over the base tile at render time (Map tools > Hiking trails, NVS).
An area download fetches each tile's trails after its base tile, so
re-running an area on the card adds just the trails; with live tiles on, a
shown tile missing its trails has them fetched. Tiles without a trail are
stored as 0-byte files (no cluster on FAT).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 09:32:00 +02:00
JakubandClaude Opus 5.5 8b231935ba feat(ui-lvgl): foldable Messages sections with New in the header, storage bar by category
- Messages: New message moves to the header; Channels / Direct / Rooms fold
  on a tap of their title (kept in NVS), a folded one shows its unread count
- Storage: the card's bar is split into one coloured segment per category
  (plus the uncounted rest), each category row keyed with its colour

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 09:08:25 +02:00
JakubandClaude Opus 5.5 5289f6b11a fix(ui-lvgl): tile badges in the corner, minimap without a fix, taller header, accent status icons
- Unread badge on Home tiles sits in the top-right corner, clear of the icon
- Minimap with no GPS fix: the saved position (hollow dot), else where the
  map was left, else the default view; a hint when no tiles cover the area
- Screen header 38 px, so the back and header buttons get a margin
- Bluetooth / WiFi connected shown in the accent colour; charging bolt green

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 01:41:46 +02:00
JakubandClaude Opus 5.5 e52ec2a97e feat(ui-lvgl): phone-style Home -- clock with telemetry, minimap, app grid
Home pages: favourites, the clock (tap for Clock) with three telemetry
fields picked by holding one (shared dashboard_fields), a minimap that
frames you, live shares and the target (tap for the Navigation map), and
the apps in 3x2 pages. The side button now returns to the clock page with
a cross-fade instead of going back. Home moves to HomeScreen.h.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 01:30:12 +02:00
JakubandClaude Opus 5.5 a40a87d02c feat(ui-lvgl): settings kit, choice picker, reorganised Settings
group() / settingRow / listRow / actionRow / choiceRow / groupNote: one card
per section, hairlines between rows, hints that wrap. Dropdowns replaced by
a centred picker list. Settings regrouped (Device, Connections, Map & data,
System); Display & power split into Display, Power and Time on L2 only
(the shared schema is unchanged), Keyboard and About get their own pages.
Radio and Repeater share one LoRa parameter block.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 01:17:48 +02:00
JakubandClaude Opus 5.5 02c2ba1313 feat(ui-lvgl): readable info cards for diagnostics, GPS, node detail and more
Shared infoCard/infoRow/infoNote helpers: muted label left, value right,
hairlines between rows, empty rows hidden. Diagnostics grouped into
sections with a received/sent packet table; GPS gets a position card and
constellation pills; About, OTA, Storage and map popups use the same
cards. Text no longer breaks after '.', clock alarm hint unclipped.
Sim-only sim_open() hook for jumping to a screen.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 00:34:34 +02:00
JakubandClaude Opus 5.5 23caa11f51 feat(ui-lvgl): power saving, review fixes, shared helpers
Power: the UI loop sleeps until something is due (never with packets
queued); screen off puts the CPU at 80 MHz, the GT911 to sleep (unless a
tap wakes the screen) and the LP5814 in standby. GPS off now cuts the
L76K's rail and UART on the L2 (L2GpsProvider). The battery divider is
powered only for a reading (measured: settles at once); readings within
2 s are reused. The SD mount is retried at most every 5 s.

Fixes: WiFi left on after leaving a scan, live tiles' WiFi kept with the
screen off on the map, an ADC read every second for the status bar, touch
polled on every pass while dark, a scan cutting an update's WiFi, relay
count not refreshing in a channel thread, raw @[nick] in room previews.
Labels skip unchanged text (no redraw).

Helpers: scrollList, dimOverlay, settingRow, flexBox, nvs::get/put,
trailFilePath (a second save in the same minute no longer overwrites),
unreadTotal. Dead code removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 00:12:08 +02:00
JakubandClaude Opus 5.5 708fde7225 feat(ui-lvgl): WiFi icon in the status bar
Shown while WiFi is switched on (Settings > WiFi), bright when connected
(live tiles, map download, update), dim otherwise. The switch is read
from NVS once and cached. The status icon signature buffer grew so
changes to the last icons aren't missed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 23:16:47 +02:00
JakubandClaude Opus 5.5 eb201bc4fd feat(ui-lvgl): favourites as a Home card, left of the main page
The six slots with unread badges on a card of their own (swiped to,
no clock), redrawn when messages arrive; the Favourites tile and screen
are gone, showFavourites() opens the card. The page dots sit at the
bottom on every card.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 23:10:50 +02:00
JakubandClaude Opus 5.5 fe92ac1153 feat(ui-lvgl): tap-to-wake switch, the side button no longer wakes
Settings > Display & power > WAKE > Tap to wake (NVS, on by default):
off, only the top WAKE button turns the screen on. The side USER button
doesn't wake the screen any more (a press in a pocket); held, it still
mutes / unmutes with the screen off. Roadmap: buttons corrected (WAKE is
the top one), USER to open the main menu page later.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 23:06:46 +02:00
JakubandClaude Opus 5.5 c7c150da58 fix(ui-lvgl): map download popup fits the screen
A progress bar and one line of counts while downloading (the zoom row
hides, it can't change then), failures on one line, the source as
"opentopomap.org", only the WiFi network's name (set up in Settings >
WiFi). The debug line (IP, RSSI, heap) is gone. Roadmap: mentions
highlight added, four items ticked.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 22:54:13 +02:00
JakubandClaude Opus 5.5 7a34f68369 feat(ui-lvgl): live map tiles kept apart, under a size limit
Tiles fetched while browsing online go to /sdcard/maps-live with an
index of arrival order; past the limit (Settings > Storage, 16 MB-1 GB,
default 64 MB) the oldest are deleted a few per loop. Storage shows
their count and size and can delete them all. Downloaded maps are never
touched. Roadmap: live tiles done, second batch of user items added.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 22:25:02 +02:00
JakubandClaude Opus 5.5 70921cd865 feat(ui-lvgl): GPS screen - sky plot, signal per satellite, fix details
- Home > GPS and Settings > System > GPS details: satellites on a sky plot
  (filled = used in the fix, colour = C/N0), a bar per satellite with its
  dB-Hz, fix state with time to first fix, DOPs, position, altitude, speed,
  UTC and per-constellation counts; a Turn on button while GPS is off
- helpers/sensors/GpsSky.h parses GSV / GSA / GGA / RMC from any talker,
  fed from MicroNMEALocationProvider behind -D GPS_SKYVIEW (L2 and the
  sim only; L1 unchanged); the sim feeds it made-up NMEA
- Roadmap: GPS indoors diagnosed with it - on the USB cable the L76K
  tracks satellites but never decodes navigation data; on battery it
  gets UTC within seconds and a fix

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 21:50:48 +02:00