Commit Graph
100 Commits
Author SHA1 Message Date
JakubandClaude Opus 5.5 7c568d6153 fix: a leftover /prefs folder (Meshtastic) no longer blocks every settings save
Our /prefs file can't be read from a folder of that name, and the rename
that commits each save can't replace one, so on a device moved over from
Meshtastic without erasing the flash no setting survived a restart.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 20:52:20 +02:00
JakubandClaude Opus 5.5 608c5b7dad fix(ui-new): on/off written On / Off everywhere, as every other value
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 14:12:27 +02:00
JakubandClaude Opus 5.5 c4217d8632 feat: a channel's scope can follow the default -- Default is the new start value
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 14:09:22 +02:00
JakubandClaude Opus 5.5 5a92d36509 fix: Bluetooth switched off stays off over a reboot or hibernate
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 14:09:22 +02:00
JakubandClaude Opus 5.5 50d0d5577b fix(ui-new): a tool opened from Home's Tools page goes back to Home
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 14:09:22 +02:00
JakubandClaude Opus 5.5 1af8ce1a85 fix(ui-new): on/off as the words ON / OFF -- the pill switch read as a battery
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 14:09:22 +02:00
JakubandClaude Opus 5.5 3326881dc1 feat(eink): screens laid out for the 4.2" -- tiles, Nodes pane, charts, Sky, nav, clock tools
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 13:44:11 +02:00
JakubandClaude Opus 5.5 cea102c152 feat(eink): 9px icon set, 5x10 hop digits and roomier bubbles on the 4.2"
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 13:44:11 +02:00
JakubandClaude Opus 5.5 5864e556ff feat(eink): 4.2" 400x300 build with the misc-fixed 9x15 font, wide sim
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 13:44:11 +02:00
JakubandClaude Opus 5.5 44ccd47a12 feat(ui-lvgl): GPS on three tabs -- Sky, Signal, Details
Each tab a screenful: the sky plot beside the fix; the bars with a fixed
dB-Hz scale and the constellations; and Details with whatever else the
receiver reports (correction, locator, course, DOPs with their rating,
signal averages, NMEA counts). Sim: sim_open("gps").

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 10:22:44 +02:00
JakubandClaude Opus 5.5 ab767e7ae1 feat(ui-lvgl): charts with a value axis, grid and a finger's readout
Chart.h draws Diagnostics > History, the noise sweep and the trail's height
profile: round values up the left, labels along the bottom, a solid fill
(no transparency) and, under a finger, the sample's value and when / where.
Sim: sim_open("histdemo") / ("profdemo") fill them with made-up data.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 10:22:44 +02:00
JakubandClaude Opus 5.5 1f2cb301e6 feat(ui-lvgl): the big clock in the splash's lettering, as on L1
Home and the lock screen draw HH:MM in ui-core/Lettering.h's block digits,
the seconds smaller on the same baseline, leaning smoothly per screen row.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 10:22:43 +02:00
JakubandClaude Opus 5.5 999f88853a perf(l2): the screen fades out and in in ~80 ms
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 10:22:43 +02:00
JakubandClaude Opus 5.5 1b7402a33e style(ui-lvgl): selected / on in the full accent, no ACCENT_DIM
ACCENT_DIM (the accent mixed toward black) came out brown on selected
tabs, chips, segments and own bubbles. They take the accent with dark
text now, as primary actions and the quick panel tiles do:
styleSelected() sets the fill, the pressed shade and every label under
it. Own bubbles: dark text, age and delivery mark (green or red can't be
read on amber); the melody editor's selected note is outlined, the
sounding one filled.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 09:35:46 +02:00
JakubandClaude Opus 5.5 aed0a8a26b fix(solo): e-ink landscape build had no DISPLAY_ROTATION
build_unflags runs after build_flags, so unflagging the board's
-D DISPLAY_ROTATION=1 in [eink_solo] also removed the landscape env's
identical flag: the panel came up portrait (GxEPDDisplay's default 0) and
UITask::applyRotation's restore was compiled out, so the rotation setting
didn't stick. Only the portrait env unflags it now.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 09:35:46 +02:00
JakubandClaude Opus 5.5 a5956d79ae fix(ui-lvgl): quick panel over a frozen screen; slider not taken by Home's swipe
- Quick panel: the screen under it is frozen and dimmed in one snapshot
  (freeze::early), so the slide redraws an image and the panel; a tap or
  swipe up under it closes it and nothing under it takes a touch. Tiles are
  two pages of four with dots; tiles restyle only when their state changes.
- Home: a touch that starts on the top layer (the quick panel, the status
  bar) is not a page swipe -- a sideways drag of the brightness slider over
  Home ended the drag and turned the page under it.
- Keyboard: OK and hide don't put the cursor back on a field whose keyboard
  is gone.
- BotScreen: adminRowBox -> centerRow (the sim build broke in 3293ac09).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 09:17:32 +02: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 d66f04ba94 fix(ui-lvgl): OTA sees a release as newer than its release candidates
versionNum() read "v2.0-rc1" as plain 2.0, so a device on a release
candidate never offered the release itself (nor a later candidate). A
"-rcN" suffix now orders before the bare version: v2.0-rc1 < v2.0-rc2 <
v2.0 < v2.0.1.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 00:26:06 +02:00
JakubandClaude Opus 5.5 8c71d7a237 docs: release notes for v2.0-rc1
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 00:12:18 +02:00
JakubandClaude Opus 5.5 c3782cb8e5 feat(prefs): v2.0 settings file -- the struct as it is, behind a CRC
/prefs holds a header (magic, format version, size, CRC-32), the node's
position and NodePrefs as it is in memory, in place of /new_prefs' two
hand-kept field lists and 0xC0DE00xx schema bumps. A new field goes at the
end of the struct; moving one bumps PREFS_VERSION. sanitize() keeps every
value in range.

The first boot after an update from before 2.0 imports only the first
block of the old file, laid out the same in upstream MeshCore and Solo
(name, position, radio, BLE PIN, scope), then deletes it; everything else
starts from the defaults. Contacts, channels, scopes, waypoints and trails
are untouched.

Gone: the page-order and GPS-interval migrations, the unused
trail_interval_idx / trail_units_idx / repeat_extra_scopes; fav_sort_off
and msg_wake_screen_off become fav_sort and msg_wake (on by default).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 00:12:18 +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 99bd41650f feat(ui-new): e-ink graphics on the OLED, a Trail height view
The OLED gets the arrows towards nodes in Nodes and the Path row with its
hop rings in a stored node's detail (its position on one line to make the
room); no compass rose. Trail gets a height-profile view on both. The
Status history, trail profile and calendar come from ui-core. A sim hook
turns on the advert's position for tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 23:26:29 +02:00
JakubandClaude Opus 5.5 699d6b6ae0 feat(ui-core): status history, trail profile and calendar math, shared
The history of the noise floor, battery, traffic and SNR, a trail's height
profile (with a sampler for trails read point by point off the card) and a
month's calendar, moved out of ui-new so ui-lvgl can draw the same.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 23:26:29 +02:00
JakubandClaude Opus 5.5 13d099cf55 docs(eink): the two e-ink builds in the docs and the site's sim
Docs: the E-ink section describes the landscape and portrait builds, what
the tall screens add, and the settings, with three screenshots; the e-ink
build flags; the release file names. Sim: each orientation gets its own
factory, so a page can load both, and simEinkScale 0 leaves the canvas'
CSS size to the page. CI builds both into solo-sim-wasm.zip (wasm/eink/).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 21:38:38 +02:00
JakubandClaude Opus 5.5 e16fe2c621 feat(eink): 7 px icons in landscape, a rounded battery
The landscape build's 8x13 digits stand 9 px, so its status, page and
check glyphs and the row-heading map markers come in a 7 px set instead
of 5. The battery outline, in the header and on the Status tile, leaves
its corner pixels out.

