feat(companion): live location sharing, Locator geofencing, trail auto-pause

Squash merge of feat/location-beacon-alerts-autopause (v1.21).

Features:
- Live Location Sharing — broadcast position as movement-gated [LOC]
  messages to a channel or contact; live shares show as map pins with
  distance/bearing in Nearby Nodes and a status-bar indicator. [LOC] is
  parsed in DMs, channel messages and room messages; DM shares name the
  sender.
- Locator (geofence) — arm a geofence around a saved waypoint or a person
  (live [LOC] or last-known position), alert on arrive/leave or near/far,
  with an optional homing beeper (gated to arrive/both modes). Arm from
  Tools > Locator, Nearby Nodes, or Waypoints; target picker lists
  favourites first, clearable via a "None" entry. Active target is drawn
  as a flag on the map.
- One active target shared across Locator / Navigate / Map via a single
  resolver (resolvePersonPos / activeTargetPos) that prefers a live
  [LOC] share over the last-advertised GPS fix.
- Follow live contacts — Navigate to a live-sharing contact follows them
  as they move and adds an ETA line; quick-share your own position from
  the Map.
- Map & status-bar upgrades — home mini-map gets a north marker, scale
  tick, and a connected trail line (was disconnected dots); status line
  shows tracked-node count, an arrow + distance to the active Locator/Nav
  target (falling back to the nearest live-tracked contact); GPS fix icon
  in the top status bar, shown only on GPS boards with GPS enabled.
- Trail auto-pause — recording freezes on stops (banking elapsed time,
  breaking the map line across the idle gap) and resumes on movement
  without ending the session.
- Streaming trail simplification — GPS points are simplified in-stream
  via a fixed-corridor (Reumann-Witkam) pass tuned for fidelity: straight
  runs collapse to their endpoints, curves stay bounded to the Min-dist
  tolerance, so the 512-point buffer covers a far longer route than a
  flat point budget would suggest.
- Collapsible Tools (Location / Comms / System sections, fold-in-place
  like Settings) and page-indicator icons on the home carousel.
- Waypoint coordinate editor — add a waypoint by scroll-editing lat/lon
  digit by digit.

Fixes:
- Critical: low-heap hang and contact loss on RAM-tight builds. Halved
  message-history scrollback rings (recovering ~14 KB free heap) and
  made contacts/channels/prefs persistence atomic (temp file + rename),
  so an interrupted save can no longer corrupt or wipe the store.
- Serial.write() bounded so a stalled USB host can't hang the device.
- Nearby Nodes: live [LOC] senders respect the type filter, sort by
  shared position, and the list refreshes so live shares bubble up.
- Map: live contacts are labelled before waypoints.
- GPS status icon hidden when GPS is off in Settings.
- Splash screen no longer truncates a pre-release tag's own dash (e.g.
  v1.21-rc1) when stripping the build's commit-hash suffix.
- Null-guarded the Locator target picker; clamped loc-share channel
  index on load.

Under the hood:
- -Os size optimisation on the e-ink and GAT562 30S solo envs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
MarekZegare4
2026-06-25 19:04:21 +02:00
parent c935287627
commit 57774d41f3
80 changed files with 2886 additions and 340 deletions

View File

@@ -6,7 +6,7 @@
| OLED | E-Ink |
| :-----------------------: | :-----------------------: |
| ![](./clk_scr_1_oled.png) | ![](./clk_scr_1_eink.png) |
| ![](./overview_oled.png) | ![](./overview_eink.png) |
A full-screen clock page on the home screen. Shows the current time and date, with up to three configurable data fields below.
@@ -27,7 +27,7 @@ If no time source is available, the screen shows _"! No time sync"_ with a hint
| OLED | E-Ink |
| :-----------------------: | :-----------------------: |
| ![](./clk_scr_2_oled.png) | ![](./clk_scr_2_eink.png) |
| ![](./fields_oled.png) | ![](./fields_eink.png) |
Up to three data fields are shown below the date separator. Each field displays a label and a value on the same line.
@@ -52,4 +52,10 @@ Sensor fields show `--` when the sensor is not connected or has no data.
### Configuring fields
| OLED | E-Ink |
| :-----------------------: | :-----------------------: |
| ![](./config_oled.png) | ![](./config_eink.png) |
<!-- screenshot pending: Dashboard Config — three field slots cycled with LEFT/RIGHT -->
**Hold Enter** (or press the **Context menu** key) on the Clock page to open the Dashboard Config screen, where each of the three field slots can be cycled with **LEFT/RIGHT**.

