Phase 5a. Trail → Hold Enter gains two rows:
- "Mark here": captures the current GPS fix and opens the keyboard for a
short label (empty → auto "WP<n>"); saved to the persistent table.
- "Waypoints" (shown when any exist): a list of saved waypoints with live
distance. Enter opens the shared NavView (To/Hdg/distance); Hold Enter
offers Rename / Delete.
All waypoint sub-screens layer over the trail views via _wp_mode and are
dismissed with Cancel. Map overlay comes next (5b).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Phase 4. Header-only navview::draw() takes a target (lat, lon, label) plus
the caller's own fix and course-over-ground, and renders distance + two
absolute bearings (To / Hdg). Source-agnostic so Waypoints, Backtrack and
Nearby node nav all reuse it. Not wired in yet (phases 5-6).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Phase 3. Fixed 16-entry table of (lat, lon, ts, label[12]) persisted to
/waypoints with a magic+version header (mirrors TrailStore's format).
Unlike the RAM-only trail, waypoints are loaded in UITask::begin() and
rewritten on change via UITask::saveWaypoints(), so they survive reboots.
add/remove/rename/clear operate on the in-RAM table; the screen layer
(phase 5) drives persistence after each edit.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Phase 2. A 5-fix GPS ring sampled every ~1 s in UITask::loop, filled
whenever there's a valid fix — independent of trail recording, so the
heading is available to navigation at all times, not only while logging
a trail.
- pushCogFix() rejects gross outliers (a fix implying > 50 m/s since the
previous one) so a single bad GPS reading can't swing the heading.
- currentCourse(deg) returns the bearing across the window (oldest→newest)
once the window spans ≥ 6 m of real movement; below that it holds the
last good heading, and returns false only until the first valid heading
exists. This is the "two absolute bearings (To / Hdg)" heading source
for the upcoming nav view.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Phase 1 of the navigation feature. The geographic helpers were private
statics in NearbyScreen; move them to a header-inline geo:: namespace so
the upcoming Waypoints / course-over-ground code can reuse them. NearbyScreen
keeps thin forwarders so its call sites are unchanged. No behaviour change.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
On a tall portrait e-ink panel the clock and lock screens wasted most of
the vertical space on a small inline "HH:MM". Render HH and MM on two
lines in a new size-4 font (built-in GFX scaled 7×, ~42×56 px) so the
digits roughly double in height and fill the narrow width. Wide panels
(OLED, landscape e-ink) keep the classic single-line size-2 layout.
- New shared drawClockTime() helper used by both the Clock home page and
the lock screen; returns the y below the time block so the date and
dashboard rows flow beneath it.
- GxEPDDisplay: size 4 = built-in font × BIG_TEXT_SCALE (7); getCharWidth /
getLineHeight / setTextSize handle it; fontAscender stays 0 (built-in is
top-left origin).
- Centre the big digits on their visible glyph width — the built-in font
advances 6 px per char but the glyph is 5 px wide, so getTextWidth over-
reports by one trailing column and the digits would sit ~half a column
left of centre. AM/PM rendered one size larger (size 2).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Before this commit, there was no way to set a different max hop count
for unscoped messages.
Now with this change, by defaul it tracks the flood.max setting, until
a user provides a flood.max.unscoped value, which tax precidence for
packets if ROUTE_TYPE_FLOOD is true.
Adds tools/trail_export.py: auto-detects the Wio port, waits for the
<?xml header, captures bytes until </gpx>, and writes a timestamped file
to tools/gpx/. Replaces the abandoned synthetic-FAT USB-MSC approach with
a simple host-side script.
- README: new "GPX Trail Export" section
- tools_screen.md: trail_export.py shown as the recommended download path,
cat/PuTTY kept as a manual fallback
- .gitignore: ignore tools/gpx/ output dir (mirrors tools/pngs/)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The channel history is a single ring shared by all channels (was 32
entries), while unread counts live in a separate per-channel array.
Once 32 messages had accumulated across channels, the oldest entry was
evicted on each new message — but the matching _ch_unread[] counter was
left untouched. The Messages badge then claimed unread messages the ring
could no longer surface, so a channel showed "N unread" but opened empty.
(The companion app stores its own copy over the protocol, which is why
the phone could still read them.)
Two changes:
- addChannelMsg() now decrements _ch_unread[evicted.ch_idx] when an entry
is pushed out of the ring, keeping the badge consistent with what is
actually displayable.
- CH_HIST_MAX raised 32 → 96 (~14 KB RAM) so eviction is far less frequent
under normal multi-channel traffic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>