The page icons go back to a row of their own under the header: beside
them on the header line only three status icons fitted, and the rest
went missing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 20:39:26 +02:00
JakubandClaude Opus 5.5 3948d65362 fix(eink): no counts of seconds on e-ink
Message ages, the GPS search time and the sky plot changed every second,
and each change is a panel refresh. On e-ink an age under a minute reads
"<1m", the search time counts minutes, and the sky redraws every 15 s
(at once after a key).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 20:20:14 +02:00
JakubandClaude Opus 5.5 6e6a0fb3ff feat(eink): use the room of tall screens, add e-ink graphics
Tall screens stack the Sky view and the map over their rows, show more
recent chats, give favourite cards the heard age and distance, and put
the trail map under the trail summary. Wide screens put the page icons
on the header line.

E-ink only (Features::IS_EINK):
- a month calendar under the clock and on the portrait lock screen, with
  the alarm's days marked; the lock screen also lists unread messages,
  the alarm and the clock fields
- 8-hour charts in Status: battery, noise and SNR, mesh traffic
- an elevation profile under the trail map, with the climb and the top
- a compass rose and the path's hops on a contact's detail, arrows by
  the distance in Nodes rows and on favourite cards
- an ordered-dither fade under chart lines

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 19:24:42 +02:00
JakubandClaude Opus 5.5 5c0b79b107 feat(eink): whole message bubbles on tall screens
A screen with room for nine lines or more shows each message bubble
fully wrapped instead of one line. The bubble's height is measured at
the width it is drawn at.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 19:24:36 +02:00
JakubandClaude Opus 5.5 95295aeec0 fix(ui): a space that overflows ends the wrapped line there
wrapLines carried the overflowing space's word to the next line too
early, so bubbles wrapped short of their width.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 19:24:36 +02:00
JakubandClaude Opus 5.5 0bbf2fa2b3 feat(trail): record altitude in trail points
Each point keeps the GPS altitude in what was padding, so a point stays
16 bytes. The save format goes to version 2; version 1 files still load,
with the altitude unknown. GPX export writes <ele>.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 19:24:36 +02:00
JakubandClaude Opus 5.5 2589fe95ce feat(sim): e-ink simulator builds
SIM_EINK=landscape|portrait builds the e-ink firmware for the browser,
drawn by the same EinkGfxDisplay as the panel. index.html and mesh.html
pick a build from the URL. The simulated sensors get a GPS switch, so the
UI's GPS toggle works.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 19:24:36 +02:00
JakubandClaude Opus 5.5 b0e24bb076 feat(eink): landscape and portrait builds, 8x13 font in landscape
The L1 e-ink solo build splits in two, one per screen shape, so each
screen can be laid out for a single shape. Settings > Rotation now only
turns the screen upside down, and the joystick turns with it.

Landscape writes in misc-fixed 8x13 at the panel's own resolution
(EINK_LARGE_FONT); portrait keeps the 6x9. All drawing moves into
EinkGfxDisplay, which the panel driver and the simulator share; the
FreeSans fonts and the CRC32 library go.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 19:24:36 +02:00
JakubandClaude Opus 5.5 a733b8bcf5 docs(solo): bring the docs up to date with the L1 refresh
- Home pages as they are now (Status, quick panels, Map page), the Status
  screen's tabs instead of Diagnostics / GPS / Sensors, lock clock Big /
  Compact and the hint on a key press, battery curve on L1 and L2, "+ Send"
  and a failed send, trail pin, vector map behind -D MAP_VECTOR.
- Developer guide: drawScreenHeader, soft frames, InfoKit, TabBar, a status
  bar without blinking, toast and millis() rules; ui-core status; build
  flags (GPS_SKYVIEW, MAP_VECTOR, HIST_ARCHIVE added, ui-tiny-only ones out).
- New simulator screenshots: the clock page and the Status page.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 14:23:15 +02:00
JakubandClaude Opus 5.5 3631e0ab85 fix(l2): no Lock clock row in Settings
The Big / Compact lock look is ui-new's; the shared schema row did nothing
on the L2's slide-to-unlock card.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 14:23:15 +02:00
JakubandClaude Opus 5.5 687a31e657 refactor(ui): remove dead code and fix stale comments after the review
- ui-core: Diagnostics::systemLines, NearbyModel::typeShort,
  rptctl::currentPresetName, RoomSessions::pendingKey, settings::isSwitch,
  UiCore::reconcileDMUnread.
- ui-new: the retired Recent page, fmtAlt, the battMvToPercent wrapper,
  setFavouriteChannelSlot, AccordionList onHeader/visibleCount, drawBoxedIcon
  and seven unused icons; info::row folded into info::valueRow.
- ui-lvgl: onOpenNav, TileDownloader::withTrails; the trail header size is
  one constant (TRAIL_HDR) for live and saved trails.
- Home quick panels save prefs only when a value changed.
- Comments brought in line with the code (status bar reserve, settings rows,
  page order, SH1106 frame skip, UiCore header).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 13:49:26 +02:00
JakubandClaude Opus 5.5 096b8e2b1d feat(ui): drop echo toasts for actions the screen already shows
L1: Counters reset, Trail reset, Contact added/deleted, Logged in (admin),
Logged out, Channel deleted, Share target / Bot channel / Bot room set,
PIN set/cleared, Preset saved/deleted, and Unpinned on the favourites grid.
L2: Channel deleted, Counters reset, Region/Preset/Scope/Waypoint deleted,
Target cleared, Trail cleared, Message history deleted, Contact added/deleted.
Failures and Unpinned where the item leaves the screen stay.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 13:49:26 +02:00
JakubandClaude Opus 5.5 6cd081add1 fix(l1): wrap-safe UI timers, a failed send keeps the text, bars stop at the ends
- The redraw, alert, splash and sensor timers compare millis() wrap-safe
  (refreshDue(), alertShowing(); 0 = now / none), so the screen keeps
  refreshing past the 49.7-day rollover.
- A failed send leaves the keyboard with its text ("Not sent" in the field
  label, as alerts don't show over it) or the quick-message list with
  "Send failed", instead of dropping to Home. No "Sent" echo.
- The dashboard's Sats field shows "--" with the GPS off, not a stale count.
- Brightness and volume in Settings stop at both ends instead of wrapping.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 13:49:16 +02:00
JakubandClaude Opus 5.5 6ae0e73663 feat(l1): clock page and lock screen in the splash lettering
The Home clock page draws HH:MM in the boot splash's slanted digits, with a
seconds bar, a centred date and the clock fields as columns. The lock screen
keeps the status bar, puts the big clock under it and shows unread messages
in a pill; the unlock hint appears for a few seconds after a key press on the
lit screen (not the waking one). Settings > Display > Lock clock adds a
compact look: a small clock with the date beside it and the clock fields as
rows (NodePrefs lock_compact, schema 0xC0DE0033). 12-hour time sets AM / PM
beside the digits.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 11:58:56 +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 1166ea8dde fix(core): a flood DM's ACK folded into the return path reaches the UI
The base class matched an ACK carried in a PATH packet without going
through onAckRecv(), so the device UI's delivery marker never saw it and
kept resending the DM until a plain ACK arrived. MyMesh now passes it on
to the listener as well.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 11:25:58 +02:00
JakubandClaude Opus 5.5 0f635c0171 feat(l1): Home quick panels, map page with live data, quieter status bar
- Icon pages become quick panels: two or three rows (Settings, Messages,
  Bluetooth, Advert, Tools, Power) inset from the edges, the last one
  opening the full screen. Brightness and auto-advert are edited in place
  (Enter, Left/Right, Enter/Back saves once).
- Tools panel shows the two tools used last with their live state.
- Map page: preview on the left, GPS / trail / live / target on the right.
- Favourites as soft cards; Status tile with a battery spark line.
- Status bar without boxes or blinking; BT only when connected, GPS
  search glyph without a fix.
- Redrawn mini icons (advert waves, cog, wrench, messages); big hovering
  icons removed. Tools list is text only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 10:23:58 +02:00
JakubandClaude Opus 5.5 6d8e56fbd0 feat(l1): one title bar, page dots, disclosure marks and push buttons
drawScreenHeader replaces drawCenteredHeader on every ui-new screen: the
title in normal case, page dots instead of "TRAIL 1/3", the menu hint, no
icon (the screen was just picked by one). Accordions in Settings and Tools
show a disclosure triangle instead of +/-, Tools sections drop their icons.
Form "[Save]" rows and "+ Send" share drawButton; form fields use valueRow.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 08:45:05 +02:00
JakubandClaude Opus 5.5 4ba171d6d8 feat(l1): one list row for settings and tool screens: values flush right, switches for on/off
InfoKit gains valueRow (value at the right edge, cut and scrolling when
selected), switchRow and listRow (exact ON/OFF becomes a switch). Settings,
Live Share, Locator, Repeater, Bot, Auto-Advert, alarm, GPIO and popup value
rows use them; units after a space, "Off" in title case, Trail summary as
blocks with a big distance and a points meter, "+ Send" as a soft pill.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 08:29:44 +02:00
JakubandClaude Opus 5.5 4d8724dcc5 feat(l1): Status page and screen replace Radio/GPS/Sensors pages and Diagnostics
Home's Radio, GPS and Sensors pages become one Status page: four tiles split
by dotted rules (radio, GPS, battery, nodes), each a headline over a detail.
Enter opens the Status screen, tabs Radio / GPS / Sky / Power / Mesh /
System, which also takes over Tools > Diagnostics and Tools > Satellites
(the sky plot now lives in SkyView.h). GPS on/off moves to Enter on its tab.

New InfoKit.h holds the shared pieces, sized from the font: label/value row
(a long value takes its own line), centred section title, meter, chip, big
number with unit, switch, history line (one sample a minute) and a block
flow that scrolls a tab by whole blocks.

The Status page keeps the old Radio bit and shows when any of the three old
pages did; the save format is unchanged. The L1 degree sign was a cp437
byte the UTF-8 renderer drew as a box; now U+00B0. The perf walk covers
every Home page and Status tab.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 23:51:47 +02:00
JakubandClaude Opus 5.5 061e5ba36b feat(l1): Home pages slide sideways, the page pill glides along
DisplayDriver gains slideBegin()/slideCompose(): the driver keeps the frame
on screen and, in the next one, moves it out while the new rows follow.
SH1106 does it on its 1 KB buffer (+1 KB RAM for the copy), the sim on its
canvas; drivers that can't read their frame back keep cutting over (e-ink).

Home slides the content below the page-icon row in 220 ms with an ease-out
(the whole screen to and from the header-less clock), and the current-page
pill glides between icons.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 23:08:40 +02:00
JakubandClaude Opus 5.5 608231ed6b feat(l1): L2 splash, hovering Home icons, soft selections, two-line DM bubbles
- Splash: the L2 wordmark and lettering, shared through ui-core/Lettering.h,
  rise in with loading dots under the version line (static frame on e-ink).
- Home: the header shows the time once the clock is set; icon pages carry
  only their title. One 32 px icon family (3 px strokes) bobs 2 px over a
  dithered blob shadow that shrinks as it rises; the unread badge rides on
  the Messages bubble; Bluetooth off is the icon at half density.
- Soft corners on every selection, the page pill, tabs and popups;
  dotted scrollbar track with a rounded thumb; the tab bar lays out whole
  tabs and shows edge arrows only in free space.
- DM bubbles drop the sender row and wrap to a second line, with the
  delivery mark and age closing it.
- Keyboards name what is typed (channel, contact, Password, Scope...)
  and show characters left near the limit; PIN entry has its own preview.
- Loading dots for waits (login, scan, GPS) and signal bars in place of
  raw RSSI in Nearby.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 22:59:44 +02:00
JakubandClaude Opus 5.5 1580de53f5 feat(l1): one soft-cornered frame for popups, alerts and bubbles; small screen fixes
DisplayDriver gets drawSoftRect / fillSoftRect / drawPanel (the unread badge's
corner knockout; a panel also clears a 1 px moat so lines below stop short of
its border). Used by alerts, popups, the accent picker, the lock hint, chat
bubbles and the send button.

Fixes: no "Msg:" toast for a message landing in the open thread; Nodes scan
text fits the screen; the PIN pad skips the blanks beside 0; Auto-Advert is a
plain value row; the Admin picker says what it needs when empty; the GPS page
shows "-" instead of 999 before a first fix.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 22:13:11 +02:00
JakubandClaude Opus 5.5 a6612ee2b0 perf(l1): -D UI_PERF_L1 walks the screens and reports draw time, heap and stacks
Opens each screen directly, types keys, and prints per step over USB: frames
drawn/sent, render and flush time (CPU cycle counter), heap used/free/largest
block, every task's stack headroom, and a screen dump. Measurement only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 22:01:53 +02:00
JakubandClaude Opus 5.5 0c739992bb perf(l1): OLED sends only the parts of the screen that changed
Each frame is hashed per 32-column block of each 8-px page and only changed
blocks go over I2C. A full frame was 1 KB / 31 ms on every change; a moved
selection, a scrolling name or a clock tick is now 1-8 ms. 128 B of hashes,
no frame copy; full sends stay for wake/clear and SPI panels.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 22:01:53 +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 71e3dd8a8b perf(l2): Nodes slides in smoothly, Diagnostics opens in half the time
Rows still to build wait while a screen slides in, so its frames aren't
a fill budget apart (Nodes: 12 frames of 24 ms instead of 7 of 33, up to
65). The crash dump in flash is read at boot, not on the first
Diagnostics (build 54 -> 20 ms). The perf tools time Nodes too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 20:56:14 +02:00
JakubandClaude Opus 5.5 0a07dc3deb perf(l2): screen sent by DMA while the next part is drawn
The panel takes each 24-line band by DMA while LVGL draws the next one
into the other buffer (two 15 KB buffers in internal RAM; LovyanGFX's
4092-byte DMA descriptors leave PSRAM unaligned for the S3's DMA, which
showed as shifted and smeared bands). LVGL draws in the panel's byte
order, and so do map tiles and the frozen screen under a popup, so
nothing is swapped on the way out. Full frames ~10-25% faster (page1
19.9 -> 14.7 ms, Home 21.4 -> 18.2 ms). Without the internal RAM the
screen is sent as before. The arduino2 env keeps the old way.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 20:56:13 +02:00
JakubandClaude Opus 5.5 ca5e66a4a1 feat(l2): tidier map overlays: activities top centre, credit + scale + zoom in one pill
Recording / live share and the download stack in a column at the top
centre, empty when nothing runs; the recording pill is plain text with
only its mark coloured (red stop, accent pause / live) -- all-red text
was hard to read over the map. The credit, scale bar and zoom share one
pill bottom left (a tap shows the credit). Every map pill is 22 px with
round ends.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 11:40:15 +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 f77c7ea901 perf(l2): screens lay out in up to half the time: row widths known before the first layout
A group row's text column (and an info row's key) grows into what the
controls on its right leave, and LVGL lays out children before their
parent, so its labels were first measured 1 px wide; LVGL also works out a
new label's height at its old width (0) before the real one. Each height
that changed then took another pass up the screen: 4-6 passes per screen.