View File

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -6,7 +6,7 @@
| OLED | E-Ink |
| :------------------------: | :------------------------: |
| ![](./fav_dial_1_oled.png) | ![](./fav_dial_1_eink.png) |
| ![](./overview_oled.png) | ![](./overview_eink.png) |
A dedicated home page showing a grid of up to 6 pinned contacts for quick access. The layout adapts to the display orientation:
@@ -41,6 +41,12 @@ If a pinned contact has been removed from the contacts list, the tile shows `(go
2. Recent DM contacts — listed after
3. All remaining chat contacts — fallback when the first two tiers are empty (e.g. fresh install before any DMs)
| OLED | E-Ink |
| :------------------------: | :------------------------: |
| ![](./picker_oled.png) | ![](./picker_eink.png) |
<!-- screenshot pending: Pin-to-dial contact picker (favourites first, then recent DMs, then all contacts) -->
Select a contact to pin it to that slot.
**From a DM conversation****Hold Enter** context menu **Pin to dial**, then choose a slot from the slot picker (Slot 16, showing the current occupant name or "empty").

View File

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

View File

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

View File

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

Before

Width:  |  Height:  |  Size: 7.5 KiB

After

Width:  |  Height:  |  Size: 7.5 KiB

View File

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

Before

Width:  |  Height:  |  Size: 6.0 KiB

After

Width:  |  Height:  |  Size: 6.0 KiB

View File

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@@ -6,7 +6,7 @@
| OLED | E-Ink |
| :-----------------------: | :-----------------------: |
| ![](./mes_scr_1_oled.png) | ![](./mes_scr_1_eink.png) |
| ![](./overview_oled.png) | ![](./overview_eink.png) |
The Messages screen is split into three modes — **DMs**, **Channels**, and **Rooms** — selectable with UP/DOWN on the mode-select screen. Each mode shows the corresponding list of conversations with unread counters.
@@ -16,7 +16,7 @@ The Messages screen is split into three modes — **DMs**, **Channels**, and **R
| OLED | E-Ink |
| :-----------------------: | :-----------------------: |
| ![](./mes_scr_2_oled.png) | ![](./mes_scr_2_eink.png) |
| ![](./compose_oled.png) | ![](./compose_eink.png) |
Press **Enter** on a contact or channel to open its history, then press **Enter** again (or select an empty send row) to compose a message. Choose between:
@@ -44,7 +44,7 @@ Sensor placeholders appear automatically in the placeholder picker when the corr
| OLED | E-Ink |
| :-----------------------: | :-----------------------: |
| ![](./mes_scr_3_oled.png) | ![](./mes_scr_3_eink.png) |
| ![](./history_oled.png) | ![](./history_eink.png) |
Each entry in the history list shows the sender name and a compact age indicator (`3m`, `2h`, `>1d`) in the top-right corner.
@@ -56,7 +56,7 @@ Each entry in the history list shows the sender name and a compact age indicator
| OLED | E-Ink |
| :-----------------------: | :-----------------------: |
| ![](./mes_scr_4_oled.png) | ![](./mes_scr_4_eink.png) |
| ![](./fullscreen_oled.png) | ![](./fullscreen_eink.png) |
Navigate between messages with **LEFT** (newer) and **RIGHT** (older). Long messages scroll with **UP/DOWN**.
@@ -64,7 +64,7 @@ If the message is a reply addressed to someone (`@[nick]`), a **To: nick** bar i
| OLED | E-Ink |
| :-----------------------: | :-----------------------: |
| ![](./mes_scr_5_oled.png) | ![](./mes_scr_5_eink.png) |
| ![](./fullscreen_menu_oled.png) | ![](./fullscreen_menu_eink.png) |
**Hold Enter** in fullscreen opens the options menu. It always offers **Reply** for an incoming message, and when the message contains a **location** it adds two more:
@@ -81,7 +81,7 @@ A location is any `lat,lon` pair in the text — exactly what the `{loc}` placeh
| OLED | E-Ink |
| :-----------------------: | :-----------------------: |
| ![](./mes_scr_6_oled.png) | ![](./mes_scr_6_eink.png) |
| ![](./ctx_contact_oled.png) | ![](./ctx_contact_eink.png) |
| Item | Action |
| ---------------------------- | ------------------------------------------------------------------------------ |
@@ -98,7 +98,7 @@ When **Pin to dial** is selected, a slot picker opens (Slot 16 showing curren
| OLED | E-Ink |
| :-----------------------: | :-----------------------: |
| ![](./mes_scr_7_oled.png) | ![](./mes_scr_7_eink.png) |
| ![](./ctx_channel_oled.png) | ![](./ctx_channel_eink.png) |
**Hold Enter** on a channel entry opens a context menu:

View File

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

@@ -6,7 +6,7 @@
| OLED | E-Ink |
| :-----------------------: | :-----------------------: |
| ![](./lck_scr_1_oled.png) | ![](./lck_scr_1_eink.png) |
| ![](./overview_oled.png) | ![](./overview_eink.png) |
Screen lock prevents accidental keypresses. While locked the display turns off and all input is ignored.
@@ -34,7 +34,7 @@ If no press is made for 3 seconds, the counter resets.
| OLED | E-Ink |
| :-----------------------: | :-----------------------: |
| ![](./lck_scr_2_oled.png) | ![](./lck_scr_2_eink.png) |
| ![](./screen_oled.png) | ![](./screen_eink.png) |
A brief press of any button wakes the display and shows the lock screen. It displays:

View File

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

Before

Width:  |  Height:  |  Size: 6.7 KiB

After

Width:  |  Height:  |  Size: 6.7 KiB

View File

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

View File

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -6,7 +6,7 @@
| OLED | E-Ink |
| :-----------------------: | :-----------------------: |
| ![](./set_scr_1_oled.png) | ![](./set_scr_1_eink.png) |
| ![](./overview_oled.png) | ![](./overview_eink.png) |
All settings are saved to flash and restored on next boot. Settings are organised into collapsible sections. Press **Enter** on a section header to expand or collapse it — all sections start collapsed for faster navigation. Press **LEFT/RIGHT** to change a value, or **Enter** for toggle items.
@@ -50,7 +50,7 @@ Melody 1 and Melody 2 are custom sequences editable in **Tools Ringtone Edit
| OLED | E-Ink |
| :-----------------------: | :-----------------------: |
| ![](./set_scr_2_oled.png) | ![](./set_scr_2_eink.png) |
| ![](./homepages_oled.png) | ![](./homepages_eink.png) |
Lists all available home screen pages. For each entry:
@@ -74,6 +74,12 @@ Lists all available home screen pages. For each entry:
| Pwr save | ON / OFF | **Battery saver.** Hardware duty-cycle receive: the SX126x cycles RX↔sleep on its own and wakes on a preamble, cutting average RX current. Trades a little receive latency; leave OFF for lowest-latency reception. Requires an SX126x radio (otherwise stays on continuous RX). **Forced off (shown as `--`) while the repeater is on** — a repeater must listen continuously; your setting is restored when the repeater is switched off. |
| Auto pwr | ON / OFF | **Adaptive Power Control.** Lowers actual TX power on strong links to save energy, ramping back up — to the **TX Pwr** ceiling — on weak or lost links. Link quality comes from direct-message ACK SNR and, for channel messages (no ACK), from hearing a repeater rebroadcast your packet. The radio page / name bar shows the live power. Default OFF (fixed TX power). **Suppressed (shown as `--`) while the repeater is on** — a repeater holds full TX power for consistent relay reach; your setting is restored when the repeater is switched off. |
| OLED | E-Ink |
| :-----------------------: | :-----------------------: |
| ![](./radio_oled.png) | ![](./radio_eink.png) |
<!-- screenshot pending: Radio — preset popup (pick/save/delete) and/or the digit-by-digit frequency editor -->
The **repeater** mode and its flood filters live on their own screen — see **Tools Repeater**.
---

View File

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

View File

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

Before

Width:  |  Height:  |  Size: 8.5 KiB

After

Width:  |  Height:  |  Size: 8.5 KiB

View File

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

View File

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

Before

Width:  |  Height:  |  Size: 8.5 KiB

After

Width:  |  Height:  |  Size: 8.5 KiB

View File

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

View File

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

View File

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -6,9 +6,9 @@
| OLED | E-Ink |
| :-----------------------: | :-----------------------: |
| ![](./tls_scr_1_oled.png) | ![](./tls_scr_1_eink.png) |
| ![](./overview_oled.png) | ![](./overview_eink.png) |
The Tools screen is a hub for GPS trail recording, nearby node browsing, ringtone editing, auto-reply bot, auto-advert, compass, device diagnostics, and repeater mode. Navigate the tool list with **UP/DOWN** and press **Enter** to open a tool.
The Tools screen is a hub for GPS trail recording, nearby node browsing, ringtone editing, auto-reply bot, auto-advert, live location sharing, locator, compass, device diagnostics, and repeater mode. Tools are grouped into collapsible **Location** / **Comms** / **System** sections — the same fold-in-place model as Settings; Tools always opens folded back to the section list. Navigate with **UP/DOWN**, press **Enter** on a section header to expand or collapse it, or on a tool to open it.
---
@@ -16,7 +16,7 @@ The Tools screen is a hub for GPS trail recording, nearby node browsing, rington
| OLED | E-Ink |
| :-----------------------: | :-----------------------: |
| ![](./tls_scr_2_oled.png) | ![](./tls_scr_2_eink.png) |
| ![](./nearby_oled.png) | ![](./nearby_eink.png) |
Browse nodes that have recently advertised on the mesh. **Filter** (which nodes) and **sort** (in what order) are independent axes and combine freely.
@@ -31,15 +31,16 @@ Filter by category with **LEFT/RIGHT** (one coherent axis — type only):
| Room | Room servers |
| Snsr | Sensors |
Select a node to see its coordinates, distance, bearing with cardinal direction, type, and last-heard time.
Select a node to see its coordinates, distance, bearing with cardinal direction, type, and last-heard time. A node that is **broadcasting its position** via Live Share is marked with a **♦ diamond** beside its name in the list (the same marker the map uses), and its detail shows `Sharing pos:` with the share age and whether it's DM-verified or channel-only.
**Hold Enter** opens the same **Options** menu everywhere (list and detail), in a fixed order — only the actions that apply appear:
| Action | Available when |
| ---------------------- | -------------------------------------------------------------------------------------- |
| Navigate | selected node has GPS |
| Navigate | selected node has GPS — for a node sharing live position, the view follows it as it moves and adds an ETA line |
| Ping | a public key is known for the node |
| Save waypoint | selected node has GPS |
| Set as target | selected node has GPS **and** a public key — pins it as the active **Locator/Nav target** right away (see **Locator**) |
| Sort: Dist/Recent | browsing stored nodes — **LEFT/RIGHT** on the row flips distance ↔ last-heard in place |
| Discover scan / Rescan | always (live `NODE_DISCOVER_REQ` scan) |
@@ -49,7 +50,7 @@ Selecting **Ping** opens the Ping popup:
| OLED | E-Ink |
| :----------------------------: | :----------------------------: |
| ![](./tls_scr_2_ping_oled.png) | ![](./tls_scr_2_ping_eink.png) |
| ![](./nearby_ping_oled.png) | ![](./nearby_ping_eink.png) |
Use **Enter** on the popups `Ping` row to send a direct mesh ping to that node. The popup then shows the RTT and SNR values on the next lines, and can be used again immediately for another ping.
@@ -62,7 +63,7 @@ Use **Enter** on the popups `Ping` row to send a direct mesh ping to that nod
| OLED | E-Ink |
| :-----------------------: | :-----------------------: |
| ![](./tls_scr_3_oled.png) | ![](./tls_scr_3_eink.png) |
| ![](./nearby_scan_oled.png) | ![](./nearby_scan_eink.png) |
**Options → Discover scan** sends a `NODE_DISCOVER_REQ`. Repeaters, sensors and room servers within zero-hop range respond immediately with name, type and signal data. This is not a separate screen — it is the **same list switched to a live-scan source**: the right-hand column shows **RSSI** instead of distance, and node detail shows the public key, signal data and contact status.
@@ -76,21 +77,26 @@ Because it is the same list, all the same keys apply — **UP/DOWN** to navigate
| OLED | E-Ink |
| :-----------------------: | :-----------------------: |
| ![](./tls_scr_4_oled.png) | ![](./tls_scr_4_eink.png) |
| ![](./trail_summary_oled.png) | ![](./trail_summary_eink.png) |
Records your route in a RAM ring buffer (up to 512 points, sampled every 1 s). Tracking runs in the background — a blinking **G** appears in the status bar. The trail survives display auto-off but is lost on reboot unless saved to flash first.
Records your route in a RAM ring buffer (up to 512 points, sampled every 1 s). The track is **simplified as it's recorded** — a long straight stretch is kept as just its two endpoints while curves keep their detail (bounded to within the **Min dist** tolerance of the real path), so the buffer covers a far longer route than a flat point budget would suggest. Tracking runs in the background — a blinking **G** appears in the status bar. The trail survives display auto-off but is lost on reboot unless saved to flash first.
> [!TIP]
> The **Map** view is also reachable directly from the home carousel — the **Map** page shows a live mini-preview (your position, trail, and tracked contacts) with a **north marker** and a bottom-left **scale tick**. The status line below reads `Track:N` (tracked-node count) and, when you have a fix and at least one tracked contact, an **arrow + distance** to the **nearest** one (e.g. `Track:3 →120m`). If a **Locator/Nav target** is set it's drawn as a **flag marker** (see **Locator**). Press **Enter** to open the full Trail Map; **Hold Enter** shares your position (see **Live Share**); **Back** returns home.
A GPS fix indicator also sits in the top status bar, alongside the trail/auto-advert/repeater icons — boxed (lit) once the receiver has a valid fix, a plain glyph while still searching. It only appears on boards with GPS hardware and while **GPS** is turned on in Settings; it's hidden the rest of the time rather than sitting there empty.
Cycle views with **LEFT / RIGHT**:
| View | Content |
| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Summary** | Distance, elapsed time, avg speed or pace, point count, tracking status |
| **Map** | Auto-fit dot-and-line plot with cos(lat) aspect correction; segment breaks marked; north arrow; square scale grid fitted to the map frame (toggle under **Hold Enter → Settings → Grid**, Map view only). Your **current GPS position** and all **waypoints** are always drawn — even with no trail recording — so the map is useful standalone |
| **Map** | Auto-fit dot-and-line plot with cos(lat) aspect correction; segment breaks marked; north arrow; square scale grid fitted to the map frame (toggle under **Hold Enter → Settings → Grid**, Map view only). Your **current GPS position**, all **waypoints**, and any **live-tracked contacts** (positions shared via Live Share) are always drawn — even with no trail recording — so the map is useful standalone. The active **Locator/Nav target**, if set, is drawn as a **flag** on top (folded into the frame so it's never off-screen). Point labels are auto-placed to avoid overlapping (a crowded cluster drops some labels rather than smearing them) |
| **List** | Per-point rows showing local time (HH:MM) and delta distance from the previous point; segment-start rows show `start`; scroll with **UP/DOWN** |
| OLED | E-Ink |
| :-----------------------: | :-----------------------: |
| ![](./tls_scr_5_oled.png) | ![](./tls_scr_5_eink.png) |
| ![](./trail_map_oled.png) | ![](./trail_map_eink.png) |
**Hold Enter** opens the **action menu**. It is two-level — a short main menu, plus **Trail file…** and **Settings…** submenus. **Cancel/Back** in a submenu returns to the main menu.
@@ -101,6 +107,7 @@ Cycle views with **LEFT / RIGHT**:
| Start / Stop tracking | Begin or end a recording session |
| Mark here | Drop a waypoint at the current GPS fix (see below) |
| Waypoints… | Open the waypoint list / navigation / add-by-coords |
| Share my pos | Send your current position as a one-shot `[LOC]` message — pick a contact or channel (see **Live Share**) |
| Trail file… | Open the file submenu (below) |
| Settings… | Open the settings submenu (below) |
@@ -116,14 +123,17 @@ Cycle views with **LEFT / RIGHT**:
**Settings…** (values cycle with **LEFT/RIGHT** or **Enter**; shown only where they apply):
| Item | Available | Action |
| -------- | --------- | ------------------------------------------------------- |
| Min dist | always | Sample gate, 4 levels — metric: 5/10/25/100 m, imperial: 15/30/75/300 ft |
| Readout | Summary view | Summary shows Speed or Pace (in the global unit system) |
| Grid | Map view | Toggle scale grid on the map |
| Item | Available | Action |
| ---------- | --------- | ------------------------------------------------------- |
| Min dist | always | Sample gate, 4 levels — metric: 5/10/25/100 m, imperial: 15/30/75/300 ft |
| Auto-pause | always | Off / 1 / 2 / 5 min — auto-freeze the trail after a stop, resume on movement (see below) |
| Readout | Summary view | Summary shows Speed or Pace (in the global unit system) |
| Grid | Map view | Toggle scale grid on the map |
(Trail file… appears only when a live or saved trail exists. Mark here needs a GPS fix; Waypoints is always available.)
**Auto-pause** — when set, a recording trail automatically **pauses** after the device has stayed within ~15 m of one spot for the chosen delay: the elapsed timer and point sampling both freeze, and the map line breaks across the idle gap. It **resumes on its own** as soon as you move again. This keeps a stop (a break, a meal, parking) out of your distance and average-speed stats without you having to remember to stop and restart tracking. A paused trail is still "on" (the **G** marker keeps blinking) — the Summary **Status** row shows `paused`. The stop is detected with its own coarse movement gate, independent of **Min dist**, so GPS jitter while you're parked doesn't keep it awake.
### Waypoints
A waypoint is a saved spot — your car, camp, a water source — that you can navigate back to later. Waypoints are **independent of the trail**: they live in their own flash file (`/waypoints`), survive a reboot, and are **not** cleared by *Reset trail*. Up to 16 can be stored — the Waypoints list header shows how many are in use (e.g. `WAYPOINTS 3/16`).
@@ -132,7 +142,14 @@ A waypoint is a saved spot — your car, camp, a water source — that you can n
**Adding by coordinates** — open **Hold Enter → Waypoints** and select the **+ Add by coords** row (always the last entry in the list). This creates a waypoint without being there — no GPS fix required (handy for a meeting point or a spot read off a map). It opens a small form with three editable rows plus **Save**:
- **Lat** / **Lon****Enter** opens the keyboard to type the value in decimal degrees (magnitude only; the keyboard has no minus sign), and **LEFT/RIGHT** toggles the hemisphere — N/S for latitude, E/W for longitude.
| OLED | E-Ink |
| :------------------------: | :------------------------: |
| ![](./waypoint_add_oled.png) | ![](./waypoint_add_eink.png) |
<!-- screenshot pending: Add-by-coords form — Lat/Lon scroll editor, hemisphere toggle, Label, Save -->
- **Lat** / **Lon****Enter** opens the digit-by-digit scroll editor (the same widget as the radio frequency field): **LEFT/RIGHT** move the cursor between decimal places, **UP/DOWN** change the digit under it, **Enter** confirms. With the editor closed, **LEFT/RIGHT** on the row toggles the hemisphere — N/S for latitude, E/W for longitude.
- **Label** — **Enter** to type a name (blank → auto `WP<n>`).
- **Save** — validates the range and stores the waypoint. Missing or out-of-range values report a brief error.
@@ -147,9 +164,15 @@ A waypoint is a saved spot — your car, camp, a water source — that you can n
Hdg: 090° E ← your current course over ground (-- when stationary)
```
| OLED | E-Ink |
| :------------------------: | :------------------------: |
| ![](./waypoint_nav_oled.png) | ![](./waypoint_nav_eink.png) |
<!-- screenshot pending: Waypoints navigation view — target label, distance, To/Hdg bearings (shared with Nearby/message Navigate) -->
There is no magnetometer, so the screen shows two *absolute* bearings and you compare them: target at 145°, travelling at 90° → bear right. The **Hdg** line is derived from GPS movement (see Compass) and reads `--` until you move.
**Managing****Hold Enter** on a waypoint row offers **Rename** / **Delete** / **Send** (the *Trail start* row is navigate-only). Delete removes one at a time; there is no bulk clear.
**Managing****Hold Enter** on a waypoint row offers **Rename** / **Delete** / **Send** / **Set as target** (the *Trail start* row is navigate-only). **Set as target** pins the waypoint as the active **Locator/Nav target** in one step (see **Locator**). Delete removes one at a time; there is no bulk clear.
**Sharing****Send** hands the waypoint to the Messages screen: pick a contact or channel, and the message is pre-filled as `[WAY]<lat>,<lon> <label>` (e.g. `[WAY]37.42123,-122.08456 CAR`) for you to confirm or edit before sending. On the receiving device, opening that message and **Hold Enter → Navigate / Save waypoint** turns it back into a navigable point (see *Messages Fullscreen message view*). The format is plain text, so it stays readable on other firmware and the phone app.
@@ -191,8 +214,92 @@ Periodically broadcasts a 0-hop advert with your GPS position. Configurable inte
---
## Live Share
| OLED | E-Ink |
| :------------------------: | :------------------------: |
| ![](./liveshare_oled.png) | ![](./liveshare_eink.png) |
<!-- screenshot pending: Live Share screen — Track loc / Auto share / To / Move / Min gap / Heartbeat rows -->
Share your live position over the mesh **as ordinary chat messages**, and put other people who do the same on your map. A position is sent as a `[LOC]<lat>,<lon>` message — the same coordinate format waypoints use, so it stays readable on other firmware and the phone app (it just looks like a coordinate to anything that doesn't know the tag).
This is **independent of Auto-Advert** and runs alongside it: Auto-Advert announces your *presence* as a 0-hop beacon for Nearby Nodes, while Live Share sends your *position* to a specific channel or contact you choose.
The tool holds both directions of sharing in one flat list. Navigate with **UP/DOWN**, change a value with **LEFT/RIGHT** (or **Enter**); **Cancel/Back** saves and returns to Tools.
| Setting | Options | Notes |
| ---------- | --------------------------- | ---------------------------------------------------------------------------------------------- |
| Track loc | ON / OFF | Receive incoming `[LOC]` shares (DM, monitored channels, and room-server posts) and pin those senders on the map / in Nearby. Off by default. |
| Auto share | ON / OFF | Periodically broadcast **your own** position to the target below while you move. |
| To | channel or contact | **Enter** opens the Messages recipient chooser to pick the target channel or DM contact. |
| Move | 50 / 100 / 250 / 500 m | Movement gate — only send after you've moved at least this far since the last share. |
| Min gap | 30 s / 1 / 2 / 5 min | Minimum time between sends, so fast movement can't flood the channel. |
| Heartbeat | Off / 5 / 15 min | Optional keep-alive: re-send even while stationary, so the other end knows you're still there. |
**How auto-share decides to send.** With **Auto share** on, the device checks a few times a minute: it transmits when you've moved at least **Move** metres *and* at least **Min gap** has passed since the last send — so a stationary device stays silent unless a **Heartbeat** is set. It also sends once immediately when you enable sharing (or change the target), so the other end gets a fresh fix right away.
**Receiving.** With **Track loc** on, incoming `[LOC]` messages update a small live table (up to 16 nodes, entries expire ~20 min after the last update). DM shares are keyed by the sender's public key (reliable); channel and room-server shares are keyed by name (best-effort, since channel names are unsigned and a room post only carries a short sender prefix). Tracked nodes appear on the **Trail Map** as a filled diamond with the first two characters of their name, and in **Nearby Nodes** with their live distance/bearing.
**One-shot share.** To send your position once without enabling auto-share, use **Tools Trail → Hold Enter → Share my pos** — it builds a `[LOC]` message and hands it to the Messages screen to pick a recipient. There's also a shortcut from the home **Map** page: **Hold Enter** sends an immediate position update to your Live Share target while auto-sharing is on (toast `Position shared`), or opens the recipient picker if it isn't — so you never broadcast to a default channel by accident.
---
## Locator
| OLED | E-Ink |
| :-----------------------: | :-----------------------: |
| ![](./locator_oled.png) | ![](./locator_eink.png) |
<!-- screenshot pending: Locator screen with a target set (e.g. "@Bob (5m)"), radius/mode/beeper rows -->
A single **geofence** that beeps and shows an alert when you cross **into** or **out of** a radius. The target can be a **saved waypoint** (a fixed place — "tell me when I'm back at camp") or a **live contact** (a person sharing their position via Live Share — "alert me when my friend gets near / falls behind"). A waypoint target is a **snapshot** (coordinate + label copied), so it keeps working even if you later edit or delete that waypoint; a contact target follows the person's latest shared position.
Navigate with **UP/DOWN**, change a value with **LEFT/RIGHT** (or **Enter**); **Cancel/Back** saves and returns to Tools.
| Setting | Options | Notes |
| ------- | -------------------------------- | -------------------------------------------------------------------------------------- |
| Alert | ON / OFF | Master switch. Enabling without a target prompts you to pick one. |
| Target | none / person / waypoint | **Enter** opens a picker — **None** first (clears the target), then **favourites** (offered even with no known position yet, so you can arm ahead of time), then any other contact with a currently-resolvable position (live-sharing *or* just last-advertised, e.g. a repeater), then waypoints; **UP/DOWN** + **Enter** to choose. **LEFT/RIGHT** quick-cycles the same set in place, including back to **None**. A person is shown with an `@` prefix, plus a compact **age tag** (e.g. `@Bob (5m)`) when the position is last-advertised rather than a live share. Shows `none` until set. |
| Radius | 50 / 100 / 250 / 500 m / 1 km | Geofence size. |
| Mode | Arrive / Leave / Both | Which crossing fires the alert — entering the radius, leaving it, or both. |
| Beeper | ON / OFF | Optional homing tone — shown only in **Arrive** / **Both** modes (see below). |
**Crossing alert.** When armed with a target, the device watches its own GPS fix and fires the alert (a short melody plus an on-screen message) the moment you cross the radius, according to **Mode**. The wording adapts to the target — `Arrived` / `Left` for a waypoint, `Near` / `Away` for a person. The edge has a little hysteresis so a fix hovering right on the boundary doesn't chatter, and the first reading after arming only seeds the in/out state — it won't fire spuriously just because you armed it while already inside.
**Following a person.** Pick a **favourite** (or any contact with a known position) as the target and the geofence tracks the distance *between you and them*, so it works even while both of you move. The position is resolved with a fixed precedence: an **active live `[LOC]` share** wins, and with no current share it **falls back to the contact's last-advertised GPS position** — so a rarely-updating but stationary node (a repeater, or someone who shared a fix once) still works as a target. You can arm it **ahead of time** — choosing a favourite locks onto their identity (pubkey), and the alert starts working as soon as a position is known. Live following requires a **DM** share (a channel share carries no stable identity to lock onto); the last-advertised fallback works for any contact regardless.
**Proximity beeper.** With **Beeper** on, the device also ticks while you're inside the radius and **shortens the gap between ticks the closer you get to the target** — slow near the edge, rapid near the centre — like a homing beeper guiding you to the exact spot. It's silent outside the radius. Because the beeper is its own opt-in toggle, turning it on **overrides the global buzzer mute** (**Settings Sound Buzzer**) — it's an explicit "I want to hear this". Since homing only makes sense while you're approaching a target, the **Beeper** row appears only in **Arrive** or **Both** mode — it's hidden in **Leave**-only mode, and stays silent there even if it was switched on earlier. Otherwise it's independent of the crossing alert (which does follow the mute), so you can use either or both.
**Setting the target from anywhere.** Besides this screen's picker, the *same* active target can be set in one step with **Set as target** from **Nearby Nodes**' or **Waypoints**' own **Hold Enter** menu — handy so you don't need a detour through Tools. Picking from this screen's picker saves on exit (so **LEFT/RIGHT** cycling stays cheap); the per-item shortcuts save immediately and confirm with a `Target set` toast.
**On the map.** Whatever the active target is — person or waypoint — it's drawn as a **flag marker** on both the home **Map** preview and the full **Trail Map**, on top of any waypoint/contact it overlaps and folded into the frame so it never sits off-screen. This shows even when the **Alert** master switch is off, so a target you set purely to navigate to still appears.
| OLED | E-Ink |
| :-----------------------------: | :-----------------------------: |
| ![](./locator_picker_oled.png) | ![](./locator_picker_eink.png) |
<!-- screenshot pending: PICK TARGET picker — None, favourites, a last-advertised contact with age tag (e.g. "@Bob (5m)"), and waypoints -->
| OLED | E-Ink |
| :-----------------------------: | :-----------------------------: |
| ![](./map_target_oled.png) | ![](./map_target_eink.png) |
<!-- screenshot pending: Trail Map (or home Map preview) with the active-target flag marker visible -->
> [!TIP]
> Mark the spot first with **Tools Trail → Hold Enter → Mark here** (or **+ Add by coords**), then set it as the Locator target.
---
## Compass
| OLED | E-Ink |
| :------------------------: | :------------------------: |
| ![](./compass_oled.png) | ![](./compass_eink.png) |
<!-- screenshot pending: Compass — scrolling heading tape with centre pointer + large degrees/cardinal readout -->
A heads-up GPS compass. The L1 has no magnetometer, so the heading is the **course over ground** — derived from how your GPS position moves over the last few seconds. The display is a horizontal **heading tape**: a fixed travel-direction pointer sits at the centre and the N..E..S..W scale scrolls underneath it as you turn, so whatever is under the pointer is your current course. A large numeric readout below shows that course in degrees and cardinal (e.g. `145° SE`).
Because the heading comes from movement, it only updates while you are actually moving: standing still shows *move to set heading* (and navigation's **Hdg** line reads `--`). Gross GPS jumps are rejected so a single bad fix can't swing the heading. The heading source runs whenever there's a GPS fix — recording a trail is **not** required.
@@ -203,7 +310,7 @@ Because the heading comes from movement, it only updates while you are actually
| OLED | E-Ink |
| :-----------------------: | :-----------------------: |
| ![](./tls_scr_6_oled.png) | ![](./tls_scr_6_eink.png) |
| ![](./ringtone_oled.png) | ![](./ringtone_eink.png) |
A step sequencer for composing custom notification melodies. Two slots — **Melody 1** and **Melody 2** — switchable from within the editor.
@@ -244,7 +351,7 @@ Melodies can be assigned in **Settings Sound** (global default) or overridde
| OLED | E-Ink |
| :-----------------------: | :-----------------------: |
| ![](./tls_scr_7_oled.png) | ![](./tls_scr_7_eink.png) |
| ![](./autoreply_oled.png) | ![](./autoreply_eink.png) |
Automatically replies to incoming messages that contain a configured trigger word (case-insensitive, contains match).
@@ -292,6 +399,12 @@ Commands also work on the **monitored channel** (the one selected for channel mo
## Diagnostics
| OLED | E-Ink |
| :------------------------: | :------------------------: |
| ![](./diagnostics_oled.png) | ![](./diagnostics_eink.png) |
<!-- screenshot pending: Diagnostics — live device/mesh stats rows (uptime, rx/tx counters, heap, RSSI/SNR, queue, errors) -->
A single read-only screen of live device and mesh stats, refreshed once a second. On a small OLED the rows scroll with **UP/DOWN**; on a larger e-ink display they all fit at once.
| Row | Shows |
@@ -319,6 +432,12 @@ The counters make the repeater behaviour observable: **Forwarded** confirms the
## Repeater
| OLED | E-Ink |
| :------------------------: | :------------------------: |
| ![](./repeater_oled.png) | ![](./repeater_eink.png) |
<!-- screenshot pending: Repeater — toggle + Network/profile + flood-filter rows -->
Turns the companion into a packet **repeater** while it keeps working as a normal companion — no separate firmware. By default, enabling it switches the radio to a dedicated repeater profile rather than relaying on whatever network you're chatting on (see **Network** below) — that matches the MeshCore community norm of repeaters sitting on a standard channel, not a private one. Loop-detection and an advert flood-depth cap are always applied. This screen keeps the toggle, the network/profile, and its flood-filter options together; live forwarding stats are on **Tools Diagnostics**.
Navigate with **UP/DOWN**; change a value with **LEFT/RIGHT** (or **Enter** for toggles). **Cancel/Back** saves and returns to Tools.

View File

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

View File

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

View File

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB