docs: the app on BT and USB at once, /prefs.json, UIColor, txdelay and agc commands

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Jakub
2026-10-15 14:42:04 +02:00
co-authored by Claude Opus 5.5
parent cdf979dcca
commit ff4ca2832f
4 changed files with 27 additions and 12 deletions
+1 -1
View File
@@ -24,7 +24,7 @@ per-user. Listed here so the rest of this page can assume them.
| Flag | Meaning | | Flag | Meaning |
| --- | --- | | --- | --- |
| `FIRMWARE_SOLO_BUILD=1` | Marks the build as Solo (full on-device UI) rather than a companion-only firmware. | | `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. | | `DUAL_SERIAL=1` | The companion app attaches over BLE and USB serial at once (upstream's `MultiSerialInterface`; the same as `ENABLE_USB_INTERFACE` next to `BLE_PIN_CODE`). 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_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. | | `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. | | `OFFLINE_QUEUE_SIZE=<n>` | How many messages queue for later delivery while the phone app is disconnected. Default 16; solo builds set 256. |
+21 -8
View File
@@ -95,6 +95,13 @@ pixel sizes** — derive everything from these:
| `width()` / `height()` | panel size in px | | `width()` / `height()` | panel size in px |
| `isEink()` | true only on landscape e-ink; branch on this, not on pixel counts | | `isEink()` | true only on landscape e-ink; branch on this, not on pixel counts |
Colours are upstream's `UIColor` roles (`DisplayDriver.h`), each display
driver's `.cpp` filling them with its own values. ui-new draws in two:
`UIColor::window_bkg` (the background: black on an OLED, white on e-ink) and
`UIColor::primary_txt` (the ink). A highlight is a `primary_txt` bar with
`window_bkg` text on it. A new driver defines all nine roles in its `.cpp`
(see `SH1106Display.cpp`), and `setColor()` takes the value as it is.
Drawing helpers (all clip/measure for you): Drawing helpers (all clip/measure for you):
- `drawScreenHeader(d, title, page=-1, pages=0, menu_hint=false, menu_open=false)` - `drawScreenHeader(d, title, page=-1, pages=0, menu_hint=false, menu_open=false)`
@@ -353,15 +360,21 @@ slow e-ink refresh:
Device settings live in one `NodePrefs` struct (`NodePrefs.h`), saved via Device settings live in one `NodePrefs` struct (`NodePrefs.h`), saved via
`the_mesh.savePrefs()` and loaded by `DataStore.cpp`. Rules when adding a field: `the_mesh.savePrefs()` and loaded by `DataStore.cpp`. Rules when adding a field:
- `/prefs` holds the struct as it is in memory, behind a header with a format - `/prefs.json` holds one JSON key per field, written by upstream's
version and a CRC. **Add a new field at the end of the struct**; nothing `ConfigSerializer`. The keys upstream MeshCore has (`radio`, `gps`,
else is needed: an older file is shorter, so the field keeps its default `repeat`, `comp`, `custom`, `wifi`) keep upstream's names; everything
(set in `MyMesh`'s constructor). Solo adds sits under `solo`, grouped by topic (`display`, `sound`, `bot` …).
- Moving, resizing or removing a field shifts the ones after it: bump - **A new field** goes anywhere in `NodePrefsFields` and gets a key in the
`NodePrefs::PREFS_VERSION`, and every device starts again from the matching `…Solo(Section&)` function of `NodePrefs`: `s.def("key", field)`
defaults. for a number or a string, `s.hex("key", field)` for a byte array or a
bit mask. A key is at most 15 characters. A file without the key leaves the
default (set in `MyMesh`'s constructor), and a key no field has any more is
skipped, so fields can be moved, resized and removed freely.
- `test/test_companion_prefs_json` checks that every field comes back byte
for byte: run `pio test -e native` after adding one; a field without a key
fails it.
- Give a field with a range a line in `sanitize()` (`DataStore.cpp`), so a - Give a field with a range a line in `sanitize()` (`DataStore.cpp`), so a
damaged byte reads as the default. Saves are atomic (temp-file + rename), damaged value reads as the default. Saves are atomic (temp-file + rename),
so a crash mid-save can't corrupt settings. so a crash mid-save can't corrupt settings.
**The `_dirty` convention:** a multi-field editor screen mutates `_node_prefs` **The `_dirty` convention:** a multi-field editor screen mutates `_node_prefs`
+3 -3
View File
@@ -91,9 +91,9 @@ connected over USB.
## Connecting the phone app ## Connecting the phone app
Every build serves the MeshCore app over **Bluetooth and USB**, one at a time: Every build serves the MeshCore app over **Bluetooth and USB** at the same
while Bluetooth is connected, USB is ignored. To use USB, disconnect Bluetooth time: the app works on either link, with no need to disconnect the other.
first or turn it off on the device. Turning Bluetooth off on the device leaves USB on.
The Bluetooth pairing PIN is shown on the Bluetooth home page until the phone The Bluetooth pairing PIN is shown on the Bluetooth home page until the phone
is paired. Switching Bluetooth off keeps it off after a restart or is paired. Switching Bluetooth off keeps it off after a restart or
+2
View File
@@ -117,6 +117,8 @@ so its documentation applies:
| `get tx`, `set tx 14` | TX power in dBm | | `get tx`, `set tx 14` | TX power in dBm |
| `get af` / `set af`, `get dutycycle` / `set dutycycle` | Airtime factor, or the same as a duty cycle in % | | `get af` / `set af`, `get dutycycle` / `set dutycycle` | Airtime factor, or the same as a duty cycle in % |
| `rxdelay`, `int.thresh`, `cad`, `radio.rxgain`, `path.hash.mode`, `multi.acks` | The advanced radio options, with `get` / `set` | | `rxdelay`, `int.thresh`, `cad`, `radio.rxgain`, `path.hash.mode`, `multi.acks` | The advanced radio options, with `get` / `set` |
| `txdelay`, `direct.txdelay` | How long a repeated packet waits, as a share of its airtime (0–2; 0.5 and 0.2 by default), with `get` / `set` |
| `agc.reset.interval` | Seconds between radio gain resets (in steps of 4, `0` off), with `get` / `set` |
| `get name`, `set name …` | The device name | | `get name`, `set name …` | The device name |
| `get pin`, `set pin 123456` | The Bluetooth PIN, `0` for a random one; used from the next start | | `get pin`, `set pin 123456` | The Bluetooth PIN, `0` for a random one; used from the next start |
| `get tz.offset`, `set tz.offset 2` | Time zone, in hours from UTC | | `get tz.offset`, `set tz.offset 2` | Time zone, in hours from UTC |