rowsFit(), run before a frame's layout (LV_EVENT_REFR_START) and by
layoutNow() in place of every explicit lv_obj_update_layout(), gives each
new row's growing child the width the flex will give it, worked out from
the other children's styles and text, and labelFit() measures wrapping
labels at their real width up front. The grow stays, so a control that
can't be sized ahead only falls back to the old passes.

Layout on the device: Display page 34 -> 24 ms, Bot 33 -> 18, Quick
37 -> 22, Radio 26 -> 18, Diagnostics 20 -> 12. Sim screenshots of 26
screens unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 10:30:27 +02:00
JakubandClaude Opus 5.5 634294ee3a perf(l2): Clock opens in half the time: own number wheel instead of lv_roller
An infinite lv_roller measures its whole option text (repeated three
times) on every style change, layout pass and frame. The hand-drawn wheel
draws only the visible rows, with the same look, drag, throw, tap and
wrap-around. Clock first frame 99 -> 54 ms (layout 42 -> 12 ms).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 09:55:24 +02:00
JakubandClaude Opus 5.5 38f5db5a61 perf(l2): bigger CPU instruction cache, TLS and BLE buffers in PSRAM
The framework libs are rebuilt for the L2's own ESP-IDF options
(solo/wio-tracker-l2/idf/sdkconfig): a 32 KB instruction cache and
64-byte data cache lines make frames ~35% faster (map 45 -> 30 ms,
settings ~36 -> 22 ms). mbedTLS and the NimBLE host allocate in PSRAM,
so internal RAM ends up with more free than before (103 KB, largest
block 57 KB; was 89.6 / 45).

Wio_Tracker_L2_idf_libs does the rebuild; it has to run before the
firmware env, also in CI. idf/arduino_tinyusb gives the rebuild the
TinyUSB options and the framework's prebuilt library, which it would
otherwise lose.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 09:23:50 +02:00
JakubandClaude Opus 5.5 2f3b95adb4 fix(l2): the GPS switch no longer jumps
UiCore::setGpsEnabled() wrote the prefs to flash at once, which stalled
the loop mid-animation. It takes save = false now (default true, the L1
unchanged); the L2 marks the prefs changed and writes them on leaving
the screen, as its other switches.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 23:23:04 +02:00
JakubandClaude Opus 5.5 b53b0e1349 feat(l2): vector map off unless built with -D MAP_VECTOR
The vector map isn't finished: without the flag it stays off whatever
was saved, and its switch and the Vector regions row are gone from the
map tools. The code stays.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 23:23:04 +02:00
JakubandClaude Opus 5.5 1ff58d4a81 perf(l2): no redraws while nothing changes
Found by counting flushes on idle screens in the sim:
- The compose field's cursor blinked (a redraw every 400 ms) as soon as
  a conversation opened, the keyboard hidden: lv_keyboard_set_textarea()
  focuses the field, and LVGL only stops a blink when the cursor moves.
  fieldFocus() sets the state and nudges the blink; also used for the
  WiFi and channel fields, which left theirs running.
- lv_obj_align_to() restyles on every call, so the status bar's icons
  redrew every second; placeLeftOf() moves them only when they move.
- Text colours set every second (nav bar, GPS status, Diag errors) go
  through setTextColor(), which skips an unchanged colour.
- A LV_LABEL_LONG_DOT label writes its "..." into its own text, so
  setText() never matched and rewrote it every refresh (the nav bar's
  hint); labelShows() reads past the dots.
- The map area, the area-outline, trail and vector-label layers were
  invalidated on every map refresh (every 3 s, a full-map redraw); now
  only when the view or what they draw changed.

Idle map: 17 -> 1 flush in 5 s; list and settings screens: 0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 23:22:31 +02:00
JakubandClaude Opus 5.5 74efb6addd perf(l2): the Quick-message editor freezes the screen under it
The editor has its own 80 % dim layer, not a popupOpen() one, so every
key press redrew the list under it and blended the dim over it. It now
freezes the screen like the popups (freeze::now); the frozen image takes
the dim layer's own level instead of assuming 60 %.

Keyboard redraw 38.7 -> 29.7 ms, full frame 46.3 -> 39.7 ms.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 23:21:59 +02:00
JakubandClaude Opus 5.5 01f35afa38 perf(l2): nothing drawn under the map tiles
- The map screen's own fill is only painted where the map area doesn't
  cover it (the strip it uncovers as it drifts in); the area and the
  opaque status bar hide the rest. ~2 ms a frame.
- With every visible cell holding a tile, the map area drops its fill and
  grid, which lie under the tiles anyway, and still reports that it
  covers what's below (coversOwnArea, shared with the frozen popup
  image). The Home minimap drops them the same way. ~5 ms a frame.

Full map frame ~50 -> 46-48 ms on the L2.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 23:21:26 +02:00
JakubandClaude Opus 5.5 31cbb1a91a perf(l2): UI_PERF_TEST draw profiles and layout counters
Kept for the optimisation pass before a release:
- perfDrawProfile(): a full redraw timed per object (its own drawing),
  totals by kind and the costliest listed -- on settings pages, Contacts,
  Home, the map and under a frozen popup; the screen's entrance frames too.
- Per screen: build, layout, first draw and redraw apart.
- A popup over Home: a full frame dimmed live and frozen.
- The reset reason and a core dump summary at start.
- -D PERF_WRAP_TEXT with -Wl,--wrap= of lv_text_get_size_attributes,
  lv_layout_apply, lv_obj_get_style_prop_internal and lv_obj_send_event:
  text measured, flex runs, style reads and events per screen.
- -D PERF_KEEP_USB leaves the USB popup up.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 14:33:32 +02:00
JakubandClaude Opus 5.5 f70019f920 perf(l2): the screen under a popup is frozen, not redrawn
Once a popup's dim is up, the screen below is drawn once into an image
(LV_USE_SNAPSHOT), darkened as the dim layer darkens it, and shown opaque
over the screen with the dim layer cleared. LVGL then starts drawing from
that image; an lv_image with no background never reports that it covers,
so it answers the cover check itself. A full frame under a popup: 45.7 ->
32.6 ms; freezing costs ~29 ms once, as the popup settles.

One popup at a time (a second one dims live), a strip keeps the status
bar dimmed under top-layer popups, the image goes with its popup, and the
popup dims live again if its screen goes first. POP_BOTTOM (over a live
map) isn't frozen.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 14:33:32 +02:00
JakubandClaude Opus 5.5 36c647cd05 perf(l2): cards, rows and notes get pixel widths up front
LVGL lays out children before their parent, and a new box's size is only
known after a pass, so every level sized in % of the one above cost a whole
extra layout pass over the screen. innerW() works the width out from the
styles instead; fillWidth() sets it on infoCard, infoLine (every row),
noteLabel and scrollList, and the screen body is px-wide.

Display & power page: 101 -> 76 flex runs, layout 68 -> 54 ms, first
frame 161 -> 146 ms; the other settings pages and Radio / Bot / Quick
alike. Four passes remain: a row's text column grows beside its control,
so its width is only known after the row's own layout.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 14:32:33 +02:00
JakubandClaude Opus 5.5 abbbdc5ae5 perf(l2): cheaper frames -- less overdraw and blending
Measured on the L2 (steady full frames): Settings page 37.7 -> 31.4 ms,
Display 41.5 -> 37.9, Contacts 34.6 -> 32.9, Home 37.3 -> 32.4.

- styleSurface() leaves boxes in the page colour unpainted; styleOpaque()
  for the screen and the status bar, which things slide under.
- group() cards clip their corners only while a row is held
  (rowPressable(), shared by the tappable rows).
- rowSlider(): the track an opaque tint (theme::mix(), also behind
  ACCENT_DIM) instead of 20 % accent blended every frame.
- Map buttons, pills, scale, nav bar and the Home minimap labels opaque.
- Screens drift in without the fading cover: its full-screen blend was in
  every entrance frame (page 39 -> 29 ms).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 14:30:45 +02:00
JakubandClaude Opus 5.5 f29c280c34 perf(l2): map tiles decode with PNGdec
PNGdec (bitbank2, 1.1.6) decodes a tile a row at a time straight into
RGB565, without lodepng's 256 KB RGBA buffer; the trail overlay goes through
it too, blended by hand for palette-with-alpha and RGBA. It lives in its own
file (ui-lvgl/TilePng.cpp) because its zlib headers put DONE/EXTRA in the
global namespace. lodepng stays the fallback and the sim's decoder.

Same 20 tiles: decode 78 -> 50 ms, trails 42 -> 17.5 ms, a whole tile
170 -> 105 ms; the map with trails is pixel-identical.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 08:53:15 +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 5f6cbe34b3 fix(ui-lvgl): tile downloads ride out server errors instead of stopping
503/429 slow the pace down, transport errors retry after 2/8/30 s, failed
tiles get a second pass; the download only gives up after 10 minutes
without a single tile or on repeated 403s.

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 69c1418e17 docs(solo): USB tools and L2 screenshot notes; Units in the L2 settings list
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 12:39:33 +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 bc266a4f5c fix(ui-lvgl): smooth WiFi/Bluetooth switches; retry 404 tiles
- The WiFi and Bluetooth rows rebuilt the whole Settings page to update
  their hint, cutting the switch's animation short; the hint is now
  rewritten in place (rowHintSet). The "Bluetooth on" echo toast is gone.
