mirror of
https://github.com/MarekZegare4/MeshCore-Solo.git
synced 2026-10-09 03:06:39 +00:00
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>
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
<p align="center"><img src="./img/hero.png" alt="MeshCore Solo" width="640"></p>
|
||||
|
||||
# MeshCore Solo
|
||||
|
||||
Solo is a MeshCore companion firmware that works on its own: messages, contacts,
|
||||
GPS navigation and tools right on the device, no phone needed. The phone app
|
||||
still connects as with the stock firmware, over Bluetooth or USB.
|
||||
|
||||
**Try it in your browser: [solo.marekzegarek.com](https://solo.marekzegarek.com)**
|
||||
|
||||
## What it does
|
||||
|
||||
- **Messages without a phone**: channels, direct messages and rooms, with
|
||||
quick replies, live placeholders (`{loc}`, `{time}`, sensor readings) and
|
||||
keyboards for Latin, Cyrillic, Greek and European diacritics.
|
||||
- **GPS navigation**: trail recording with GPX export, waypoints, a compass,
|
||||
navigate to any node, waypoint or shared location, live location sharing
|
||||
and a geofence alert (Locator).
|
||||
- **Nearby nodes**: who's around, signal and distance, ping, and remote
|
||||
admin of your repeaters and rooms.
|
||||
- **Tools**: clock with alarm, timer and stopwatch, a remote bot that answers
|
||||
commands over the mesh, repeater mode, a ringtone editor, diagnostics.
|
||||
- **Screen lock** with an optional PIN.
|
||||
- **On the Wio Tracker L2**: an offline map, message history on the SD card,
|
||||
the SD card as a USB drive, updates over WiFi.
|
||||
|
||||
## Devices
|
||||
|
||||
The firmware is the same everywhere; how you drive it depends on the device.
|
||||
These docs mark the differences with notes like this:
|
||||
|
||||
> [!NOTE]
|
||||
> **Wio Tracker L2:** what's different on the touch screen.
|
||||
|
||||
| Group | Devices | Controls |
|
||||
| ----- | ------- | -------- |
|
||||
| **Joystick** | Wio Tracker L1 (OLED / e-ink), GAT562 30S, GAT562 Watch13, Heltec V3/V4 with a joystick | Four directions, Enter, Back |
|
||||
| **Keyboard** | M5Stack Cardputer ADV, T-Echo Lite + KeyShield, any board with a CardKB | Keys; arrows or their Fn combinations for the directions |
|
||||
| **Touch** | Wio Tracker L2 | Taps, swipes and holds; two side buttons |
|
||||
|
||||
| OLED / e-ink | | Wio Tracker L2 |
|
||||
| :---: | :---: | :---: |
|
||||
|  |  |  |
|
||||
|
||||
The joystick and keyboard devices share one interface, sized for small OLED
|
||||
and e-ink screens. The Wio Tracker L2 has its own, built for a 320 × 240
|
||||
colour touch screen, with extras its hardware allows: an offline map, message
|
||||
history on the SD card and updates over WiFi.
|
||||
|
||||
## Pages
|
||||
|
||||
- [Getting started](./getting-started.md): controls, the home screen, the phone app, updates
|
||||
- [Messages](./messages.md): channels, direct messages, rooms, typing
|
||||
- [Contacts](./contacts.md): nearby nodes, favourites, remote admin
|
||||
- [Navigation](./navigation.md): GPS, trail, waypoints, compass, sharing your location, the map
|
||||
- [Tools](./tools.md): clock tools, bot, repeater, ringtones, diagnostics
|
||||
- [Settings](./settings.md): what is where
|
||||
- [Screen lock](./lock.md): locking, auto-lock, PIN
|
||||
- [Hardware](./hardware.md): external keyboards and joysticks, e-ink, SD card, WiFi
|
||||
|
||||
For developers: [UI Core](./developer/ui-core.md), [UI framework](./developer/ui-framework.md), [build flags](./developer/build-flags.md).
|
||||
|
||||
## Contributors
|
||||
|
||||
Big thanks to the people who contributed to Solo:
|
||||
[vanous](https://github.com/vanous), [marczykm](https://github.com/marczykm),
|
||||
[tchellow](https://github.com/tchellow) and [3urobeat](https://github.com/3urobeat).
|
||||
|
||||
Built on [MeshCore](https://github.com/meshcore-dev/MeshCore) and the work of
|
||||
its [community](https://github.com/meshcore-dev/MeshCore/graphs/contributors).
|
||||
@@ -0,0 +1,69 @@
|
||||
# Contacts
|
||||
|
||||
## Nearby nodes
|
||||
|
||||
**Tools › Nodes** lists the nodes the device has heard: their type, how long
|
||||
ago, and, when they have a position, distance and bearing. **Left / Right**
|
||||
filters by type (All, Fav, Companions, Repeaters, Rooms, Sensors); the sort,
|
||||
by distance or by last heard, is in the options. A ★ marks a favourite, a ♦
|
||||
someone sharing their position live.
|
||||
|
||||
**Enter** shows a node's details. **Hold Enter**, in the list or the details,
|
||||
for what you can do with it:
|
||||
|
||||
| Option | Does |
|
||||
| ------ | ---- |
|
||||
| Navigate | Distance and bearing to the node; follows it if it shares its position live |
|
||||
| Ping | Sends a ping and shows the round trip time and SNR |
|
||||
| Save waypoint, Set as target | Its position as a waypoint, or as the [Locator](./navigation.md#locator) target |
|
||||
| Fav, Pin to dial | Favourite, or a place on the Favourites page |
|
||||
| Admin | Remote admin, for a repeater or room (below) |
|
||||
| Discover scan | Asks the repeaters, rooms and sensors in direct range to answer, with their signal |
|
||||
|
||||
> [!NOTE]
|
||||
> **Wio Tracker L2:** the **Nodes** app. Filter chips at the top, the sort in
|
||||
> the header, and a map button that shows every node with a position. A
|
||||
> node's card has buttons for the same actions, plus **Add** for a node that
|
||||
> isn't a contact yet and **Delete**.
|
||||
|
||||
## Favourites
|
||||
|
||||
The **Favourites** home page holds six slots for the conversations you open
|
||||
most: a contact, a room or a channel, each with its unread count. **Enter** on
|
||||
an empty slot picks one from Messages; **hold Enter** on a filled slot to
|
||||
replace or remove it. **Pin to dial** in any contact, channel or node menu
|
||||
does the same.
|
||||
|
||||
A pinned slot is not the same as a favourite. **Fav** is the star shared with
|
||||
the phone app: it sorts contacts to the top and drives the "favourites only"
|
||||
filters in Settings › Contacts.
|
||||
|
||||
> [!NOTE]
|
||||
> **Wio Tracker L2:** the first home page. Tap a slot to open it, hold it to
|
||||
> change it.
|
||||
|
||||
## Cleaning up
|
||||
|
||||
Settings › Contacts › **Expire** (7, 30 or 90 days) sets when a contact you
|
||||
haven't heard from counts as inactive, and **Prune now** removes those after
|
||||
showing how many it will delete. Favourites are always kept, and nothing is
|
||||
removed without **Prune now**.
|
||||
|
||||
## Remote admin
|
||||
|
||||
**Tools › Admin** manages a repeater or room server you are an admin on, the
|
||||
same way the phone app does. Pick the node, type its admin password (saved for
|
||||
next time), then choose a field:
|
||||
|
||||
- **System**: name, owner info, admin password.
|
||||
- **Radio**: frequency, bandwidth, spreading factor, coding rate, TX power.
|
||||
- **Routing**: repeat on/off, advert intervals, max hops.
|
||||
- **Actions**: send an advert, sync its clock, reboot, start OTA, or any
|
||||
[CLI command](../cli_commands.md).
|
||||
|
||||
Name and owner info are read from the node first, so you edit the current
|
||||
value. Reboot and OTA ask before sending.
|
||||
|
||||
> [!WARNING]
|
||||
> Admin commands change the remote node. Check the node and the value before
|
||||
> sending.
|
||||
@@ -0,0 +1,116 @@
|
||||
## Build Flags
|
||||
|
||||
[Go back](../README.md)
|
||||
|
||||
Reference for the `-D` build flags a Solo build understands beyond the
|
||||
per-board defaults already set in `solo/<board>/platformio.ini`. Add any of
|
||||
these to your own board's `build_flags` (in `solo/<board>/platformio.ini`) to
|
||||
enable optional hardware you've wired up yourself, or to tune a default.
|
||||
Everything below is a no-op when left unset — adding a flag for hardware that
|
||||
isn't there costs nothing and can't brick a build; the exceptions (pin
|
||||
conflicts, wrong polarity) are called out per flag.
|
||||
|
||||
None of this needs touching to get a board running — see the
|
||||
[environment list](../../../README.md#building) for the flag-free
|
||||
default build for each supported board.
|
||||
|
||||
---
|
||||
|
||||
### Already part of every solo build
|
||||
|
||||
Set once per board in `solo/<board>/platformio.ini`, not usually touched
|
||||
per-user. Listed here so the rest of this page can assume them.
|
||||
|
||||
| Flag | Meaning |
|
||||
| --- | --- |
|
||||
| `FIRMWARE_SOLO_BUILD=1` | Marks the build as Solo (full on-device UI) rather than a companion-only firmware. |
|
||||
| `DUAL_SERIAL=1` | The companion app can attach over BLE *or* USB serial, whichever it finds; BLE wins if both are live. Every solo build sets this — Solo standardises on one build per board rather than splitting BLE-only / USB-only variants. |
|
||||
| `MAX_CONTACTS=<n>` | Size of the contact table. Default is 32 if unset; solo builds set 350. |
|
||||
| `MAX_GROUP_CHANNELS=<n>` | Size of the channel list. Required for channel support to compile in at all — not optional the way the rest of this page is. |
|
||||
| `OFFLINE_QUEUE_SIZE=<n>` | How many messages queue for later delivery while the phone app is disconnected. Default 16; solo builds set 256. |
|
||||
| `UI_SENSORS_PAGE=1` | Enables the on-device sensors dashboard page. |
|
||||
| `BLE_PIN_CODE=<n>` | See below — not a plain fixed value in practice. |
|
||||
| `DISPLAY_CLASS=<Class>` | Selects the display driver (e.g. `SSD1306Display`, `GxEPDDisplay`, `ST7789Display` — see `src/helpers/ui/` for the full set). Fixed by whatever panel the board actually has; only relevant if you're wiring on a *different* display than stock, in which case the matching driver's `.cpp` also needs adding to `build_src_filter`. |
|
||||
|
||||
**`BLE_PIN_CODE`** has a special case baked in: if it's left at the literal
|
||||
value `123456` *and* the board has a display, pairing uses a random 6-digit
|
||||
PIN generated fresh each session and shown on-device, rather than a fixed one.
|
||||
Any other numeric value is used as a static PIN instead. Leaving the flag out
|
||||
entirely disables the PIN prompt (BLE pairing is unauthenticated).
|
||||
|
||||
---
|
||||
|
||||
### Input hardware add-ons
|
||||
|
||||
Each of these assumes you're wiring something up yourself — check pins are
|
||||
actually free on your board first (see the board's `variant.h` and its
|
||||
existing `solo/<board>/platformio.ini` for what's already claimed).
|
||||
|
||||
| Flag | Adds |
|
||||
| --- | --- |
|
||||
| `ENV_PIN_SDA` / `ENV_PIN_SCL` | CardKB (M5Stack I2C keyboard, addr `0x5F`) on a second I2C bus (resolves to `Wire1`). Probed at boot — harmless with nothing plugged in. See [External Keyboard & Joystick](../hardware.md#external-keyboard-and-joystick). |
|
||||
| `CARDKB_I2C=<Wire\|Wire1>` | Same CardKB support, naming the bus directly — for a board with no free pins for a second bus, set `CARDKB_I2C=Wire` to share the primary bus (already used by the display/RTC) instead of defining `ENV_PIN_SDA`/`ENV_PIN_SCL`. Takes precedence if both are somehow set. |
|
||||
| `UI_HAS_JOYSTICK=1` + `UI_HAS_JOYSTICK_UPDOWN=1` (optional) + `JOYSTICK_UP` / `JOYSTICK_DOWN` / `JOYSTICK_LEFT` / `JOYSTICK_RIGHT` + `PIN_USER_BTN` + `PIN_BACK_BTN` | A wired joystick (four direction contacts + a press contact for Enter). Replaces single-button navigation entirely once enabled. See [External Keyboard & Joystick](../hardware.md#external-keyboard-and-joystick). |
|
||||
| `PIN_GPIO1` .. `PIN_GPIO4` | Up to four general-purpose pins, each independently switchable between Off / Input / Output (GPIO1/GPIO2 also get an Analog step, if wired to an ADC-capable pin) from Tools › GPIO, and via the `!gpio1`..`!gpio4` bot commands. Not restricted to any particular board — works anywhere the pins are actually free. See [Tools › GPIO](../tools.md#gpio). |
|
||||
| `PIN_HALL_SENSOR` + `HALL_ACTIVE_HIGH=1` (optional) | A Hall-effect or reed sensor for a magnetic flip cover: closing locks and blanks the screen instantly, opening unlocks and wakes it, no combo either way. `HALL_ACTIVE_HIGH` is only for a module wired to pull the pin high (rather than low) when the magnet is near. See [Screen lock › Magnetic cover](../lock.md#magnetic-cover). |
|
||||
| `PIN_GPS_SWITCH` | A physical on/off switch for GPS power, read alongside the software GPS toggle — the Tools screen shows `gps off(hw)` / `gps off(sw)` when the two disagree, instead of silently trusting one over the other. |
|
||||
|
||||
---
|
||||
|
||||
### Output / feedback hardware
|
||||
|
||||
| Flag | Adds |
|
||||
| --- | --- |
|
||||
| `PIN_BUZZER` (+ `PIN_BUZZER_EN`, optional) | A passive piezo buzzer, driven by direct PWM (nRF52) or the `NonBlockingRtttl` library (everything else) for RTTTL ringtones and UI beeps. `PIN_BUZZER_EN` is an optional enable line some boards wire separately from the signal pin. |
|
||||
| `PIN_VIBRATION` | A vibration motor for haptic notification, via `GenericVibration`. |
|
||||
|
||||
---
|
||||
|
||||
### GPS
|
||||
|
||||
| Flag | Meaning |
|
||||
| --- | --- |
|
||||
| `ENV_INCLUDE_GPS=1` | Compiles in GPS support (NMEA parsing, location provider) at all. |
|
||||
| `GPS_BAUD_RATE=<n>` | Baud rate for the GPS module's serial link (`Serial1`). Match your module's default. |
|
||||
|
||||
`PIN_GPS_SWITCH` (above) is independent of both — it's a hardware kill switch
|
||||
layered on top of GPS support, not a requirement for it.
|
||||
|
||||
---
|
||||
|
||||
### Display & battery tuning
|
||||
|
||||
| Flag | Meaning |
|
||||
| --- | --- |
|
||||
| `OLED_MISC_FIXED_FONT=1` | Pulls in a full Latin/Greek/Cyrillic 6×9 fixed font (~14 KB flash) so typed text in those alphabets renders as itself instead of block placeholders. Worth it on any board with a keyboard; skip it on space-constrained builds without one. |
|
||||
| `DISPLAY_ROTATION=<0-3>` | Rotates the panel in 90° steps, for a board mounted sideways or upside down. |
|
||||
| `ENABLE_SCREENSHOT` | Lets Solo Tools read the framebuffer over USB to capture a screenshot. |
|
||||
| `KEEP_DISPLAY_ON_USB` | Refreshes the auto-off deadline continuously while externally (USB) powered, so the auto-off timer only starts counting once power is actually removed. Off by default because OLED panels burn in quickly with a permanently-lit screen — only worth enabling for an LCD/e-ink target, or a display you don't mind replacing. |
|
||||
| `AUTO_OFF_MILLIS=<ms>` | How long the display stays on before auto-off. Default 15000 (15s); `0` disables auto-off entirely. |
|
||||
| `UI_RECENT_LIST_SIZE=<n>` | How many entries the recent-activity lists show before scrolling. Default 4. |
|
||||
|
||||
---
|
||||
|
||||
### External PA / TX power curve
|
||||
|
||||
For a board with an always-on external power amplifier after the SX1262. Both
|
||||
flags are needed together; leave them unset on a bare-SX1262 board.
|
||||
|
||||
| Flag | Meaning |
|
||||
| --- | --- |
|
||||
| `LORA_TX_POWER=<dBm>` / `MAX_LORA_TX_POWER=<dBm>` | Default and ceiling for the TX power the app/CLI may request. The SX1262 alone tops out at 22; a board with a PA sets these to what the PA really delivers (the GAT562 30S sets 30). |
|
||||
| `NUM_PA_POINTS=<n>` + `TX_GAIN_LORA=<g0,g1,…>` | The PA's measured gain in dB for each SX1262 register setting `0 … n-1`. Requesting *X* dBm picks the lowest register setting whose `setting + gain` reaches *X*, clamped at the last entry once the PA saturates — so the reported power matches the radiated one. A request below the PA's floor gain still radiates at that floor, and the value stored/reported back is the power actually applied. |
|
||||
|
||||
The GAT562 30S uses the vendor-measured 869 MHz curve from
|
||||
[meshtastic/firmware#11212](https://github.com/meshtastic/firmware/pull/11212).
|
||||
Note the on-device **Settings › Radio › TX Pwr** row is still capped at 22 dBm;
|
||||
values above that are set from the phone app or the CLI.
|
||||
|
||||
---
|
||||
|
||||
### Misc
|
||||
|
||||
| Flag | Meaning |
|
||||
| --- | --- |
|
||||
| `ADVERT_NAME='"name"'` | Sets the default node name baked into a fresh device, instead of the hex of the first 4 bytes of its public key. Note the doubled quoting — it's a C string literal passed through a build flag. |
|
||||
| `MESHCORE_VERSION='"x.y"'` | Overrides the upstream MeshCore base-version string shown in diagnostics, for boards whose port hasn't been rebased onto the latest yet. Cosmetic only — doesn't change protocol behaviour. |
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,420 @@
|
||||
# Solo UI framework — a guide for adding features
|
||||
|
||||
[Go back](../README.md)
|
||||
|
||||
This is a developer guide to the reusable building blocks behind the
|
||||
`companion_radio` **solo** firmware UI (the `ui-new` screens). It is not a
|
||||
user manual — for what each screen *does*, see the [documentation](../README.md).
|
||||
The goal here is so that adding a new screen or feature means *wiring together
|
||||
existing helpers*, not reinventing list scrolling, text wrapping, or persistence.
|
||||
|
||||
Everything below lives under `examples/companion_radio/` unless a path says
|
||||
otherwise. The screen fragments (`ui-new/*.h`) are all `#include`d, in order,
|
||||
into one translation unit (`ui-new/UITask.cpp`) — so a `static inline` helper in
|
||||
an earlier header is visible to later ones. Header-include order in `UITask.cpp`
|
||||
therefore matters; new screens go near the others.
|
||||
|
||||
> **Single-TU only.** These fragments compile *only* as part of `UITask.cpp`.
|
||||
> Some define external-linkage symbols at file scope (e.g.
|
||||
> `NearbyScreen::FILTER_LABELS`), so including a fragment from a second `.cpp`
|
||||
> is a duplicate-symbol link error. Anything genuinely shared across TUs must
|
||||
> live in a real header (`icons.h`, `GeoUtils.h`, `DisplayDriver.h`), not a
|
||||
> screen fragment.
|
||||
|
||||
---
|
||||
|
||||
## 1. The screen model
|
||||
|
||||
Every screen implements `UIScreen` (`src/helpers/ui/UIScreen.h`):
|
||||
|
||||
```cpp
|
||||
class UIScreen {
|
||||
public:
|
||||
virtual int render(DisplayDriver& display) = 0; // returns ms until the next render
|
||||
virtual bool handleInput(char c) { return false; }
|
||||
virtual void poll() { }
|
||||
virtual void onShow() { } // reset per-visit state
|
||||
};
|
||||
```
|
||||
|
||||
- **`render()`** draws one frame and returns *how long until it wants to be
|
||||
drawn again*, in milliseconds. Return a big number (`2000`) for a static
|
||||
screen, a small one (`50`–`200`) while something animates or a popup is open.
|
||||
This return value is the main lever for the e-ink cost/latency trade-off — see
|
||||
§9. `UITask` owns `startFrame()`/`endFrame()`; **`render()` must not call them.**
|
||||
- **`handleInput(c)`** gets one key (`KEY_*`, see §7). Return `true` if consumed.
|
||||
- **`poll()`** runs every loop tick regardless of focus — rare, for background
|
||||
housekeeping (e.g. the shutdown button).
|
||||
- **`onShow()`** is called by `setCurrScreen()` every time the screen becomes
|
||||
current — override it to reset per-visit state (`_sel = 0`, `_dirty = false`,
|
||||
sub-views). Default no-op for screens that keep state across visits. Because
|
||||
it's invoked centrally, a navigator can't forget to reset on show.
|
||||
|
||||
### Wiring a screen into UITask
|
||||
|
||||
1. Add a `UIScreen* my_screen;` member in `UITask.h` (near the others).
|
||||
2. Construct it in `UITask::begin()` (`UITask.cpp`):
|
||||
`my_screen = new MyScreen(this, …);`
|
||||
3. Add a navigator — usually just the one line (the cast-free `onShow()` runs
|
||||
inside `setCurrScreen`):
|
||||
```cpp
|
||||
void UITask::gotoMyScreen() { setCurrScreen(my_screen); }
|
||||
```
|
||||
Only screens needing a *parameter* at entry add a typed call after it (e.g.
|
||||
`gotoRingtoneEditor` → `selectSlot(slot)`, `gotoMapScreen` → `showMapView()`).
|
||||
4. Reach it from somewhere — usually a row in `ToolsScreen.h` (add an `Action`
|
||||
enum value, a row in the right section table, and a `dispatch()` case).
|
||||
|
||||
That's the whole contract. Steps 1, 3 and 4 are compiler-checked (a mismatch
|
||||
won't link); only a forgotten step 2 can slip through — every screen pointer is
|
||||
nullptr-initialised in `UITask.h` and `setCurrScreen()` bails on null, so a
|
||||
missed `new` is an inert no-op rather than a null deref.
|
||||
|
||||
The constructor takes `UITask* task` plus whatever it needs (`NodePrefs*`,
|
||||
`KeyboardWidget*`, …); the task back-pointer is how a screen calls shared
|
||||
services (`_task->showAlert(...)`, `_task->waypoints()`, …).
|
||||
|
||||
---
|
||||
|
||||
## 2. Layout metrics & text (DisplayDriver)
|
||||
|
||||
`DisplayDriver` (`src/helpers/ui/DisplayDriver.h`) abstracts OLED vs e-ink and,
|
||||
crucially, font scale: landscape e-ink renders text at 2×, so **never hard-code
|
||||
pixel sizes** — derive everything from these:
|
||||
|
||||
| Call | Meaning |
|
||||
| --- | --- |
|
||||
| `getLineHeight()` | pixel rows per text line (8 at 1×, 16 at 2×) |
|
||||
| `lineStep()` | row pitch = line height + gap; use for row `y` stepping |
|
||||
| `getCharWidth()` / `getTextWidth(s)` | advance width; `getTextWidth` is font-accurate |
|
||||
| `headerH()` / `listStart()` | title-bar height / first content row `y` |
|
||||
| `listVisible(itemH)` | how many rows fit below the header |
|
||||
| `valCol()` | conventional x for a right-hand value column |
|
||||
| `width()` / `height()` | panel size in px |
|
||||
| `isEink()` | true only on landscape e-ink; branch on this, not on pixel counts |
|
||||
|
||||
Drawing helpers (all clip/measure for you):
|
||||
|
||||
- `drawCenteredHeader(title, menu_hint=false, menu_open=false)` — plain centered
|
||||
title + separator.
|
||||
- `drawInvertedHeader(label, menu_hint=false, menu_open=false)` — filled title
|
||||
bar (used by detail views).
|
||||
- Both take an optional `menu_hint`: pass `true` on a screen with a Hold-Enter
|
||||
context menu to reserve a `≡` glyph (`menuHintWidth()`/`drawContextMenuHint()`)
|
||||
in the header, so the menu is discoverable without already knowing the
|
||||
shortcut; `menu_open` highlights it while the menu is actually up.
|
||||
- `drawSelectionRow(x, y, w, h, sel)` — the highlight bar behind a list row.
|
||||
- `drawTextEllipsized(x, y, max_w, str, selected=false)` — truncates with `…`;
|
||||
**use this for any user string** (names, labels) so long/UTF-8 text can't
|
||||
overrun. Pass `selected=true` for the currently-selected row and the text
|
||||
that doesn't fit marquee-scrolls into view (pause → scroll to the end →
|
||||
pause → scroll back), instead of just sitting behind the ellipsis; returns
|
||||
the ms until the next redraw is needed for that animation to stay smooth
|
||||
(0 when nothing is scrolling) — thread it into your screen's own `render()`
|
||||
return value the same way you already clamp for anything else that needs a
|
||||
faster redraw. Only one row UI-wide marquees at a time (whichever is
|
||||
currently selected), so there's no risk of two animations racing each
|
||||
other for the shared timing state.
|
||||
- `drawTextCentered(mid_x, y, str)`.
|
||||
- `translateUTF8ToBlocks(dst, src, n)` — map UTF-8 to the panel's glyph set for
|
||||
*display only*. Never run text through it before sending it over the air or
|
||||
storing it (it is lossy) — see the reply-prefix note in §5.
|
||||
|
||||
---
|
||||
|
||||
## 3. Lists — `drawList`
|
||||
|
||||
`drawList` (`ui-new/icons.h`) is the workhorse for any scrolling list. It
|
||||
computes the visible window from font metrics, keeps `sel` in view, reserves the
|
||||
scrollbar column, draws each visible row through your callback, and draws the
|
||||
indicator:
|
||||
|
||||
```cpp
|
||||
drawList(display, count, _sel, _scroll, [&](int idx, int y, bool sel, int reserve) {
|
||||
drawRowSelection(display, y, sel, reserve); // canonical highlight bar
|
||||
display.drawTextEllipsized(2, y, display.width() - reserve - 4, items[idx].name);
|
||||
});
|
||||
```
|
||||
|
||||
`reserve` is the width the scrollbar took (0 when the list fits) — subtract it
|
||||
from any right-aligned content so nothing slides under the indicator. The row
|
||||
callback owns its own selection bar: `drawRowSelection(d, y, sel, reserve)`
|
||||
(`ui-new/icons.h`) draws the standard one (full row minus reserve, one pixel
|
||||
short); call `display.drawSelectionRow()` directly only when a row needs a
|
||||
non-standard geometry (full-width, custom height). For the
|
||||
fold-in-place pattern (sections that expand/collapse) use `AccordionList`
|
||||
instead (`ui-new/AccordionList.h`) — same idea, two callbacks (header + item).
|
||||
|
||||
Standalone scroll indicators (`drawScrollIndicator`, `…Px`) and the reserve
|
||||
calculator (`scrollIndicatorReserve`) are exposed for hand-laid lists.
|
||||
|
||||
---
|
||||
|
||||
## 4. Reusable components
|
||||
|
||||
| Component | Header | Use for |
|
||||
| --- | --- | --- |
|
||||
| `PopupMenu` | `PopupMenu.h` | a modal action menu over any screen |
|
||||
| `AccordionList` | `AccordionList.h` | collapsible sectioned lists (Tools, Settings) |
|
||||
| `KeyboardWidget` | `KeyboardWidget.h` | on-screen text entry |
|
||||
| `DigitEditor` | `DigitEditor.h` | scroll-edit one number, digit by digit |
|
||||
| `FullscreenMsgView` | `FullscreenMsgView.h` | scrollable full-message reader + word wrap |
|
||||
| `NavView` | `NavView.h` | bearing/distance/ETA "navigate to a point" view |
|
||||
|
||||
All follow the same shape: a `begin(...)` to open, an `active` flag, a
|
||||
`handleInput(c)` returning a small `Result` enum, and a `render()`/`draw()`.
|
||||
Typical embedding:
|
||||
|
||||
```cpp
|
||||
if (_menu.active) { // popup eats input while open
|
||||
auto r = _menu.handleInput(c);
|
||||
if (r == PopupMenu::SELECTED) runAction(_menu.selectedIndex());
|
||||
return true;
|
||||
}
|
||||
...
|
||||
_menu.begin("Options", 6); // open it
|
||||
_menu.addItem("Navigate"); _menu.addItem("Ping");
|
||||
_menu.active = true;
|
||||
```
|
||||
|
||||
`KeyboardWidget` additionally supports **placeholders** (`{loc}`, `{time}`,
|
||||
sensor tokens) via `addPlaceholder()` / `clearPlaceholders()`; the shared
|
||||
`kbAddSensorPlaceholders()` (`ui-new/SensorPlaceholders.h`) adds only the tokens
|
||||
the board's sensors actually provide. Expand them with `expandMsg()` at send time.
|
||||
|
||||
Two layouts share every grid: **ABC** (one key per letter) and **T9**
|
||||
(phone-keypad multi-tap — repeated Enter within `KB_T9_TIMEOUT_MS` cycles a
|
||||
cell's letter group, then its digit). Page 0 is no longer hardcoded to Latin:
|
||||
`NodePrefs::keyboard_main_alphabet`/`keyboard_alt_alphabet` (Settings >
|
||||
Keyboard's Main/Additional rows) each pick a script — Latin, Cyrillic, or
|
||||
Greek — for page 0 and page 1 respectively (`KeyboardWidget::mainScript()`/
|
||||
`altScript()`); equal values collapse to a single script + Symbols (2 pages
|
||||
instead of 3, see `hasAltAlphabet()`). `scriptCellStr()`/`scriptT9GroupStr()`
|
||||
dispatch each script to its own ABC grid (`KB_CHARS`/`KB_CYRILLIC_CHARS`/
|
||||
`KB_GREEK_CHARS`) and T9 group table (`KB_T9_GROUPS`/`KB_T9_GROUPS_CYRILLIC`/
|
||||
`_GREEK`) so the two layouts always offer the same letters regardless of which
|
||||
page they're on. Latin-diacritic letters (Polish, Czech, German, etc.) aren't
|
||||
alt-alphabet pages — they're reached by Hold-Enter on whichever page currently
|
||||
shows Latin instead (see `KB_ACCENT_VARIANTS` below).
|
||||
Shift is one-shot by default (capitalises the next letter, including whichever
|
||||
candidate a T9 multi-tap cycle settles on) or Hold-Enter to toggle caps-lock;
|
||||
Hold-Clear erases the whole field. **UP from the top letter row** enters
|
||||
**cursor mode** (LEFT/RIGHT move the insertion point; UP/DOWN jump to
|
||||
start/end, then — pressed again once already at that boundary — continue on
|
||||
to the special row / letter grid, the same destinations the plain grid wrap
|
||||
used to reach directly) so edits/inserts can target any point in the typed
|
||||
text, not just the end; Enter/Cancel exit immediately from anywhere.
|
||||
Hold-Enter on a Latin-page letter cell with accented variants instead opens
|
||||
the **accent popup**: one horizontal row of `KB_ACCENT_VARIANTS[group]`
|
||||
(a UTF-8 string per base letter, same shape as a T9 group string), LEFT/RIGHT
|
||||
to pick, Enter to insert via the shared `insertGlyph()` helper, Cancel to
|
||||
dismiss. Holding a letter with no variants, or any T9/alt-alphabet/symbols
|
||||
cell, is a no-op.
|
||||
|
||||
`FullscreenMsgView::wrapLines()` is a standalone pixel-accurate word-wrapper
|
||||
(O(n), variable-width-font aware) reusable by any multi-line layout; it writes
|
||||
into the shared `s_wrap_trans` / `s_wrap_lines` scratch (single-threaded render,
|
||||
never held across a yield — see §9).
|
||||
|
||||
---
|
||||
|
||||
## 5. Domain helpers
|
||||
|
||||
**Geo (`GeoUtils.h`, namespace `geo`, all pure/header-inline):**
|
||||
|
||||
- `haversineKm(lat1,lon1,lat2,lon2)`, `bearingDeg(...)`, `bearingCardinal(deg)`.
|
||||
- `fmtDist(buf,n,km,imperial)` — "850m"/"2.3km" or feet/miles.
|
||||
- `fmtAgeShort(buf,n,now,ts)` — compact "12s"/"5m"/"3h"/"2d" tag, "" for unknown.
|
||||
**This is the one age formatter** — don't reimplement the s/m/h ladder.
|
||||
- `parseLatLon(text, lat, lon, label?, n?)` — pull a `lat,lon` out of message
|
||||
text; reads the `[WAY]` label if tagged.
|
||||
- `parseLocShare(text, lat, lon)` — true only for an explicit `[LOC]` share.
|
||||
|
||||
Coordinates are **int32 degrees × 1e6** everywhere (GPS, contacts, trail,
|
||||
prefs). The message tags are `LOCATION_MSG_TAG` (`[LOC]`, the sender's own live
|
||||
position) and `WAYPOINT_MSG_TAG` (`[WAY]`, a saved point to share); both stay
|
||||
human-readable on clients that don't know them.
|
||||
|
||||
**State stores:** `TrailStore` (`Trail.h`, GPS breadcrumb ring + GPX export),
|
||||
`LiveTrackStore` (`LiveTrack.h`, RAM table of others' `[LOC]` positions, expiring),
|
||||
`WaypointStore` (`Waypoint.h`, persisted saved points). Reach them via the task
|
||||
(`_task->trail()`, `_task->liveTrack()`, `_task->waypoints()`).
|
||||
|
||||
**Message reply prefix:** `msgReplyBody(text, nick?, n?)` (`FullscreenMsgView.h`)
|
||||
parses a leading `@[nick] ` reply marker, returning the body and optionally the
|
||||
addressee. Use it instead of re-scanning for `@[`. The stored/sent prefix is raw
|
||||
UTF-8 (it goes over the air) — never transliterate it.
|
||||
|
||||
---
|
||||
|
||||
## 6. Mini-icons & the status bar
|
||||
|
||||
Small glyphs are authored as ASCII art and packed at compile time
|
||||
(`ui-new/icons.h`):
|
||||
|
||||
```cpp
|
||||
MINI_ICON(ICON_FOO, 5,
|
||||
packRow("..#.."),
|
||||
packRow(".###."),
|
||||
packRow("#####"));
|
||||
```
|
||||
|
||||
Draw with `miniIconDraw(display, x, topY, ICON_FOO)` (auto-scaled & centered),
|
||||
`miniIconDrawTop` (exact placement), or the boxed/slot variants
|
||||
(`drawBoxedIcon` = lit when active, `drawSlotIcon` = plain). Bigger page glyphs
|
||||
use `BIG_ICON` / `bigIconDraw`. The home status bar composes these right-to-left
|
||||
with a `blinkOn()` cadence for "leave it on and forget" broadcasts (auto-advert,
|
||||
Live Share, trail, repeater) — follow that pattern when adding an indicator:
|
||||
always shown on e-ink, blinking on OLED.
|
||||
|
||||
Icons are drawn from a fixed priority-ordered table (`HomeScreen::renderBatteryIndicator()`,
|
||||
`UITask.cpp`); once the row runs out of horizontal space the loop just stops,
|
||||
so the lowest-priority icons silently drop first rather than the whole bar
|
||||
crushing the node name. A blinking icon still reserves its width on the
|
||||
off-phase of its blink, so the row's layout can't visibly shift width as icons
|
||||
blink in and out.
|
||||
|
||||
Screens with a Hold-Enter context menu (Nodes, Bot, Admin, Diagnostics, …) pass
|
||||
`menu_hint=true` to their header call (see §2) so a `≡` glyph advertises the
|
||||
menu; `KEY_CONTEXT_MENU` (Hold-Enter) opens it.
|
||||
|
||||
---
|
||||
|
||||
## 7. Input
|
||||
|
||||
Keys arrive as the `KEY_*` codes in `UIScreen.h`: `KEY_UP/DOWN/LEFT/RIGHT`,
|
||||
`KEY_ENTER`, `KEY_CANCEL`, `KEY_CONTEXT_MENU` (the "Hold Enter" menu key).
|
||||
|
||||
Use the **prev/next convention** for value changes so the rotary encoder and the
|
||||
D-pad agree: `keyIsPrev(c)` (LEFT or encoder-prev) and `keyIsNext(c)` (RIGHT or
|
||||
encoder-next). `KEY_CANCEL` and `KEY_CONTEXT_MENU` stay screen-specific. Joystick
|
||||
rotation is handled upstream (`rotateJoystickKey`) — screens see already-rotated
|
||||
keys.
|
||||
|
||||
### From hardware to `handleInput`
|
||||
|
||||
Each physical button is a `MomentaryButton` (`src/helpers/ui/MomentaryButton.h`);
|
||||
`UITask::begin()` must call `begin()` on **every** one (the joystick directions
|
||||
and Back included, not just the user button) — that sets `pinMode` and, where
|
||||
enabled, claims the interrupt. `UITask::loop()` polls each button, maps its event
|
||||
to a `KEY_*` code, and dispatches it to the current screen.
|
||||
|
||||
Two mechanisms keep input responsive when **`endFrame()` blocks the loop** for a
|
||||
slow e-ink refresh:
|
||||
|
||||
- **IRQ edge capture** (`-D BUTTON_USE_INTERRUPTS`, e-ink boards). A GPIO
|
||||
interrupt latches each press/release edge into a per-button ring buffer with
|
||||
its own timestamp, so taps that land *during* a refresh aren't lost; `check()`
|
||||
replays them afterwards. The nRF52 has only 8 GPIOTE channels (the radio takes
|
||||
one) — if none is free a button silently falls back to polling, so it still
|
||||
works, just without mid-refresh capture.
|
||||
- **Key queue + coalesced redraw.** `loop()` drains *all* pending events from the
|
||||
buttons into a small key FIFO, applies the whole burst (`handleInput` per key),
|
||||
then redraws **once**. So three joystick flicks captured during one refresh move
|
||||
the selection three steps for the cost of a single panel update, instead of
|
||||
collapsing into an ignored multi-click or one-step-per-refresh. Buttons created
|
||||
with `multiclick=false` therefore emit one discrete `CLICK` per release;
|
||||
`multiclick=true` buttons still report double/triple-click.
|
||||
|
||||
---
|
||||
|
||||
## 8. Persistence
|
||||
|
||||
Device settings live in one `NodePrefs` struct (`NodePrefs.h`), saved via
|
||||
`the_mesh.savePrefs()` and loaded by `DataStore.cpp`. Rules when adding a field:
|
||||
|
||||
- **Append only**, and bump `NodePrefs::SCHEMA_SENTINEL`. Serialization is
|
||||
binary-positional, so order is the on-disk format; never insert in the middle.
|
||||
- Add a matching `rd(...)` in `DataStore::loadPrefsInt()` and a `file.write(...)`
|
||||
in `savePrefs()`, in the same position, and **clamp on load** (an upgrader's
|
||||
file lacks the field and reads stray bytes — clamp to a sane default). Saves
|
||||
are atomic (temp-file + rename), so a crash mid-save can't corrupt settings.
|
||||
|
||||
**The `_dirty` convention:** a multi-field editor screen mutates `_node_prefs`
|
||||
live for instant feedback but only persists once, on exit, gated by a `_dirty`
|
||||
flag — so LEFT/RIGHT value-cycling doesn't thrash flash. Set `_dirty = true` at
|
||||
each edit site, then on the exit path call `_task->savePrefsIfDirty(_dirty)`
|
||||
(`UITask`) — it saves once *iff* dirty and clears the flag, so every screen's
|
||||
save-on-exit reads the same and the "did we touch flash?" decision lives in one
|
||||
place. A one-shot action from a popup (no exit hook) calls `the_mesh.savePrefs()`
|
||||
immediately. Follow whichever matches your screen.
|
||||
|
||||
**The shared "active target"** (Locator/Nav destination) is set through
|
||||
`UITask::setTarget()` (defines it), `setTargetNow()` (defines + saves + toast),
|
||||
or `clearTarget()` — one definition used by the Locator screen, the map, and the
|
||||
Nearby/Waypoints "Set as target" actions. Resolve a person's current position
|
||||
with `resolvePersonPos()` (live `[LOC]` share, else last-advertised fix).
|
||||
|
||||
---
|
||||
|
||||
## 9. Conventions & gotchas
|
||||
|
||||
- **Single-threaded render.** Rendering and input run on one thread, so shared
|
||||
static scratch (`s_wrap_*`) is safe *as long as it's never held across a
|
||||
yield*. Don't add scratch that outlives one `render()`.
|
||||
- **e-ink blocks.** `endFrame()` on e-ink stalls the main loop for hundreds of
|
||||
ms. Keep `render()`'s return value honest so the panel isn't redrawn more than
|
||||
needed, and don't depend on `loop()` cadence for timing that must be exact
|
||||
(the ringtone player moved to a hardware timer for this reason).
|
||||
- **Reference cleanup.** Anything that remembers a contact by pubkey (favourite
|
||||
slot, Locator/Live-Share target, per-contact mute/melody) or a channel by
|
||||
index must drop that reference when the entity goes away. The UI Core does
|
||||
it for every frontend: `UiCore::cleanupContactPrefs()` (ui-core/UiCore.h) and
|
||||
`chanctl::onRemoved()` (ui-core/ChannelControl.h). New per-contact or
|
||||
per-channel state should clear there too.
|
||||
- **Toasts:** `_task->showAlert("msg", duration_ms)` overlays a transient banner
|
||||
over any screen; no redraw plumbing needed.
|
||||
- **Strings:** always `strncpy`+NUL or `snprintf`; treat every name/label as
|
||||
untrusted-length and render through `drawTextEllipsized`.
|
||||
|
||||
---
|
||||
|
||||
## 10. Worked example — a new Tools screen
|
||||
|
||||
```cpp
|
||||
// ui-new/MyToolScreen.h — included by UITask.cpp near the other screens
|
||||
#pragma once
|
||||
#include "icons.h" // drawList + mini-icons
|
||||
#include "../NodePrefs.h"
|
||||
|
||||
class MyToolScreen : public UIScreen {
|
||||
UITask* _task;
|
||||
NodePrefs* _prefs;
|
||||
int _sel = 0, _scroll = 0;
|
||||
bool _dirty = false;
|
||||
static const int ROWS = 3;
|
||||
public:
|
||||
MyToolScreen(UITask* t, NodePrefs* p) : _task(t), _prefs(p) {}
|
||||
void onShow() override { _sel = 0; _scroll = 0; _dirty = false; }
|
||||
|
||||
int render(DisplayDriver& d) override {
|
||||
d.setTextSize(1);
|
||||
d.drawCenteredHeader("MY TOOL");
|
||||
drawList(d, ROWS, _sel, _scroll, [&](int i, int y, bool sel, int reserve) {
|
||||
drawRowSelection(d, y, sel, reserve);
|
||||
d.setCursor(4, y);
|
||||
d.print(i == 0 ? "Alpha" : i == 1 ? "Bravo" : "Charlie");
|
||||
});
|
||||
return 500;
|
||||
}
|
||||
|
||||
bool handleInput(char c) override {
|
||||
if (c == KEY_CANCEL) {
|
||||
_task->savePrefsIfDirty(_dirty); // saves once iff dirty, then clears
|
||||
_task->gotoToolsScreen();
|
||||
return true;
|
||||
}
|
||||
if (c == KEY_UP) { _sel = (_sel + ROWS - 1) % ROWS; return true; }
|
||||
if (c == KEY_DOWN) { _sel = (_sel + 1) % ROWS; return true; }
|
||||
if (keyIsPrev(c) || keyIsNext(c) || c == KEY_ENTER) {
|
||||
/* mutate _prefs…, set _dirty = true */ return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
Then: `#include "MyToolScreen.h"` in `UITask.cpp`, add the member + constructor +
|
||||
`gotoMyToolScreen()` (§1), and add a row in `ToolsScreen.h`. Done — scrolling,
|
||||
the scrollbar, font scaling, e-ink pacing and persistence batching all come from
|
||||
the framework.
|
||||
@@ -0,0 +1,66 @@
|
||||
# Getting started
|
||||
|
||||
## Controls
|
||||
|
||||
On joystick devices:
|
||||
|
||||
| Input | Does |
|
||||
| ----- | ---- |
|
||||
| **Up / Down** | Move through a list |
|
||||
| **Left / Right** | Switch home pages; change the value of a setting |
|
||||
| **Enter** | Open, select, confirm |
|
||||
| **Hold Enter** | Options for the selected item (a message, contact, channel…) |
|
||||
| **Back** | One step back; from a screen, back to the home screen |
|
||||
|
||||
Lists wrap around at both ends. Any key wakes a dark screen without acting on it.
|
||||
|
||||
Keyboard devices use the same controls, from the arrow keys (or their Fn
|
||||
combinations), Enter and Esc. [Hardware](./hardware.md) has the key maps.
|
||||
|
||||
> [!NOTE]
|
||||
> **Wio Tracker L2:** tap to open, hold for options, swipe sideways between
|
||||
> pages. The top button turns the screen off and on. The side button goes
|
||||
> back to the home screen; hold it and let go to mute or unmute the sound.
|
||||
> Holding the side button while pressing the top one takes a screenshot.
|
||||
|
||||
## Home screen
|
||||
|
||||
The home screen is a row of pages. On joystick devices, **Left / Right** steps
|
||||
through them and **Enter** opens the one shown:
|
||||
|
||||
Clock, Recent, Radio, Bluetooth, Advert, GPS, Sensors, Tools, Shutdown,
|
||||
Settings, Messages, Favourites, Map.
|
||||
|
||||
Settings › Home Pages sets their order and hides the ones you don't use;
|
||||
Settings and Messages are always shown.
|
||||
|
||||
> [!NOTE]
|
||||
> **Wio Tracker L2:** pages you swipe between: favourite chats, the clock
|
||||
> (where it starts), a minimap, then the apps, six to a page. Hold an app to
|
||||
> arrange or hide them.
|
||||
|
||||
## Connecting the phone app
|
||||
|
||||
Every build serves the MeshCore app over **Bluetooth and USB**, one at a time:
|
||||
while Bluetooth is connected, USB is ignored. To use USB, disconnect Bluetooth
|
||||
first or turn it off on the device.
|
||||
|
||||
The Bluetooth pairing PIN is shown on the Bluetooth home page until the phone
|
||||
is paired.
|
||||
|
||||
> [!NOTE]
|
||||
> **Wio Tracker L2:** the PIN is under Settings › Bluetooth.
|
||||
|
||||
Everything you do on the device and in the app stays in sync: contacts,
|
||||
channels and messages are the same data.
|
||||
|
||||
## Updating
|
||||
|
||||
Download the new file from the [releases page](https://github.com/MarekZegare4/MeshCore-Solo/releases)
|
||||
and flash it the way you did the first time (see the [main README](../../README.md#flashing)).
|
||||
Settings, contacts and messages are kept.
|
||||
|
||||
> [!NOTE]
|
||||
> **Wio Tracker L2:** Settings › Firmware update checks GitHub for a newer
|
||||
> release and installs it over WiFi. Set up a network under Settings › WiFi
|
||||
> first.
|
||||
@@ -0,0 +1,99 @@
|
||||
# Hardware
|
||||
|
||||
## External keyboard and joystick
|
||||
|
||||
Two optional add-ons, detected at boot; a build with them enabled works the
|
||||
same with nothing plugged in.
|
||||
|
||||
| Device | CardKB | Wired joystick |
|
||||
| ------ | :----: | :------------: |
|
||||
| Wio Tracker L1 (OLED / e-ink) | Grove connector | built in |
|
||||
| GAT562 30S Mesh Kit | — | built in |
|
||||
| Heltec V3 / V4 | soldered (below) | soldered (below) |
|
||||
| ProMicro | on the main I2C bus (required: it has no buttons) | — |
|
||||
| Cardputer ADV, T-Echo Lite + KeyShield | built-in keyboard instead | — |
|
||||
|
||||
### CardKB
|
||||
|
||||
An M5Stack CardKB types straight into any text field. It sends plain
|
||||
characters, so the keyboard alphabet settings don't apply to it.
|
||||
|
||||
| Key | Does |
|
||||
| --- | ---- |
|
||||
| Arrows, Enter, Esc | Same as the joystick, Enter and Back |
|
||||
| Backspace | Deletes before the cursor |
|
||||
| **Fn+Enter** | Submits the field |
|
||||
| **Fn+letter** | Accents for that letter (Fn+A → á à ä…) |
|
||||
| **Tab** | Hold Enter (options menus) |
|
||||
| **Fn+Esc** | Locks / unlocks the screen |
|
||||
|
||||
Settings › Keyboard › **Ext. KB**: **Full** keeps the on-screen grid, so the
|
||||
CardKB and the joystick can be mixed; **Compact** hides it, the arrows move the
|
||||
text cursor and Enter submits. Compact needs no joystick at all.
|
||||
|
||||
### Wired joystick
|
||||
|
||||
Four direction contacts and a press contact (Enter), each shorted to ground
|
||||
when pressed; the firmware enables the pull-ups, so no resistors are needed.
|
||||
The board's own button becomes Back. Settings › Display › **Joystick
|
||||
rotation** turns the directions for a stick mounted sideways.
|
||||
|
||||
### Wiring on the Heltec V3 / V4
|
||||
|
||||
Neither board has a joystick or a keyboard connector, so both are soldered to
|
||||
free pins. V3 and V4 use the same pins (confirmed on a V4).
|
||||
|
||||
| Function | GPIO |
|
||||
| -------- | :--: |
|
||||
| CardKB SDA / SCL | 3 / 4 (second I2C bus, not the display's) |
|
||||
| Joystick up / down / left / right | 23 / 6 / 47 / 48 |
|
||||
| Joystick press (Enter) | 33 |
|
||||
| Back | 0 (the PRG button, nothing to wire) |
|
||||
|
||||
The pins are set in [`solo/heltec_v3/platformio.ini`](../../solo/heltec_v3/platformio.ini)
|
||||
and [`solo/heltec_v4/platformio.ini`](../../solo/heltec_v4/platformio.ini).
|
||||
For a CardKB-only build, comment out the joystick block there and use
|
||||
Ext. KB = Compact.
|
||||
|
||||
### Built-in keyboards
|
||||
|
||||
The Cardputer ADV has a QWERTY keyboard, the T-Echo Lite a T9 keypad on its
|
||||
KeyShield add-on (without it the board has no usable input). Their keymaps
|
||||
are in each board's driver under `variants/`; they don't follow the CardKB's
|
||||
Fn shortcuts.
|
||||
|
||||
## E-ink
|
||||
|
||||
The e-ink Wio Tracker L1 (250 × 122) has a few settings of its own in
|
||||
Settings › Display:
|
||||
|
||||
- **Rotation**: landscape or portrait, applied at once; every screen reflows.
|
||||
- **Joystick rotation**, independent of the display's.
|
||||
- **Full refresh**: how many partial updates between full ones, against
|
||||
ghosting.
|
||||
|
||||
Clock seconds are hidden by default, and live timers refresh coarsely, to
|
||||
spare the panel.
|
||||
|
||||
## Wio Tracker L2
|
||||
|
||||
- **Buttons**: the top one turns the screen off and on; the side one goes
|
||||
home, and held and let go mutes the sound. Side + top takes a screenshot.
|
||||
Holding the side button in the first seconds after power-on starts the CLI
|
||||
rescue on USB serial.
|
||||
- **SD card**: holds the message history, maps, GPX trails and live map tiles.
|
||||
Settings › Storage shows what takes the space, how many messages each
|
||||
conversation keeps, and deletes the history.
|
||||
- **USB drive**: plugged into a computer, the device asks whether to only
|
||||
charge or to lend the computer the SD card. While lent, the device can't use
|
||||
the card; eject it on the computer and the device restarts. With a screen
|
||||
PIN set, it doesn't ask until the screen is unlocked.
|
||||
- **WiFi**: used only for map downloads, live tiles and updates, and off the
|
||||
rest of the time. Settings › WiFi saves several networks and joins the
|
||||
strongest; the WiFi switch in Settings forbids it entirely.
|
||||
|
||||
## Build flags
|
||||
|
||||
Extra hardware (a buzzer, a vibration motor, a Hall sensor for a magnetic
|
||||
cover, GPIO, an external PA) is enabled with build flags in your own
|
||||
`solo/<board>/platformio.ini`; see [Build flags](./developer/build-flags.md).
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 3.3 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 34 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.2 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.5 KiB |
@@ -0,0 +1,45 @@
|
||||
# Screen lock
|
||||
|
||||
The lock keeps pocket presses from doing anything. It locks the screen only:
|
||||
messages still arrive, alarms still ring and the phone app still connects.
|
||||
|
||||
## Locking and unlocking
|
||||
|
||||
**Hold Back and press Enter three times** within 3 seconds; the same locks
|
||||
and unlocks. A hint on the lock screen counts the presses. With a CardKB,
|
||||
**Fn+Esc** does it in one press.
|
||||
|
||||
**Settings › Display › Lock screen** locks the device whenever the screen
|
||||
turns off by itself.
|
||||
|
||||
The lock screen shows the time, the date and the first two of the Clock
|
||||
page's fields, but not the device's name.
|
||||
|
||||
> [!NOTE]
|
||||
> **Wio Tracker L2:** with **Lock screen** on (Settings › Display), waking
|
||||
> the screen shows a clock card with **slide to unlock**. Settings › Display
|
||||
> › **Tap to wake** decides whether a tap wakes it or only the top button.
|
||||
|
||||
## PIN
|
||||
|
||||
**Settings › Display › Lock PIN** adds a PIN to unlocking, asked also right
|
||||
after power-up.
|
||||
|
||||
- Enter on the row opens a number pad. Type the PIN (at least 4 characters),
|
||||
confirm, then type it again. The pad's keyboard key switches to letters.
|
||||
- A wrong PIN shows how many tries are left; after 5 in a row, entry pauses
|
||||
for 30 seconds.
|
||||
- Enter on the row again removes the PIN.
|
||||
|
||||
The PIN is stored as a salted hash, never as the PIN itself.
|
||||
|
||||
> [!NOTE]
|
||||
> **Wio Tracker L2:** **Settings › Display › Screen PIN**, digits only. With a PIN, the lock card comes up every time the screen wakes, and
|
||||
> the SD card isn't offered as a USB drive until it's unlocked.
|
||||
|
||||
## Magnetic cover
|
||||
|
||||
A Hall or reed sensor wired to a free pin (`PIN_HALL_SENSOR`, see
|
||||
[Build flags](./developer/build-flags.md)) locks and blanks the screen when a
|
||||
magnetic cover closes and unlocks it when it opens (asking for the PIN, if
|
||||
one is set).
|
||||
@@ -0,0 +1,117 @@
|
||||
# Messages
|
||||
|
||||
Messages holds three lists: **Channels**, **Direct** messages and **Rooms**.
|
||||
Unread counts show on each conversation, on the list and on the home screen.
|
||||
|
||||
> [!NOTE]
|
||||
> **Wio Tracker L2:** one screen with all three as sections; tap a section
|
||||
> title to fold it. **New** in the header starts a conversation with any
|
||||
> contact.
|
||||
|
||||
## Reading
|
||||
|
||||
Open a conversation to see its history as chat bubbles: yours on the right,
|
||||
received ones on the left, newest at the bottom. Each bubble shows the sender,
|
||||
its age and a small hop count: on a received message, how many repeaters it
|
||||
came through; on your own, how many repeaters were heard passing it on.
|
||||
|
||||
**Enter** on a message opens it full screen; **Left / Right** there pages to the
|
||||
older and newer one.
|
||||
|
||||
**Hold Enter** on a message for its options:
|
||||
|
||||
- **Reply**: starts a message addressed to the sender (`@[name]`).
|
||||
- **Navigate**, **Save waypoint**, **Set as target**: when the message
|
||||
contains a location (see [Navigation](./navigation.md)).
|
||||
- **Path** on a received message: the repeaters it came through.
|
||||
**Relayed by** on your own channel post: the repeaters heard repeating it.
|
||||
|
||||
> [!NOTE]
|
||||
> **Wio Tracker L2:** hold a bubble. The card shows when it was sent, the hop
|
||||
> count, the path as a diagram, and **Reply** / **Set target**.
|
||||
|
||||
### How much is kept
|
||||
|
||||
The device keeps the newest 48 channel messages and 32 direct messages, all
|
||||
conversations together. When a busy conversation pushes out messages you
|
||||
haven't read, its unread count gets a **+** (for example `48+`).
|
||||
|
||||
> [!NOTE]
|
||||
> **Wio Tracker L2:** 256 channel and 128 direct messages in memory, and with
|
||||
> an SD card every conversation is also saved to the card (100 to 2000
|
||||
> messages each, Settings › Storage). The history survives a reboot and
|
||||
> scrolls back through everything on the card.
|
||||
|
||||
## Writing
|
||||
|
||||
Open a conversation and choose **[+ send]** (or Enter at the bottom of the
|
||||
history). Pick a quick message or **Custom message** for the keyboard.
|
||||
|
||||
- **Quick messages**: ten of your own, edited in Settings › Messages.
|
||||
- **Placeholders** fill in live data when the message is sent:
|
||||
`{loc}` (your GPS position), `{time}`, `{batt}`, and the readings of any
|
||||
sensor the device has: `{temp}`, `{hum}`, `{pres}`, `{alt}`, `{lux}`,
|
||||
`{dist}`, `{co2}`.
|
||||
|
||||
> [!NOTE]
|
||||
> **Wio Tracker L2:** the compose bar sits under the conversation; its **+**
|
||||
> opens quick messages and placeholders. Quick messages are edited in
|
||||
> Settings › Messages & contacts.
|
||||
|
||||
### The keyboard
|
||||
|
||||
The on-screen keyboard is a letter grid (or a phone-style T9 keypad, Settings ›
|
||||
Keyboard › Layout). Up from the top row moves the cursor through the text.
|
||||
|
||||
Accented letters don't need a language setting: **hold Enter** on the base
|
||||
letter and pick from its variants, for example `a` → `á à â ä å ą…`, `z` →
|
||||
`ź ż ž`. This covers Polish, Czech, German, French, Spanish, Nordic and the
|
||||
other European languages written in Latin.
|
||||
|
||||
Settings › Keyboard picks two scripts, **Main** and **Additional**, from Latin,
|
||||
Cyrillic and Greek; the keyboard's **#@/abc** key cycles between them and
|
||||
symbols. Cyrillic includes the Ukrainian, Belarusian and Serbian letters
|
||||
(under the key they sit on). Received messages in any of these scripts display
|
||||
correctly whatever the keyboard is set to.
|
||||
|
||||
> [!NOTE]
|
||||
> **Wio Tracker L2:** a phone-style keyboard. Hold a key for its accents; the
|
||||
> globe key switches between the two alphabets.
|
||||
|
||||
## Channels
|
||||
|
||||
**Hold Enter** on a channel for its options:
|
||||
|
||||
| Option | Does |
|
||||
| ------ | ---- |
|
||||
| Mark all read | Clears its unread count |
|
||||
| Notif, Melody | Its own notification and sound, instead of the global ones |
|
||||
| Fav | Marks it as a favourite (shared with the app) |
|
||||
| Scope | The region its messages are tagged with (`*` = none); the list is in Settings › Radio › Scope |
|
||||
| Pin to dial | Puts it on the Favourites page |
|
||||
| Edit, Delete | Renames it or changes its secret; removes it |
|
||||
|
||||
**+ Add channel** at the end of the list adds:
|
||||
|
||||
- **Public**: the default public channel, if you deleted it.
|
||||
- **Hashtag**: a topic such as `#hiking`. Anyone who types the same topic
|
||||
joins the same channel.
|
||||
- **Private**: a name and a secret, typed as a passphrase or as the 32-digit
|
||||
hex key (the format of channel QR codes).
|
||||
|
||||
> [!NOTE]
|
||||
> **Wio Tracker L2:** hold a channel, or tap ⚙ in an open channel, for its
|
||||
> options.
|
||||
|
||||
## Direct messages
|
||||
|
||||
**Hold Enter** on a contact: Mark as read, Notif, Melody, Fav, Pin to dial.
|
||||
**Fav** is the star shared with the app and sorts favourites to the top;
|
||||
**Pin to dial** only puts the contact on the Favourites page.
|
||||
|
||||
## Rooms
|
||||
|
||||
Opening a room logs in first. You type the password once (empty if the room
|
||||
has none); it's saved on the device, including passwords set from the app,
|
||||
so the next time it logs in without asking. A wrong password is forgotten and
|
||||
you're asked again. **Hold Enter** on a room offers **Login…** and **Logout**.
|
||||
@@ -0,0 +1,125 @@
|
||||
# Navigation
|
||||
|
||||
Everything here works from the device's own GPS; no magnetometer or extra
|
||||
hardware is needed. Distances and speeds follow Settings › System › Units
|
||||
(metric or imperial).
|
||||
|
||||
## Trail
|
||||
|
||||
**Tools › Trail** records your route in the background while you use the rest
|
||||
of the device (a blinking **G** in the status bar). Straight stretches are
|
||||
stored as their two ends, so the 512 points cover a long route. **Left / Right**
|
||||
switches between the **Summary** (distance, time, speed or pace), the **Map**
|
||||
(your route, waypoints, people sharing their position, the target) and the
|
||||
point **List**.
|
||||
|
||||
**Hold Enter** for the trail menu: start / stop, mark a waypoint, the waypoint
|
||||
list, **Track back** (retrace the route to where it started), share your
|
||||
position, the trail file, and the trail settings:
|
||||
|
||||
- **Min dist**: how far apart points are recorded.
|
||||
- **Auto-pause**: pauses the trail after you stop for 1–5 minutes and resumes
|
||||
when you move, so breaks don't count.
|
||||
- **Mark avg**: averages the GPS for 5–30 seconds when marking a waypoint.
|
||||
- **Auto-save**: saves the trail when the device powers off, including a
|
||||
low-battery shutdown.
|
||||
|
||||
The trail lives in memory: save it (Trail file › Save) or turn on Auto-save
|
||||
to keep it through a reboot.
|
||||
|
||||
### GPX export
|
||||
|
||||
Connect USB, open [Solo Tools](https://marekzegare4.github.io/Solo-tools/) in
|
||||
Chrome or Edge and click **Connect device**, then on the device choose
|
||||
**Trail file › Export**. The GPX includes your waypoints. Without a browser,
|
||||
`uv run tools/trail_export.py` does the same. Disconnect the phone app first if
|
||||
it's connected over USB.
|
||||
|
||||
> [!NOTE]
|
||||
> **Wio Tracker L2:** the trail holds 4096 points and is drawn on the map.
|
||||
> Its controls (start / stop, save, load, track back, reset) are in the map's
|
||||
> **Map tools**, the settings in Settings › Map. **GPX** writes the trail to
|
||||
> the SD card; take it off with the card as a USB drive
|
||||
> ([Hardware](./hardware.md)).
|
||||
|
||||
## Waypoints
|
||||
|
||||
A waypoint is a saved spot (the car, a camp, water) with a short label, up to
|
||||
16 of them, kept through reboots. Add one with **Mark here** at your position,
|
||||
or **+ Add by coords** to type coordinates. The list starts with **Trail
|
||||
start**, so you can always go back to where the trail began.
|
||||
|
||||
**Hold Enter** on a waypoint: Rename, Delete, **Send** (as a message to a
|
||||
contact or channel) and **Set as target**.
|
||||
|
||||
> [!NOTE]
|
||||
> **Wio Tracker L2:** up to 64 waypoints. The pin button on the map opens the
|
||||
> list; hold a spot on the map to make it a waypoint or the target.
|
||||
|
||||
## Navigating to something
|
||||
|
||||
**Navigate** on a waypoint, a node in Nearby or a location in a message opens
|
||||
the navigation view: the distance, the bearing **To** the target and your own
|
||||
heading (**Hdg**), with the time to arrival once you're getting closer. Turn
|
||||
until the two bearings match. The heading comes from your movement, so it
|
||||
shows `--` while you stand still.
|
||||
|
||||
**Tools › Compass** shows the same heading as a scrolling tape with the
|
||||
degrees and direction.
|
||||
|
||||
> [!NOTE]
|
||||
> **Wio Tracker L2:** the target is drawn on the map with a line to it, and a
|
||||
> bar with distance, bearing, heading and time to arrival. The Compass app is
|
||||
> a turning dial.
|
||||
|
||||
## Sharing locations
|
||||
|
||||
- **Once**: **Share my pos** in the trail menu sends your position to a
|
||||
contact or channel you pick.
|
||||
- **A waypoint**: **Send** in the waypoint menu.
|
||||
- **Live**: **Tools › Live Share** sends your position while you move, to one
|
||||
contact or channel, for 1 to 12 hours. It only sends after you've moved
|
||||
(50–500 m) and never more often than you set; an optional heartbeat repeats
|
||||
it while you stand still.
|
||||
|
||||
With **Track loc** on in Live Share, other people's shares show on the map
|
||||
and in Nearby, with live distance and bearing.
|
||||
|
||||
Locations travel as ordinary text (`[LOC]lat,lon`, or `[WAY]lat,lon label`
|
||||
for a waypoint), readable in the phone app and on other firmware. On the
|
||||
receiving side, **hold Enter** on the message to navigate to it, save it or
|
||||
set it as the target.
|
||||
|
||||
## Locator
|
||||
|
||||
**Tools › Locator** alerts you when you cross a circle around a target:
|
||||
arriving at a waypoint, leaving it, or a person coming near or moving away.
|
||||
|
||||
- **Target**: a waypoint (a fixed place) or a contact (follows their live
|
||||
share, else their last known position).
|
||||
- **Radius**: 50 m to 1 km.
|
||||
- **Mode**: alert on arriving, leaving, or both.
|
||||
- **Beeper**: ticks faster as you get closer, even when the sound is muted.
|
||||
|
||||
**Set as target** in Nearby, the waypoint list or a message sets the target
|
||||
in one step. The target shows as a flag on the map.
|
||||
|
||||
## Map
|
||||
|
||||
The **Map** home page shows your position, the trail, waypoints, people
|
||||
sharing their position and the target. **Enter** opens the trail map;
|
||||
**hold Enter** shares your position.
|
||||
|
||||
> [!NOTE]
|
||||
> **Wio Tracker L2:** a real map with offline tiles on the SD card. Drag to
|
||||
> pan, +/− to zoom, the crosshair follows your GPS again.
|
||||
>
|
||||
> - **Download**: frame an area on the map and download its tiles over WiFi.
|
||||
> Downloaded areas can be renamed, refreshed or deleted.
|
||||
> - **Live tiles**: with WiFi on, tiles for where you look are fetched and
|
||||
> cached, up to the size set in Settings › Storage.
|
||||
> - **Vector regions**: whole regions as packs made with
|
||||
> `tools/maps/osm_vector.py`, copied to the card.
|
||||
>
|
||||
> The minimap on the home screen shows your surroundings at a glance; tap it
|
||||
> for the full map. The **Nodes** map shows every node with a position.
|
||||
@@ -0,0 +1,40 @@
|
||||
# Settings
|
||||
|
||||
Settings are saved as you change them and kept through reboots and updates.
|
||||
On joystick devices they're folding sections: **Enter** on a section opens
|
||||
it, **Left / Right** changes a value, **Back** leaves. Only the rows your
|
||||
board supports are shown.
|
||||
|
||||
| Section | What's there |
|
||||
| ------- | ------------ |
|
||||
| **Display** | Brightness, auto-off, lock screen and [PIN](./lock.md), battery as icon / % / volts, clock format and seconds, wake on message; on e-ink also rotation and full refresh |
|
||||
| **Sound** | Buzzer on / off / auto (quiet while the app is connected), volume, quiet hours, the melody for messages, channels and adverts |
|
||||
| **Home Pages** | Order of the home pages, and which are shown |
|
||||
| **Radio** | TX power, preset, frequency, SF / BW / CR, saved presets, Auto pwr, the scope list |
|
||||
| **System** | Device name, time zone, low-battery shutdown, GPS power saving, units, reboot |
|
||||
| **Keyboard** | ABC or T9 layout, the two scripts, CardKB mode |
|
||||
| **Contacts** | Show all or favourites only (DMs, channels, rooms), favourites on top, contact expiry and prune |
|
||||
| **Messages** | Automatic resend of direct messages, the ten quick messages |
|
||||
|
||||
A few notes:
|
||||
|
||||
- **Auto pwr** lowers the TX power on strong links and raises it back on weak
|
||||
ones; the power set above is the ceiling.
|
||||
- **Scope** is a list of named regions (plus `*`, no region). Messages are
|
||||
tagged with a scope so repeaters can tell communities on the same frequency
|
||||
apart. The default one is used for direct messages and repeating, and each
|
||||
channel picks its own (see [Messages](./messages.md#channels)). Anyone who
|
||||
types the same name gets the same scope; it isn't encryption.
|
||||
- **Low battery** shuts the device down at the voltage you choose, which is
|
||||
also 0 % on the battery indicator.
|
||||
|
||||
> [!NOTE]
|
||||
> **Wio Tracker L2:** one list of pages:
|
||||
>
|
||||
> - **Device**: Display (including the lock and **Tap to wake**), Home apps,
|
||||
> Power (battery, GPS), Sound (with the melody editor), Keyboard, Messages
|
||||
> & contacts.
|
||||
> - **Connections**: Radio, Bluetooth, WiFi, GPS.
|
||||
> - **Map & data**: Map (trail, live sharing and arrival alert options),
|
||||
> Storage.
|
||||
> - **System**: Name, Time, Firmware update, About; Reboot and Power off.
|
||||
@@ -0,0 +1,114 @@
|
||||
# Tools
|
||||
|
||||
On joystick devices, the tools are in **Tools**, grouped into Location, Comms
|
||||
and System. The navigation tools are on the [Navigation](./navigation.md)
|
||||
page, Nodes and Admin on [Contacts](./contacts.md).
|
||||
|
||||
> [!NOTE]
|
||||
> **Wio Tracker L2:** the tools are apps on the home screen: Messages, Nodes,
|
||||
> Settings, Compass, Clock, GPS, Bot, Repeater, Admin, Diagnostics. The trail,
|
||||
> live sharing and the locator are on the map (its tools button) and in
|
||||
> Settings › Map.
|
||||
|
||||
## Clock
|
||||
|
||||
The **Clock** home page shows the time, the date and up to three fields you
|
||||
choose (hold Enter): battery, temperature, humidity, pressure, altitude
|
||||
(barometric or GPS), light, CO₂, GPS position, satellites, contacts, unread
|
||||
messages. The time comes from GPS or the phone app; the time zone is in
|
||||
Settings.
|
||||
|
||||
**Enter** on the clock opens the clock tools:
|
||||
|
||||
- **Alarm**: a time, repeating daily, on weekdays, at weekends or once. It
|
||||
rings with the screen off or locked, but not after a shutdown.
|
||||
- **Timer**: a countdown that rings when it reaches zero, whatever screen
|
||||
you're on.
|
||||
- **Stopwatch**.
|
||||
|
||||
Any key silences the ringing; it stops by itself after a minute.
|
||||
|
||||
## Remote bot
|
||||
|
||||
**Tools › Bot** answers messages for you. It watches your direct messages, one
|
||||
channel and one room, each switched on separately:
|
||||
|
||||
- **Trigger and reply**: when a message contains the trigger (several can be
|
||||
separated by commas, `*` matches any message), the bot sends the reply. The
|
||||
reply can use placeholders, plus `{name}` (the sender) and `{hops}`.
|
||||
- **Commands**: messages starting with `!` get live data back:
|
||||
|
||||
| Command | Reply |
|
||||
| ------- | ----- |
|
||||
| `!ping` | `pong` |
|
||||
| `!batt`, `!temp`, `!time`, `!loc` | battery, temperature, time, position |
|
||||
| `!hops` | how many hops the command took |
|
||||
| `!status` | battery, position and time |
|
||||
| `!help` | the list of commands |
|
||||
|
||||
Several in one message get one reply: `!batt !time` → `4.10V | 14:30`.
|
||||
- **Actions** (off by default) let the command change the device: `!buzz`
|
||||
(sounds the buzzer to find it), `!gps on` / `!gps off`, `!gps fix` (turns
|
||||
the GPS on, waits for a good fix and sends it), `!advert`.
|
||||
|
||||
**DM allow = Fav** limits the DM bot to your favourites, and **quiet hours**
|
||||
stop trigger replies at night (commands still answer). Replies are rate
|
||||
limited so two bots can't answer each other forever. The room bot uses the
|
||||
room's saved login.
|
||||
|
||||
## Auto-advert
|
||||
|
||||
**Tools › Auto-advert** sends an advert with your position every 30 seconds
|
||||
to 1 hour, so others see you in their Nearby list. With it on at both ends
|
||||
and Settings › Sound › **AD sound** set, each device beeps when it hears the
|
||||
other: a hands-free "still in range".
|
||||
|
||||
## Repeater
|
||||
|
||||
**Tools › Repeater** makes the device relay other people's packets while it
|
||||
keeps working as a companion. By default it relays on a separate repeater
|
||||
profile (the community's repeater frequency) and switches back when you turn
|
||||
it off; **Network = Current** relays on your own frequency instead.
|
||||
|
||||
Optional filters keep a mobile repeater from adding noise: skip adverts, a
|
||||
maximum hop count, **Yield** (let fixed repeaters go first), a minimum SNR,
|
||||
drop duplicates already relayed by someone else, and relay only your
|
||||
**scopes**. Diagnostics shows how much it forwards.
|
||||
|
||||
## Ringtones
|
||||
|
||||
**Tools › Ringtone** composes two melodies of up to 32 notes (pitch, octave,
|
||||
length, tempo). Use them for notifications in Settings › Sound, or for a
|
||||
single contact or channel from its options.
|
||||
|
||||
> [!NOTE]
|
||||
> **Wio Tracker L2:** the melody editor is under Settings › Sound.
|
||||
|
||||
## Diagnostics
|
||||
|
||||
**Tools › Diagnostics** shows live counters (packets received and sent by
|
||||
type, forwarded, errors), memory, the radio's noise floor and the last
|
||||
packet's signal; a **System** tab with the firmware and radio settings; and a
|
||||
**Font** tab with a sample of every script the font covers. Hold Enter on the
|
||||
live tab to reset the counters.
|
||||
|
||||
> [!NOTE]
|
||||
> **Wio Tracker L2:** the Diagnostics app, with a **Noise** tab that
|
||||
> measures the noise floor over time.
|
||||
|
||||
## GPIO
|
||||
|
||||
*Wio Tracker L1 only.* **Tools › GPIO** sets four spare pins as input,
|
||||
output or (GPIO1–2) analog input, shows their level and switches outputs. The
|
||||
bot's `!gpio1`…`!gpio4` commands read and set the same pins.
|
||||
|
||||
## GPS and sensors
|
||||
|
||||
The **GPS** home page shows the fix and satellites; **Sensors** shows the
|
||||
readings of the sensors the board has. **GPS pwr** in Settings › System turns
|
||||
the GPS off between fixes to save battery; anything that needs your position
|
||||
(the trail, live share, the locator) keeps it on.
|
||||
|
||||
> [!NOTE]
|
||||
> **Wio Tracker L2:** the **GPS** app draws a sky plot of the satellites and
|
||||
> each one's signal strength.
|
||||
Reference in New Issue
Block a user