- Map download: a 404 gets the same one retry as other errors
  (OpenTopoMap sends the odd 404 for a tile not rendered yet), and a
  download that ends with gaps says "Done, N missing - download again".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 10:41:18 +02:00
JakubandClaude Opus 5.5 890ee6533e build(sim): colour-touch (ui-lvgl) WASM in the solo-sim release zip
- build-solo-sim.yml clones LVGL at the version pinned in
  solo/wio-tracker-l2/platformio.ini and builds the SIM_UI=lvgl sim into
  wasm/lvgl/ of solo-sim-wasm.zip, for the site's device switch.
- build_wasm.sh: LVGL_DIR can be overridden (CI has no .pio libdeps).
- sim_test_set_node_name(): names a sim node, for site screenshots.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 10:18:39 +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 b09a9d4082 docs: plan-next -- stage E (OTA) tested
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 07:20:57 +02:00
JakubandClaude Opus 5.5 6164526057 docs: plan-next -- stage H ticked
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 00:41:05 +02:00
JakubandClaude Opus 5.5 7826360d13 build(solo): offline queue 128 on the nRF52 builds
The phone app's offline queue took 256 x 177 B = 45 KB, which with the
on-device UI left the L1 ~5 KB of heap from boot (the sign buffer of #38
never fit). The UI keeps its own message history, and a full queue drops
the oldest channel messages first, so 128 is plenty: the L1 heap now has
~27 KB free. L1 OLED / e-ink, ProMicro, T-Echo Lite, both GAT562; the
upstream variants/ are unchanged (build_unflags in solo/).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 00:29:22 +02:00
JakubandClaude Opus 5.5 e28b5c5c51 fix(ui-lvgl): no USB drive behind the screen PIN; drop the NVS PIN migration
The SD card (message history) could be lent to a computer from the lock
card before the PIN was entered. While locked with a PIN, usbPoll() no
longer reacts; a cable plugged in meanwhile is offered once unlocked.

The plain-text NVS PIN never shipped, so there's nothing to migrate.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 00:13:33 +02:00
JakubandClaude Opus 5.5 4bda4f3a76 fix(ui-new): keyboard letters were blank
Since bf1e6fca kbApplyCapsUtf8 wanted 4 bytes free before each codepoint;
the ABC grid passes a 3-byte cell buffer, so every cell came out empty (T9
and CardKB Compact draw no such cells, so it went unnoticed). A codepoint
is now copied when it fits with the NUL.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 00:06:01 +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 0e1b3c9cfd fix(companion): signing takes the buffer it can get (#38)
CMD_SIGN_START promised the app 8K before a malloc it never checked. On
the L1 the nRF52 heap has ~5K free from boot, so the malloc always failed
and every CMD_SIGN_DATA answered ERR_CODE_BAD_STATE. Now it allocates
first, halving down to 256 B, and reports the size it got; nothing at all
is an error on SIGN_START itself.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 23:34:29 +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 0a669e1fd7 docs: plan-next -- stage F ticked
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 22:35:32 +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 1def29335c feat(companion): quiet hours -- muted on a schedule, the alarm still rings
Settings > Sound > Quiet hours (L2: its own group; L1: Quiet hrs / from /
until): from quiet_from to quiet_to (local hours, wrapping past midnight)
the buzzer is muted and a message doesn't wake the screen; the clock alarm
still rings. A manual mute or unmute inside the window stands until the
window ends. Off with the clock unset.

NodePrefs gains quiet_hours / quiet_from / quiet_to (appended to the file,
sentinel 0xC0DE0030; sizeof 2832 on nRF52 and ESP32), off by default,
22:00-07:00. hourInWindow() / localHour() are shared with the bot's quiet
hours. soundctl::tick() replaces autoTick() (Auto and quiet hours).

Also fixes the L2 side-button mute: it went by the mode, not by whether
the buzzer was muted, so with Auto + an app connected (and now quiet hours)
the first press muted instead of unmuting.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 21:59:40 +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 a11df8431b 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>
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 95c4c093f0 feat(ui-new): Tools > Satellites -- sky plot and signal bars on the 128x64 OLED
Two views, swapped with left / right: a polar sky plot (filled = used in the
fix, hollow = tracked, a pixel = no signal) beside fix, used / in view, HDOP,
PDOP and time to first fix; and a signal bar per satellite (used first, a
dotted 30 dB-Hz line) under per-constellation counts. The GPS stays awake
while it's open. Data from GpsSky (-D GPS_SKYVIEW, now on the L1 solo build).
The simulator's made-up sky moves from the L2's GpsScreen into
helpers/sensors/GpsSkySim.h, so both sims show it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 16:33:41 +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