diff --git a/.github/workflows/build-observer-firmwares.yml b/.github/workflows/build-observer-firmwares.yml
index 3c13bf0c..1671010c 100644
--- a/.github/workflows/build-observer-firmwares.yml
+++ b/.github/workflows/build-observer-firmwares.yml
@@ -10,7 +10,7 @@ on:
- mqtt-bridge-implementation-flex
# Only rebuild firmware when something that affects the binaries changes.
# Docs, the changelog, the changelog generator, CI files, and repo-meta
- # files do not change firmware output — docs/changelog are handled by
+ # files do not change firmware output - docs/changelog are handled by
# sync-flasher-content.yml instead. NOTE: a push is skipped only if EVERY
# changed file matches a pattern below; one stray unlisted file (e.g. an
# earlier .gitignore edit) triggers a full build, so keep meta files listed.
@@ -181,7 +181,7 @@ jobs:
fi
# Upload this build, overwriting same-named assets. Exclude the .partsig
- # files — those are consumed locally by the slim-manifest generator below,
+ # files - those are consumed locally by the slim-manifest generator below,
# not published as release assets (and they'd break the hash-based prune).
gh release upload "$RELEASE_TAG" $(find out -maxdepth 1 -type f ! -name '*.partsig') --clobber
@@ -285,7 +285,7 @@ jobs:
run: |
# Stage everything: update-firmware.py edits config.json in place, but the
# slim manifests (v/*.json) and the build counter can be NEW files, which
- # `commit -am` would miss — so add -A and check the staged diff.
+ # `commit -am` would miss - so add -A and check the staged diff.
git add -A
if git diff --cached --quiet; then
echo "No flasher changes to commit."
diff --git a/.github/workflows/build-observer-plus-firmwares.yml b/.github/workflows/build-observer-plus-firmwares.yml
index 438f6f62..93005098 100644
--- a/.github/workflows/build-observer-plus-firmwares.yml
+++ b/.github/workflows/build-observer-plus-firmwares.yml
@@ -1,7 +1,7 @@
name: Build MQTT Observer+ Firmwares (keymind channel)
# ============================================================================
-# STATUS: STAGED BUT NOT ENABLED — manual dispatch only.
+# STATUS: STAGED BUT NOT ENABLED - manual dispatch only.
#
# This is the experimental "plus" channel (mqtt-observer-plus = flex +
# mcarper/keymindCascade). It mirrors build-observer-firmwares.yml with the
@@ -14,13 +14,13 @@ name: Build MQTT Observer+ Firmwares (keymind channel)
# - device pull-OTA: OTA_MANIFEST_BASE overridden to .../v-plus so
# `ota update` on a plus node can never "update" it back onto the stable
# channel (the per-env platformio.ini bakes .../v; the env-injected flag
-# below wins because PLATFORMIO_BUILD_FLAGS append after ini flags —
+# below wins because PLATFORMIO_BUILD_FLAGS append after ini flags -
# verified by building and checking the embedded URL string)
#
# NOTE ON DISPATCH: GitHub does not index a workflow_dispatch-only workflow
# that exists only on a non-default branch and has never run, so this workflow
# will not appear in the Actions UI / `gh workflow list` (and cannot be
-# dispatched) until its push trigger fires once. That is intentional here —
+# dispatched) until its push trigger fires once. That is intentional here -
# "staged, not enabled" means nothing can fire it yet. Uncommenting the push
# trigger below and pushing both registers it and runs the first build; from
# then on `gh workflow run build-observer-plus-firmwares.yml --ref
@@ -28,7 +28,7 @@ name: Build MQTT Observer+ Firmwares (keymind channel)
#
# TO ENABLE THE CHANNEL:
# 1. Uncomment the `push:` trigger below.
-# 2. Flip SYNC_FLASHER to "true" — but FIRST the flasher repo
+# 2. Flip SYNC_FLASHER to "true" - but FIRST the flasher repo
# (agessaman/flasher.meshcore.io) needs channel support; until it does,
# the sync step below fails closed on purpose so the stable config.json
# cannot be clobbered. Flasher-side prerequisites:
@@ -37,11 +37,11 @@ name: Build MQTT Observer+ Firmwares (keymind channel)
# - SPA: a channel picker (Observer / Observer+ experimental)
# - gen-slim-manifests.py output dir flasher/v-plus (arg already
# parameterized; wired below)
-# - a plus changelog target (CHANGELOG-plus.md) — the stable
+# - a plus changelog target (CHANGELOG-plus.md) - the stable
# CHANGELOG.md tracks the flex branch only
#
# Until SYNC_FLASHER is enabled, the per-base build counter has no writer, so
-# every manual dispatch publishes as build .1 — fine for hand-flashed testing
+# every manual dispatch publishes as build .1 - fine for hand-flashed testing
# off the release page.
# ============================================================================
@@ -78,7 +78,7 @@ env:
# Channel-tagged version: embedded in firmware filenames and (with the
# -observer tag + build number appended by build.sh) in `ver`/MQTT/SNMP.
FIRMWARE_VERSION: v1.16.0-plus
- # Rolling release tag for this channel — distinct from observer-mqtt-latest.
+ # Rolling release tag for this channel - distinct from observer-mqtt-latest.
RELEASE_TAG: observer-mqtt-plus-latest
# Channel-isolated pull-OTA manifest base baked into these binaries
# (overrides the .../v default from the per-env platformio.ini flags).
@@ -88,7 +88,7 @@ env:
jobs:
- # Discover the *_observer_mqtt envs and shard them across runners — same
+ # Discover the *_observer_mqtt envs and shard them across runners - same
# env set as the stable channel; only version/flags/tag differ.
enumerate:
runs-on: ubuntu-latest
@@ -179,7 +179,7 @@ jobs:
steps:
- name: Clone Repo
uses: actions/checkout@v4
- # Shallow on purpose — see the stable workflow's note: build.sh named
+ # Shallow on purpose - see the stable workflow's note: build.sh named
# the assets from a shallow clone's 7-char short hash.
- name: Download All Shard Artifacts
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f6cbc39b..43f51dea 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,227 +1,227 @@
-# MeshCore Observer — Changelog
+# MeshCore Observer - Changelog
Changes to the MQTT observer / bridge work that powers the firmware offered on
[flasher.meshcore.io](https://flasher.meshcore.io). Newest changes are at the top.
-**Legend:** **New** = new capability · **Fix** = bug fix · **Improvement** = enhancement to
-existing behavior · **Internal** = refactor / under-the-hood · **Docs** = documentation ·
-**Build** / **CI** = build system & automation. **⬆ Upstream sync** marks a merge of the
+**Legend:** **New** = new capability * **Fix** = bug fix * **Improvement** = enhancement to
+existing behavior * **Internal** = refactor / under-the-hood * **Docs** = documentation *
+**Build** / **CI** = build system & automation. **[UP] Upstream sync** marks a merge of the
upstream MeshCore `dev` branch, which generally pulls in a new MeshCore software version.
### June 2026
-- **New** — AlertReporter integration in MyMesh for Room Server 2026-06-17 · `985fda13`
-- **Improvement** — Cumulative packet statistics (`packets_sent` / `packets_received`) added to the status message 2026-06-16 · `8bf590b1`
-- **Improvement** — Packet path now published as an array of lowercase hex hop tokens 2026-06-16 · `e80a5ded`
-- **CI** — Sync documentation files to the flasher site from the build workflow 2026-06-16 · `0844eee1`
-- **CI** — Reworked the GitHub Actions workflow for rolling releases 2026-06-08 · `8fa38707`
-- **New** — Radio Watchdog feature for the MQTT observer 2026-06-08 · `002e8d4a`
-- **Improvement** — Firmware build-date format and UTC timestamps (always emit `+00:00`) 2026-06-06 · `b45373a3`
-- **CI** — Build observer firmwares across 14 shards (up from 4) 2026-06-06 · `c29ede05`
-- **CI** — Cache PlatformIO toolchains in CI 2026-06-06 · `6723776f`
-- **CI** — Automated firmware builds via GitHub Actions 2026-06-06 · `981ed246`
-- ⬆ **Upstream sync** — Synced with upstream MeshCore dev (brings MeshCore v1.16.0) 2026-06-06 · `dcc8f826`
+- **New** - AlertReporter integration in MyMesh for Room Server 2026-06-17 * `985fda13`
+- **Improvement** - Cumulative packet statistics (`packets_sent` / `packets_received`) added to the status message 2026-06-16 * `8bf590b1`
+- **Improvement** - Packet path now published as an array of lowercase hex hop tokens 2026-06-16 * `e80a5ded`
+- **CI** - Sync documentation files to the flasher site from the build workflow 2026-06-16 * `0844eee1`
+- **CI** - Reworked the GitHub Actions workflow for rolling releases 2026-06-08 * `8fa38707`
+- **New** - Radio Watchdog feature for the MQTT observer 2026-06-08 * `002e8d4a`
+- **Improvement** - Firmware build-date format and UTC timestamps (always emit `+00:00`) 2026-06-06 * `b45373a3`
+- **CI** - Build observer firmwares across 14 shards (up from 4) 2026-06-06 * `c29ede05`
+- **CI** - Cache PlatformIO toolchains in CI 2026-06-06 * `6723776f`
+- **CI** - Automated firmware builds via GitHub Actions 2026-06-06 * `981ed246`
+- [UP] **Upstream sync** - Synced with upstream MeshCore dev (brings MeshCore v1.16.0) 2026-06-06 * `dcc8f826`
### May 2026
-- **New** — NZ Analyzer MQTT preset 2026-05-25 · `c0c845f5`
-- **Internal** — Region definition helpers; docs refresh 2026-05-21 · `1296aa77`
-- **Docs** — Clarified WiFi configuration instructions (password optional for open networks) 2026-05-21 · `38568eb1`
-- **Improvement** — Alert PSK access now restricted by sender timestamp 2026-05-17 · `f717d673`
-- **Docs** — Trimmed the fault-alert section in the implementation guide 2026-05-17 · `c41805d0`
-- **Change** — Renamed CLI command `region bulk` → `region def` 2026-05-16 · `f3c6c348`
-- **Internal** — Timestamp handling in MQTTMessageBuilder 2026-05-15 · `5a004f37`
-- **New** — T-LoRa V2.1–1.6 observer configurations 2026-05-13 · `3cde9c10`
-- **Fix** · `mqtt` — Hardened the T-LoRa MQTT observer and trimmed MQTT task stack usage 2026-05-13 · `daf9ee47`
-- **Internal** — Alert PSK handling is now hex-only 2026-05-12 · `a8668840`
-- **Improvement** — Alert PSK accepts both base64 and hex 2026-05-12 · `a865d0a3`
-- **New** · `cli` — Bulk region hierarchy command 2026-05-12 · `19f95001`
-- **Improvement** — Path hash size included in AlertReporter flood messages 2026-05-11 · `6a3ed5d4`
-- **New** — T-Beam 1W observer support and partition handling 2026-05-11 · `fc99d6b3`
-- **Improvement** — Fault alerts gained region-based scoping and banned channels 2026-05-11 · `16dc49fa`
-- **New** — Fault alerts for WiFi and MQTT disconnections 2026-05-10 · `80355f32`
-- **New** — ColoradoMesh MQTT preset 2026-05-10 · `08847342`
-- **New** — MQTT origin handling and a string utility 2026-05-09 · `b9f478c9`
-- **Improvement** — Repeat status included in the MQTT status message 2026-05-09 · `303cf8c2`
-- **Internal** — MQTT origin now uses effective-origin logic 2026-05-09 · `ff031f48`
-- ⬆ **Upstream sync** — Synced with upstream MeshCore dev 2026-05-09 · `b37db668`
-- **New** · `mqtt` — T-LoRa V2.1-1.6 observer environments 2026-05-02 · `78e1500a`
+- **New** - NZ Analyzer MQTT preset 2026-05-25 * `c0c845f5`
+- **Internal** - Region definition helpers; docs refresh 2026-05-21 * `1296aa77`
+- **Docs** - Clarified WiFi configuration instructions (password optional for open networks) 2026-05-21 * `38568eb1`
+- **Improvement** - Alert PSK access now restricted by sender timestamp 2026-05-17 * `f717d673`
+- **Docs** - Trimmed the fault-alert section in the implementation guide 2026-05-17 * `c41805d0`
+- **Change** - Renamed CLI command `region bulk` -> `region def` 2026-05-16 * `f3c6c348`
+- **Internal** - Timestamp handling in MQTTMessageBuilder 2026-05-15 * `5a004f37`
+- **New** - T-LoRa V2.1-1.6 observer configurations 2026-05-13 * `3cde9c10`
+- **Fix** * `mqtt` - Hardened the T-LoRa MQTT observer and trimmed MQTT task stack usage 2026-05-13 * `daf9ee47`
+- **Internal** - Alert PSK handling is now hex-only 2026-05-12 * `a8668840`
+- **Improvement** - Alert PSK accepts both base64 and hex 2026-05-12 * `a865d0a3`
+- **New** * `cli` - Bulk region hierarchy command 2026-05-12 * `19f95001`
+- **Improvement** - Path hash size included in AlertReporter flood messages 2026-05-11 * `6a3ed5d4`
+- **New** - T-Beam 1W observer support and partition handling 2026-05-11 * `fc99d6b3`
+- **Improvement** - Fault alerts gained region-based scoping and banned channels 2026-05-11 * `16dc49fa`
+- **New** - Fault alerts for WiFi and MQTT disconnections 2026-05-10 * `80355f32`
+- **New** - ColoradoMesh MQTT preset 2026-05-10 * `08847342`
+- **New** - MQTT origin handling and a string utility 2026-05-09 * `b9f478c9`
+- **Improvement** - Repeat status included in the MQTT status message 2026-05-09 * `303cf8c2`
+- **Internal** - MQTT origin now uses effective-origin logic 2026-05-09 * `ff031f48`
+- [UP] **Upstream sync** - Synced with upstream MeshCore dev 2026-05-09 * `b37db668`
+- **New** * `mqtt` - T-LoRa V2.1-1.6 observer environments 2026-05-02 * `78e1500a`
### April 2026
-- **Fix** · `mqtt` — Corrected ColoradoMesh preset details 2026-04-30 · `9f8f2d05`
-- **New** · `platformio` — PSRAM support for the TBeam Supreme SX1262 variant 2026-04-28 · `0b05aede`
-- **New** · `mqtt` — ColoradoMesh preset 2026-04-28 · `457edf69`
-- **Fix** · `mqtt` — Added missing MQTTBridge error codes 2026-04-27 · `5a5e5373`
-- **New** · `mqtt` — EastIdahoMesh preset and richer CLI preset listing 2026-04-25 · `753eb72f`
-- **Fix** · `mqtt` — Removed errant `clearLastWill` from PsychicMqttClient 2026-04-25 · `32449e62`
-- **Fix** · `mqtt` — Restored `origin` field position in packet messages 2026-04-25 · `673361b6`
-- **Internal** · `mqtt` — Reorganized packet message structure 2026-04-25 · `8be27e4e`
-- **Internal** · `mqtt` — Default TX packet settings and documentation 2026-04-25 · `bfcf0176`
-- **New** · `platformio` — MQTT observer configs for the Heltec V4 expansion kit 2026-04-24 · `0a4f002f`
-- **New** · `cli` — Radio watchdog command 2026-04-24 · `7b21be3e`
-- **Internal** · `mqtt` — Standardized MQTT preset formatting 2026-04-24 · `6211885c`
-- **Internal** · `heltec_tracker_v2` — Aligned FEM with meshcore-dev KCT8103L 2026-04-24 · `0d280257`
-- **New** · `heltec_tracker_v2` — Restored LoRaFEMControl for GC1109 2026-04-24 · `255fce7f`
-- ⬆ **Upstream sync** — Synced with upstream MeshCore dev 2026-04-24 · `c1989d6f`
-- **New** · `mqtt` — chimesh and meshat.se presets 2026-04-24 · `ee4e39bc`
-- **Fix** · `mqtt` — Restored legacy outbox behavior for QoS0 async publishes 2026-04-23 · `70722a58`
-- **Fix** · `mqtt` — Config marked dirty on setter calls; applied on connect/reconnect 2026-04-23 · `bb67b04e`
-- **Improvement** · `mqtt` — Better QoS handling and retry logic 2026-04-23 · `7d0c5bce`
-- **Internal** · `mqtt` — Removed legacy `mqtt.server`/`port`/`username`/`password` CLI 2026-04-21 · `cfec3a5b`
-- **Fix** · `mqtt` — Legacy CLI aligned with slot prefs; restored `isConfigValid` 2026-04-21 · `ac2662c5`
-- **Internal** — PsychicMqttClient memory management; fixed-size callback arrays (v0.2.2) 2026-04-21 · `e9ff1ae0`
-- **Improvement** — MQTTBridge memory management, deferred logging, and QoS on publish 2026-04-19 · `1ff9437f`
-- ⬆ **Upstream sync** — Synced with upstream MeshCore dev 2026-04-19 · `563d9a7d`
-- **Build** — Disabled MQTT debug config for heltec_v4 builds 2026-04-17 · `0ed29cb7`
-- **Change** — gitignore generated cert files; meshmapper timeout reset to 0 2026-04-17 · `ab121708`
-- **Change** — MeshMapper preset update; removed generated cert files 2026-04-17 · `074ac3e7`
-- **Improvement** — Critical heap monitoring and deferred TLS; hard restart on prolonged low memory 2026-04-17 · `f4a4fd7c`
-- **Internal** — Inline `StaticJsonDocument` allocations to reduce heap fragmentation 2026-04-16 · `ab6685a1`
-- **New** — nashmesh preset 2026-04-14 · `13fbcd91`
-- **Internal** — MQTTMessageBuilder takes an external JsonDocument to cut stack usage 2026-04-14 · `ff2df2b7`
-- **Internal** — Removed the raw-data mutex in favor of core-specific staging 2026-04-14 · `0c190dac`
-- **Improvement** — Radio watchdog avoids false alarms in quiet meshes 2026-04-14 · `961f8293`
-- **New** — Radio diagnostics in MyMesh and CommonCLI 2026-04-10 · `7e4f75c9`
-- **Improvement** — Internal heap stats in status messages 2026-04-06 · `74e45a79`
-- **New** — TennMesh preset (fixed username/password auth) 2026-04-05 · `3b46e9ee`
-- **Docs** — Single `set radio` command in the guide 2026-04-04 · `1c524a93`
-- **Internal** — RX/TX packet logging controlled by MQTT settings (`mqtt_rx_enabled` pref) 2026-04-04 · `47b632aa`
-- **Improvement** — Runtime-configurable MQTT slot limits based on PSRAM 2026-04-04 · `258a0d72`
-- **Improvement** — Disconnect count/time diagnostics; per-variant WiFi TX power 2026-04-02 · `f0962eff`
+- **Fix** * `mqtt` - Corrected ColoradoMesh preset details 2026-04-30 * `9f8f2d05`
+- **New** * `platformio` - PSRAM support for the TBeam Supreme SX1262 variant 2026-04-28 * `0b05aede`
+- **New** * `mqtt` - ColoradoMesh preset 2026-04-28 * `457edf69`
+- **Fix** * `mqtt` - Added missing MQTTBridge error codes 2026-04-27 * `5a5e5373`
+- **New** * `mqtt` - EastIdahoMesh preset and richer CLI preset listing 2026-04-25 * `753eb72f`
+- **Fix** * `mqtt` - Removed errant `clearLastWill` from PsychicMqttClient 2026-04-25 * `32449e62`
+- **Fix** * `mqtt` - Restored `origin` field position in packet messages 2026-04-25 * `673361b6`
+- **Internal** * `mqtt` - Reorganized packet message structure 2026-04-25 * `8be27e4e`
+- **Internal** * `mqtt` - Default TX packet settings and documentation 2026-04-25 * `bfcf0176`
+- **New** * `platformio` - MQTT observer configs for the Heltec V4 expansion kit 2026-04-24 * `0a4f002f`
+- **New** * `cli` - Radio watchdog command 2026-04-24 * `7b21be3e`
+- **Internal** * `mqtt` - Standardized MQTT preset formatting 2026-04-24 * `6211885c`
+- **Internal** * `heltec_tracker_v2` - Aligned FEM with meshcore-dev KCT8103L 2026-04-24 * `0d280257`
+- **New** * `heltec_tracker_v2` - Restored LoRaFEMControl for GC1109 2026-04-24 * `255fce7f`
+- [UP] **Upstream sync** - Synced with upstream MeshCore dev 2026-04-24 * `c1989d6f`
+- **New** * `mqtt` - chimesh and meshat.se presets 2026-04-24 * `ee4e39bc`
+- **Fix** * `mqtt` - Restored legacy outbox behavior for QoS0 async publishes 2026-04-23 * `70722a58`
+- **Fix** * `mqtt` - Config marked dirty on setter calls; applied on connect/reconnect 2026-04-23 * `bb67b04e`
+- **Improvement** * `mqtt` - Better QoS handling and retry logic 2026-04-23 * `7d0c5bce`
+- **Internal** * `mqtt` - Removed legacy `mqtt.server`/`port`/`username`/`password` CLI 2026-04-21 * `cfec3a5b`
+- **Fix** * `mqtt` - Legacy CLI aligned with slot prefs; restored `isConfigValid` 2026-04-21 * `ac2662c5`
+- **Internal** - PsychicMqttClient memory management; fixed-size callback arrays (v0.2.2) 2026-04-21 * `e9ff1ae0`
+- **Improvement** - MQTTBridge memory management, deferred logging, and QoS on publish 2026-04-19 * `1ff9437f`
+- [UP] **Upstream sync** - Synced with upstream MeshCore dev 2026-04-19 * `563d9a7d`
+- **Build** - Disabled MQTT debug config for heltec_v4 builds 2026-04-17 * `0ed29cb7`
+- **Change** - gitignore generated cert files; meshmapper timeout reset to 0 2026-04-17 * `ab121708`
+- **Change** - MeshMapper preset update; removed generated cert files 2026-04-17 * `074ac3e7`
+- **Improvement** - Critical heap monitoring and deferred TLS; hard restart on prolonged low memory 2026-04-17 * `f4a4fd7c`
+- **Internal** - Inline `StaticJsonDocument` allocations to reduce heap fragmentation 2026-04-16 * `ab6685a1`
+- **New** - nashmesh preset 2026-04-14 * `13fbcd91`
+- **Internal** - MQTTMessageBuilder takes an external JsonDocument to cut stack usage 2026-04-14 * `ff2df2b7`
+- **Internal** - Removed the raw-data mutex in favor of core-specific staging 2026-04-14 * `0c190dac`
+- **Improvement** - Radio watchdog avoids false alarms in quiet meshes 2026-04-14 * `961f8293`
+- **New** - Radio diagnostics in MyMesh and CommonCLI 2026-04-10 * `7e4f75c9`
+- **Improvement** - Internal heap stats in status messages 2026-04-06 * `74e45a79`
+- **New** - TennMesh preset (fixed username/password auth) 2026-04-05 * `3b46e9ee`
+- **Docs** - Single `set radio` command in the guide 2026-04-04 * `1c524a93`
+- **Internal** - RX/TX packet logging controlled by MQTT settings (`mqtt_rx_enabled` pref) 2026-04-04 * `47b632aa`
+- **Improvement** - Runtime-configurable MQTT slot limits based on PSRAM 2026-04-04 * `258a0d72`
+- **Improvement** - Disconnect count/time diagnostics; per-variant WiFi TX power 2026-04-02 * `f0962eff`
### March 2026
-- **Improvement** — Pre-allocated status JSON buffer in PSRAM; non-blocking NTP refresh 2026-03-31 · `cf4ec3f6`
-- **New** — WiFi disconnect diagnostics 2026-03-31 · `0e82271e`
-- **Fix** — Audience command parsing handles the 9-character prefix 2026-03-30 · `a4324a83`
-- **New** — JWT audience configuration for custom slots 2026-03-30 · `1263e71d`
-- **Improvement** — Partition-table guidance, merged-firmware flashing, embedded CA bundle 2026-03-30 · `da775bb2`
-- **Docs** — Removed obsolete MQTT design docs 2026-03-28 · `384b80fc`
-- **Docs** — README links to the MQTT Implementation Guide 2026-03-28 · `ff49d28f`
-- **New** — Optional SNMP monitoring; concurrent MQTT slots 3 → 5 2026-03-28 · `202acacf`
-- ⬆ **Upstream sync** — Synced with upstream MeshCore dev 2026-03-28 · `cc17c6b7`
-- **New** — meshomatic preset; disable slots with no server configured 2026-03-27 · `3eacf56e`
-- **Improvement** — Pre-allocated JSON publish buffer in PSRAM 2026-03-26 · `47dd2182`
-- **New** — `reconnect()` added to PsychicMqttClient 2026-03-25 · `1b5884bd`
-- **Improvement** — Lightweight JWT reconnect (token refresh without full teardown) 2026-03-25 · `af52a989`
-- **Internal** — Full teardown/setup for JWT slots on reconnect 2026-03-24 · `e29c3d96`
-- **Internal** — Standardized MQTT slot log format 2026-03-24 · `2bbca206`
-- **New** — cascadiamesh preset; keepalive configuration 2026-03-24 · `eaf5fcbc`
-- **Improvement** — Re-create JWT tokens when NTP reveals stale time 2026-03-24 · `1c98d1c6`
-- **Internal** — Serial-availability checks before logging; circuit-breaker reconnect logic 2026-03-23 · `4a60f166`
-- **New** — MeshRank preset (token auth, custom topic templates) 2026-03-22 · `95874f0c`
-- **Fix** — Dynamic MQTTBridge allocation to avoid an ESP32 static-init crash 2026-03-21 · `c67fb12b`
-- **Build** — Build script: cached project config, artifact handling, ESP32 binary merge 2026-03-21 · `bf8a3a7a`
-- **Improvement** — Active connection slots limited by available memory (PSRAM-aware) 2026-03-20 · `542f8a31`
-- **New** — Up to 3 configurable connection slots with built-in presets (LetsMesh US/EU, MeshMapper) 2026-03-20 · `b43e9618`
-- ⬆ **Upstream sync** — Synced with upstream MeshCore dev 2026-03-20 · `abe6e046`
-- **Change** — Stopped enabling MQTT analyzer servers by default in the example sketches 2026-03-07 · `304719e8`
-- ⬆ **Upstream sync** — Synced with upstream MeshCore dev 2026-03-06 · `db8b4419`
-- ⬆ **Upstream sync** — Synced with upstream MeshCore dev 2026-03-04 · `56ac85fa`
-- ⬆ **Upstream sync** — Synced with upstream MeshCore dev 2026-03-03 · `dcbbe5a6`
+- **Improvement** - Pre-allocated status JSON buffer in PSRAM; non-blocking NTP refresh 2026-03-31 * `cf4ec3f6`
+- **New** - WiFi disconnect diagnostics 2026-03-31 * `0e82271e`
+- **Fix** - Audience command parsing handles the 9-character prefix 2026-03-30 * `a4324a83`
+- **New** - JWT audience configuration for custom slots 2026-03-30 * `1263e71d`
+- **Improvement** - Partition-table guidance, merged-firmware flashing, embedded CA bundle 2026-03-30 * `da775bb2`
+- **Docs** - Removed obsolete MQTT design docs 2026-03-28 * `384b80fc`
+- **Docs** - README links to the MQTT Implementation Guide 2026-03-28 * `ff49d28f`
+- **New** - Optional SNMP monitoring; concurrent MQTT slots 3 -> 5 2026-03-28 * `202acacf`
+- [UP] **Upstream sync** - Synced with upstream MeshCore dev 2026-03-28 * `cc17c6b7`
+- **New** - meshomatic preset; disable slots with no server configured 2026-03-27 * `3eacf56e`
+- **Improvement** - Pre-allocated JSON publish buffer in PSRAM 2026-03-26 * `47dd2182`
+- **New** - `reconnect()` added to PsychicMqttClient 2026-03-25 * `1b5884bd`
+- **Improvement** - Lightweight JWT reconnect (token refresh without full teardown) 2026-03-25 * `af52a989`
+- **Internal** - Full teardown/setup for JWT slots on reconnect 2026-03-24 * `e29c3d96`
+- **Internal** - Standardized MQTT slot log format 2026-03-24 * `2bbca206`
+- **New** - cascadiamesh preset; keepalive configuration 2026-03-24 * `eaf5fcbc`
+- **Improvement** - Re-create JWT tokens when NTP reveals stale time 2026-03-24 * `1c98d1c6`
+- **Internal** - Serial-availability checks before logging; circuit-breaker reconnect logic 2026-03-23 * `4a60f166`
+- **New** - MeshRank preset (token auth, custom topic templates) 2026-03-22 * `95874f0c`
+- **Fix** - Dynamic MQTTBridge allocation to avoid an ESP32 static-init crash 2026-03-21 * `c67fb12b`
+- **Build** - Build script: cached project config, artifact handling, ESP32 binary merge 2026-03-21 * `bf8a3a7a`
+- **Improvement** - Active connection slots limited by available memory (PSRAM-aware) 2026-03-20 * `542f8a31`
+- **New** - Up to 3 configurable connection slots with built-in presets (LetsMesh US/EU, MeshMapper) 2026-03-20 * `b43e9618`
+- [UP] **Upstream sync** - Synced with upstream MeshCore dev 2026-03-20 * `abe6e046`
+- **Change** - Stopped enabling MQTT analyzer servers by default in the example sketches 2026-03-07 * `304719e8`
+- [UP] **Upstream sync** - Synced with upstream MeshCore dev 2026-03-06 * `db8b4419`
+- [UP] **Upstream sync** - Synced with upstream MeshCore dev 2026-03-04 * `56ac85fa`
+- [UP] **Upstream sync** - Synced with upstream MeshCore dev 2026-03-03 * `dcbbe5a6`
### February 2026
-- ⬆ **Upstream sync** — Synced with upstream MeshCore dev (Packet.cpp fix) 2026-02-26 · `8b359452`
-- **Internal** — Multibyte path handling in Packet / MQTTMessageBuilder 2026-02-25 · `c8e220b1`
-- ⬆ **Upstream sync** — Synced with upstream MeshCore dev 2026-02-25 · `51e9907f`
-- **Change** — Removed an unused preprocessor directive in MyMesh 2026-02-25 · `ee1a9b4a`
-- ⬆ **Upstream sync** — Synced with upstream MeshCore dev 2026-02-24 · `397a48fc`
-- **Improvement** — Keepalive set to 45s to hold WebSocket connections through Cloudflare 2026-02-16 · `2d4b7a64`
-- **Build** — heltec_v4 uses the `heltec_v4_oled` config for observer / room-server 2026-02-16 · `88ce0d33`
-- ⬆ **Upstream sync** — Synced with upstream MeshCore dev 2026-02-15 · `e89cbee8`
-- **Improvement** — Simplified KISS noise-floor sampling 2026-02-07 · `776131e2`
-- **Fix** · `kiss_modem` — Improved RX delivery and noise-floor sampling 2026-02-06 · `f445b5ac`
-- **Improvement** — Better NTP synchronization in MQTTBridge 2026-02-06 · `ba4243b5`
-- **Improvement** — WiFi management and fragmentation recovery 2026-02-04 · `26aaace9`
-- **New** — PSRAM-backed memory management in MQTTBridge 2026-02-04 · `1d0d33f9`
-- **New** — PSRAM diagnostics at MQTTBridge init 2026-02-04 · `abcfe609`
-- **Build** — mbedTLS configuration tuned across variants 2026-02-04 · `21158e1f`
-- **Fix** · `kiss` — Periodic noise-floor calibration and AGC reset 2026-02-03 · `0fb57033`
-- **Fix** — Memory use and status reporting in the MQTT bridge 2026-02-02 · `498566e6`
+- [UP] **Upstream sync** - Synced with upstream MeshCore dev (Packet.cpp fix) 2026-02-26 * `8b359452`
+- **Internal** - Multibyte path handling in Packet / MQTTMessageBuilder 2026-02-25 * `c8e220b1`
+- [UP] **Upstream sync** - Synced with upstream MeshCore dev 2026-02-25 * `51e9907f`
+- **Change** - Removed an unused preprocessor directive in MyMesh 2026-02-25 * `ee1a9b4a`
+- [UP] **Upstream sync** - Synced with upstream MeshCore dev 2026-02-24 * `397a48fc`
+- **Improvement** - Keepalive set to 45s to hold WebSocket connections through Cloudflare 2026-02-16 * `2d4b7a64`
+- **Build** - heltec_v4 uses the `heltec_v4_oled` config for observer / room-server 2026-02-16 * `88ce0d33`
+- [UP] **Upstream sync** - Synced with upstream MeshCore dev 2026-02-15 * `e89cbee8`
+- **Improvement** - Simplified KISS noise-floor sampling 2026-02-07 * `776131e2`
+- **Fix** * `kiss_modem` - Improved RX delivery and noise-floor sampling 2026-02-06 * `f445b5ac`
+- **Improvement** - Better NTP synchronization in MQTTBridge 2026-02-06 * `ba4243b5`
+- **Improvement** - WiFi management and fragmentation recovery 2026-02-04 * `26aaace9`
+- **New** - PSRAM-backed memory management in MQTTBridge 2026-02-04 * `1d0d33f9`
+- **New** - PSRAM diagnostics at MQTTBridge init 2026-02-04 * `abcfe609`
+- **Build** - mbedTLS configuration tuned across variants 2026-02-04 * `21158e1f`
+- **Fix** * `kiss` - Periodic noise-floor calibration and AGC reset 2026-02-03 * `0fb57033`
+- **Fix** - Memory use and status reporting in the MQTT bridge 2026-02-02 * `498566e6`
### January 2026
-- **Internal** — Clarified packet ownership / memory management in MQTTBridge 2026-01-31 · `f91b715e`
-- **New** — `recv_errors` in the `CMD_GET_STATS` packets response 2026-01-29 · `019bbf74`
-- **New** — Receive-error tracking in Dispatcher and the message builder 2026-01-29 · `ee577314`
-- **Fix** — Display header include for heltec_v4 when `DISPLAY_CLASS` is set directly 2026-01-27 · `bb27cded`
-- ⬆ **Upstream sync** — Synced with upstream MeshCore dev 2026-01-27 · `d96c900f`
-- ⬆ **Upstream sync** — Synced with upstream MeshCore dev 2026-01-26 · `2a84e58b`
-- **Improvement** — MQTTBridge memory usage and NTP handling 2026-01-24 · `9e1ce23e`
-- **Improvement** — MQTTBridge status handling and publish logic 2026-01-23 · `c67c3295`
-- **Internal** — MQTTBridge connection handling for stability / responsiveness 2026-01-22 · `c055228c`
-- ⬆ **Upstream sync** — Synced with upstream MeshCore dev 2026-01-21 · `39ba020f`
-- **Internal** — MQTTBridge moved onto a FreeRTOS task for responsiveness 2026-01-21 · `ed27ebd0`
-- **New** — Configurable WiFi power-save mode preference 2026-01-18 · `56f98997`
-- **Internal** — HeltecTrackerV2 / HeltecV4 init for GC1109 FEM 2026-01-13 · `3eaca31f`
-- **Docs** — Owner configuration commands documented 2026-01-12 · `58cdd60c`
-- **Internal** — Advert timer type consistency in MyMesh / SensorMesh 2026-01-08 · `ffa2a1ec`
-- **New** — Device metadata setup at bridge init / restart 2026-01-03 · `2bd61c13`
-- **Internal** — Config validation allows optional username / password 2026-01-03 · `d1093b43`
-- **Internal** — JWTHelper / MQTTBridge efficiency cleanup 2026-01-02 · `e754317c`
-- **Fix** — MQTT connection stability and CLI responsiveness 2026-01-02 · `98f176c7`
-- **New** — Neighbour-info sorting; MQTTBridge memory tuning 2026-01-01 · `2185523d`
+- **Internal** - Clarified packet ownership / memory management in MQTTBridge 2026-01-31 * `f91b715e`
+- **New** - `recv_errors` in the `CMD_GET_STATS` packets response 2026-01-29 * `019bbf74`
+- **New** - Receive-error tracking in Dispatcher and the message builder 2026-01-29 * `ee577314`
+- **Fix** - Display header include for heltec_v4 when `DISPLAY_CLASS` is set directly 2026-01-27 * `bb27cded`
+- [UP] **Upstream sync** - Synced with upstream MeshCore dev 2026-01-27 * `d96c900f`
+- [UP] **Upstream sync** - Synced with upstream MeshCore dev 2026-01-26 * `2a84e58b`
+- **Improvement** - MQTTBridge memory usage and NTP handling 2026-01-24 * `9e1ce23e`
+- **Improvement** - MQTTBridge status handling and publish logic 2026-01-23 * `c67c3295`
+- **Internal** - MQTTBridge connection handling for stability / responsiveness 2026-01-22 * `c055228c`
+- [UP] **Upstream sync** - Synced with upstream MeshCore dev 2026-01-21 * `39ba020f`
+- **Internal** - MQTTBridge moved onto a FreeRTOS task for responsiveness 2026-01-21 * `ed27ebd0`
+- **New** - Configurable WiFi power-save mode preference 2026-01-18 * `56f98997`
+- **Internal** - HeltecTrackerV2 / HeltecV4 init for GC1109 FEM 2026-01-13 * `3eaca31f`
+- **Docs** - Owner configuration commands documented 2026-01-12 * `58cdd60c`
+- **Internal** - Advert timer type consistency in MyMesh / SensorMesh 2026-01-08 * `ffa2a1ec`
+- **New** - Device metadata setup at bridge init / restart 2026-01-03 * `2bd61c13`
+- **Internal** - Config validation allows optional username / password 2026-01-03 * `d1093b43`
+- **Internal** - JWTHelper / MQTTBridge efficiency cleanup 2026-01-02 * `e754317c`
+- **Fix** - MQTT connection stability and CLI responsiveness 2026-01-02 * `98f176c7`
+- **New** - Neighbour-info sorting; MQTTBridge memory tuning 2026-01-01 * `2185523d`
### December 2025
-- **Docs** — WiFi power-save configuration documented 2025-12-30 · `915f8b8e`
-- **Internal** — `loadPrefs` handles fresh installs and upgrades; migrates old prefs 2025-12-30 · `8fa99611`
-- **Improvement** — Loop prioritizes radio RX for responsiveness 2025-12-29 · `617e3b17`
-- **Internal** — Defer broker connect until WiFi + NTP ready; socket error logging 2025-12-17 · `5d4f1e4c`
-- **Improvement** — Event-driven WiFi diagnostics and faster reconnect 2025-12-12 · `f7f1f838`
-- **New** — WiFi power-save mode configuration (none / min / max) 2025-12-08 · `34c8bea7`
-- **Fix** — LPS22HB pressure now reported in hPa (was kPa) 2025-12-08 · `b91b854a`
-- **New** — WiFi TX power configuration (default 11 dBm) 2025-12-07 · `b82d7e43`
-- **New** — Default bridge settings for fresh installs; JSON reuse to avoid fragmentation 2025-12-07 · `0b7c0888`
-- **Improvement** — MQTT debug macros check Serial to prevent hangs 2025-12-06 · `3589043b`
-- **Change** — Removed redundant settings writes in `savePrefs` 2025-12-04 · `03d519f8`
-- **Build** — Source filter for the Xiao S3 WIO variant 2025-12-01 · `4f548ffc`
+- **Docs** - WiFi power-save configuration documented 2025-12-30 * `915f8b8e`
+- **Internal** - `loadPrefs` handles fresh installs and upgrades; migrates old prefs 2025-12-30 * `8fa99611`
+- **Improvement** - Loop prioritizes radio RX for responsiveness 2025-12-29 * `617e3b17`
+- **Internal** - Defer broker connect until WiFi + NTP ready; socket error logging 2025-12-17 * `5d4f1e4c`
+- **Improvement** - Event-driven WiFi diagnostics and faster reconnect 2025-12-12 * `f7f1f838`
+- **New** - WiFi power-save mode configuration (none / min / max) 2025-12-08 * `34c8bea7`
+- **Fix** - LPS22HB pressure now reported in hPa (was kPa) 2025-12-08 * `b91b854a`
+- **New** - WiFi TX power configuration (default 11 dBm) 2025-12-07 * `b82d7e43`
+- **New** - Default bridge settings for fresh installs; JSON reuse to avoid fragmentation 2025-12-07 * `0b7c0888`
+- **Improvement** - MQTT debug macros check Serial to prevent hangs 2025-12-06 * `3589043b`
+- **Change** - Removed redundant settings writes in `savePrefs` 2025-12-04 * `03d519f8`
+- **Build** - Source filter for the Xiao S3 WIO variant 2025-12-01 * `4f548ffc`
### November 2025
-- **New** — `adc_multiplier` added to NodePrefs 2025-11-23 · `8fbdc98a`
-- **Change** — IATA codes uppercased for consistency 2025-11-23 · `5cadf3d9`
-- **Change** — `CMD_GET_STATS` split into core / radio / packet sub-types 2025-11-17 · `a3c9a073`
-- **New** — IP address shown for MQTT bridge devices in UITask 2025-11-16 · `6b0c0e93`
-- **New** — T-Beam ESP32 support; error log when source = tx but TX logging is off 2025-11-16 · `3c7ac9d1`
-- **Build** — `MQTT_DEBUG` enabled for several build configs 2025-11-16 · `3726c472`
-- **New** — MQTT observer support for Room Servers 2025-11-15 · `0cd0c36a`
-- **Change** — Switched MQTT configs to observer mode across variants 2025-11-13 · `304a32db`
-- **Internal** — Client version string generation 2025-11-13 · `aa242e78`
-- **Improvement** — WiFi power-save + reduced TX power to limit LoRa interference; retry fix when no brokers connected 2025-11-10 · `bafa5426`
-- **Internal** — Statistics use binary frames instead of JSON 2025-11-09 · `80d6dd43`
-- **New** — IATA validation blocks publishing without an IATA code 2025-11-08 · `25d40f02`
-- **Fix** — Restored status-publish retry delay to prevent server spam 2025-11-08 · `9e06fffc`
-- **New** — Added missing token-expiration / reconnection member variables 2025-11-08 · `453c4184`
-- **Change** — Removed WiFi credentials from platformio.ini (use CLI / build flags) 2025-11-08 · `921c6112`
-- **Internal** — MQTT preferences handling in CommonCLI 2025-11-08 · `5a816be8`
-- **Internal** — `formatStatsReply` uses the new member variables 2025-11-08 · `c9aa536c`
-- **Build** — Explicit `paulstoffregen/Time` dependency to fix PIO compile 2025-11-07 · `72ca50b9`
-- **Build** — Dropped the CayenneLPP patch script; updated ArduinoJson 2025-11-07 · `fef9225b`
-- **Fix** — Timezone command parsing lengths corrected 2025-11-07 · `b89ceeea`
-- **New** — Statistics commands and response handling in MyMesh 2025-11-07 · `df4dab85`
-- **New** — MQTT email support in CommonCLI / JWTHelper 2025-11-06 · `1ed18f9e`
-- **New** — MQTT owner public-key support 2025-11-06 · `e5dbd56b`
-- **Improvement** — Status reporting and stats collection 2025-11-01 · `e4ff06cb`
+- **New** - `adc_multiplier` added to NodePrefs 2025-11-23 * `8fbdc98a`
+- **Change** - IATA codes uppercased for consistency 2025-11-23 * `5cadf3d9`
+- **Change** - `CMD_GET_STATS` split into core / radio / packet sub-types 2025-11-17 * `a3c9a073`
+- **New** - IP address shown for MQTT bridge devices in UITask 2025-11-16 * `6b0c0e93`
+- **New** - T-Beam ESP32 support; error log when source = tx but TX logging is off 2025-11-16 * `3c7ac9d1`
+- **Build** - `MQTT_DEBUG` enabled for several build configs 2025-11-16 * `3726c472`
+- **New** - MQTT observer support for Room Servers 2025-11-15 * `0cd0c36a`
+- **Change** - Switched MQTT configs to observer mode across variants 2025-11-13 * `304a32db`
+- **Internal** - Client version string generation 2025-11-13 * `aa242e78`
+- **Improvement** - WiFi power-save + reduced TX power to limit LoRa interference; retry fix when no brokers connected 2025-11-10 * `bafa5426`
+- **Internal** - Statistics use binary frames instead of JSON 2025-11-09 * `80d6dd43`
+- **New** - IATA validation blocks publishing without an IATA code 2025-11-08 * `25d40f02`
+- **Fix** - Restored status-publish retry delay to prevent server spam 2025-11-08 * `9e06fffc`
+- **New** - Added missing token-expiration / reconnection member variables 2025-11-08 * `453c4184`
+- **Change** - Removed WiFi credentials from platformio.ini (use CLI / build flags) 2025-11-08 * `921c6112`
+- **Internal** - MQTT preferences handling in CommonCLI 2025-11-08 * `5a816be8`
+- **Internal** - `formatStatsReply` uses the new member variables 2025-11-08 * `c9aa536c`
+- **Build** - Explicit `paulstoffregen/Time` dependency to fix PIO compile 2025-11-07 * `72ca50b9`
+- **Build** - Dropped the CayenneLPP patch script; updated ArduinoJson 2025-11-07 * `fef9225b`
+- **Fix** - Timezone command parsing lengths corrected 2025-11-07 * `b89ceeea`
+- **New** - Statistics commands and response handling in MyMesh 2025-11-07 * `df4dab85`
+- **New** - MQTT email support in CommonCLI / JWTHelper 2025-11-06 * `1ed18f9e`
+- **New** - MQTT owner public-key support 2025-11-06 * `e5dbd56b`
+- **Improvement** - Status reporting and stats collection 2025-11-01 * `e4ff06cb`
### October 2025
-- **Improvement** — MQTTBridge logging and connection handling 2025-10-31 · `7df28557`
-- **Improvement** — Packet handling and memory management in MQTTMessageBuilder / MQTTBridge 2025-10-31 · `eee02025`
-- **New** — Memory monitoring and queue-size reporting 2025-10-29 · `e77beff0`
-- **Internal** — In-place base64url encoding; cleanup frees queued packets / timezone objects 2025-10-27 · `1219668f`
-- **Change** — Defer MQTT connect until WiFi credentials are valid (requires reboot) 2025-10-27 · `3851a84b`
-- **Improvement** — Dynamic build date and client versioning 2025-10-26 · `c23663da`
-- **New** — CLI config for `mqtt.server` / `username` / `password` / `port` 2025-10-26 · `eab70c3c`
-- **New** — Let's Mesh Analyzer integration 2025-10-26 · `a6453556`
-- **New** — Station G2 repeater bridge build target; docs 2025-10-26 · `f6a77d2f`
-- **New** — Timezone handling; raw-data packet output with proper hashes / timestamps 2025-10-25 · `787af7b0`
-- **New** — Timezone support in the MQTT Bridge and CLI 2025-10-25 · `26c4b970`
-- **New** — Initial MQTT Bridge implementation with WiFi CLI configuration 2025-10-25 · `efc15eb6`
+- **Improvement** - MQTTBridge logging and connection handling 2025-10-31 * `7df28557`
+- **Improvement** - Packet handling and memory management in MQTTMessageBuilder / MQTTBridge 2025-10-31 * `eee02025`
+- **New** - Memory monitoring and queue-size reporting 2025-10-29 * `e77beff0`
+- **Internal** - In-place base64url encoding; cleanup frees queued packets / timezone objects 2025-10-27 * `1219668f`
+- **Change** - Defer MQTT connect until WiFi credentials are valid (requires reboot) 2025-10-27 * `3851a84b`
+- **Improvement** - Dynamic build date and client versioning 2025-10-26 * `c23663da`
+- **New** - CLI config for `mqtt.server` / `username` / `password` / `port` 2025-10-26 * `eab70c3c`
+- **New** - Let's Mesh Analyzer integration 2025-10-26 * `a6453556`
+- **New** - Station G2 repeater bridge build target; docs 2025-10-26 * `f6a77d2f`
+- **New** - Timezone handling; raw-data packet output with proper hashes / timestamps 2025-10-25 * `787af7b0`
+- **New** - Timezone support in the MQTT Bridge and CLI 2025-10-25 * `26c4b970`
+- **New** - Initial MQTT Bridge implementation with WiFi CLI configuration 2025-10-25 * `efc15eb6`
-
+ <------- OTA_PROOF(mid, block_idx, n_proof, proof)
+ (verify proof vs merkle_root -> write block -> write leaves[i])
+ when all blocks present: verify full merkle_root + image_hash -> COMPLETE
```
### 8.4 Message bodies (transfer)
@@ -399,14 +399,14 @@ OTA_MANIFEST: manifest_id[4] frag_idx(1) frag_total(1) bytes[] # up
OTA_REQ: manifest_id[4] block_idx(uint16) want_mask(uint16) # bit k = send fragment k of block
OTA_DATA: manifest_id[4] block_idx(uint16) frag_off(uint16) data[] # up to OTA_FRAG_DATA=160 B
OTA_REQ_PROOF: manifest_id[4] block_idx(uint16)
-OTA_PROOF: manifest_id[4] block_idx(uint16) n_proof(1) proof[] # n_proof × 4 bytes
+OTA_PROOF: manifest_id[4] block_idx(uint16) n_proof(1) proof[] # n_proof x 4 bytes
OTA_GET_LEAVES: manifest_id[4] want_mask(uint16) # bit k = send leaves fragment k; 0xFFFF = all
OTA_LEAVES: manifest_id[4] frag_idx(1) frag_total(1) bytes[] # up to OTA_LEAVES_FRAG=176 leaf bytes
```
-- **Warm-start / leaf-diff (`OTA_GET_LEAVES`/`OTA_LEAVES`) — motatool folder-capture only.** Capturing a
+- **Warm-start / leaf-diff (`OTA_GET_LEAVES`/`OTA_LEAVES`) - motatool folder-capture only.** Capturing a
device's firmware into a `motatool serve` folder is slow (a full image is hundreds of blocks). Because
- builds here are non-deterministic, you cannot reproduce the exact target on the host — but a *similar*
+ builds here are non-deterministic, you cannot reproduce the exact target on the host - but a *similar*
build (e.g. a fresh recompile) is ~99% identical. So `motatool serve --seed ` stages that
build's payload into the destination `.part`, and `ota pull <#> folder validate` makes the fetcher (1) bulk-
fetch the target's `leaves[]` via `OTA_GET_LEAVES`/`OTA_LEAVES` (bitmap-fragmented with a `want_mask`, same
@@ -414,21 +414,21 @@ OTA_LEAVES: manifest_id[4] frag_idx(1) frag_total(1) bytes[] # up
manifest root (authenticate), then (3) keep every seeded block whose leaf matches and pull full `OTA_DATA`
only for the blocks that differ. The `want_mask` is a fixed **uint16**, so `leaves[]` is capped at
`OTA_LEAVES_MAXFRAG=16` fragments (`OTA_DIFF_MAX_BLOCKS=704` blocks); larger images just fall back to a full
- fetch. **Normal P2P nodes never use this** — they target only the blocks they want; the only always-on part
+ fetch. **Normal P2P nodes never use this** - they target only the blocks they want; the only always-on part
is answering `OTA_GET_LEAVES` with leaves the node already holds, so any node's firmware can be captured.
-- **Block ⇆ fragments:** a 1 KB block is split into self-describing `OTA_DATA` fragments. `frag_off` is the
- byte offset of `data` within the block, so the global position is `block_idx*block_size + frag_off` —
+- **Block <-> fragments:** a 1 KB block is split into self-describing `OTA_DATA` fragments. `frag_off` is the
+ byte offset of `data` within the block, so the global position is `block_idx*block_size + frag_off` -
a fragment is self-placing when returned by the source. The fetcher tracks a
per-block slice bitmap and reassembles before requesting the proof.
- **Fragment-level requests (anti-deadlock + anti-congestion):** both `OTA_REQ` and `OTA_GET_MANIFEST` carry
- a `want_mask` — bit *k* asks for fragment *k*. A fetcher requests the **full** mask on the first ask
+ a `want_mask` - bit *k* asks for fragment *k*. A fetcher requests the **full** mask on the first ask
(`(1< 7 frags per 1 KB block |
+| `OTA_MANIFEST` | 7 B | `OTA_MF_FRAG = 176` -> signed manifest ~ 2 frags |
+| `OTA_HAVE` | 12 B | 12 rows x 14 B per fragment |
+| `OTA_PROOF` | 8 B | up to ~44 sibling digests (>> any real tree) |
-A served mota supports up to `OTA_MAX_BLOCK/4` leaves in the default 4 KB proof scratch (≤1024 blocks ≈ 1 MB
+A served mota supports up to `OTA_MAX_BLOCK/4` leaves in the default 4 KB proof scratch (<=1024 blocks ~ 1 MB
payload); larger self-images pass a bigger scratch buffer.
### 8.6 Temporary-radio and transfer boundary
@@ -459,16 +459,16 @@ update as one transmitter and one receiver while still allowing active temporary
- **`target_id`** (4 B): `sha2-256:4(pio_env_name)` (little-endian uint32). The env name uniquely captures
hardware **and** role/partition, so a node auto-fetches only matching firmware (a companion image is not
fetched onto a repeater even though it shares `hw_id`). It is **self-described in the firmware's EndF**
- (§2, written by `pio_endf.py`) and read via `ota_self_firmware()`, so it is correct on any build;
+ (Section 2, written by `pio_endf.py`) and read via `ota_self_firmware()`, so it is correct on any build;
`-D MOTA_TARGET_ID` / `MainBoard::getOtaTargetId()` is the fallback when no EndF identity is present.
`tools/mota` reads it from the firmware's EndF (or `--target-env`). A manual `ota pull`/`want` can override
- target (deliberate role switch); the `hw_id` brick-safety gate (§4) still applies at apply time.
-- **`target_id` vs `hw_id`** — complementary, not redundant: `target_id` is the fetch-routing key
+ target (deliberate role switch); the `hw_id` brick-safety gate (Section 4) still applies at apply time.
+- **`target_id` vs `hw_id`** - complementary, not redundant: `target_id` is the fetch-routing key
(hw + role + partition); `hw_id` is the human-readable brick-safety key (hardware only). Same board, two
- roles ⇒ same `hw_id`, different `target_id`.
+ roles => same `hw_id`, different `target_id`.
- **Naming a `target_id` locally:** only the 4-byte `target_id` ever travels on the wire. To show *which*
- board/role a target is, a node (and `motatool`) reverse-looks-it-up in `src/helpers/ota/OtaTargets.h` —
- a generated `target_id → env-name` table covering every `ENABLE_OTA` env (`tools/mota/gen_targets.py`,
+ board/role a target is, a node (and `motatool`) reverse-looks-it-up in `src/helpers/ota/OtaTargets.h` -
+ a generated `target_id -> env-name` table covering every `ENABLE_OTA` env (`tools/mota/gen_targets.py`,
resolved from `pio project config`). So `ota ls` can render `[Heltec_v3_repeater]` for a neighbour's
beacon without the string being transmitted. Unknown ids show as `other hw` / `N/A`.
- **`fw_version`:** packed comparable uint32 (`MAJOR<<24 | MINOR<<16 | PATCH<<8 | pre`); also self-described
@@ -480,9 +480,9 @@ update as one transmitter and one receiver while still allowing active temporary
- **Signing & allowlist:** a node keeps a runtime allowlist of trusted Ed25519 signer pubkeys (none embedded
in firmware; `ota key add/list/rm`). A `.mota` is eligible for **auto-install** only if signed by an
allowlisted key, the signature verifies, and `image_hash` matches; otherwise it is manual-apply only with
- explicit confirmation. **Transfer needs no trust** — blocks are content-addressed against the signed root.
-- **Policies (persisted):** `autofetch` ∈ {off, any, signed} (default off) gates automatic block fetching of
- own-target adverts; `autoinstall` ∈ {off, trusted} (default off) gates auto-apply of a COMPLETE signed +
+ explicit confirmation. **Transfer needs no trust** - blocks are content-addressed against the signed root.
+- **Policies (persisted):** `autofetch` in {off, any, signed} (default off) gates automatic block fetching of
+ own-target adverts; `autoinstall` in {off, trusted} (default off) gates auto-apply of a COMPLETE signed +
allowlisted fetch. Conservative defaults: a fresh node discovers + announces but never fetches/installs
without operator intent.
- **Supersession:** a newer version announced mid-download does not abort the in-progress transfer
@@ -501,15 +501,15 @@ targets an external mota. Every fetch message carries `manifest_id`, so dispatch
The same host-folder link is also a **pull destination** (the reverse direction): `ota pull <#> folder`
fetches a `.mota` off the mesh and streams it onto the host as `.mota` via the seeder STORAGE ops
(`OP_STAT/BEGIN/WRITE/SREAD/FIN`, see `MotaSeederProto.h`), using a `FolderMotaStore` as the fetch's
-`OtaStore` instead of RAM/flash. This captures an exact copy of a device's firmware — e.g. to build a delta
+`OtaStore` instead of RAM/flash. This captures an exact copy of a device's firmware - e.g. to build a delta
against firmware you don't have. Resume is bookkeeping-free: `BEGIN` 0xFF-fills the file and, on reconnect
-after a link drop (the fetch PAUSES, holding progress on the host — no RAM/flash fallback), `STAT`+`SREAD`
+after a link drop (the fetch PAUSES, holding progress on the host - no RAM/flash fallback), `STAT`+`SREAD`
let the fetcher recompute and refill only the missing blocks.
### 10.1 The `MotaSource` abstraction (`OtaSource.h`)
Transport-agnostic provider of one or more complete `.mota` as random-access bytes. The same serve code
-drives USB-serial, BLE, a WiFi URL list, an NFS/samba mount, etc. — only `read()` differs.
+drives USB-serial, BLE, a WiFi URL list, an NFS/samba mount, etc. - only `read()` differs.
```cpp
struct MotaDesc { // catalog metadata + region offsets (no whole image in RAM)
@@ -523,25 +523,25 @@ class MotaSource {
};
```
-To serve an external mota the node reads its manifest-minus-leaves + `leaves[]` into RAM (≤4 KB for ≤1024
+To serve an external mota the node reads its manifest-minus-leaves + `leaves[]` into RAM (<=4 KB for <=1024
blocks) and streams payload blocks from the source on demand; proofs are generated from the read leaves.
### 10.2 The `mota-seeder` transport (`MotaSeederProto.h`)
A `MotaSource` is fed by a host that serves a folder over the device's **USB serial** (the same console the
-CLI uses — no extra hardware) or, on an ESP32 WiFi companion, over **WiFi (TCP)**. The host is the
+CLI uses - no extra hardware) or, on an ESP32 WiFi companion, over **WiFi (TCP)**. The host is the
standalone Rust tool [`motatool`](https://github.com/vk496/motatool) (`motatool serve --serial ` /
`--tcp `, which also builds + verifies + inspects `.mota`). The device only emits request frames *while
actively serving a fetch*, and reads the reply synchronously, so over the shared USB console binary frames
coexist with the text CLI/logs (resync on magic + checksum). Little-endian, XOR-checksummed:
```
-request (device → host): 'M' 'S' op(1) args... xsum(1 = XOR of op+args)
-response (host → device): 'm' 's' op(1) status(1) payload... xsum(1 = XOR of all prior)
+request (device -> host): 'M' 'S' op(1) args... xsum(1 = XOR of op+args)
+response (host -> device): 'm' 's' op(1) status(1) payload... xsum(1 = XOR of all prior)
-OP_COUNT 0x01 args: - → payload: count(1)
-OP_DESCRIBE 0x02 args: idx(1) → payload: MotaDesc wire (38 B)
-OP_READ 0x03 args: idx(1) off(4) len(2) → payload: len bytes
+OP_COUNT 0x01 args: - -> payload: count(1)
+OP_DESCRIBE 0x02 args: idx(1) -> payload: MotaDesc wire (38 B)
+OP_READ 0x03 args: idx(1) off(4) len(2) -> payload: len bytes
MotaDesc wire (38 B): mid[4] target_id(4) fw_version(4) codec(1) flags(1)
total_size(4) leaves_off(4) block_count(4) payload_off(4) payload_size(4)
status: 0 = OK, non-zero = error (out of range / past EOF).
@@ -558,19 +558,19 @@ Device CLI: `ota folder on` (attach + announce), `ota folder` (list), `ota folde
`OTA_FOLDER_SERIAL` (default stream = console `Serial`; override `OTA_FOLDER_SERIAL_STREAM` + define
`OTA_FOLDER_SERIAL_BEGIN` for a dedicated UART). On an ESP32 WiFi companion the node also runs a second
`WiFiServer` on a **dedicated seeder port** (`OTA_SEEDER_TCP_PORT`, default `5001`), separate from the
-companion app port (`TCP_PORT`, default `5000`) — so `motatool serve --tcp` can feed updates while a phone
+companion app port (`TCP_PORT`, default `5000`) - so `motatool serve --tcp` can feed updates while a phone
app stays connected. The node auto-attaches the source when a seeder client connects and detaches when it
closes (no `ota folder on` needed over TCP). Verified on hardware: a RAK4631 relays a host folder to a
Heltec V3 over one USB cable, and a host feeds a Heltec V3 over WiFi (`:5001`) while the companion serves a
-phone on `:5000` — every block merkle-checked.
+phone on `:5000` - every block merkle-checked.
**Transport-agnostic by design.** The request/response *semantics* (`COUNT` / `DESCRIBE(idx)` /
`READ(idx, off, len)` over a folder catalog) are independent of the link. The 2-byte magic + XOR checksum +
resync framing above exists for the shared USB-UART (an unframed byte stream); it is harmless over a
-reliable stream and the **WiFi (TCP)** transport reuses it as-is — both ends just treat the socket as a
+reliable stream and the **WiFi (TCP)** transport reuses it as-is - both ends just treat the socket as a
byte stream (on-device, `SerialMotaSource` runs verbatim over an Arduino `Stream`-compatible `WiFiClient`;
`motatool`'s `TcpTransport` mirrors its `SerialTransport`). A future framed link such as **BLE GATT** (an
-Android phone relaying a folder) could carry the same ops with no magic/checksum at all — a request
+Android phone relaying a folder) could carry the same ops with no magic/checksum at all - a request
characteristic write delivers `op + args`, the reply notifies `status + payload`. `motatool` reflects this
split: a transport-free `SeederCore` (the catalog logic) under a swappable framing/transport layer.
@@ -582,7 +582,7 @@ User-facing OTA data should travel via `CMD_OTA_*` companion binary frames; the
debug/operator oriented and replies are `snprintf`-bounded into a 160-byte buffer.
Commands take intuitive aliases (matched by the first word; see `is_cmd` in `OtaCli.cpp`) so they're easy
-to type and read — `status`/`neighbors`/`pull`/`drop`/`applydelta` are the canonical names, the aliases are
+to type and read - `status`/`neighbors`/`pull`/`drop`/`applydelta` are the canonical names, the aliases are
the recommended user-facing forms. Output is plain-language (a user-facing guide lives at
[ota_user_guide.md](ota_user_guide.md)).
@@ -613,19 +613,19 @@ ota dev ... bring-up helpers (stage/recv/serve/verify)
the modified bootloader (`Adafruit_nRF52_Bootloader_OTAFIX`). The bootloader:
1. **scans flash for `MAGIC`** to find the staged `.mota` (it must NOT trust any stored size),
2. re-checks `TRAILER`, `image_hash`, `approval == "APRV"`, and that the delta's `base_hash` equals the
- running firmware's `EndF.body_hash` (recomputed by scanning for `EndF` — never trust `bank_0_size`),
+ running firmware's `EndF.body_hash` (recomputed by scanning for `EndF` - never trust `bank_0_size`),
3. applies the in-place codec over the app region and boots only if the result hashes to `image_hash`.
-The signature proves author authenticity; `approval` proves local owner consent — both required to apply.
+The signature proves author authenticity; `approval` proves local owner consent - both required to apply.
-> **Bootloader testing note:** always test apply with a *real different* image (base ≠ target). A same-image
-> (X→X) "delta" trivially reproduces the target and gives a false positive.
+> **Bootloader testing note:** always test apply with a *real different* image (base != target). A same-image
+> (X->X) "delta" trivially reproduces the target and gives a false positive.
---
## 13. Versioning of this spec
-`format_ver = 2`. A parser accepts exactly this value and rejects anything else — there is one container
+`format_ver = 2`. A parser accepts exactly this value and rejects anything else - there is one container
format, fixed-layout, and no compatibility shims to carry. If the format ever needs to change, bump
`format_ver`; the multihash `hash_algo` separately allows swapping the digest family without a format
bump. Unknown `codec_id` / `ota_msg_type` values are ignored (a node simply won't fetch what it can't apply).
diff --git a/docs/ota_user_guide.md b/docs/ota_user_guide.md
index 7abcf7d7..b92028c8 100644
--- a/docs/ota_user_guide.md
+++ b/docs/ota_user_guide.md
@@ -1,7 +1,7 @@
-# Updating your node over the air (OTA) — user guide
+# Updating your node over the air (OTA) - user guide
This guide is for **node operators**: how to update your MeshCore device's firmware over the radio, in
-plain language. No cables, no programmer — your node can download a new firmware from a neighbour and
+plain language. No cables, no programmer - your node can download a new firmware from a neighbour and
install it. (For the technical wire format, see [the OTA protocol spec](ota_protocol.md).)
LoRa OTA is present only in supported Keymind repeater build artifacts whose filename contains `-ota-`.
@@ -42,23 +42,23 @@ battery telemetry, and `battery.alert` behavior.
## The important part first: it's safe
- **Nothing installs by itself.** Your node can *discover* and *download* an update in the background, but
- it only **installs** when you say so (unless you deliberately turn on auto-install — see below).
+ it only **installs** when you say so (unless you deliberately turn on auto-install - see below).
- **Bad downloads can't sneak in.** Every piece of the firmware is checked against a cryptographic
fingerprint as it arrives, and the whole image is verified again before install. A corrupt or tampered
download is rejected, not installed.
- **You choose who to trust.** Updates can be *signed* by their author. You can tell your node to only
auto-install firmware signed by keys you've added.
-- **It won't disrupt your mesh.** OTA traffic is always the **lowest priority** — your node only spends
+- **It won't disrupt your mesh.** OTA traffic is always the **lowest priority** - your node only spends
spare airtime on it. Messages and routing always come first; a busy node simply updates later. Think of
it as *"eventually upgradable."*
- **It can recover.** If an install ever fails, the node falls back to a safe recovery mode (you can
- re-flash a known-good firmware over USB) — it won't be left bricked.
+ re-flash a known-good firmware over USB) - it won't be left bricked.
---
## How to talk to your node
-Connect to your node's **console** — usually a USB serial terminal at **115200 baud** (or whatever tool
+Connect to your node's **console** - usually a USB serial terminal at **115200 baud** (or whatever tool
you already use to manage the node). You type `ota ...` commands and the node replies in plain words.
The commands have short, friendly names (and most accept aliases, so you don't have to remember exact
@@ -77,14 +77,14 @@ ota status
Shows your current firmware version, your node's update "target" (its hardware/role id), and whether a
download is in progress.
-For a denser admin view — your firmware's content id (`mid`) **and** its body hash, the fingerprint of the
-set you're serving, live download progress, and the current policy — use:
+For a denser admin view - your firmware's content id (`mid`) **and** its body hash, the fingerprint of the
+set you're serving, live download progress, and the current policy - use:
```
ota stats
```
-On a remote node this is **admin-only** (the remote command console requires the admin password) — send it
+On a remote node this is **admin-only** (the remote command console requires the admin password) - send it
from the app's repeater command screen, or the WiFi/serial OTA console.
### 2. Find updates available near me
@@ -93,12 +93,12 @@ from the app's repeater command screen, or the WiFi/serial OTA console.
ota ls
```
-Your node asks around and lists the firmware updates other nodes nearby are offering, in plain words —
+Your node asks around and lists the firmware updates other nodes nearby are offering, in plain words -
each with a **number**, its version, whether it's a full image or a small delta, how many nodes have it,
and how recently it was seen. For example:
```
-Updates nearby (2 src) — `ota get <#>` to download:
+Updates nearby (2 src) - `ota get <#>` to download:
1) v1.2.3 delta [yours] 3n 5s
2) v1.2.0 full [other hw] 1n 12s [downloading]
```
@@ -106,11 +106,11 @@ Updates nearby (2 src) — `ota get <#>` to download:
Each row shows the version, full-vs-delta, **whether it fits your node**, how many nodes have it, and how
long ago it was seen. The fit marker:
-- **[yours]** — built for your exact hardware **and** role; safe to install.
-- **[other hw]** — a different board or role (e.g. a companion image, or another board). Don't install it.
-- **[?]** — can't tell (a build with no target id set, e.g. a bare IDE build rather than a release build).
+- **[yours]** - built for your exact hardware **and** role; safe to install.
+- **[other hw]** - a different board or role (e.g. a companion image, or another board). Don't install it.
+- **[?]** - can't tell (a build with no target id set, e.g. a bare IDE build rather than a release build).
-Run it again after a few seconds — discovery happens in the background, so the list fills in. Nothing is
+Run it again after a few seconds - discovery happens in the background, so the list fills in. Nothing is
downloaded yet; this is just looking around. (`ota neighbors` / `ota updates` also work.)
### 3. Download an update
@@ -123,32 +123,32 @@ ota pull 1 folder # capture it onto a connected motatool folder as ` and add **`validate`**: the node fetches just the target's block fingerprints, keeps
-every block your seed already matches, and pulls over the radio only the handful that actually differ —
+every block your seed already matches, and pulls over the radio only the handful that actually differ -
turning a ~30-minute capture into seconds. The result is still a byte-exact, verified copy of the target.
-*Where the seed comes from:* it is the **`--seed ` you pass to `motatool serve`** — **not** a file you
+*Where the seed comes from:* it is the **`--seed ` you pass to `motatool serve`** - **not** a file you
drop into the capture (`--dir`) folder, which is only the destination and starts empty. There is exactly one
configured seed. When you run `... folder validate`, the node asks motatool to begin the capture and motatool
-stamps that seed's payload into the fresh `.part` in the same step — so it is always the file you named, with
+stamps that seed's payload into the fresh `.part` in the same step - so it is always the file you named, with
no guessing. **`validate` is the switch:** a plain `folder` pull ignores any seed and fetches from scratch;
re-running a `validate` pull re-begins fresh (it never resumes a stale partial). Nothing about the seed is
-trusted — every kept block is checked against the target's own fingerprints, so a mismatched or missing seed
+trusted - every kept block is checked against the target's own fingerprints, so a mismatched or missing seed
just means those blocks are fetched over the radio (correct result, only slower).
The node fetches from one source, **at low priority**, one block at a time. Mesh repeaters may carry the
packets, but only while their temporary-radio windows are active. Check progress with `ota status`.
-If a `folder` pull loses its link mid-transfer, `ota status` shows **paused** — the host keeps the
+If a `folder` pull loses its link mid-transfer, `ota status` shows **paused** - the host keeps the
partial and the pull resumes (filling only what's missing) the moment you reconnect motatool; it never
falls back to flash. To **stop** a download you no longer want:
@@ -166,7 +166,7 @@ ota install
The node verifies the firmware one last time, and if everything checks out it installs it and **reboots
into the new version**. If the check fails, it tells you why and does **not** install. (If you haven't
-added the signer's key, an unsigned/untrusted image will only install with this explicit command — never
+added the signer's key, an unsigned/untrusted image will only install with this explicit command - never
automatically.)
After it reboots, run `ota status` to confirm the new version.
@@ -174,7 +174,7 @@ After it reboots, run `ota status` to confirm the new version.
### 5. If something goes wrong
- A download that stalls or gets interrupted just **resumes** later, or you can `ota cancel` and try again.
-- If an **install** fails, the node won't boot a broken image — it lands in **recovery mode**:
+- If an **install** fails, the node won't boot a broken image - it lands in **recovery mode**:
- **nRF52:** it appears as a USB drive; drag a known-good firmware `.uf2` for that exact board onto it
to recover.
- **ESP32:** it keeps the previous firmware in the other slot and rolls back.
@@ -184,7 +184,7 @@ After it reboots, run `ota status` to confirm the new version.
## Optional: let it update automatically
-By default your node only *discovers* updates — it won't download or install on its own. If you want more
+By default your node only *discovers* updates - it won't download or install on its own. If you want more
automation (e.g. for a remote node you can't easily reach), you can opt in. These settings are saved.
```
@@ -230,18 +230,18 @@ install anything yourself, on your own responsibility.
### Relay a folder of firmware from a computer
If your node is connected to a computer (e.g. a gateway on a Raspberry Pi), it can **hand out** a whole
-folder of firmware files to the mesh — without storing them itself. Useful for seeding a new release to a
+folder of firmware files to the mesh - without storing them itself. Useful for seeding a new release to a
remote area.
-1. Put the firmware files (`.mota` files — see below) in a folder on the computer.
-2. Install the helper tool once — the standalone `motatool` CLI () —
- then point it at your node and the folder — over the node's **USB serial**, or over **WiFi** if it's
+1. Put the firmware files (`.mota` files - see below) in a folder on the computer.
+2. Install the helper tool once - the standalone `motatool` CLI () -
+ then point it at your node and the folder - over the node's **USB serial**, or over **WiFi** if it's
an ESP32 companion on your network:
```
git clone https://github.com/vk496/motatool && cargo install --path ./motatool
# over USB serial:
motatool serve --dir ./my_firmware/ --serial /dev/ttyACM0 -v
- # …or over WiFi (ESP32 companion): the seeder is on a DEDICATED port (5001), separate from the
+ # ...or over WiFi (ESP32 companion): the seeder is on a DEDICATED port (5001), separate from the
# phone-app port (5000), so a phone can stay connected while you serve:
motatool serve --dir ./my_firmware/ --tcp 192.168.1.50:5001 -v
```
@@ -249,34 +249,34 @@ remote area.
`ota get` them like any other. (A WiFi node prints its IP + seeder port to the serial log on connect.
Details: .)
-To stop, just stop the daemon — over WiFi the node auto-detaches when the connection closes; over USB you
+To stop, just stop the daemon - over WiFi the node auto-detaches when the connection closes; over USB you
can also run `ota folder off` on the node. `ota folder` on its own lists what your node is offering.
### Everyone helps share
You don't have to be a gateway to help. Once **any** node finishes downloading an update, it automatically
offers it to *its* neighbours too. So a new firmware spreads outward node-to-node, instead of everyone
-hammering the one node that had it first — and no node is ever overloaded, because all of this stays
+hammering the one node that had it first - and no node is ever overloaded, because all of this stays
lowest-priority.
---
## Where firmware files come from
-OTA distributes **`.mota`** files — a packaged, verifiable firmware image (full image or a small "delta"
+OTA distributes **`.mota`** files - a packaged, verifiable firmware image (full image or a small "delta"
that only contains what changed). You get them by:
- **Downloading a build.** This fork publishes a rolling **`dev-latest`** release on GitHub with the
current firmware for many boards, each accompanied by a `.full.mota` and a tiny `.delta.mota`. Grab the
one for your board to test.
-- **Building your own** with the `mota` packaging tool — see [tools/mota/README.md](../tools/mota/README.md)
+- **Building your own** with the `mota` packaging tool - see [tools/mota/README.md](../tools/mota/README.md)
(this is for people distributing updates, not everyday operators).
---
## Quick reference
-| I want to… | Command |
+| I want to... | Command |
|---|---|
| List all commands | `ota help` |
| See my firmware + any download | `ota status` (or just `ota`) |
@@ -297,13 +297,13 @@ that only contains what changed). You get them by:
## A few terms
-- **Firmware** — the software running your node. Updating it can add features or fix bugs.
-- **`.mota`** — a packaged firmware update file, with built-in integrity checks.
-- **Target** — your node's hardware + role identity. Your node only auto-fetches updates built for the
+- **Firmware** - the software running your node. Updating it can add features or fix bugs.
+- **`.mota`** - a packaged firmware update file, with built-in integrity checks.
+- **Target** - your node's hardware + role identity. Your node only auto-fetches updates built for the
same target, so it won't grab firmware meant for a different board.
-- **Delta** — a small update containing only the changes from your current firmware (faster to send than a
+- **Delta** - a small update containing only the changes from your current firmware (faster to send than a
full image). Your node rebuilds the complete firmware from it and verifies the result before installing.
-- **Signed** — the update carries the author's cryptographic signature, so you can verify who made it.
+- **Signed** - the update carries the author's cryptographic signature, so you can verify who made it.
For the full technical details (the file format and the radio protocol), see
[the OTA protocol spec](ota_protocol.md).
diff --git a/docs/stats_binary_frames.md b/docs/stats_binary_frames.md
index 00e00b8f..4170577b 100644
--- a/docs/stats_binary_frames.md
+++ b/docs/stats_binary_frames.md
@@ -113,7 +113,7 @@ struct StatsRadio {
- Counters are cumulative from boot and may wrap.
- `recv = flood_rx + direct_rx`
- `sent = flood_tx + direct_tx`
-- Clients should accept frame length ≥ 26; if length ≥ 30, parse `recv_errors` at offset 26.
+- Clients should accept frame length >= 26; if length >= 30, parse `recv_errors` at offset 26.
### Example Structure (C/C++)
diff --git a/docs/terminal_chat_cli.md b/docs/terminal_chat_cli.md
index 31616144..f9f10fbd 100644
--- a/docs/terminal_chat_cli.md
+++ b/docs/terminal_chat_cli.md
@@ -35,7 +35,7 @@ Sets the transmit duty cycle limit (1-100%). Example: `set dutycycle 10` for 10%
```
set af {air-time-factor}
```
-Sets the transmit air-time-factor. Deprecated — use `set dutycycle` instead.
+Sets the transmit air-time-factor. Deprecated - use `set dutycycle` instead.
```
diff --git a/examples/companion_radio/main.cpp b/examples/companion_radio/main.cpp
index 58005f58..67aa6537 100644
--- a/examples/companion_radio/main.cpp
+++ b/examples/companion_radio/main.cpp
@@ -192,7 +192,7 @@ void halt() {
}
#endif
-/* WIFI OTA SEEDER — relay a host folder of .mota over WiFi (motatool `serve --tcp`), on a DEDICATED port
+/* WIFI OTA SEEDER - relay a host folder of .mota over WiFi (motatool `serve --tcp`), on a DEDICATED port
separate from the companion (TCP_PORT), so a phone app stays connected while motatool feeds updates. */
#if defined(ESP32) && defined(WIFI_SSID) && defined(ENABLE_OTA)
#include
@@ -230,7 +230,7 @@ void halt() {
if (mesh::ota::ota_ctx().manager.fetchState() == mesh::ota::OtaManager::PAUSED)
mesh::ota::ota_ctx().manager.resumeStaged(nullptr);
mesh::ota::ota_ctx().manager.announce(); // new served set -> advertise the folder's fw to peers
- WIFI_DEBUG_PRINTLN("OTA seeder: client connected (%s) — relay + folder pull-dest ready", di);
+ WIFI_DEBUG_PRINTLN("OTA seeder: client connected (%s) - relay + folder pull-dest ready", di);
} else {
ota_seeder_client.stop(); // no free source slot
}
@@ -238,9 +238,9 @@ void halt() {
}
#endif
-/* WIFI OTA CONSOLE — a tiny text CLI for OTA over WiFi. A WiFi companion has no serial text console, so
+/* WIFI OTA CONSOLE - a tiny text CLI for OTA over WiFi. A WiFi companion has no serial text console, so
without this its OTA is only reachable through the phone app. Connect with e.g. `nc 5002` and type
- `ota status` / `ota ls` / `ota announce` / ... — one client at a time, on a DEDICATED port separate from
+ `ota status` / `ota ls` / `ota announce` / ... - one client at a time, on a DEDICATED port separate from
the companion (5000) and the seeder (5001). */
#if defined(ESP32) && defined(WIFI_SSID) && defined(ENABLE_OTA)
#include // mesh::ota::handle_ota_command(line, reply, board)
@@ -256,7 +256,7 @@ void halt() {
if (!ota_console_client || !ota_console_client.connected()) {
WiFiClient c = ota_console_server.available();
if (c) { ota_console_client = c; ota_console_len = 0;
- ota_console_client.print("OTA console — type `ota ...` (e.g. ota status / ota ls / ota announce)\r\n> "); }
+ ota_console_client.print("OTA console - type `ota ...` (e.g. ota status / ota ls / ota announce)\r\n> "); }
return;
}
while (ota_console_client.available()) {
diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp
index b97e59e8..4d0d8113 100644
--- a/examples/simple_repeater/MyMesh.cpp
+++ b/examples/simple_repeater/MyMesh.cpp
@@ -917,7 +917,7 @@ void MyMesh::logRxRaw(float snr, float rssi, const uint8_t raw[], int len) {
void MyMesh::logRx(mesh::Packet *pkt, int len, float score) {
#ifdef WITH_MQTT_BRIDGE
- // MQTT bridge: always feed RX packets — bridge decides based on mqtt.rx setting
+ // MQTT bridge: always feed RX packets - bridge decides based on mqtt.rx setting
if (mqtt_bridge) mqtt_bridge->onPacketReceived(pkt);
#elif defined(WITH_BRIDGE)
// Non-MQTT bridge: use bridge.source setting
@@ -947,7 +947,7 @@ void MyMesh::logRx(mesh::Packet *pkt, int len, float score) {
void MyMesh::logTx(mesh::Packet *pkt, int len) {
#ifdef WITH_MQTT_BRIDGE
- // MQTT bridge: always feed TX packets — bridge decides based on mqtt.tx setting
+ // MQTT bridge: always feed TX packets - bridge decides based on mqtt.tx setting
if (mqtt_bridge) mqtt_bridge->sendPacket(pkt);
#elif defined(WITH_BRIDGE)
// Non-MQTT bridge: use bridge.source setting
@@ -2531,10 +2531,10 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc
_prefs.battery_alert_low_percent = BATTERY_ALERT_LOW_PERCENT_DEFAULT;
_prefs.battery_alert_critical_percent = BATTERY_ALERT_CRITICAL_PERCENT_DEFAULT;
#ifdef WITH_MQTT_BRIDGE
- _prefs.agc_reset_interval = 7; // 28 seconds (secs/4) — prevents AGC drift on long-running observers
+ _prefs.agc_reset_interval = 7; // 28 seconds (secs/4) - prevents AGC drift on long-running observers
#endif
// Observer defaults (radio_watchdog, alert.*, snmp.*) moved to applyMQTTDefaults()
- // in MQTTDefaults.h — they live in /mqtt_prefs now, not NodePrefs.
+ // in MQTTDefaults.h - they live in /mqtt_prefs now, not NodePrefs.
_prefs.rx_ps_rx_us = RX_POWERSAVING_DEFAULT_RX_US;
_prefs.rx_ps_sleep_us = RX_POWERSAVING_DEFAULT_SLEEP_US;
@@ -2571,7 +2571,7 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc
memset(default_scope.key, 0, sizeof(default_scope.key));
}
-// OTA mesh-integration (receive/begin/loop) is centralized in mesh::Mesh — no per-example wiring.
+// OTA mesh-integration (receive/begin/loop) is centralized in mesh::Mesh - no per-example wiring.
void MyMesh::begin(FILESYSTEM *fs) {
mesh::Mesh::begin(); // also starts OTA (ota_ctx().begin) for all roles
@@ -2721,7 +2721,7 @@ bool MyMesh::sendFloodScoped(const TransportKey& scope, mesh::Packet* pkt, uint3
bool MyMesh::resolveAlertScope(TransportKey& dest) {
// Prefer an explicit alert.region override; look it up lazily via
// RegionMap so the operator can name a region that doesn't exist yet
- // without polluting region_map state — we just silently fall through
+ // without polluting region_map state - we just silently fall through
// to default_scope on miss.
#ifdef WITH_MQTT_BRIDGE
const char* alert_region = _cli.getObserverPrefs()->alert_region;
diff --git a/examples/simple_room_server/MyMesh.cpp b/examples/simple_room_server/MyMesh.cpp
index 38ec8bef..8608ff37 100644
--- a/examples/simple_room_server/MyMesh.cpp
+++ b/examples/simple_room_server/MyMesh.cpp
@@ -231,7 +231,7 @@ void MyMesh::logRxRaw(float snr, float rssi, const uint8_t raw[], int len) {
void MyMesh::logRx(mesh::Packet *pkt, int len, float score) {
#ifdef WITH_MQTT_BRIDGE
- // MQTT bridge: always feed RX packets — bridge decides based on mqtt.rx setting
+ // MQTT bridge: always feed RX packets - bridge decides based on mqtt.rx setting
if (_prefs.bridge_enabled && bridge) bridge->onPacketReceived(pkt);
#endif
@@ -255,7 +255,7 @@ void MyMesh::logRx(mesh::Packet *pkt, int len, float score) {
}
void MyMesh::logTx(mesh::Packet *pkt, int len) {
#ifdef WITH_MQTT_BRIDGE
- // MQTT bridge: always feed TX packets — bridge decides based on mqtt.tx setting
+ // MQTT bridge: always feed TX packets - bridge decides based on mqtt.tx setting
if (_prefs.bridge_enabled && bridge) bridge->sendPacket(pkt);
#endif
@@ -776,7 +776,7 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc
_prefs.advert_loc_policy = ADVERT_LOC_PREFS;
_prefs.radio_fem_rxgain = 1;
- // Observer defaults (alert.*, etc.) moved to applyMQTTDefaults() — they live
+ // Observer defaults (alert.*, etc.) moved to applyMQTTDefaults() - they live
// in /mqtt_prefs now, not NodePrefs.
// bridge defaults (same as repeater)
diff --git a/lib/PsychicMqttClient/CHANGELOG.md b/lib/PsychicMqttClient/CHANGELOG.md
index 116ad5cc..ff94eba1 100644
--- a/lib/PsychicMqttClient/CHANGELOG.md
+++ b/lib/PsychicMqttClient/CHANGELOG.md
@@ -1,66 +1,66 @@
-# Changelog
-
-All notable changes to this project will be documented in this file.
-
-## [0.2.4] - Fixes
-
-### Fixed
-
-- Binary payloads truncated by strcpy and switched to memcpy [18](https://github.com/theelims/PsychicMqttClient/pull/18)
-- Fixed broken link to Adafruit cert repository
-
-### Changed
-
-- Replaced `mqtt.eclipseprojects.io` with `broker.hivemq.com`
-- Replaced debug tag with 🐙
-
-## [0.2.3] - Various Fixes and Certificate Validation
-
-### Fixed
-
-- Issue [16](https://github.com/theelims/PsychicMqttClient/issues/16) was fixed.
-
-### Added
-
-- PR [17](https://github.com/theelims/PsychicMqttClient/pull/17) adds functionality to authenticate via client key + certificate.
-
-## [0.2.2] - Various Fixes
-
-### Fixed
-
-- Issue [14](https://github.com/theelims/PsychicMqttClient/issues/14) was fixed.
-- Issue [13](https://github.com/theelims/PsychicMqttClient/issues/13) was fixed.
-
-### Added
-
-- The function `forceStop()` was added to address issue [12](https://github.com/theelims/PsychicMqttClient/issues/12).
-
-## [0.2.1] - Fixing Wildcard Behavior
-
-### Changed
-
-- Issue [6](https://github.com/theelims/PsychicMqttClient/issues/6) was fixed.
-
-### Added
-
-- Example for wildcards in topics.
-
-## [0.2.0] - Compatible with Arduino 3 / ESP-IDF 5
-
-### Added
-
-- CI workflow
-- ESP-IDF 5 compatibility
-
-### Changed
-
-- updated library.json
-
-## [0.1.1] - Stability Fixes
-
-### Fixed
-
-- Fixed subscribe error when not connected
-- Freezing of event-loop when disconnect() is called while the MQTT server is not connected
-
-## [0.1.0] - Initial Release
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+## [0.2.4] - Fixes
+
+### Fixed
+
+- Binary payloads truncated by strcpy and switched to memcpy [18](https://github.com/theelims/PsychicMqttClient/pull/18)
+- Fixed broken link to Adafruit cert repository
+
+### Changed
+
+- Replaced `mqtt.eclipseprojects.io` with `broker.hivemq.com`
+- Replaced debug tag with [OCTOPUS]
+
+## [0.2.3] - Various Fixes and Certificate Validation
+
+### Fixed
+
+- Issue [16](https://github.com/theelims/PsychicMqttClient/issues/16) was fixed.
+
+### Added
+
+- PR [17](https://github.com/theelims/PsychicMqttClient/pull/17) adds functionality to authenticate via client key + certificate.
+
+## [0.2.2] - Various Fixes
+
+### Fixed
+
+- Issue [14](https://github.com/theelims/PsychicMqttClient/issues/14) was fixed.
+- Issue [13](https://github.com/theelims/PsychicMqttClient/issues/13) was fixed.
+
+### Added
+
+- The function `forceStop()` was added to address issue [12](https://github.com/theelims/PsychicMqttClient/issues/12).
+
+## [0.2.1] - Fixing Wildcard Behavior
+
+### Changed
+
+- Issue [6](https://github.com/theelims/PsychicMqttClient/issues/6) was fixed.
+
+### Added
+
+- Example for wildcards in topics.
+
+## [0.2.0] - Compatible with Arduino 3 / ESP-IDF 5
+
+### Added
+
+- CI workflow
+- ESP-IDF 5 compatibility
+
+### Changed
+
+- updated library.json
+
+## [0.1.1] - Stability Fixes
+
+### Fixed
+
+- Fixed subscribe error when not connected
+- Freezing of event-loop when disconnect() is called while the MQTT server is not connected
+
+## [0.1.0] - Initial Release
diff --git a/lib/PsychicMqttClient/src/PsychicMqttClient.cpp b/lib/PsychicMqttClient/src/PsychicMqttClient.cpp
index da276e3c..90fadad3 100644
--- a/lib/PsychicMqttClient/src/PsychicMqttClient.cpp
+++ b/lib/PsychicMqttClient/src/PsychicMqttClient.cpp
@@ -4,7 +4,7 @@
#include
-static const char *TAG = "🐙";
+static const char *TAG = "[OCTOPUS]";
static void log_error_if_nonzero(const char *message, int error_code)
{
@@ -742,7 +742,7 @@ void PsychicMqttClient::_onError(esp_mqtt_event_handle_t &event)
// Broker accepted the socket but rejected the MQTT CONNECT. The return
// code distinguishes auth failures (4=bad user/pass, 5=not authorized)
// from server unavailable (3), protocol mismatch (1), and client-id
- // rejection (2) — the key signal for diagnosing a server-side lockout.
+ // rejection (2) - the key signal for diagnosing a server-side lockout.
ESP_LOGE(TAG, "Connection refused, return code 0x%x", err->connect_return_code);
}
@@ -798,7 +798,7 @@ bool PsychicMqttClient::_isTopicMatch(const char *topic, const char *subscriptio
if (s_done)
{
- // Subscription ended but topic has more levels → no match,
+ // Subscription ended but topic has more levels -> no match,
// unless the subscription ended with '+' and topic also has no
// more levels (already handled above).
return false;
diff --git a/lib/PsychicMqttClient/src/PsychicMqttClient.h b/lib/PsychicMqttClient/src/PsychicMqttClient.h
index 80961d22..890e5243 100644
--- a/lib/PsychicMqttClient/src/PsychicMqttClient.h
+++ b/lib/PsychicMqttClient/src/PsychicMqttClient.h
@@ -56,7 +56,7 @@ typedef std::function OnPublishUserCallback;
typedef std::function OnErrorUserCallback;
// Fixed caps sized for MeshCore's usage patterns. All callback and subscription
-// storage is inline in the client object — zero dynamic allocation on register.
+// storage is inline in the client object - zero dynamic allocation on register.
#ifndef PSYCHIC_MAX_CONNECT_CB
#define PSYCHIC_MAX_CONNECT_CB 4
#endif
diff --git a/platformio.ini b/platformio.ini
index 05a50006..72beb1a8 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -64,7 +64,7 @@ monitor_filters = esp32_exception_decoder
; deltas/full images of any size fetch over the air (RX-safe, sector-coalesced). The build always includes
; it; for `applydelta` to actually run, the board must use a dual-app/OTA partition table
; (board_build.partitions) with two app slots + otadata (boards without one fetch-refuse cleanly).
-; pio_endf appends the EndF self-identity trailer. (nRF52 single-slot OTA is enabled per-board, not here —
+; pio_endf appends the EndF self-identity trailer. (nRF52 single-slot OTA is enabled per-board, not here -
; it needs a custom bootloader; RP2040/STM32 have no A/B path yet.)
extra_scripts =
pre:scripts/generate_webconfig_html.py
@@ -118,7 +118,7 @@ lib_deps =
; applied in place by the custom OTAFIX bootloader (in-place detools); the app only stages + verifies +
; approves it. OtaFlashLayout_nrf52.h uses FS_START 0xD4000 as the safe staging ceiling for the supported
; S140 v6/v7 linker scripts. The device must run the matching OTAFIX
-; bootloader for `applydelta` to actually apply. (detools.c is intentionally NOT built into the app —
+; bootloader for `applydelta` to actually apply. (detools.c is intentionally NOT built into the app -
; only the bootloader decodes.) The [rak4631] base defines this same recipe inline because it carries
; board-specific extras (fix_bsec_lib.py, BSEC lib) and a fixed post-script order.
[nrf52_lora_ota]
diff --git a/scripts/gen_changelog.py b/scripts/gen_changelog.py
index d7cb3d4e..9a470ea1 100644
--- a/scripts/gen_changelog.py
+++ b/scripts/gen_changelog.py
@@ -50,7 +50,7 @@ UPSTREAM_MERGE = re.compile(r"[Mm]erge.*(?:upstream|origin)/dev")
# rebase / merge artifacts that should never appear in the visible log
NOISE = re.compile(r"conflict marker|duplicate (?:mqtt )?function|unnecessary comments"
r"|from rebase|^revert |whoops|^remove final|^remove remaining", re.I)
-HASH_TOKEN = re.compile(r"·\s*`([0-9a-f]{7,40})`")
+HASH_TOKEN = re.compile(r"*\s*`([0-9a-f]{7,40})`")
MANIFEST_HASH = re.compile(r"\b[0-9a-f]{7,40}\b")
@@ -58,7 +58,7 @@ def clean(subj: str) -> str:
s = subj.strip().lstrip("* ").strip()
s = re.split(r"(?<=[.!?])\s+", s)[0].rstrip(". ")
if len(s) > 140:
- s = s[:137].rstrip() + "…"
+ s = s[:137].rstrip() + "..."
return s[:1].upper() + s[1:] if s else s
@@ -89,7 +89,7 @@ def render(h, date, is_merge, subj):
if UPSTREAM_MERGE.search(subj):
ver = re.search(r"v\d+\.\d+\.\d+", subj)
tail = f" ({ver.group()})" if ver else ""
- return f"- ⬆ **Upstream sync** — Synced with upstream MeshCore dev{tail} {date} · `{h}`"
+ return f"- [UP] **Upstream sync** - Synced with upstream MeshCore dev{tail} {date} * `{h}`"
return None
if NOISE.search(subj):
return None
@@ -100,8 +100,8 @@ def render(h, date, is_merge, subj):
else:
typ = scope = None
summary = clean(subj)
- sc = f" · `{scope}`" if scope else ""
- return f"- **{label_for(typ, summary)}**{sc} — {summary} {date} · `{h}`"
+ sc = f" * `{scope}`" if scope else ""
+ return f"- **{label_for(typ, summary)}**{sc} - {summary} {date} * `{h}`"
def insert_entry(lines, date, bullet):
diff --git a/scripts/partition_signature.py b/scripts/partition_signature.py
index 326b9e51..f90fe27d 100644
--- a/scripts/partition_signature.py
+++ b/scripts/partition_signature.py
@@ -4,7 +4,7 @@
Used to decide OTA partition compatibility: the observer firmware computes the
same signature at runtime from its *flashed* partition table (via esp_partition),
and `ota update` refuses only when the target build's signature differs from the
-running device's — i.e. a real partition-table change, not a blanket flag.
+running device's - i.e. a real partition-table change, not a blanket flag.
The signature MUST be computed identically here and in firmware
(src/helpers/ESP32Board.cpp). Definition:
diff --git a/src/Mesh.cpp b/src/Mesh.cpp
index 89eb0737..486a5119 100644
--- a/src/Mesh.cpp
+++ b/src/Mesh.cpp
@@ -872,11 +872,11 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) {
// to the seen table: a copy heard on the normal channel must not suppress one received after temp radio starts.
if (!isTempRadioActive()) break;
uint8_t n = pkt->getPathHashCount(); // hops travelled to reach us (flood path-hash count)
- // Accept-gate (duty-cycle horizon): ignore OTA from further than our hop limit — neither process nor
+ // Accept-gate (duty-cycle horizon): ignore OTA from further than our hop limit - neither process nor
// relay it. 0 = only directly-received OTA. Runtime-tunable via `ota config hops`.
if (n > getOtaHopLimit()) break;
// ALWAYS process every accepted copy: OTA handlers are idempotent, and "eventually reliable" retries
- // deliberately re-send IDENTICAL requests — if we gated processing on hasSeen(), the dedup would
+ // deliberately re-send IDENTICAL requests - if we gated processing on hasSeen(), the dedup would
// suppress those retries and the transfer could never recover from a lost reply. hasSeen() is used
// ONLY to avoid re-flooding the same packet more than once.
bool seen = _tables->wasSeen(pkt);
@@ -887,7 +887,7 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) {
ota::ota_ctx().track_session(ota::ota_ctx().manager.fetchState(), _ms->getMillis());
onOtaRecv(pkt); // optional per-example hook
// Re-flood at the LOWEST priority and only while still under the hop limit, so OTA never competes with
- // mesh traffic. The free-pool guard keeps heavy OTA from monopolising the shared packet pool — dropping
+ // mesh traffic. The free-pool guard keeps heavy OTA from monopolising the shared packet pool - dropping
// a relay is safe (OTA is best-effort; the source retries).
if (!seen && pkt->isRouteFlood() && !pkt->isMarkedDoNotRetransmit()
&& n < getOtaHopLimit()
diff --git a/src/Mesh.h b/src/Mesh.h
index b6f949d3..78d6dbf7 100644
--- a/src/Mesh.h
+++ b/src/Mesh.h
@@ -438,7 +438,7 @@ protected:
*/
virtual void onOtaRecv(Packet* packet) { }
- /** \returns max OTA flood reach in hops — accept up to N, relay while < N (0=direct). `ota config hops`. */
+ /** \returns max OTA flood reach in hops - accept up to N, relay while < N (0=direct). `ota config hops`. */
virtual uint8_t getOtaHopLimit() const;
// OTA mesh-integration is centralized in Mesh::begin()/loop()/dispatch, so every role (repeater,
diff --git a/src/MeshCore.h b/src/MeshCore.h
index 2ec8aa19..fa0bc7a4 100644
--- a/src/MeshCore.h
+++ b/src/MeshCore.h
@@ -77,7 +77,7 @@ public:
virtual bool canControlLoRaFemLna() const { return false; }
virtual bool isLoRaFemLnaEnabled() const { return false; }
#if defined(ENABLE_OTA)
- // 4-byte build-target discriminator for OTA-over-LoRa (docs/ota_protocol.md §9). Default is the
+ // 4-byte build-target discriminator for OTA-over-LoRa (docs/ota_protocol.md Section 9). Default is the
// MOTA_TARGET_ID build flag injected by build.sh; 0 when unset (e.g. a bare IDE build).
virtual uint32_t getOtaTargetId() const {
#ifdef MOTA_TARGET_ID
diff --git a/src/helpers/AlertReporter.cpp b/src/helpers/AlertReporter.cpp
index 89542aad..ba2a74f3 100644
--- a/src/helpers/AlertReporter.cpp
+++ b/src/helpers/AlertReporter.cpp
@@ -6,7 +6,7 @@
#include
// Header layout for PAYLOAD_TYPE_GRP_TXT before encryption:
-// [0..3] timestamp (uint32_t LE) — also helps make packet_hash unique
+// [0..3] timestamp (uint32_t LE) - also helps make packet_hash unique
// [4] TXT_TYPE_PLAIN
// [5..] ": " (null-terminated by sender for legacy parsers)
#ifndef MAX_ALERT_TEXT_LEN
@@ -74,9 +74,9 @@ struct BannedAlertChannel {
static const BannedAlertChannel BANNED_ALERT_CHANNELS[] = {
// Public group PSK ("izOH6cXN6mrJ5e26oRXNcg==")
{ "PUBLIC", "8b3387e9c5cdea6ac9e5edbaa115cd72" },
- // sha256("#test")[0..15] — auto-responders in many regions
+ // sha256("#test")[0..15] - auto-responders in many regions
{ "#test", "9cd8fcf22a47333b591d96a2b848b73f" },
- // sha256("#bot")[0..15] — generic bot channel, frequent auto-responders
+ // sha256("#bot")[0..15] - generic bot channel, frequent auto-responders
{ "#bot", "eb50a1bcb3e4e5d7bf69a57c9dada211" },
};
@@ -102,7 +102,7 @@ const char* alertReporterBannedChannelMatchHex(const char* psk_hex) {
bool AlertReporter::resolveChannel(mesh::GroupChannel& out) const {
if (!_prefs) return false;
- // alert_psk_hex is the single source of truth — `set alert.hashtag`
+ // alert_psk_hex is the single source of truth - `set alert.hashtag`
// pre-derives the hex-encoded PSK from sha256("#name")[0..15] at CLI time.
// Only 16-byte secrets (32 hex chars) are supported; 32-byte channel keys
// are not used anywhere in MeshCore practice and not represented in the
@@ -163,7 +163,7 @@ bool AlertReporter::sendChannel(const char* text) {
}
// Ride the repeater's default scope (or `alert.region` override) when the
- // host MyMesh provides one — same path MyMesh uses for adverts and
+ // host MyMesh provides one - same path MyMesh uses for adverts and
// broadcast channel messages. Falls back to plain (unscoped) flood when
// no callbacks are wired or no scope is configured, matching the
// pre-scoped behavior on builds without RegionMap.
diff --git a/src/helpers/AlertReporter.h b/src/helpers/AlertReporter.h
index 390262b6..053bb5af 100644
--- a/src/helpers/AlertReporter.h
+++ b/src/helpers/AlertReporter.h
@@ -13,14 +13,14 @@
* the channels in the BANNED_ALERT_CHANNELS table (e.g. "PUBLIC", "#test",
* "#bot"), or nullptr otherwise. Centralized here so both AlertReporter and
* the CommonCLI `set alert.psk` / `set alert.hashtag` handlers can share one
- * source of truth — adding a new banned channel is a one-line table edit.
+ * source of truth - adding a new banned channel is a one-line table edit.
*/
const char* alertReporterBannedChannelMatch(const uint8_t* secret16);
/**
* Convenience: hex-decodes \a psk_hex (32 lowercase/uppercase hex chars) and
* forwards to alertReporterBannedChannelMatch. Returns nullptr if not banned
- * (or if the input isn't a valid 32-char hex string — only 16-byte secrets
+ * (or if the input isn't a valid 32-char hex string - only 16-byte secrets
* are present in the banned table).
*/
const char* alertReporterBannedChannelMatchHex(const char* psk_hex);
@@ -31,13 +31,13 @@ const char* alertReporterBannedChannelMatchHex(const char* psk_hex);
*
* Polls WiFi and per-MQTT-slot outage timers from MQTTBridge. When any timer
* exceeds its configured threshold, floods a single PAYLOAD_TYPE_GRP_TXT
- * message on the configured alert channel ("WiFi down 47m — MyObserver"),
+ * message on the configured alert channel ("WiFi down 47m - MyObserver"),
* then arms a "recovered" message for the next state transition.
*
* The alert channel must be explicitly configured to either a private hex
* PSK (`set alert.psk`) or a hashtag name (`set alert.hashtag`); the
* well-known PUBLIC group key (and a small list of other auto-responder
- * channels — see BANNED_ALERT_CHANNELS in AlertReporter.cpp) are rejected on
+ * channels - see BANNED_ALERT_CHANNELS in AlertReporter.cpp) are rejected on
* purpose so fault alerts never spam community channels.
*
* Edge-triggered + rate-limited via NodePrefs::alert_min_interval_min so a
@@ -56,7 +56,7 @@ public:
/**
* Wire up the reporter. Must be called from MyMesh::begin() after prefs
- * are loaded. \a callbacks is optional — when non-null the reporter uses
+ * are loaded. \a callbacks is optional - when non-null the reporter uses
* it to resolve a TransportKey scope for outgoing alert floods (so the
* packet rides the repeater's default scope or an `alert.region` override).
*/
diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp
index e1984096..19c0f847 100644
--- a/src/helpers/CommonCLI.cpp
+++ b/src/helpers/CommonCLI.cpp
@@ -854,7 +854,7 @@ void CommonCLI::loadPrefs(FILESYSTEM* fs) {
}
#ifdef WITH_MQTT_BRIDGE
// Load observer preferences (MQTT/WiFi/timezone/SNMP/alert) from /mqtt_prefs.
- // Readers (MQTTBridge, AlertReporter, observer CLI) use _mqtt_prefs directly —
+ // Readers (MQTTBridge, AlertReporter, observer CLI) use _mqtt_prefs directly -
// these fields no longer exist in NodePrefs, so there is nothing to sync.
loadMQTTPrefs(fs);
@@ -950,13 +950,13 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) {
file.read((uint8_t *)&_prefs->discovery_mod_timestamp, sizeof(_prefs->discovery_mod_timestamp)); // 162
file.read((uint8_t *)&_prefs->adc_multiplier, sizeof(_prefs->adc_multiplier)); // 166
file.read((uint8_t *)_prefs->owner_info, sizeof(_prefs->owner_info)); // 170
- // MQTT/observer settings are no longer stored in /com_prefs — they live in
+ // MQTT/observer settings are no longer stored in /com_prefs - they live in
// /mqtt_prefs (loaded by loadMQTTPrefs). Old fork firmware wrote a zero-filled
// MQTT gap here followed by a trailing observer block; detect that layout by the
// extra length, skip the gap, and recover the tail so those settings survive
// the upgrade (the file is rewritten in the new layout by loadPrefs afterwards).
// Defaults for the trailing fields that older/shorter files may not contain.
- // (upstream defaults: FEM RX gain on, CAD off) — overwritten below if present.
+ // (upstream defaults: FEM RX gain on, CAD off) - overwritten below if present.
_prefs->radio_fem_rxgain = 1;
_prefs->cad_enabled = 0;
_prefs->rx_powersaving_enabled = 0;
@@ -1005,7 +1005,7 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) {
remaining -= n;
}
file.read((uint8_t *)&_prefs->rx_boosted_gain, sizeof(_prefs->rx_boosted_gain));
- // Tail layout: flood_max_unscoped, flood_max_advert, then the snmp fields —
+ // Tail layout: flood_max_unscoped, flood_max_advert, then the snmp fields -
// except legacy flex-branch files where snmp starts right after
// rx_boosted_gain (no flood_max_*). Same heuristic the old firmware used:
// snmp_enabled is 0/1 and the first community char is printable (> 64).
@@ -1026,7 +1026,7 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) {
_legacy_tail.alert_min_interval_min = 60;
#endif
if (have_flood_bytes && b1 <= 1 && b2 > 64) {
- // Legacy variant: no flood_max_* — b1/b2 are snmp_enabled + community[0]
+ // Legacy variant: no flood_max_* - b1/b2 are snmp_enabled + community[0]
#ifdef WITH_MQTT_BRIDGE
_legacy_tail.snmp_enabled = b1;
_legacy_tail.snmp_community[0] = (char) b2;
@@ -1510,7 +1510,7 @@ void CommonCLI::loadMQTTPrefs(FILESYSTEM* fs) {
_mqtt_prefs_hold = false;
// Whether the loaded /mqtt_prefs already contained the observer fields (snmp/
- // watchdog/alert) appended in Phase 2 — if not, they may be carried over from an
+ // watchdog/alert) appended in Phase 2 - if not, they may be carried over from an
// old-format /com_prefs trailing block below.
bool has_observer_fields = false;
@@ -1556,7 +1556,7 @@ void CommonCLI::loadMQTTPrefs(FILESYSTEM* fs) {
if (!versioned) {
// Headerless (legacy) file. Detect the historical on-disk layout by size,
- // migrate it into the compact versioned struct, and re-save — which adds the
+ // migrate it into the compact versioned struct, and re-save - which adds the
// header and drops the vestigial `_legacy_*` fields. Reopen because the peek
// above advanced the read cursor past the (non-matching) leading bytes.
File file = openMqttPrefsRead(fs);
@@ -1621,7 +1621,7 @@ void CommonCLI::loadMQTTPrefs(FILESYSTEM* fs) {
saveMQTTPrefs(fs);
}
} else if (file_size > 0 && file_size <= sizeof(ThreeSlotMQTTPrefs)) {
- // 3-slot format → compact 6-slot migration
+ // 3-slot format -> compact 6-slot migration
// Array sizes changed from [3] to [6], shifting all field offsets.
// Read into old layout struct and field-copy to new layout.
ThreeSlotMQTTPrefs old3;
@@ -1669,7 +1669,7 @@ void CommonCLI::loadMQTTPrefs(FILESYSTEM* fs) {
} else if (file_size > 0) {
// Headerless 6-slot layout as shipped on mqtt-bridge-implementation-flex
// (the deployed fleet). Same field order as the compact struct but with the
- // vestigial `_legacy_*` block mid-struct and no observer tail — so read it
+ // vestigial `_legacy_*` block mid-struct and no observer tail - so read it
// into Legacy6SlotMQTTPrefs and field-copy across, dropping `_legacy_*`.
Legacy6SlotMQTTPrefs old6;
memset(&old6, 0, sizeof(old6));
@@ -1718,8 +1718,8 @@ void CommonCLI::loadMQTTPrefs(FILESYSTEM* fs) {
}
}
} else {
- // No /mqtt_prefs file — defaults already set. (MQTT slot/WiFi settings from
- // pre-/mqtt_prefs-split fork firmware are NOT recovered from /com_prefs — that
+ // No /mqtt_prefs file - defaults already set. (MQTT slot/WiFi settings from
+ // pre-/mqtt_prefs-split fork firmware are NOT recovered from /com_prefs - that
// offset-based migration was fragile and was removed; those users re-enter
// their MQTT config. The observer trailing block IS recovered, below.)
}
diff --git a/src/helpers/CommonCLI.h b/src/helpers/CommonCLI.h
index ad0a07ef..0f23281f 100644
--- a/src/helpers/CommonCLI.h
+++ b/src/helpers/CommonCLI.h
@@ -159,7 +159,7 @@ struct NodePrefs { // persisted to file
float adc_multiplier;
char owner_info[120];
// NOTE: member order below matches mcarper/keymindCascade (upstream/dev order).
- // It is in-memory only — /com_prefs is read/written field-by-field in loadPrefsInt/
+ // It is in-memory only - /com_prefs is read/written field-by-field in loadPrefsInt/
// savePrefs, whose canonical file order (identical to the flex fleet's through
// offset 294, keymind retry tail at 295+) is what devices actually persist.
uint8_t rx_boosted_gain; // power settings
@@ -223,7 +223,7 @@ struct NodePrefs { // persisted to file
};
#ifdef WITH_MQTT_BRIDGE
-// Old MQTT preferences layout (pre-slot firmware) — used only for migration detection
+// Old MQTT preferences layout (pre-slot firmware) - used only for migration detection
struct OldMQTTPrefs {
char mqtt_origin[32];
char mqtt_iata[8];
@@ -247,7 +247,7 @@ struct OldMQTTPrefs {
char mqtt_email[64];
};
-// 3-slot MQTTPrefs layout — used for migrating from 3-slot to 6-slot format.
+// 3-slot MQTTPrefs layout - used for migrating from 3-slot to 6-slot format.
// Changing array sizes from [3] to [6] shifts all field offsets, so raw file.read()
// into the new struct would corrupt data. This struct preserves the old binary layout.
struct ThreeSlotMQTTPrefs {
@@ -319,7 +319,7 @@ struct Legacy6SlotMQTTPrefs {
};
// The legacy layouts above describe files already written to the deployed fleet's
-// flash, so their sizes are frozen forever — loadMQTTPrefs() tells the eras apart
+// flash, so their sizes are frozen forever - loadMQTTPrefs() tells the eras apart
// by file size and reads each file as a raw struct dump. These asserts pin the
// layouts on every target toolchain; if one fires, the compiler (or an edit to a
// legacy struct or MAX_MQTT_SLOTS) has changed a layout and fleet files would be
diff --git a/src/helpers/CommonCLI_Observer.cpp b/src/helpers/CommonCLI_Observer.cpp
index 1a694c66..47010cd1 100644
--- a/src/helpers/CommonCLI_Observer.cpp
+++ b/src/helpers/CommonCLI_Observer.cpp
@@ -1,4 +1,4 @@
-// CommonCLI_Observer.cpp — fork-owned observer/MQTT/WiFi/timezone/alert/SNMP CLI
+// CommonCLI_Observer.cpp - fork-owned observer/MQTT/WiFi/timezone/alert/SNMP CLI
// command handling, split out of CommonCLI.cpp so the upstream-tracked file carries
// only two small delegation hooks. These are CommonCLI member functions, so they
// retain full access to _prefs/_callbacks/_board/savePrefs() with no re-plumbing.
@@ -431,7 +431,7 @@ bool CommonCLI::handleObserverSetCmd(uint32_t sender_timestamp, const char* conf
sprintf(reply, "OK - slot %d JWT audience cleared (using username/password auth)", slot + 1);
}
} else if (memcmp(subcmd, "audience", 8) == 0 && subcmd[8] == '\0') {
- // "set mqttN.audience" with no value — clear the audience
+ // "set mqttN.audience" with no value - clear the audience
_mqtt_prefs.mqtt_slot_audience[slot][0] = '\0';
savePrefs();
_callbacks->restartBridgeSlot(slot);
@@ -548,7 +548,7 @@ bool CommonCLI::handleObserverSetCmd(uint32_t sender_timestamp, const char* conf
} else {
StrHelper::strncpy(_mqtt_prefs.alert_psk_hex, normalized, sizeof(_mqtt_prefs.alert_psk_hex));
// The new PSK is operator-supplied, so any previously-derived
- // hashtag name is no longer accurate provenance — drop it.
+ // hashtag name is no longer accurate provenance - drop it.
_mqtt_prefs.alert_hashtag[0] = '\0';
savePrefs();
_callbacks->onAlertConfigChanged();
@@ -585,7 +585,7 @@ bool CommonCLI::handleObserverSetCmd(uint32_t sender_timestamp, const char* conf
// Derive the channel key once: first 16 bytes of sha256("#name"),
// store hex-encoded in alert_psk_hex. We don't re-derive on every
- // send — operators can later override with `set alert.psk` without
+ // send - operators can later override with `set alert.psk` without
// leaving stale hashtag text behind.
uint8_t digest[32];
mesh::Utils::sha256(digest, sizeof(digest),
@@ -650,7 +650,7 @@ bool CommonCLI::handleObserverSetCmd(uint32_t sender_timestamp, const char* conf
} else if (memcmp(config, "alert.interval ", 15) == 0) {
int mins = (int)_atoi(&config[15]);
// Floor at 60 min: faster re-fires would let a flapping link spam the
- // mesh with a fresh GRP_TXT flood every minute — terrible for airtime.
+ // mesh with a fresh GRP_TXT flood every minute - terrible for airtime.
if (mins < 60 || mins > 10080) {
strcpy(reply, "Error: alert.interval must be 60-10080 minutes");
} else {
@@ -763,7 +763,7 @@ bool CommonCLI::handleObserverGetCmd(uint32_t sender_timestamp, const char* conf
if (_mqtt_prefs.mqtt_slot_audience[slot][0] != '\0') {
sprintf(reply, "> %s", _mqtt_prefs.mqtt_slot_audience[slot]);
} else {
- strcpy(reply, "> (not set — custom slots use username/password auth)");
+ strcpy(reply, "> (not set - custom slots use username/password auth)");
}
} else if (memcmp(subcmd, "diag", 4) == 0) {
MQTTBridge::formatSlotDiagReply(reply, 160, slot);
@@ -936,13 +936,13 @@ bool CommonCLI::handleObserverCommand(uint32_t sender_timestamp, char* command,
} else if (memcmp(command, "ota check", 9) == 0) {
// Check is synchronous so its result lands in this reply, and runs with the
// MQTT bridge UP: the slim per-variant manifest is tiny, so the fetch only
- // costs a single TLS handshake (no large JSON doc) — which fits alongside
+ // costs a single TLS handshake (no large JSON doc) - which fits alongside
// the live MQTT sessions even on no-PSRAM boards. No bridge bounce needed.
_board->otaFromManifest(_callbacks->getFirmwareVer(), true, reply);
} else {
// `ota update`: cheap pre-check first (plain HTTP, bridge stays up). Only
- // schedule the real update — which tears the bridge down, flashes, and
- // reboots — when an applicable build actually exists. otaFromManifest(dry)
+ // schedule the real update - which tears the bridge down, flashes, and
+ // reboots - when an applicable build actually exists. otaFromManifest(dry)
// returns true iff so; otherwise it leaves the explanation (up to date /
// cable flash / error) in reply, which we send without disturbing the
// bridge or misleading the user with a "Beginning update..." that no-ops.
diff --git a/src/helpers/ESP32Board.cpp b/src/helpers/ESP32Board.cpp
index b23e9d20..00d286b3 100644
--- a/src/helpers/ESP32Board.cpp
+++ b/src/helpers/ESP32Board.cpp
@@ -417,7 +417,7 @@ static void ota_parseVersion(const char* ver, char* base_out, size_t base_sz, in
}
}
-// Canonical signature of the FLASHED partition table — MUST match
+// Canonical signature of the FLASHED partition table - MUST match
// scripts/partition_signature.py: each entry "type:subtype:offset:size" in
// lowercase hex, sorted by offset, joined by ','. Lets `ota update` compare the
// target build's partition layout (carried in the manifest as partSig) against
@@ -472,7 +472,7 @@ static void ota_task_entry(void* param) {
bool ESP32Board::otaFromManifest(const char* current_ver, bool dry_run, char reply[]) {
// The TLS handshake (cert-bundle verify) + JSON parse / HTTPUpdate use far more
- // stack than the ~8 KB loop task offers — especially when reached via the deep
+ // stack than the ~8 KB loop task offers - especially when reached via the deep
// mesh-receive call chain (it overflows the loopTask canary). Run the work in a
// dedicated 24 KB-stack task and block here until it finishes. The big stack is
// freed when the task exits; on a successful update the chip reboots inside it.
@@ -510,7 +510,7 @@ bool ESP32Board::otaFromManifestImpl(const char* current_ver, bool dry_run, char
}
// --- Fetch this variant's slim manifest ----------------------------------
- // /.json — a ~180 byte per-variant file, not the
+ // /.json - a ~180 byte per-variant file, not the
// full config.json.
char murl[200];
HTTPClient http;
@@ -519,7 +519,7 @@ bool ESP32Board::otaFromManifestImpl(const char* current_ver, bool dry_run, char
if (dry_run) {
// `ota check`: fetch over PLAIN HTTP. With no TLS handshake the fetch costs
// negligible heap, so the check runs with the MQTT bridge UP even on no-PSRAM
- // — where the cert-bundle TLS verify would otherwise exhaust internal heap
+ // - where the cert-bundle TLS verify would otherwise exhaust internal heap
// alongside the two live MQTT TLS sessions (free heap collapses to a few KB
// and the handshake + the bridge both fail). This only reads version info; the
// firmware download below (ota update) is always TLS-verified. Requires the
@@ -591,7 +591,7 @@ bool ESP32Board::otaFromManifestImpl(const char* current_ver, bool dry_run, char
return false;
}
- // Partition compatibility: prefer the precise per-build signature — compare the
+ // Partition compatibility: prefer the precise per-build signature - compare the
// target build's partition layout (manifest partSig) to what's actually flashed
// on THIS device. Refuse only on a real mismatch (OTA can't rewrite the table).
// Fall back to the legacy bool for manifests that predate partSig.
diff --git a/src/helpers/ESP32Board.h b/src/helpers/ESP32Board.h
index 670410a9..96fa20bd 100644
--- a/src/helpers/ESP32Board.h
+++ b/src/helpers/ESP32Board.h
@@ -185,7 +185,7 @@ public:
bool isOTAUpdateRunning() const override { return ota_server != nullptr; }
bool otaFromManifest(const char* current_ver, bool dry_run, char reply[]) override;
// Heavy body (TLS + JSON / HTTPUpdate). Runs in a dedicated large-stack task
- // spawned by otaFromManifest() — public only so that task entry point can call
+ // spawned by otaFromManifest() - public only so that task entry point can call
// it; not meant to be invoked directly.
bool otaFromManifestImpl(const char* current_ver, bool dry_run, char reply[]);
diff --git a/src/helpers/MQTTMessageBuilder.h b/src/helpers/MQTTMessageBuilder.h
index c3f10b69..39128996 100644
--- a/src/helpers/MQTTMessageBuilder.h
+++ b/src/helpers/MQTTMessageBuilder.h
@@ -27,7 +27,7 @@ public:
* Always UTC with an explicit "+00:00" offset, ISO-8601
* "%Y-%m-%dT%H:%M:%S.uuuuuu+00:00" (matches Python
* datetime.now(timezone.utc).isoformat()). The `timezone` parameter is retained
- * for API compatibility but ignored — the system clock is UTC.
+ * for API compatibility but ignored - the system clock is UTC.
*
* `usec` is the sub-second component in microseconds (0..999999), normally taken
* from the same gettimeofday() read as `now` so the two don't tear at a second
diff --git a/src/helpers/MQTTPresets.h b/src/helpers/MQTTPresets.h
index 9069f650..ed25337a 100644
--- a/src/helpers/MQTTPresets.h
+++ b/src/helpers/MQTTPresets.h
@@ -3,7 +3,7 @@
#include
// Maximum number of configurable MQTT connection slots (available to all builds for struct layout).
-// Used in NodePrefs/MQTTPrefs for persistent storage — do NOT change without migration.
+// Used in NodePrefs/MQTTPrefs for persistent storage - do NOT change without migration.
static const int MAX_MQTT_SLOTS = 6;
// Runtime slot array size: fewer slots on non-PSRAM boards to save ~1.2KB of heap.
diff --git a/src/helpers/RegionMap.cpp b/src/helpers/RegionMap.cpp
index 620320d4..998e1425 100644
--- a/src/helpers/RegionMap.cpp
+++ b/src/helpers/RegionMap.cpp
@@ -457,7 +457,7 @@ size_t RegionMap::exportTo(char *dest, size_t max_len) const {
if (!dest || max_len == 0) return 0;
BufStream bs(dest, max_len);
- exportTo(bs); // ← reuse existing logic
+ exportTo(bs); // <- reuse existing logic
return bs.length();
}
diff --git a/src/helpers/RxReservePacketManager.h b/src/helpers/RxReservePacketManager.h
index 88c5a767..fce0672a 100644
--- a/src/helpers/RxReservePacketManager.h
+++ b/src/helpers/RxReservePacketManager.h
@@ -4,7 +4,7 @@
#include
// Fork-owned (not upstream-tracked). Observer builds capture every received packet
-// to MQTT, but RX processing needs a free pool packet first — Dispatcher::checkRecv()
+// to MQTT, but RX processing needs a free pool packet first - Dispatcher::checkRecv()
// discards the received bytes before logRx() when allocNew() fails. Under duty-cycle
// throttling the outbound queue can park the entire pool waiting on TX budget, which
// starves RX allocation and silently caps MQTT capture at the TX rate (each completed
@@ -19,7 +19,7 @@
// own responses/ACKs (pri 0) and login/PATH replies (pri 1) still queue. Below the
// smaller emergency floor everything is shed to keep capture alive.
// 2. Stale-packet expiry. A queued packet still untransmitted STALE_OUTBOUND_MS past
-// its scheduled time is dropped at the next dequeue — a repeat delayed that long is
+// its scheduled time is dropped at the next dequeue - a repeat delayed that long is
// noise (the flood has long since propagated), and a CLI response that old has
// already timed out at the client. Under normal load the queue drains in
// milliseconds and this never triggers; under throttle it frees the pool and lets
diff --git a/src/helpers/SNMPAgent.h b/src/helpers/SNMPAgent.h
index c1bfe0f5..d2488735 100644
--- a/src/helpers/SNMPAgent.h
+++ b/src/helpers/SNMPAgent.h
@@ -6,7 +6,7 @@
#include
#include
-// Temporary private enterprise OID base — replace with registered PEN when available.
+// Temporary private enterprise OID base - replace with registered PEN when available.
// All MeshCore OIDs live under this subtree.
#define MESHCORE_OID_BASE ".1.3.6.1.4.1.99999"
diff --git a/src/helpers/TxtDataHelpers.cpp b/src/helpers/TxtDataHelpers.cpp
index f994c9de..5d9f2ef0 100644
--- a/src/helpers/TxtDataHelpers.cpp
+++ b/src/helpers/TxtDataHelpers.cpp
@@ -156,7 +156,7 @@ const char* StrHelper::ftoa(float f) {
const char* StrHelper::ftoa3(float f) {
static char s[16];
- int v = (int)(f * 1000.0f + (f >= 0 ? 0.5f : -0.5f)); // rounded ×1000
+ int v = (int)(f * 1000.0f + (f >= 0 ? 0.5f : -0.5f)); // rounded x1000
int w = v / 1000; // whole
int d = abs(v % 1000); // decimals
snprintf(s, sizeof(s), "%d.%03d", w, d);
diff --git a/src/helpers/bridges/MQTTBridge.cpp b/src/helpers/bridges/MQTTBridge.cpp
index e41962a9..92da9ec9 100644
--- a/src/helpers/bridges/MQTTBridge.cpp
+++ b/src/helpers/bridges/MQTTBridge.cpp
@@ -489,7 +489,7 @@ MQTTBridge::MQTTBridge(const MQTTNodeInfo& node_info, MQTTPrefs *obs,
_slots[i].enabled = false;
_slots[i].client = nullptr;
_slots[i].preset = nullptr;
- // auth_token[0] == '\0' after memset above — no valid token
+ // auth_token[0] == '\0' after memset above - no valid token
_slots[i].connected = false;
_slots[i].initial_connect_done = false;
_slots[i].token_expires_at = 0;
@@ -530,7 +530,7 @@ MQTTBridge::MQTTBridge(const MQTTNodeInfo& node_info, MQTTPrefs *obs,
#endif
// On PSRAM boards, allocate raw radio buffer and JSON char buffers in PSRAM to preserve
- // internal heap. On non-PSRAM boards these are inline arrays in the class object —
+ // internal heap. On non-PSRAM boards these are inline arrays in the class object -
// no separate allocation needed.
#if defined(BOARD_HAS_PSRAM)
_last_raw_data = (uint8_t*)psram_malloc(LAST_RAW_DATA_SIZE);
@@ -539,7 +539,7 @@ MQTTBridge::MQTTBridge(const MQTTNodeInfo& node_info, MQTTPrefs *obs,
#else
memset(_last_raw_data, 0, sizeof(_last_raw_data));
#endif
- // JSON document scratch space is now a StaticJsonDocument inline class member —
+ // JSON document scratch space is now a StaticJsonDocument inline class member -
// no heap allocation needed; reused via doc.clear() on every publish.
}
@@ -863,16 +863,16 @@ void MQTTBridge::end() {
destroySlotClients();
// Timezone is inline class storage (_timezone_storage) since Phase 3 of
- // the MQTT memory-defrag work — nothing to delete. _timezone always
+ // the MQTT memory-defrag work - nothing to delete. _timezone always
// points at &_timezone_storage and stays valid for the bridge lifetime.
- // Free PSRAM-backed buffers (non-PSRAM builds use inline class arrays — no free needed)
+ // Free PSRAM-backed buffers (non-PSRAM builds use inline class arrays - no free needed)
#if defined(BOARD_HAS_PSRAM)
psram_free(_last_raw_data); _last_raw_data = nullptr;
psram_free(_publish_json_buffer); _publish_json_buffer = nullptr;
psram_free(_status_json_buffer); _status_json_buffer = nullptr;
#endif
- // JSON documents are now StaticJsonDocument inline members — no heap allocation to free.
+ // JSON documents are now StaticJsonDocument inline members - no heap allocation to free.
_initialized = false;
_slots_setup_done = false; // Reset so deferred setup runs again on next begin()
@@ -907,8 +907,8 @@ void MQTTBridge::initializeWiFiInTask() {
WiFi.setAutoConnect(true);
// Set up WiFi event handlers for better diagnostics and immediate disconnection
- // detection. Register ONCE — the bridge is reused across restarts (e.g. stopped
- // for `ota check`/`ota update`, or `set mqtt…` reconfigure) and WiFi.onEvent()
+ // detection. Register ONCE - the bridge is reused across restarts (e.g. stopped
+ // for `ota check`/`ota update`, or `set mqtt...` reconfigure) and WiFi.onEvent()
// never removes prior callbacks, so re-registering leaks handlers and duplicates
// every log line.
if (!_wifi_event_registered) {
@@ -935,7 +935,7 @@ void MQTTBridge::initializeWiFiInTask() {
// Only (re)start the WiFi association if it isn't already up. end() leaves the
// STA link connected, so on a restart (e.g. after `ota check`) calling
- // WiFi.begin() again forces a needless disconnect/reconnect — which also races
+ // WiFi.begin() again forces a needless disconnect/reconnect - which also races
// the MQTT task's first DNS lookup (getaddrinfo fails until WiFi/DNS recovers).
// When already connected, the deferred slot setup still fires in mqttTaskLoop()
// because _ntp_synced persists across end() (only _slots_setup_done is reset).
@@ -944,7 +944,7 @@ void MQTTBridge::initializeWiFiInTask() {
WiFi.begin(_obs->wifi_ssid, _obs->wifi_password);
}
} else if (!_ntp_synced && !_ntp_sync_pending) {
- _ntp_sync_pending = true; // already connected but never synced — kick NTP now
+ _ntp_sync_pending = true; // already connected but never synced - kick NTP now
}
// NOTE: Slot setup is deferred until after NTP sync in mqttTaskLoop().
@@ -989,7 +989,7 @@ void MQTTBridge::mqttTaskLoop() {
unsigned long now = millis();
bool wifi_just_connected = handleWiFiConnection(now);
if (wifi_just_connected) {
- // WiFi recovered — reset last_reconnect_attempt for disconnected slots so they
+ // WiFi recovered - reset last_reconnect_attempt for disconnected slots so they
// retry immediately rather than waiting up to 5 min for backoff timers to expire.
for (int i = 0; i < RUNTIME_MQTT_SLOTS; i++) {
if (_slots[i].enabled && _slots[i].initial_connect_done && !_slots[i].connected) {
@@ -1025,7 +1025,7 @@ void MQTTBridge::mqttTaskLoop() {
}
// Process a CLI-requested NTP connectivity diagnostic (queued from Core 1).
- // Probe-only — never touches the system clock.
+ // Probe-only - never touches the system clock.
if (_ntp_diag_requested) {
_ntp_diag_requested = false;
runNtpDiagProbe();
@@ -1066,7 +1066,7 @@ void MQTTBridge::mqttTaskLoop() {
}
char reason[80];
if (!isSlotReady(i, reason, sizeof(reason))) {
- MQTT_DEBUG_PRINTLN("MQTT%d not ready — run '%s' to connect", i + 1, reason);
+ MQTT_DEBUG_PRINTLN("MQTT%d not ready - run '%s' to connect", i + 1, reason);
continue;
}
setupSlot(i);
@@ -1096,7 +1096,7 @@ void MQTTBridge::mqttTaskLoop() {
processPacketQueue();
#ifdef WITH_SNMP
- // SNMP agent loop — process incoming UDP requests
+ // SNMP agent loop - process incoming UDP requests
if (_snmp_agent) {
if (!_snmp_agent->isRunning() && WiFi.isConnected() && _obs->snmp_enabled) {
_snmp_agent->begin(_obs->snmp_community);
@@ -1117,7 +1117,7 @@ void MQTTBridge::mqttTaskLoop() {
// Periodic configuration check (throttled to avoid spam)
checkConfigurationMismatch();
- // Periodic NTP refresh (every hour) — lightweight, non-blocking.
+ // Periodic NTP refresh (every hour) - lightweight, non-blocking.
// Uses async SNTP instead of the heavy syncTimeWithNTP() which blocks Core 0
// for up to 20+ seconds with DNS lookups, UDP sockets, and retry loops.
if (WiFi.status() == WL_CONNECTED && now - _last_ntp_sync > 3600000) {
@@ -1204,7 +1204,7 @@ void MQTTBridge::mqttTaskLoop() {
// Allocate one PsychicMqttClient per slot and register its persistent callbacks.
// Called exactly once per bridge lifetime from begin(); the objects live until
// destroySlotClients(). Reconfiguring a slot (preset change, JWT renewal,
-// reconnect) reuses the same client — no delete/new cycles, so the mbedTLS
+// reconnect) reuses the same client - no delete/new cycles, so the mbedTLS
// context and its ~40 KB of internal-heap buffers are allocated once instead
// of every reconfigure.
void MQTTBridge::initSlotClients() {
@@ -1248,7 +1248,7 @@ void MQTTBridge::initSlotClients() {
_slots[index].last_sock_errno = error.esp_transport_sock_errno;
_slots[index].last_error_time = millis();
if (error.error_type == MQTT_ERROR_TYPE_CONNECTION_REFUSED) {
- // Broker rejected the MQTT CONNECT itself — not a transport failure.
+ // Broker rejected the MQTT CONNECT itself - not a transport failure.
// return code: 1=protocol, 2=client-id rejected, 3=server unavailable,
// 4=bad username/password, 5=not authorized. Codes 3/4/5 point at a
// server-side lockout or auth problem rather than the network.
@@ -1293,9 +1293,9 @@ void MQTTBridge::setupSlot(int index) {
}
// Persistent client is expected to have been allocated by initSlotClients().
- // If it hasn't, we can't proceed — bail loudly rather than silently leaking.
+ // If it hasn't, we can't proceed - bail loudly rather than silently leaking.
if (slot.client == nullptr) {
- MQTT_DEBUG_PRINTLN("MQTT%d: setupSlot before initSlotClients() — skipping", index + 1);
+ MQTT_DEBUG_PRINTLN("MQTT%d: setupSlot before initSlotClients() - skipping", index + 1);
return;
}
@@ -1367,7 +1367,7 @@ void MQTTBridge::setupSlot(int index) {
}
}
} else {
- // Custom broker slot — build persistent URI
+ // Custom broker slot - build persistent URI
// If host already has a scheme (mqtt://, mqtts://, ws://, wss://), preserve the full URI
// (including optional path/query) and only inject :port when the authority has no explicit port.
// Otherwise, infer protocol from port number.
@@ -1447,7 +1447,7 @@ void MQTTBridge::setupSlot(int index) {
MQTT_DEBUG_PRINTLN("MQTT%d TLS: no embedded cert bundle available", index + 1);
}
} else {
- // Global bundle already loaded — just attach the callback for this client.
+ // Global bundle already loaded - just attach the callback for this client.
slot.client->attachArduinoCACertBundle(true);
}
MQTT_DEBUG_PRINTLN("MQTT%d TLS verify: CA bundle %s", index + 1,
@@ -1458,7 +1458,7 @@ void MQTTBridge::setupSlot(int index) {
// Custom slot authentication: JWT if audience is set, else username/password
if (slot.audience[0] != '\0') {
- // JWT auth for custom slot — create initial token (buffer is always inline)
+ // JWT auth for custom slot - create initial token (buffer is always inline)
createSlotAuthToken(index);
if (slot.auth_token[0] != '\0') {
slot.client->setCredentials(_jwt_username, slot.auth_token);
@@ -1609,7 +1609,7 @@ void MQTTBridge::maintainSlotConnection(int index, unsigned long now_millis, uns
_radio ? _radio->getRadioState() : -1,
(_radio && _radio->getLastRecvMillis() > 0) ? (_ms->getMillis() - _radio->getLastRecvMillis()) : 0);
} else {
- // Token renewed but old one still valid — just update credentials for next reconnect
+ // Token renewed but old one still valid - just update credentials for next reconnect
slot.client->setCredentials(_jwt_username, slot.auth_token);
}
} else {
@@ -1690,7 +1690,7 @@ void MQTTBridge::maintainSlotConnection(int index, unsigned long now_millis, uns
if (slot_uses_jwt) {
// Always lightweight reconnect on the persistent client. A stale/expired
// token is handled by regenerating it in place and updating credentials
- // — no teardown is needed because the client and its mbedTLS context
+ // - no teardown is needed because the client and its mbedTLS context
// persist for the bridge lifetime.
if (createSlotAuthToken(index)) {
slot.client->setCredentials(_jwt_username, slot.auth_token);
@@ -1700,7 +1700,7 @@ void MQTTBridge::maintainSlotConnection(int index, unsigned long now_millis, uns
}
slot.client->reconnect();
} else {
- // Non-JWT slots — lightweight reconnect on existing client.
+ // Non-JWT slots - lightweight reconnect on existing client.
MQTT_DEBUG_PRINTLN("MQTT%d reconnect (non-JWT, backoff %d)", index + 1, slot.reconnect_backoff);
slot.client->reconnect();
}
@@ -1779,7 +1779,7 @@ bool MQTTBridge::publishToSlot(int index, const char* topic, const char* payload
}
// QoS 0 for the high-rate packet/raw publish paths: no PUBACK, no outbox store,
- // no per-message heap alloc — critical for non-PSRAM fragmentation. QoS 1 is used
+ // no per-message heap alloc - critical for non-PSRAM fragmentation. QoS 1 is used
// only for low-rate retained status messages where delivery matters.
//
// esp_mqtt_client_enqueue return convention: QoS 0 returns msg_id == 0 on success
@@ -1920,7 +1920,7 @@ void MQTTBridge::publishStatusToSlot(int index) {
char radio_info[64];
// Status timestamp: UTC with explicit +00:00 offset, same as packet/raw JSON
- // `timestamp` (system clock is UTC — SNTP offset 0; prefs Timezone is separate).
+ // `timestamp` (system clock is UTC - SNTP offset 0; prefs Timezone is separate).
struct timeval now_tv;
gettimeofday(&now_tv, nullptr);
MQTTMessageBuilder::formatIsoTimestampForMqtt(now_tv.tv_sec, now_tv.tv_usec, _timezone, timestamp, sizeof(timestamp));
@@ -2055,7 +2055,7 @@ void MQTTBridge::applySlotPreset(int slot_index, const char* preset_name) {
if (_initialized) {
char reason[80];
if (!isSlotReady(slot_index, reason, sizeof(reason))) {
- MQTT_DEBUG_PRINTLN("MQTT%d (%s) not ready — run '%s' to connect", slot_index + 1, preset_name, reason);
+ MQTT_DEBUG_PRINTLN("MQTT%d (%s) not ready - run '%s' to connect", slot_index + 1, preset_name, reason);
return;
}
setupSlot(slot_index);
@@ -2082,11 +2082,11 @@ void MQTTBridge::setSlotCustomBroker(int slot_index, const char* host, uint16_t
// ---------------------------------------------------------------------------
void MQTTBridge::checkConfigurationMismatch() {
- // Warn if packets are enabled but both rx and tx are off — nothing will be published
+ // Warn if packets are enabled but both rx and tx are off - nothing will be published
if (_obs->mqtt_packets_enabled && !_obs->mqtt_rx_enabled && _obs->mqtt_tx_enabled == 0) {
unsigned long now = millis();
if (_last_config_warning == 0 || (now - _last_config_warning > CONFIG_WARNING_INTERVAL)) {
- MQTT_DEBUG_PRINTLN("MQTT: Both mqtt.rx and mqtt.tx are off — no packets will be published. Run 'set mqtt.rx on' or 'set mqtt.tx on' to fix.");
+ MQTT_DEBUG_PRINTLN("MQTT: Both mqtt.rx and mqtt.tx are off - no packets will be published. Run 'set mqtt.rx on' or 'set mqtt.tx on' to fix.");
_last_config_warning = now;
}
} else {
@@ -2283,7 +2283,7 @@ void MQTTBridge::loop() {
// Periodic configuration check (throttled to avoid spam)
checkConfigurationMismatch();
- // Periodic NTP refresh (every hour) — lightweight, non-blocking.
+ // Periodic NTP refresh (every hour) - lightweight, non-blocking.
if (WiFi.status() == WL_CONNECTED && millis() - _last_ntp_sync > 3600000) {
refreshNTP();
}
@@ -2377,7 +2377,7 @@ void MQTTBridge::sendPacket(mesh::Packet *packet) {
if (tx_mode == 2) {
if (packet->getPayloadType() != PAYLOAD_TYPE_ADVERT) return;
if (packet->payload_len < PUB_KEY_SIZE) return;
- // Advert payload starts with advertiser's 32-byte public key — compare to our identity
+ // Advert payload starts with advertiser's 32-byte public key - compare to our identity
if (!_identity || memcmp(_identity->pub_key, packet->payload, PUB_KEY_SIZE) != 0) return;
}
@@ -2458,7 +2458,7 @@ void MQTTBridge::processPacketQueue() {
break;
}
- // Update Core 0-owned last-raw-data for publishStatus() — no mutex needed since
+ // Update Core 0-owned last-raw-data for publishStatus() - no mutex needed since
// _last_raw_data is now written only here (Core 0) and read only by publishStatus() (Core 0).
if (!queued.is_tx && queued.has_raw_data && _last_raw_data) {
memcpy(_last_raw_data, queued.raw_data, queued.raw_len);
@@ -2641,7 +2641,7 @@ bool MQTTBridge::publishStatus() {
char radio_info[64];
// Status timestamp: UTC with explicit +00:00 offset, same as packet/raw JSON
- // `timestamp` (system clock is UTC — SNTP offset 0; prefs Timezone is separate).
+ // `timestamp` (system clock is UTC - SNTP offset 0; prefs Timezone is separate).
struct timeval now_tv;
gettimeofday(&now_tv, nullptr);
MQTTMessageBuilder::formatIsoTimestampForMqtt(now_tv.tv_sec, now_tv.tv_usec, _timezone, timestamp, sizeof(timestamp));
@@ -2731,7 +2731,7 @@ bool MQTTBridge::publishPacket(mesh::Packet* packet, bool is_tx,
// Memory pressure check: Skip publishes when there's not enough contiguous
// heap for the publish itself (JSON buffer + esp-mqtt outbox frame + WiFi TX
- // path). Headroom only — NOT an mbedTLS preflight: persistent clients keep
+ // path). Headroom only - NOT an mbedTLS preflight: persistent clients keep
// their TLS contexts allocated for the bridge lifetime, so the old ~52 KB
// "reserve space for reconnect" guard is obsolete post Phase 1. Publish
// payload is capped at PUBLISH_JSON_BUFFER_SIZE (2 KB); 8 KB is a safe
@@ -2780,7 +2780,7 @@ bool MQTTBridge::publishPacket(mesh::Packet* packet, bool is_tx,
strncpy(origin_id, _device_id, sizeof(origin_id) - 1);
origin_id[sizeof(origin_id) - 1] = '\0';
- // Firmware rebroadcast "score" for this packet — only meaningful for RX packets
+ // Firmware rebroadcast "score" for this packet - only meaningful for RX packets
// (depends on receive SNR). Recomputed here exactly as Dispatcher::checkRecv() does,
// via the radio's packetScore(snr, len); NaN signals "omit" (tx, or no radio).
// buildPacketMessage scales it x1000 to match the integer in the serial RX log.
@@ -2904,14 +2904,14 @@ void MQTTBridge::queuePacket(mesh::Packet* packet, bool is_tx) {
QueuedPacket queued;
memset(&queued, 0, sizeof(QueuedPacket));
- queued.packet_copy = *packet; // full value copy — safe from Dispatcher free
+ queued.packet_copy = *packet; // full value copy - safe from Dispatcher free
queued.timestamp = millis();
queued.is_tx = is_tx;
queued.snr = 0.0f;
queued.rssi = 0.0f;
// Consume staged raw data (written by storeRawRadioData() on Core 1, same call sequence).
- // No mutex needed — both sites run on Core 1 before xQueueSend() crosses the core boundary.
+ // No mutex needed - both sites run on Core 1 before xQueueSend() crosses the core boundary.
if (!is_tx && _staged_raw_valid) {
if (_staged_raw_len <= (int)sizeof(queued.raw_data)) {
memcpy(queued.raw_data, _staged_raw, _staged_raw_len);
@@ -2959,7 +2959,7 @@ void MQTTBridge::queuePacket(mesh::Packet* packet, bool is_tx) {
QueuedPacket& queued = _packet_queue[_queue_tail];
memset(&queued, 0, sizeof(QueuedPacket));
- queued.packet_copy = *packet; // full value copy — safe from Dispatcher free
+ queued.packet_copy = *packet; // full value copy - safe from Dispatcher free
queued.timestamp = millis();
queued.is_tx = is_tx;
queued.snr = 0.0f;
@@ -3143,7 +3143,7 @@ bool MQTTBridge::syncTimeWithNTP(bool force, bool primary_only) {
bool slot_jwt = (_slots[i].preset && _slots[i].preset->auth_type == MQTT_AUTH_JWT) ||
(!_slots[i].preset && _slots[i].audience[0] != '\0');
if (_slots[i].enabled && slot_jwt && _slots[i].client) {
- // Token created before NTP corrected the clock — refresh credentials
+ // Token created before NTP corrected the clock - refresh credentials
// in place and reconnect the persistent client. No teardown needed.
if (_slots[i].token_expires_at > 0 && current_time > _slots[i].token_expires_at) {
MQTT_DEBUG_PRINTLN("MQTT%d token stale after time correction, re-creating", i + 1);
@@ -3156,7 +3156,7 @@ bool MQTTBridge::syncTimeWithNTP(bool force, bool primary_only) {
}
}
- // Set timezone from string (with DST support) — only if changed.
+ // Set timezone from string (with DST support) - only if changed.
// Reuses the inline _timezone_storage via setRules() instead of
// deleting/newing a Timezone, which was a per-change heap alloc pair.
static char last_timezone[64] = "";
@@ -3204,7 +3204,7 @@ bool MQTTBridge::requestForcedNtpSync(uint32_t timeout_ms) {
// Runs on the MQTT task (Core 0). Probes every configured NTP server for connectivity
// and records the time each reports. Deliberately does NOT call configTime() or update
-// the RTC — this is a read-only diagnostic and must leave the system clock untouched.
+// the RTC - this is a read-only diagnostic and must leave the system clock untouched.
void MQTTBridge::runNtpDiagProbe() {
const char* servers[kMaxNtpServers];
int count = 0;
@@ -3309,7 +3309,7 @@ bool MQTTBridge::ntpDiag(char* reply, size_t reply_size, bool verbose) {
int n = snprintf(reply + used, reply_size - used, "%s%s %s",
used ? "\n" : "", r.server, r.ok ? "ok" : "fail");
if (n < 0 || (size_t)n >= reply_size - used) {
- reply[used] = '\0'; // out of room — truncate cleanly
+ reply[used] = '\0'; // out of room - truncate cleanly
break;
}
used += (size_t)n;
@@ -3324,7 +3324,7 @@ bool MQTTBridge::ntpDiag(char* reply, size_t reply_size, bool verbose) {
// Populates dst_out and std_out with the DST/standard TimeChangeRules for the
// given timezone string. Returns true on match, false on unknown strings. Zero
-// heap allocation — the caller then passes these into Timezone::setRules() on
+// heap allocation - the caller then passes these into Timezone::setRules() on
// an existing Timezone object.
bool MQTTBridge::timezoneRulesFromString(const char* tz_string, TimeChangeRule& dst_out, TimeChangeRule& std_out) {
// GCC refuses to implicitly build a TimeChangeRule temporary from a bare
@@ -3492,7 +3492,7 @@ void MQTTBridge::optimizeMqttClientConfig(PsychicMqttClient* client, bool needs_
// Keep the output buffer (used to build the CONNECT/PUBLISH frames) in lockstep
// with the input buffer. setBufferSize() above only sets buffer.size, so out_size
// must be set here. The previous conditional only ever shrank out_size or set it
- // from 0 — when a slot was reconfigured from a small non-JWT buffer (512) up to
+ // from 0 - when a slot was reconfigured from a small non-JWT buffer (512) up to
// the JWT buffer (896), out_size stayed at 512 and the JWT CONNECT frame (username
// + ~537-768B token) overflowed it, producing esp-mqtt "Connect message cannot be
// created". Always matching MQTT_CLIENT_BUFFER_SIZE fixes the grow case.
diff --git a/src/helpers/bridges/MQTTBridge.h b/src/helpers/bridges/MQTTBridge.h
index 24bdb1fe..64bf7ae7 100644
--- a/src/helpers/bridges/MQTTBridge.h
+++ b/src/helpers/bridges/MQTTBridge.h
@@ -111,7 +111,7 @@ private:
unsigned long last_log_time; // Throttle disconnect log messages
unsigned long last_deferred_log_ms; // Throttle "connect deferred" log spam (Phase 1)
- // Last error (stored for CLI diagnostics — serial-free debugging)
+ // Last error (stored for CLI diagnostics - serial-free debugging)
int32_t last_tls_err; // esp_tls_last_esp_err (0 = no error)
int32_t last_tls_stack_err; // mbedTLS stack error
int last_sock_errno; // socket errno
@@ -168,7 +168,7 @@ private:
#if defined(BOARD_HAS_PSRAM)
static const int MAX_QUEUE_SIZE = 50;
#else
- // Reduced from 10: raw_data[256] adds ~256 bytes/item; 6×543 ≈ 3.3 KB vs old 10×282 ≈ 2.8 KB.
+ // Reduced from 10: raw_data[256] adds ~256 bytes/item; 6x543 ~ 3.3 KB vs old 10x282 ~ 2.8 KB.
// Net increase is <500 bytes; _last_raw_data (256 bytes) is eliminated to offset further.
static const int MAX_QUEUE_SIZE = 6;
#endif
@@ -252,7 +252,7 @@ private:
Timezone* _timezone;
// Core 1-only staging: written by storeRawRadioData(), consumed by queuePacket().
- // No mutex needed — both call sites run on Core 1 in guaranteed sequence.
+ // No mutex needed - both call sites run on Core 1 in guaranteed sequence.
static const size_t LAST_RAW_DATA_SIZE = 256;
uint8_t _staged_raw[LAST_RAW_DATA_SIZE];
int _staged_raw_len = 0;
@@ -260,7 +260,7 @@ private:
float _staged_rssi = 0.0f;
bool _staged_raw_valid = false;
- // Core 0-owned copy of the most recent raw data — written only by processPacketQueue()
+ // Core 0-owned copy of the most recent raw data - written only by processPacketQueue()
// on Core 0, read only by publishStatus() on Core 0. No mutex required.
// On PSRAM boards: heap pointer into PSRAM. On non-PSRAM: inline array in class object.
static const size_t LAST_RAW_DATA_SIZE_MEMBER = 256; // mirrors LAST_RAW_DATA_SIZE
@@ -274,7 +274,7 @@ private:
float _last_rssi;
unsigned long _last_raw_timestamp;
- // JSON publish/status serialization buffers — reused for every publish (no alloc/free churn).
+ // JSON publish/status serialization buffers - reused for every publish (no alloc/free churn).
// On PSRAM boards: heap pointer into PSRAM to save internal heap. On non-PSRAM: inline in
// class object so these allocations don't interleave with large TLS buffers at startup.
static const size_t PUBLISH_JSON_BUFFER_SIZE = 2048;
@@ -287,14 +287,14 @@ private:
char _status_json_buffer[STATUS_JSON_BUFFER_SIZE];
#endif
- // JSON document scratch space — inline StaticJsonDocument keeps the pool off the MQTT
+ // JSON document scratch space - inline StaticJsonDocument keeps the pool off the MQTT
// task stack and eliminates two separate heap allocations (fragmentation reduction).
StaticJsonDocument _packet_json_doc;
StaticJsonDocument _status_json_doc;
// Memory pressure monitoring (per-publish skip; see publishPacket()).
// The broader fragmentation-recovery machinery was removed in Phase 4 of
- // the MQTT memory-defrag work — persistent MQTT clients no longer churn
+ // the MQTT memory-defrag work - persistent MQTT clients no longer churn
// the heap, so gray-zone / critical-restart trackers are unnecessary.
unsigned long _last_memory_check;
int _skipped_publishes; // Exposed via SNMP; count of publishes skipped when max_alloc is too low
@@ -330,7 +330,7 @@ private:
bool _wifi_status_initialized;
unsigned long _wifi_disconnected_time; // 0 when connected
unsigned long _last_wifi_reconnect_attempt;
- uint8_t _wifi_reconnect_backoff_attempt; // 0..5 → 15s, 30s, 60s, 120s, 300s; reset on connect
+ uint8_t _wifi_reconnect_backoff_attempt; // 0..5 -> 15s, 30s, 60s, 120s, 300s; reset on connect
unsigned long _last_slot_reconnect_ms; // guards against concurrent TLS handshakes (15 s inter-slot gap)
// Optional pointers for collecting stats internally (set by mesh if available)
@@ -351,7 +351,7 @@ private:
// - destroySlotClients() disconnects and deletes each client. Runs once in end().
// - setupSlot() configures an already-allocated client (server, credentials,
// CA) and calls connect(). Safe to call multiple times to reconfigure.
- // - teardownSlot() only disconnects — it never deletes the client. Leaves
+ // - teardownSlot() only disconnects - it never deletes the client. Leaves
// the mbedTLS/transport state ready for a subsequent setupSlot().
// This avoids delete/new cycles that shed ~40 KB of mbedTLS buffers per
// reconfigure and fragment the internal heap on non-PSRAM boards.
@@ -464,7 +464,7 @@ public:
* - getSlotCurrentOutageStartMs(): millis() of the current outage start
* (0 when the slot is connected). Reset on each reconnect.
* - isSlotEnabledAndAttempted(): true when the slot is enabled (preset
- * != "none") and has reached at least one connect attempt — i.e. it is
+ * != "none") and has reached at least one connect attempt - i.e. it is
* meaningful to alarm on its connection state.
* - getSlotPresetName(): preset name for friendly status text. Returns
* "custom"/"none"/preset->name; never null.
diff --git a/src/helpers/esp32/WebConfigServer.cpp b/src/helpers/esp32/WebConfigServer.cpp
index 95c9634a..ee55af86 100644
--- a/src/helpers/esp32/WebConfigServer.cpp
+++ b/src/helpers/esp32/WebConfigServer.cpp
@@ -517,7 +517,7 @@ void WebConfigServer::drainBatch(uint32_t now) {
// Fallback only: the real 3 s reboot timer is armed when the client reads
// /api/config/result (handleConfigResult), so the browser gets its
// confirmation before the AP/WiFi drops. This covers a client that
- // disconnected and never polls — generous enough for a phone that got
+ // disconnected and never polls - generous enough for a phone that got
// bounced off the AP mid-save to rejoin and fetch its confirmation.
_reboot_at = now + 30000;
if (_reboot_at == 0) _reboot_at = 1;
@@ -894,7 +894,7 @@ void WebConfigServer::handleConfigResult(AsyncWebServerRequest* req) {
}
// State stays DONE (re-readable) until the next POST claims the slot.
if (_batch_reboot && !_batch_reboot_armed) {
- // Confirmation delivered — reboot 3 s from now (replaces the 30 s
+ // Confirmation delivered - reboot 3 s from now (replaces the 30 s
// drain-time fallback) so the UI can show its countdown first. Armed
// once; re-reads must not keep pushing the deadline out.
_batch_reboot_armed = true;
diff --git a/src/helpers/esp32/WebConfigServer.h b/src/helpers/esp32/WebConfigServer.h
index 717f8217..1569c5e4 100644
--- a/src/helpers/esp32/WebConfigServer.h
+++ b/src/helpers/esp32/WebConfigServer.h
@@ -117,7 +117,7 @@ public:
static bool getSetupInfo(char* ssid, size_t ssid_len, char* ip, size_t ip_len);
// For the device display: true once a config save completed and the node is
- // about to reboot — ground truth for the user even if the browser lost its
+ // about to reboot - ground truth for the user even if the browser lost its
// connection before the confirmation arrived. Loop task only.
static bool isRebootPending();
diff --git a/src/helpers/nrf52/EthernetCLI.h b/src/helpers/nrf52/EthernetCLI.h
index 34802e56..967e42f0 100644
--- a/src/helpers/nrf52/EthernetCLI.h
+++ b/src/helpers/nrf52/EthernetCLI.h
@@ -38,7 +38,7 @@ static void ethernet_task(void* param) {
Serial.println("ETH: Initializing hardware");
// WB_IO2 (power enable) is already driven HIGH by early constructor
// in RAK4631Board.cpp to support POE boot.
- // Skip hardware reset — the W5100S comes out of power-on reset cleanly,
+ // Skip hardware reset - the W5100S comes out of power-on reset cleanly,
// and toggling reset kills the PHY link which breaks POE power.
pinMode(PIN_ETHERNET_RESET, OUTPUT);
digitalWrite(PIN_ETHERNET_RESET, HIGH);
diff --git a/src/helpers/nrf52/SerialEthernetInterface.cpp b/src/helpers/nrf52/SerialEthernetInterface.cpp
index b25a36b1..fff51cc1 100644
--- a/src/helpers/nrf52/SerialEthernetInterface.cpp
+++ b/src/helpers/nrf52/SerialEthernetInterface.cpp
@@ -26,7 +26,7 @@ bool SerialEthernetInterface::begin() {
// WB_IO2 (power enable) is already driven HIGH by early constructor
// in RAK4631Board.cpp to support POE boot.
- // Skip hardware reset — the W5100S comes out of power-on reset cleanly,
+ // Skip hardware reset - the W5100S comes out of power-on reset cleanly,
// and toggling reset kills the PHY link which breaks POE power.
#ifdef PIN_ETHERNET_RESET
pinMode(PIN_ETHERNET_RESET, OUTPUT);
@@ -138,7 +138,7 @@ size_t SerialEthernetInterface::checkRecvFrame(uint8_t dest[]) {
// Use accept() (not available()) so we only see newly-accepted sockets.
// available() also returns existing connected sockets that have data,
// which would cause us to treat each inbound packet as a "new client"
- // and stop() the underlying socket — disconnecting the companion.
+ // and stop() the underlying socket - disconnecting the companion.
auto newClient = server.accept();
if (newClient) {
IPAddress new_ip = newClient.remoteIP();
diff --git a/src/helpers/ota/BlockBitmap.h b/src/helpers/ota/BlockBitmap.h
index c22835f5..467cbbf8 100644
--- a/src/helpers/ota/BlockBitmap.h
+++ b/src/helpers/ota/BlockBitmap.h
@@ -4,7 +4,7 @@
#include
#include
-// Block-availability helpers (docs/ota_protocol.md §7).
+// Block-availability helpers (docs/ota_protocol.md Section 7).
//
// Availability is *derived* from the staged manifest's leaves[]: block i is present iff its 4-byte
// leaf slot is non-erased (!= FF FF FF FF). No separate persistent structure. A compact bitmap
diff --git a/src/helpers/ota/FirmwareInfo.cpp b/src/helpers/ota/FirmwareInfo.cpp
index c58cbfcc..4b97ffa7 100644
--- a/src/helpers/ota/FirmwareInfo.cpp
+++ b/src/helpers/ota/FirmwareInfo.cpp
@@ -20,7 +20,7 @@ bool find_self_firmware(const uint8_t* region, uint32_t region_len,
if (verify_body) {
uint8_t h[8];
mh8(h, region, body_len);
- if (memcmp(h, region + off + 8, 8) != 0) continue; // coincidental marker — keep scanning
+ if (memcmp(h, region + off + 8, 8) != 0) continue; // coincidental marker - keep scanning
}
out.valid = true;
out.endf_offset = off;
diff --git a/src/helpers/ota/FirmwareInfo.h b/src/helpers/ota/FirmwareInfo.h
index a08bdc75..86ba8776 100644
--- a/src/helpers/ota/FirmwareInfo.h
+++ b/src/helpers/ota/FirmwareInfo.h
@@ -5,7 +5,7 @@
#include "OtaFormat.h"
// Locate the EndF trailer in a firmware image to learn the running firmware's size + identity
-// (docs/ota_protocol.md §2). Portable: operates on a contiguous, readable region — on nRF52/ESP32
+// (docs/ota_protocol.md Section 2). Portable: operates on a contiguous, readable region - on nRF52/ESP32
// the application flash is memory-mapped, so the region pointer is just (const uint8_t*)APP_BASE.
namespace mesh {
@@ -17,15 +17,15 @@ struct SelfFwInfo {
uint32_t image_len = 0; // body_len + ENDF_LEN (what a delta base / full image hashes over)
uint32_t endf_offset = 0; // offset of the "EndF" marker within the region (== body_len)
uint8_t body_hash[8] = {0}; // sha2-256:8 of the body (read from EndF; == a delta's base_hash)
- // Self-describing identity — always present in the fixed 56-byte trailer (zero/"" means "unknown",
+ // Self-describing identity - always present in the fixed 56-byte trailer (zero/"" means "unknown",
// e.g. a dev build with no dotted version).
uint32_t fw_version = 0; // packed MAJOR<<24|MINOR<<16|PATCH<<8|pre
- uint32_t target_id = 0; // sha2-256:4(env) as uint32 — hw+role+partition (fetch routing)
+ uint32_t target_id = 0; // sha2-256:4(env) as uint32 - hw+role+partition (fetch routing)
char hw_id[33] = {0}; // readable hardware tag (NUL-terminated), e.g. "RAK4631"
};
// Scan `region[0..region_len)` for the firmware's EndF trailer. The body starts at offset 0, so the
-// trailer's offset must equal its stored body_len — this uniquely identifies the running firmware's
+// trailer's offset must equal its stored body_len - this uniquely identifies the running firmware's
// EndF even if a staged `.mota` (which contains its own embedded EndF) sits higher in the region.
// If `verify_body` is true the body hash is recomputed and must match (rules out coincidental markers).
bool find_self_firmware(const uint8_t* region, uint32_t region_len,
diff --git a/src/helpers/ota/FolderMotaStore.h b/src/helpers/ota/FolderMotaStore.h
index 9a3ff673..6e60ffba 100644
--- a/src/helpers/ota/FolderMotaStore.h
+++ b/src/helpers/ota/FolderMotaStore.h
@@ -5,7 +5,7 @@
// An OtaStore that captures an in-transit `.mota` onto a HOST folder over the mota-seeder link (the WRITE
// half of MotaSeederProto: OP_STAT/BEGIN/WRITE/SREAD/FIN). This is the destination for `ota pull <#> folder`:
-// blocks stream straight to the host `.mota` — the device holds NO RAM/flash staging for it.
+// blocks stream straight to the host `.mota` - the device holds NO RAM/flash staging for it.
//
// begin(total) -> OP_BEGIN (host creates a 0xFF-filled .mota.part)
// write(off,data) -> OP_WRITE (split into <= MOTA_SEEDER_WRITE_MAX chunks)
@@ -24,13 +24,13 @@ class FolderMotaStore : public OtaStore {
public:
explicit FolderMotaStore(Stream& io, uint32_t timeout_ms = 3000) : _io(io), _to(timeout_ms) {}
- // The container being pulled — set from the chosen `ota pull` mid before begin()/reopen().
+ // The container being pulled - set from the chosen `ota pull` mid before begin()/reopen().
void set_mid(const uint8_t mid[4]) { memcpy(_mid, mid, 4); }
bool begin(uint32_t total_size) override;
bool write(uint32_t off, const uint8_t* data, uint32_t len) override;
bool read(uint32_t off, uint8_t* buf, uint32_t len) const override;
- uint32_t capacity() const override { return 0xF0000000u; } // host disk — effectively unbounded
+ uint32_t capacity() const override { return 0xF0000000u; } // host disk - effectively unbounded
uint32_t staged_size() const override { return _total; }
void clear() override { _total = 0; }
bool finalize() override;
diff --git a/src/helpers/ota/MerkleTree.h b/src/helpers/ota/MerkleTree.h
index 15987f5b..5c82b6d9 100644
--- a/src/helpers/ota/MerkleTree.h
+++ b/src/helpers/ota/MerkleTree.h
@@ -3,7 +3,7 @@
#include
#include
-// Merkle tree over PAYLOAD blocks, sha2-256:4 (4-byte) leaves/nodes. See docs/ota_protocol.md §6.
+// Merkle tree over PAYLOAD blocks, sha2-256:4 (4-byte) leaves/nodes. See docs/ota_protocol.md Section 6.
//
// Scheme: leaf = H(block); node = H(left || right); on an odd level the last node is promoted
// unchanged (no duplication). Root = single remaining node.
diff --git a/src/helpers/ota/MotaContainer.cpp b/src/helpers/ota/MotaContainer.cpp
index dcdd9538..4a4c5ed2 100644
--- a/src/helpers/ota/MotaContainer.cpp
+++ b/src/helpers/ota/MotaContainer.cpp
@@ -11,7 +11,7 @@ bool MotaManifest::is_approved() const {
return approval && memcmp(approval, APPROVAL_YES, 4) == 0;
}
-// Fixed-layout parse (docs/ota_protocol.md §4): every field sits at a constant offset — base_hash(8),
+// Fixed-layout parse (docs/ota_protocol.md Section 4): every field sits at a constant offset - base_hash(8),
// signer_pubkey(32) and signature(64) are ALWAYS present (zero-filled when not applicable), so there are
// no conditionals. Only leaves[]/payload (after `approval`) is variable, read by the caller. The signature
// always covers manifest[0, MOTA_SIGNED_LEN). Returns false on any over-read or bad format_ver.
diff --git a/src/helpers/ota/MotaContainer.h b/src/helpers/ota/MotaContainer.h
index f344fd97..3f6686da 100644
--- a/src/helpers/ota/MotaContainer.h
+++ b/src/helpers/ota/MotaContainer.h
@@ -5,7 +5,7 @@
#include "OtaFormat.h"
// Parse/validate a `.mota` container that is fully present in a RAM buffer (docs/ota_protocol.md
-// §3-§4). Variable-length parts are referenced by pointer into the caller's buffer — no copies, no
+// Section 3-Section 4). Variable-length parts are referenced by pointer into the caller's buffer - no copies, no
// allocation. (Device flash-backed staging gets a streaming variant in a later milestone; the field
// layout here is the single source of truth.)
@@ -24,7 +24,7 @@ struct MotaManifest {
uint8_t codec_id = 0;
uint32_t block_count = 0;
- // Fixed layout (docs/ota_protocol.md §4): every field below sits at a constant offset and is ALWAYS
+ // Fixed layout (docs/ota_protocol.md Section 4): every field below sits at a constant offset and is ALWAYS
// present; base_hash/signer_pubkey/signature are zero-filled when not applicable (full / unsigned).
const uint8_t* merkle_root = nullptr; // 4 @20
const uint8_t* image_hash = nullptr; // 32 @24
diff --git a/src/helpers/ota/MotaSeederProto.h b/src/helpers/ota/MotaSeederProto.h
index e534075b..a5d36593 100644
--- a/src/helpers/ota/MotaSeederProto.h
+++ b/src/helpers/ota/MotaSeederProto.h
@@ -4,12 +4,12 @@
// Wire contract for the "mota-seeder" link: a device (CLIENT) pulls `.mota` bytes on demand from a host
// daemon (SERVER) that owns a folder of `.mota` files. This is the FIRST concrete MotaSource transport
-// (docs/ota_protocol.md §9) — the device speaks it over a dedicated Stream (a spare UART / USB-UART), so
+// (docs/ota_protocol.md Section 9) - the device speaks it over a dedicated Stream (a spare UART / USB-UART), so
// it never contends with the line-based text CLI on the main console.
//
// The device always initiates; every request gets exactly one response. Framing is resync-safe: the
// reader scans for the 2-byte magic, so line noise / a half-read frame just times out and is retried
-// (OTA is lowest priority — eventually-upgradable). All multi-byte fields are little-endian.
+// (OTA is lowest priority - eventually-upgradable). All multi-byte fields are little-endian.
//
// request (device -> host): 'M' 'S' op(1) args... xsum(1 = XOR of op+args)
// response (host -> device): 'm' 's' op(1) status(1) payload... xsum(1 = XOR of all prior)
@@ -50,7 +50,7 @@ static const uint8_t MS_OP_STAT = 0x04; // storage: does .mota exist
static const uint8_t MS_OP_BEGIN = 0x05; // storage: create/truncate .mota (0xFF-filled)
static const uint8_t MS_OP_WRITE = 0x06; // storage: write bytes at offset
static const uint8_t MS_OP_SREAD = 0x07; // storage: read bytes back (resume: recompute missing blocks)
-static const uint8_t MS_OP_FIN = 0x08; // storage: transfer complete — validate + make servable
+static const uint8_t MS_OP_FIN = 0x08; // storage: transfer complete - validate + make servable
static const uint8_t MS_STATUS_OK = 0x00;
static const uint8_t MS_STATUS_ERR = 0x01;
diff --git a/src/helpers/ota/MotaSourceSerial.cpp b/src/helpers/ota/MotaSourceSerial.cpp
index 9b302329..b1ef7a73 100644
--- a/src/helpers/ota/MotaSourceSerial.cpp
+++ b/src/helpers/ota/MotaSourceSerial.cpp
@@ -79,7 +79,7 @@ bool SerialMotaSource::describe(uint8_t idx, MotaDesc& out) {
out.block_count = rd_u32le(w + 22);
out.payload_off = rd_u32le(w + 26);
out.payload_size = rd_u32le(w + 30);
- // bytes [34,38) reserved (zero) — kept for forward compat without changing MOTA_DESC_WIRE
+ // bytes [34,38) reserved (zero) - kept for forward compat without changing MOTA_DESC_WIRE
return true;
}
diff --git a/src/helpers/ota/MotaSourceSerial.h b/src/helpers/ota/MotaSourceSerial.h
index 26b9e298..b4570d3f 100644
--- a/src/helpers/ota/MotaSourceSerial.h
+++ b/src/helpers/ota/MotaSourceSerial.h
@@ -5,7 +5,7 @@
// A MotaSource backed by a host "mota-seeder" daemon over a dedicated Stream (a spare UART / USB-UART).
// The device pulls catalog + bytes on demand (MotaSeederProto.h); the folder image is never held on the
-// device — it streams through. Use a stream that is NOT the text-CLI console so the binary framing never
+// device - it streams through. Use a stream that is NOT the text-CLI console so the binary framing never
// collides with command/log text. Reads block on the Stream up to `timeout_ms` (OTA is lowest priority,
// so a serial round-trip's latency is acceptable; keep the daemon on a fast link).
diff --git a/src/helpers/ota/OtaApply.cpp b/src/helpers/ota/OtaApply.cpp
index 81425f5f..e06afd1c 100644
--- a/src/helpers/ota/OtaApply.cpp
+++ b/src/helpers/ota/OtaApply.cpp
@@ -147,7 +147,7 @@ static int dt_to_write(void* arg, const uint8_t* buf, size_t size) {
// staged container's payload (which lives at/below write_start, disjoint from the working region). The
// decoded image is hashed against the signed image_hash BEFORE arming, so a bad decode never boots; the
// callbacks are bounded to [0, write_start) so they fail gracefully instead of touching the patch.
-// (Sequential is still preferred on ESP32 — it streams straight to the slot with no base-copy; in-place
+// (Sequential is still preferred on ESP32 - it streams straight to the slot with no base-copy; in-place
// exists only for single-artifact distribution. Requires the patch built with --inplace-segment 4096.)
struct InPlaceCtx {
const esp_partition_t* slot; // in-place working memory = slot[0, mem_max)
@@ -182,7 +182,7 @@ static int ip_mem_erase(void* a, uintptr_t addr, size_t n) {
// esp_partition_erase_range requires a SECTOR-aligned size; detools' final in-place segment is partial
// (the image tail past the last full sector). addr is sector-aligned (== --inplace-segment), so round
// the length UP to a full sector. The over-erased bytes are scratch beyond image_size (never hashed),
- // and — since detools processes high→low and erases-before-writing — they are never live patch data.
+ // and - since detools processes high->low and erases-before-writing - they are never live patch data.
const uint32_t SEC = 4096;
if ((uint32_t)addr % SEC != 0) return ip_fail(c, "er!align", (uint32_t)addr, n, 0);
uint32_t len = ((uint32_t)n + SEC - 1) & ~(SEC - 1);
@@ -254,7 +254,7 @@ static bool esp32_inplace_apply(OtaStoreFlashEsp32& store, const MotaManifest& m
st.slot_ok = (memcmp(hh, m.image_hash, 32) == 0);
if (!st.slot_ok) { strcpy(msg, "image_hash MISMATCH after in-place decode"); return false; }
if (esp_ota_set_boot_partition(slot) != ESP_OK) { strcpy(msg, "set_boot failed"); return false; }
- sprintf(msg, "verified%s; in-place decoded %u B, image hash OK — armed, rebooting to apply",
+ sprintf(msg, "verified%s; in-place decoded %u B, image hash OK - armed, rebooting to apply",
m.is_signed() ? " (signer trusted)" : " (unsigned)", (unsigned)m.image_size);
return true;
}
@@ -310,7 +310,7 @@ bool ota_apply_detools_mota(OtaStoreFlashEsp32& store, const SignerAllowlist& al
st.slot_ok = (memcmp(hh, m.image_hash, 32) == 0);
if (!st.slot_ok) { strcpy(msg, "image_hash MISMATCH (slot)"); return false; }
if (esp_ota_set_boot_partition(slot) != ESP_OK) { strcpy(msg, "set_boot failed"); return false; }
- sprintf(msg, "verified%s full image %u B in slot — armed, rebooting to apply",
+ sprintf(msg, "verified%s full image %u B in slot - armed, rebooting to apply",
m.is_signed() ? " (trusted)" : " (unsigned)", (unsigned)m.image_size);
return true;
}
@@ -348,7 +348,7 @@ bool ota_apply_detools_mota(OtaStoreFlashEsp32& store, const SignerAllowlist& al
if (!st.slot_ok) { esp_ota_abort(h); strcpy(msg, "image_hash MISMATCH after decode"); return false; }
if (esp_ota_end(h) != ESP_OK) { strcpy(msg, "ota_end failed"); return false; }
if (esp_ota_set_boot_partition(slot) != ESP_OK) { strcpy(msg, "set_boot failed"); return false; }
- sprintf(msg, "verified%s; decoded %u B, image hash OK — armed, rebooting to apply",
+ sprintf(msg, "verified%s; decoded %u B, image hash OK - armed, rebooting to apply",
m.is_signed() ? " (signer trusted)" : " (unsigned)", (unsigned)m.image_size);
return true;
}
@@ -394,7 +394,7 @@ bool ota_apply_detools_mota(const uint8_t* buf, uint32_t len, const SignerAllowl
if (!st.slot_ok) { esp_ota_abort(h); strcpy(msg, "image_hash MISMATCH after decode"); return false; }
if (esp_ota_end(h) != ESP_OK) { strcpy(msg, "ota_end failed"); return false; }
if (esp_ota_set_boot_partition(out) != ESP_OK) { strcpy(msg, "set_boot failed"); return false; }
- sprintf(msg, "verified%s; decoded %u B, image hash OK — armed, rebooting to apply",
+ sprintf(msg, "verified%s; decoded %u B, image hash OK - armed, rebooting to apply",
m.is_signed() ? " (signer trusted)" : " (unsigned)", (unsigned)m.image_size);
return true;
}
@@ -444,13 +444,13 @@ bool ota_apply_mota_nrf52(const uint8_t* buf, uint32_t len, const SignerAllowlis
memcpy(st.image_hash, m.image_hash, 32);
st.manifest_ok = true;
- // 0) THIS device's bootloader must be able to apply this .mota — otherwise staging + approving + rebooting
+ // 0) THIS device's bootloader must be able to apply this .mota - otherwise staging + approving + rebooting
// just bounces back unchanged (a legacy/stock/older-OTAFIX bootloader). Refuse here, before any reboot.
{
OtaBlCaps bl = ota_bootloader_caps();
- if (!bl.present) { strcpy(msg, "this bootloader has no OTA-apply support — update the bootloader first"); return false; }
+ if (!bl.present) { strcpy(msg, "this bootloader has no OTA-apply support - update the bootloader first"); return false; }
if (bl.apply_abi < m.format_ver || !(bl.codec_mask & (1u << m.codec_id))) {
- snprintf(msg, 159, "bootloader too old to apply this update (bl abi=%u codecs=0x%x; need fmt>=%u codec=%u) — update the bootloader",
+ snprintf(msg, 159, "bootloader too old to apply this update (bl abi=%u codecs=0x%x; need fmt>=%u codec=%u) - update the bootloader",
bl.apply_abi, bl.codec_mask, m.format_ver, m.codec_id);
return false;
}
@@ -487,9 +487,9 @@ bool ota_apply_mota_nrf52(const uint8_t* buf, uint32_t len, const SignerAllowlis
flash_nrf5x_flush();
if (memcmp((const void*)(uintptr_t)approval_addr, APPROVAL_YES, 4) != 0) { strcpy(msg, "approval not set"); return false; }
- // Approved. Do NOT reset here — return so the caller can deliver `msg` to the operator first; the
+ // Approved. Do NOT reset here - return so the caller can deliver `msg` to the operator first; the
// deferred ota_reboot_to_apply() (after the reply is sent) does the actual handoff to the bootloader.
- sprintf(msg, "verified%s; applying — rebooting into bootloader once this reply is sent",
+ sprintf(msg, "verified%s; applying - rebooting into bootloader once this reply is sent",
vr.is_signed ? " (signer trusted)" : " (unsigned)");
return true;
}
diff --git a/src/helpers/ota/OtaApply.h b/src/helpers/ota/OtaApply.h
index 42e2f7b1..e6a6b1f1 100644
--- a/src/helpers/ota/OtaApply.h
+++ b/src/helpers/ota/OtaApply.h
@@ -45,13 +45,13 @@ bool ota_apply_detools_mota(const uint8_t* buf, uint32_t len,
// nRF52 (RAK4631) single-slot apply. The running app can't rewrite itself, so it does NOT decode: it
// runs the gated verification chain (payload hash -> built-for-this-firmware -> signature/trust) and,
-// only if all pass, marks the staged manifest APPROVED in flash. It does NOT reboot — so the caller can
-// first send the result back to the operator — the actual handoff is ota_reboot_to_apply() below.
+// only if all pass, marks the staged manifest APPROVED in flash. It does NOT reboot - so the caller can
+// first send the result back to the operator - the actual handoff is ota_reboot_to_apply() below.
// Returns true (msg = "verified...") when approved, false (msg = the first failing gate) otherwise.
bool ota_apply_mota_nrf52(const uint8_t* buf, uint32_t len,
const SignerAllowlist& allow, ApplyState& st, char* msg);
-// Commit the (already approved/armed) update and reboot into it — does NOT return. Call this only after
+// Commit the (already approved/armed) update and reboot into it - does NOT return. Call this only after
// a successful ota_apply_* AND after the confirmation reply has been delivered, so the operator knows
// the apply started (over LoRa the device then goes silent while the bootloader applies). nRF52: set
// the GPREGRET apply magic + reset (the bootloader does the in-place decode + verify). ESP32: reboot
diff --git a/src/helpers/ota/OtaBlInfo.h b/src/helpers/ota/OtaBlInfo.h
index d22468fa..199cde38 100644
--- a/src/helpers/ota/OtaBlInfo.h
+++ b/src/helpers/ota/OtaBlInfo.h
@@ -8,7 +8,7 @@
// bootloader that silently can't apply (stock Adafruit, or an OLDER OTAFIX predating a `.mota` format
// change) and the device would just come back up unchanged.
//
-// Mirror of Adafruit_nRF52_Bootloader_OTAFIX/src/ota_bl_info.h — keep byte-identical.
+// Mirror of Adafruit_nRF52_Bootloader_OTAFIX/src/ota_bl_info.h - keep byte-identical.
// nRF52 only (the bootloader flash is memory-mapped + readable by the app); a no-op elsewhere.
#if defined(NRF52_PLATFORM)
diff --git a/src/helpers/ota/OtaByteIO.h b/src/helpers/ota/OtaByteIO.h
index d4834cd8..2665c856 100644
--- a/src/helpers/ota/OtaByteIO.h
+++ b/src/helpers/ota/OtaByteIO.h
@@ -3,7 +3,7 @@
#include
#include
-// A tiny bounds-checked little-endian cursor for reading the `.mota` container (docs/ota_protocol.md §3-§4)
+// A tiny bounds-checked little-endian cursor for reading the `.mota` container (docs/ota_protocol.md Section 3-Section 4)
// in a self-documenting way: each field is read by name in order, instead of hand-computed byte offsets
// (`p[0]`, `rd_u32(p+3)`, `p += 89`, `NEED(n)` ...). Any over-read flips `ok` false and yields zero/null, so
// callers parse the whole struct then check `r.ok` once. 32-bit offsets (a container can be >64 KB; the
@@ -25,7 +25,7 @@ inline void wr_u32le(uint8_t* p, uint32_t v) {
p[0] = (uint8_t)v; p[1] = (uint8_t)(v >> 8); p[2] = (uint8_t)(v >> 16); p[3] = (uint8_t)(v >> 24);
}
-// Round to a multiple of `unit` (a power of two — a flash sector/page size). Names the `& ~(unit-1)`
+// Round to a multiple of `unit` (a power of two - a flash sector/page size). Names the `& ~(unit-1)`
// idiom so flash-geometry math in the stores reads as intent (align down / align up).
inline uint32_t align_down(uint32_t x, uint32_t unit) { return x & ~(unit - 1); }
inline uint32_t align_up(uint32_t x, uint32_t unit) { return (x + unit - 1) & ~(unit - 1); }
diff --git a/src/helpers/ota/OtaCli.cpp b/src/helpers/ota/OtaCli.cpp
index 1fd73751..55a6f942 100644
--- a/src/helpers/ota/OtaCli.cpp
+++ b/src/helpers/ota/OtaCli.cpp
@@ -47,7 +47,7 @@ static const char* state_word(OtaManager::FetchState s) {
case OtaManager::FETCHING: return "downloading";
case OtaManager::COMPLETE: return "ready to install";
case OtaManager::FAILED: return "failed";
- case OtaManager::PAUSED: return "paused (folder link lost — reconnect to resume)";
+ case OtaManager::PAUSED: return "paused (folder link lost - reconnect to resume)";
default: return "?";
}
}
@@ -135,18 +135,18 @@ bool handle_ota_command(const char* command, char* reply, mesh::MainBoard& board
c.serving ? "on" : "off", (unsigned)c.manager.servedCount(),
(unsigned)c.allow.count(), (unsigned)c.manager.target(), tenv ? tenv : "?");
#if defined(NRF52_PLATFORM)
- // nRF52 applies via the bootloader — show (cached) whether it can, so `ota get`/`install` won't surprise.
+ // nRF52 applies via the bootloader - show (cached) whether it can, so `ota get`/`install` won't surprise.
// blrc = the bootloader's last in-place-apply code (diagnostic; 0xB8=success, see ota_delta.c).
if (n < 146) n += snprintf(reply + n, 160 - n, " | bl:%s blrc:%02X",
c.bootloaderCaps().present ? "apply" : "NONE", ota_bootloader_last_rc());
#endif
// ---- admin OTA stats: crypto identities (our fw's content-id + body_hash), serving set, live fetch,
- // policy — one dense line. The remote-admin CLI path is admin-gated, so this is admin-only over the
+ // policy - one dense line. The remote-admin CLI path is admin-gated, so this is admin-only over the
// mesh (send it from the app's repeater command screen, or the WiFi/serial OTA console).
} else if (is_cmd(a, "stats", &rest)) {
// our running fw's merkle content-id (mid) comes from the self serve entry; the EndF body_hash (fw
- // identity, matched against a delta base) is separate — surface BOTH (only body_hash showed before).
+ // identity, matched against a delta base) is separate - surface BOTH (only body_hash showed before).
const OtaManager::ServeEntry* self = nullptr;
for (uint8_t i = 0; i < c.manager.servedCount(); i++) {
const OtaManager::ServeEntry* e = c.manager.servedEntry(i);
@@ -178,12 +178,12 @@ bool handle_ota_command(const char* command, char* reply, mesh::MainBoard& board
// ---- what's available around me (catalogued from beacons + OTA_HAVE), best/most-recent first ----
} else if (is_cmd(a, "neighbors|nbrs|updates|ls|n", &rest)) {
- // Kick a fresh round of catalog queries (async — rows arrive over the next seconds); render what we
+ // Kick a fresh round of catalog queries (async - rows arrive over the next seconds); render what we
// have now in plain words. The reply buffer is 160 B (serial / one LoRa packet for remote-admin), so
// writes are bounded and extra rows collapse to "+N more".
c.manager.queryAll();
const int CAP = 160;
- int n = snprintf(reply, CAP, "Updates nearby (%u src) — `ota get <#>` to download:",
+ int n = snprintf(reply, CAP, "Updates nearby (%u src) - `ota get <#>` to download:",
(unsigned)c.manager.sourceCount());
OtaManager::FetchState fs = c.manager.fetchState();
const uint8_t* cur = (fs != OtaManager::IDLE) ? c.manager.fetchManifestId() : nullptr;
@@ -193,7 +193,7 @@ bool handle_ota_command(const char* command, char* reply, mesh::MainBoard& board
const OtaManager::CatRow* h = c.manager.catalogRow(i);
if (CAP - n < 48) { more++; continue; }
bool on = cur && memcmp(cur, h->mid, 4) == 0;
- // Tag the active session by real fetch state (not always "downloading" — COMPLETE is ready).
+ // Tag the active session by real fetch state (not always "downloading" - COMPLETE is ready).
const char* tag = "";
if (on) {
if (fs == OtaManager::COMPLETE) tag = " [ready]";
@@ -204,7 +204,7 @@ bool handle_ota_command(const char* command, char* reply, mesh::MainBoard& board
uint32_t age = (now - h->last_ms) / 1000; if (age > 99999) age = 99999;
char ver[20]; ver_str(ver, sizeof ver, h->fw_version);
// What is this update for? "yours" if same target (hw+role) as us; else the target's env name when we
- // know it (named locally from its 4-byte target_id — no string travels on the wire); else the raw
+ // know it (named locally from its 4-byte target_id - no string travels on the wire); else the raw
// target_id hex (an env this build's OtaTargets.h table doesn't know) or '?' for an unset target.
char hwbuf[16];
const char* fit;
@@ -218,13 +218,13 @@ bool handle_ota_command(const char* command, char* reply, mesh::MainBoard& board
shown++;
}
if (more && n < CAP) snprintf(reply + n, CAP - n, "\n +%d more", more);
- if (shown == 0) strcpy(reply, "No updates seen yet — re-run `ota ls` in a few seconds (just asked around).");
+ if (shown == 0) strcpy(reply, "No updates seen yet - re-run `ota ls` in a few seconds (just asked around).");
// ---- start fetching a specific catalogued mOTA (by list index or manifest_id) ----
} else if (is_cmd(a, "pull|get|download", &rest)) {
const char* p = rest; while (*p == ' ') p++;
// split into " [destination]": selector = #N / N (catalogue index) or mid hex; destination =
- // flash | folder (MANDATORY — `folder` captures the .mota onto the connected motatool folder as .mota).
+ // flash | folder (MANDATORY - `folder` captures the .mota onto the connected motatool folder as .mota).
char selstr[24]; int i = 0;
while (p[i] && p[i] != ' ' && i < (int)sizeof(selstr) - 1) { selstr[i] = p[i]; i++; }
selstr[i] = 0;
@@ -248,7 +248,7 @@ bool handle_ota_command(const char* command, char* reply, mesh::MainBoard& board
snprintf(reply, 160, "choose a destination: `ota pull %s flash` | `ota pull %s folder` (folder: %s)",
selstr, selstr, c.folder_dest_info);
else
- snprintf(reply, 160, "choose a destination: `ota pull %s flash` (folder: none connected — motatool serve)",
+ snprintf(reply, 160, "choose a destination: `ota pull %s flash` (folder: none connected - motatool serve)",
selstr);
return true;
}
@@ -312,7 +312,7 @@ bool handle_ota_command(const char* command, char* reply, mesh::MainBoard& board
} else if (is_cmd(a, "install|apply|applydelta", &rest)) {
// Apply the fetched update. Destructive (reflashes + reboots) and GATED, not interactive (no "type
- // yes" round-trip — unreliable over LoRa): refuse unless the fetch is COMPLETE, then the apply path
+ // yes" round-trip - unreliable over LoRa): refuse unless the fetch is COMPLETE, then the apply path
// validates in order (payload hash -> built-for-this-firmware -> signature/trust) and returns the
// FIRST failing gate, so the operator knows exactly why it refused; it proceeds only if all pass.
if (c.manager.fetchState() != OtaManager::COMPLETE || c.fetch_store.staged_size() == 0) {
@@ -321,7 +321,7 @@ bool handle_ota_command(const char* command, char* reply, mesh::MainBoard& board
(unsigned)c.manager.blocksTotal());
return true;
}
- // On success the slot is armed but NOT yet rebooted — defer so this reply reaches the operator first;
+ // On success the slot is armed but NOT yet rebooted - defer so this reply reaches the operator first;
// the mesh loop reboots once it has been transmitted (same path used by auto-install).
char m2[100];
bool ok = c.apply_fetched(m2);
@@ -373,17 +373,17 @@ bool handle_ota_command(const char* command, char* reply, mesh::MainBoard& board
long n = atol(p + 11);
if (n < 0 || n > 4096) { strcpy(reply, "ERR usage: ota config checkpoint <0..4096> (blocks; 0=never)"); return true; }
c.manager.set_checkpoint_blocks((uint16_t)n); c.config_dirty = true;
- sprintf(reply, "OK checkpoint every %ld blocks (saved)%s", n, n == 0 ? " — periodic resume disabled" : "");
+ sprintf(reply, "OK checkpoint every %ld blocks (saved)%s", n, n == 0 ? " - periodic resume disabled" : "");
} else if (strncmp(p, "advert ", 7) == 0) { // beacon re-advertise cadence (minutes; 0=disable)
long m = atol(p + 7);
if (m < 0 || m > 10080) { strcpy(reply, "ERR usage: ota config advert <0..10080> (minutes; 0=disable)"); return true; }
c.manager.set_advert_mins((uint16_t)m); c.config_dirty = true;
- sprintf(reply, "OK re-advertise every %ld min (saved)%s", m, m == 0 ? " — periodic advert disabled" : "");
+ sprintf(reply, "OK re-advertise every %ld min (saved)%s", m, m == 0 ? " - periodic advert disabled" : "");
} else if (strncmp(p, "hops ", 5) == 0) { // OTA flood reach in hops (0 = direct only)
long h = atol(p + 5);
if (h < 0 || h > 8) { strcpy(reply, "ERR usage: ota config hops <0..8> (hops; 0 = direct only)"); return true; }
c.manager.set_max_hops((uint8_t)h); c.config_dirty = true;
- sprintf(reply, "OK OTA reach = %ld hop%s (saved)%s", h, h == 1 ? "" : "s", h == 0 ? " — direct only" : "");
+ sprintf(reply, "OK OTA reach = %ld hop%s (saved)%s", h, h == 1 ? "" : "s", h == 0 ? " - direct only" : "");
} else { // show current policy
uint8_t af = c.manager.autofetch();
sprintf(reply, "ota config: autofetch=%s autoinstall=%s checkpoint=%u advert=%umin hops=%u keys=%u (persisted)",
@@ -444,7 +444,7 @@ static bool handle_dev(const char* d, char* reply, OtaContext& c) {
char midhx[9]; mesh::Utils::toHex(midhx, c.serve_self_manifest + 20, 4);
uint32_t img = (uint32_t)c.serve_self_manifest[11] | ((uint32_t)c.serve_self_manifest[12] << 8)
| ((uint32_t)c.serve_self_manifest[13] << 16) | ((uint32_t)c.serve_self_manifest[14] << 24);
- sprintf(reply, "OK serving self fw mid=%s (%u B, flash-backed) — peers can pull it", midhx, (unsigned)img);
+ sprintf(reply, "OK serving self fw mid=%s (%u B, flash-backed) - peers can pull it", midhx, (unsigned)img);
} else strcpy(reply, "ERR serve self (no EndF / image too big / OOM)");
} else if (strncmp(d, "serve", 5) == 0) {
c.serving = c.manager.serve(c.serve_buf, c.serve_expected);
diff --git a/src/helpers/ota/OtaContext.h b/src/helpers/ota/OtaContext.h
index 70f11269..abd92aeb 100644
--- a/src/helpers/ota/OtaContext.h
+++ b/src/helpers/ota/OtaContext.h
@@ -7,7 +7,7 @@
#include "SignerAllowlist.h"
#include "OtaApply.h"
#include "OtaFormat.h"
-#include "OtaSelf.h" // ota_self_firmware() — prefer self-describing EndF identity at begin()
+#include "OtaSelf.h" // ota_self_firmware() - prefer self-describing EndF identity at begin()
#include "OtaBlInfo.h" // bootloader OTA-apply capability marker (nRF52); cached after first read
#if defined(NRF52_PLATFORM) && defined(OTA_FLASH_STORE)
#include "OtaStoreFlashNrf52.h"
@@ -31,7 +31,7 @@
// nRF52 stages into FLASH (OtaStoreFlashNrf52): a delta can be 100 KB+, too big to hold in RAM, and the
// COMPLETE container must persist so the bootloader can apply it after reboot. A flash page-erase halts
// the CPU (~85 ms) and starves the LoRa RX, so the store COALESCES writes to the 4 KB page (the erase
-// unit) and commits each page once, off the per-packet path (see OtaManager.h) — RAM stays O(one page).
+// unit) and commits each page once, off the per-packet path (see OtaManager.h) - RAM stays O(one page).
// (v1 has no mid-transfer resume; an interrupted fetch simply restarts.) ESP32/native use the RAM store.
namespace mesh {
@@ -67,7 +67,7 @@ struct OtaContext {
bool serving = false; // manager.serve() succeeded
// flash-backed self-serve: cached merkle leaves (heap, freed on re-serve) + assembled manifest of our
// own running firmware. The payload is read from flash per block; only the metadata is held in RAM.
- // serve_self_proof is the proof-gen working buffer (>= block_count*4) — sized to OUR image's block
+ // serve_self_proof is the proof-gen working buffer (>= block_count*4) - sized to OUR image's block
// count (the manager's fixed 4 KB scratch only covers <=1024 blocks; a >1 MB image needs more).
uint8_t* serve_self_leaves = nullptr;
uint8_t* serve_self_proof = nullptr;
@@ -82,7 +82,7 @@ struct OtaContext {
char hw_id[33] = {0}; // this device's hardware tag (from board.getOtaHwId(), set in begin)
// ---- Pull destinations (`ota pull <#> `): where a fetched .mota is staged. `flash` (fetch_store,
- // always present) or `folder` — an external host folder over the seeder link, registered by the app
+ // always present) or `folder` - an external host folder over the seeder link, registered by the app
// while a motatool `serve` connection is attached (else no `folder` dest is offered). Captures the
// container to the host as .mota so an exact firmware copy can be pulled for delta-building. ----
FolderMotaStore* folder_dest = nullptr; // non-null == a folder destination is currently connected
@@ -107,7 +107,7 @@ struct OtaContext {
// so the deferred-reboot path (mesh loop) takes over. Caller ensures the fetch is COMPLETE. Shared by
// manual `ota applydelta` and the auto-install path.
bool apply_fetched(char* msg) {
- // hardware-compatibility gate (brick-safety) — refuse a .mota whose hw_id is for different hardware,
+ // hardware-compatibility gate (brick-safety) - refuse a .mota whose hw_id is for different hardware,
// independent of signature; covers a manual cross-target `ota dev want` onto an incompatible board.
{
uint8_t hdr[8], mb[256];
@@ -170,7 +170,7 @@ struct OtaContext {
manager.clear_sources(); // idempotent re-attach
if (!manager.add_source(&src)) { strncpy(msg, "ERR no free source slot", cap); return false; }
folder_active = true;
- snprintf(msg, cap, "OK folder attached (serial) — serving %u mOTA total (own fw + folder)",
+ snprintf(msg, cap, "OK folder attached (serial) - serving %u mOTA total (own fw + folder)",
(unsigned)manager.servedCount());
return true;
}
@@ -185,8 +185,8 @@ struct OtaContext {
}
void begin(uint32_t target_id, OtaSend send, void* ctx, const char* hw = nullptr) {
- // Prefer the firmware's SELF-DESCRIBING EndF identity (docs §2) over the build-flag values the caller
- // passed — it's correct on any build (build.sh injection, bare IDE build, ...), so `ota ls`/`status`
+ // Prefer the firmware's SELF-DESCRIBING EndF identity (docs Section 2) over the build-flag values the caller
+ // passed - it's correct on any build (build.sh injection, bare IDE build, ...), so `ota ls`/`status`
// and fetch-routing show the right hardware/role instead of 0 / "".
SelfFwInfo _fi;
if (ota_self_firmware(_fi) && _fi.valid) {
diff --git a/src/helpers/ota/OtaFlashLayout_nrf52.h b/src/helpers/ota/OtaFlashLayout_nrf52.h
index 6c4c068c..a73b1a15 100644
--- a/src/helpers/ota/OtaFlashLayout_nrf52.h
+++ b/src/helpers/ota/OtaFlashLayout_nrf52.h
@@ -68,10 +68,10 @@ inline uint32_t mota_nrf52_stage_capacity(uint32_t app_base) {
// Plan where to stage a received `.mota` of `total_size` bytes. It is placed bottom-aligned within the
// highest flash page below FS_START (the bootloader scans downward from there), and it must sit
// ENTIRELY above both the running image and the in-place decoder workspace, and below the filesystem
-// region (ExtraFS/InternalFS — where user prefs live, assumed immutable). Reserving the full workspace
+// region (ExtraFS/InternalFS - where user prefs live, assumed immutable). Reserving the full workspace
// here prevents accepting a large container that the bootloader would later reject as overlapping it.
// Returns false (and leaves out_start untouched) if it does not fit. This is the SINGLE place the FS
-// ceiling + app-collision bounds are enforced; begin()/reopen() both go through it. Pure — no flash I/O —
+// ceiling + app-collision bounds are enforced; begin()/reopen() both go through it. Pure - no flash I/O -
// so it is unit-tested natively in test/test_ota/test_ota_flashplan.cpp.
inline bool mota_nrf52_stage_plan(uint32_t total_size, uint32_t app_base, uint32_t app_end,
uint32_t& out_start) {
diff --git a/src/helpers/ota/OtaFormat.h b/src/helpers/ota/OtaFormat.h
index 554899f0..a660455f 100644
--- a/src/helpers/ota/OtaFormat.h
+++ b/src/helpers/ota/OtaFormat.h
@@ -15,7 +15,7 @@ namespace ota {
static const uint8_t MOTA_MAGIC[4] = { 'm', 'O', 'T', 'A' }; // 6D 4F 54 41
static const uint8_t MOTA_TRAILER[5] = { 'v', 'k', '4', '9', '6' }; // 76 6B 34 39 36
static const uint8_t ENDF_MAGIC[4] = { 'E', 'n', 'd', 'F' }; // 45 6E 64 46
-// Fixed 56-byte trailer (docs/ota_protocol.md §2): marker(4) body_len(4) body_hash8(8) + a self-describing
+// Fixed 56-byte trailer (docs/ota_protocol.md Section 2): marker(4) body_len(4) body_hash8(8) + a self-describing
// identity block fw_version(4) target_id(4) hw_id(32). No optional/variable parts.
static const uint32_t ENDF_LEN = 56;
@@ -23,12 +23,12 @@ static const uint32_t ENDF_LEN = 56;
static const uint8_t MOTA_FORMAT_VER = 2; // fixed-layout manifest (see offsets below)
static const uint8_t HASH_ALGO_SHA256 = 0x12; // multihash code
-// Fixed manifest layout (manifest-minus-leaves) — every field is present at a constant offset, so the
-// parser is plain offset reads (docs/ota_protocol.md §4). base_hash/signer_pubkey/signature are always
+// Fixed manifest layout (manifest-minus-leaves) - every field is present at a constant offset, so the
+// parser is plain offset reads (docs/ota_protocol.md Section 4). base_hash/signer_pubkey/signature are always
// present (zero-filled when not applicable); only leaves[] (after `approval`) is variable.
static const uint32_t MOTA_OFF_BASE_HASH = 89; // 8 (zero for a full image)
static const uint32_t MOTA_OFF_SIGNER = 97; // 32 (zero when unsigned)
-static const uint32_t MOTA_OFF_SIGNATURE = 129; // 64 (zero when unsigned) — covers manifest[0,129)
+static const uint32_t MOTA_OFF_SIGNATURE = 129; // 64 (zero when unsigned) - covers manifest[0,129)
static const uint32_t MOTA_OFF_APPROVAL = 193; // 4
static const uint32_t MOTA_MFL = 197; // manifest-minus-leaves length (constant)
static const uint32_t MOTA_SIGNED_LEN = 129; // bytes the signature covers (manifest[0, signer_end))
diff --git a/src/helpers/ota/OtaManager.cpp b/src/helpers/ota/OtaManager.cpp
index b2208bda..a1843a18 100644
--- a/src/helpers/ota/OtaManager.cpp
+++ b/src/helpers/ota/OtaManager.cpp
@@ -20,7 +20,7 @@ void OtaManager::begin(uint32_t my_target_id, OtaSend send, void* ctx) {
//
// A node offers a SET of mOTAs: its own firmware (view0) plus any external "folder" sources (OtaSource).
// Every fetch message carries the manifest_id, so a request dispatches to the matching ServeView via
-// resolve() — view0 is resident; an external mota is (re)loaded on demand into _srcv. The catalog (what
+// resolve() - view0 is resident; an external mota is (re)loaded on demand into _srcv. The catalog (what
// we advertise / answer OTA_QUERY with) is the lightweight _serve[] registry.
bool OtaManager::serve(const uint8_t* mota, uint32_t len) {
@@ -112,7 +112,7 @@ OtaManager::ServeView* OtaManager::resolve(const uint8_t* mid) {
// Load an external mota into the on-demand _srcv: read its manifest-minus-leaves + leaves[] from the
// source into RAM, parse, and wire a payload reader that streams blocks from the source on REQ. (The
-// payload itself is NOT held in RAM — only the small head + the leaves, <=4 KB for <=1024 blocks.)
+// payload itself is NOT held in RAM - only the small head + the leaves, <=4 KB for <=1024 blocks.)
bool OtaManager::loadSource(const ServeEntry& e) {
const MotaDesc& d = e.desc;
if (d.leaves_off < 8) return false;
@@ -145,7 +145,7 @@ bool OtaManager::srcReadTramp(void* c, uint32_t off, uint8_t* buf, uint32_t len)
return x->src->read(x->idx, x->payload_off + off, buf, len);
}
-// sha2-256:4 over the SORTED set of mids we serve — peers use it to tell if our offering changed. Sorting
+// sha2-256:4 over the SORTED set of mids we serve - peers use it to tell if our offering changed. Sorting
// makes it canonical across nodes regardless of insert order; for a single mota it is mh4(mid) (unchanged).
void OtaManager::setDigest(uint8_t out[4]) const {
if (_n_serve == 0) { memset(out, 0, 4); return; }
@@ -170,7 +170,7 @@ void OtaManager::announce() { // tiny per-node beacon (constant size, inde
emit(b, encode_adv(b, sizeof(b), a), true);
}
-// OTA_QUERY: two roles. (1) OVERHEAR-SUPPRESSION — any node that has a pending query for the same
+// OTA_QUERY: two roles. (1) OVERHEAR-SUPPRESSION - any node that has a pending query for the same
// {source,digest} cancels it (someone else already asked; the broadcast HAVE is coming). (2) If the query
// is addressed to US, reply with our catalog (broadcast, tagged with our digest so every overhearer caches
// it). All served mOTAs matching filter_target are returned, fragmented if they exceed one packet.
@@ -232,7 +232,7 @@ void OtaManager::handleGetManifest(const uint8_t* m, uint16_t n) {
}
// Serve the target's merkle leaves[] in fragments (for a motatool folder-capture warm-start). Only the
-// fragments set in want_mask are emitted, so a want_mask retry re-sends just the holes — never a full burst
+// fragments set in want_mask are emitted, so a want_mask retry re-sends just the holes - never a full burst
// (same anti-deadlock rationale as OTA_MANIFEST). This is the only leaf-diff piece that runs on every node.
void OtaManager::handleGetLeaves(const uint8_t* m, uint16_t n) {
GetLeavesMsg gl;
@@ -255,7 +255,7 @@ void OtaManager::handleGetLeaves(const uint8_t* m, uint16_t n) {
}
// Smallest mask covering `nf` fragments: bit k set for k in [0, nf). Caps at 16 (matches _reasm_mask and
-// the manifest reassembly), which bounds a block at 16 fragments — our 1 KB blocks are 7.
+// the manifest reassembly), which bounds a block at 16 fragments - our 1 KB blocks are 7.
static inline uint16_t frag_full_mask(uint32_t nf) {
return (nf >= 16) ? 0xFFFFu : (uint16_t)((1u << nf) - 1);
}
@@ -340,7 +340,7 @@ void OtaManager::handleAdv(const uint8_t* m, uint16_t n) {
if (interested && !s.have_catalog) scheduleQuery(a.seeder_id, a.set_digest); // jittered + suppressible
}
-// Schedule a catalog query after a random jitter (id ⊕ digest, so neighbours pick different delays). The
+// Schedule a catalog query after a random jitter (id +/ digest, so neighbours pick different delays). The
// node with the shortest jitter sends; the rest overhear that QUERY (or the broadcast HAVE) and suppress.
void OtaManager::scheduleQuery(const uint8_t* seeder, const uint8_t* digest) {
if (_pq_active && memcmp(_pq_seeder, seeder, 4) == 0 && memcmp(_pq_digest, digest, 4) == 0) return; // already pending
@@ -356,7 +356,7 @@ void OtaManager::sendQuery(const uint8_t* seeder, const uint8_t* digest, uint32_
emit(b, encode_query(b, sizeof(b), q), true); // FLOODED so neighbours overhear it and suppress
}
-// User-initiated browse (`ota neighbors`): ask every known source now (no jitter — infrequent + explicit).
+// User-initiated browse (`ota neighbors`): ask every known source now (no jitter - infrequent + explicit).
void OtaManager::queryAll() { for (uint8_t i = 0; i < _n_src; i++) sendQuery(_sources[i].seeder, _sources[i].digest, 0); }
// A catalog reply: record each mOTA (deduped by mid; distinct-source count for the UI), and if a row
@@ -367,7 +367,7 @@ void OtaManager::handleHave(const uint8_t* m, uint16_t n) {
bool have_sid = (_seeder_id[0] | _seeder_id[1] | _seeder_id[2] | _seeder_id[3]) != 0;
if (have_sid && memcmp(hv.seeder_id, _seeder_id, 4) == 0) return; // our own catalog
// PASSIVE: any overheard HAVE marks its source catalogued + cancels a pending query for it (storm
- // suppression) — every node caches the rows below, even one that never queried.
+ // suppression) - every node caches the rows below, even one that never queried.
for (uint8_t i = 0; i < _n_src; i++)
if (memcmp(_sources[i].seeder, hv.seeder_id, 4) == 0 && memcmp(_sources[i].digest, hv.set_digest, 4) == 0)
_sources[i].have_catalog = true;
@@ -429,7 +429,7 @@ void OtaManager::startFetch(const uint8_t* mid, uint32_t target, bool validate)
if (!_fetch || _fstate == FETCHING || _fstate == WANT_MANIFEST || _fstate == WANT_LEAVES || _fstate == PAUSED) return;
_validate = validate; // motatool folder-capture warm-start (seed leaf-diff)
// A validate pull is a FRESH seed capture, not a resume: the store already holds the seed's payload (not a
- // real partial), so never adopt it via resumeStaged — always re-begin and run the manifest→leaves→diff.
+ // real partial), so never adopt it via resumeStaged - always re-begin and run the manifest->leaves->diff.
if (!validate && resumeStaged(mid)) return; // (non-validate) resume a partial container left in flash
memcpy(_fid, mid, 4);
_fstate = WANT_MANIFEST;
@@ -470,15 +470,15 @@ void OtaManager::handleManifest(const uint8_t* m, uint16_t n) {
const uint8_t* mf = _mf_buf; // fully reassembled manifest-minus-leaves
uint32_t mfl = _mf_len;
if (mfl != MOTA_MFL) { _fstate = FAILED; return; } // manifest-minus-leaves is a fixed 197 bytes
- if (!codecOk(mf[56])) { _fstate = IDLE; return; } // codec we can't apply (lying/stale ADV) — abort
+ if (!codecOk(mf[56])) { _fstate = IDLE; return; } // codec we can't apply (lying/stale ADV) - abort
uint32_t payload_size = rd_u32le(mf + 15);
uint8_t bsl = mf[19];
if (bsl >= 32) { _fstate = FAILED; return; }
uint32_t bs = 1u << bsl;
- // a block must fit our reassembly buffer (and be non-empty) — reject an oversized block_size up front
+ // a block must fit our reassembly buffer (and be non-empty) - reject an oversized block_size up front
if (bs == 0 || bs > OTA_MAX_BLOCK || payload_size == 0) { _fstate = FAILED; return; }
uint32_t bc = (payload_size + bs - 1) / bs;
- if (bc > 0xFFFFu) { _fstate = FAILED; return; } // block_idx is uint16 on the wire — can't address more
+ if (bc > 0xFFFFu) { _fstate = FAILED; return; } // block_idx is uint16 on the wire - can't address more
memcpy(_froot, mf + 20, 4);
uint32_t leaves_off = 8 + mfl;
@@ -523,7 +523,7 @@ void OtaManager::freeLeaves() {
}
// Enter WANT_LEAVES: allocate the target-leaves buffer and request them (bitmap-fragmented). Returns false
-// — caller falls back to a normal full fetch — if the image is too big for the fixed uint16 leaves bitmap or
+// - caller falls back to a normal full fetch - if the image is too big for the fixed uint16 leaves bitmap or
// the heap allocation fails. Called from handleManifest once geometry is known.
bool OtaManager::beginLeafDiff() {
freeLeaves();
@@ -714,7 +714,7 @@ void OtaManager::handleProof(const uint8_t* m, uint16_t n) {
void OtaManager::requestMissing() {
if (_fstate != FETCHING) return;
// Per-block serial flow (split data/proof). If the current block's data is fully reassembled and we
- // are waiting on its proof, (re)send the proof request rather than re-fetching the data — this also
+ // are waiting on its proof, (re)send the proof request rather than re-fetching the data - this also
// recovers from a lost PROOF reply.
if (_awaiting_proof && _reasm_block != NO_BLOCK) {
ReqProofMsg rp; memcpy(rp.manifest_id, _fid, 4); rp.block_idx = (uint16_t)_reasm_block;
@@ -724,14 +724,14 @@ void OtaManager::requestMissing() {
return;
}
// Otherwise request the DATA fragments of the next missing block. One block at a time keeps the
- // server's TX queue tiny so OTA never floods the mesh (docs/ota_protocol.md §8); a block's fragments
+ // server's TX queue tiny so OTA never floods the mesh (docs/ota_protocol.md Section 8); a block's fragments
// are self-describing (frag_off) so missing slices can be retried without re-sending a full block.
uint32_t start = pickMissingBlock();
if (start >= _fbc) return;
_req_start = start; _req_count = 1;
// Ask only for fragments we still lack: the holes of the in-flight partial block (_reasm_need & ~mask),
// or all fragments of a fresh block. A lost fragment then costs one fragment to re-fetch, not the whole
- // block — and the re-REQ can't collide with a multi-fragment burst (half-duplex) as before.
+ // block - and the re-REQ can't collide with a multi-fragment burst (half-duplex) as before.
uint32_t nf = (blockLen(start) + OTA_FRAG_DATA - 1) / OTA_FRAG_DATA;
uint16_t need = frag_full_mask(nf);
uint16_t have = (start == _reasm_block) ? _reasm_mask : 0;
@@ -760,7 +760,7 @@ void OtaManager::loop() {
sendQuery(_pq_seeder, _pq_digest, 0); // unfiltered: one broadcast HAVE serves everyone
}
if (_fstate == WANT_MANIFEST) {
- // Retry GET_MANIFEST ONLY when a tick passed with no new fragment — re-bursting every tick would congest
+ // Retry GET_MANIFEST ONLY when a tick passed with no new fragment - re-bursting every tick would congest
// the link and burn the retry cap while fragments are still arriving (mirrors FETCHING + WANT_LEAVES).
// Give up after a cap of stalled retries so an unreachable mid doesn't pin the single fetch slot forever.
if (_mf_mask == _loop_last_mfmask) {
@@ -776,8 +776,8 @@ void OtaManager::loop() {
return;
}
if (_fstate == WANT_LEAVES) {
- if (_diffing) { diffStep(); return; } // leaves in — diff the seed a batch at a time (non-blocking)
- // Re-request ONLY when a whole tick passed with no new fragment — otherwise re-bursting the holes every
+ if (_diffing) { diffStep(); return; } // leaves in - diff the seed a batch at a time (non-blocking)
+ // Re-request ONLY when a whole tick passed with no new fragment - otherwise re-bursting the holes every
// tick congests the link (and burns the retry cap) while fragments are still streaming in. On a stall,
// ask for just the missing bitmap (anti-burst); give up (FAILED) after a cap of stalled retries.
if (_lv_mask == _loop_last_lvmask) {
@@ -794,7 +794,7 @@ void OtaManager::loop() {
return;
}
if (_fstate != FETCHING) return;
- // retry only when a whole tick passed with NO progress — neither a committed block nor a new fragment
+ // retry only when a whole tick passed with NO progress - neither a committed block nor a new fragment
// of the in-flight block. This avoids re-request spam while a block's fragments are still streaming in.
if (_have == _loop_last_have && _reasm_mask == _loop_last_mask) requestMissing();
_loop_last_have = _have;
diff --git a/src/helpers/ota/OtaManager.h b/src/helpers/ota/OtaManager.h
index 0a04ee4a..7d4a966f 100644
--- a/src/helpers/ota/OtaManager.h
+++ b/src/helpers/ota/OtaManager.h
@@ -8,7 +8,7 @@
#include "MotaContainer.h"
#include "OtaSource.h"
-// Transport-agnostic OTA session engine (docs/ota_protocol.md §5/§8). It SERVES a complete `.mota`
+// Transport-agnostic OTA session engine (docs/ota_protocol.md Section 5/Section 8). It SERVES a complete `.mota`
// (answering GET_MANIFEST / REQ) and/or FETCHES one into an OtaStore (verifying every block against
// the signed merkle root via proofs). It is portable (no Arduino / radio / Ed25519) so it can be
// driven by a host simulation; a thin Mesh adapter wires it to PAYLOAD_TYPE_OTA on device.
@@ -50,7 +50,7 @@ typedef bool (*ServeReadFn)(void* ctx, uint32_t off, uint8_t* buf, uint32_t len)
#define OTA_MF_MAXFRAG 4 // max manifest fragments (the fixed 197 B manifest is always 2)
#endif
#ifndef OTA_MANIFEST_MAX_RETRY
-#define OTA_MANIFEST_MAX_RETRY 20 // give up (FAILED) after this many GET_MANIFEST retries — frees the slot
+#define OTA_MANIFEST_MAX_RETRY 20 // give up (FAILED) after this many GET_MANIFEST retries - frees the slot
#endif
// Leaf-diff warm-start (motatool folder-capture only): bulk-fetch the target's leaves[], diff a seed build
// locally, pull DATA only for mismatches. OTA_LEAVES is bitmap-fragmented like OTA_MANIFEST so a want_mask
@@ -95,7 +95,7 @@ typedef bool (*ServeReadFn)(void* ctx, uint32_t off, uint8_t* buf, uint32_t len)
#define OTA_REQ_SPREAD_MS 3000 // initial random hold before a fetch's first REQ (de-sync N fetchers)
#endif
#ifndef OTA_REQ_SUPPRESS_MS
-#define OTA_REQ_SUPPRESS_MS 2500 // after overhearing a peer's REQ for a block, don't also request it —
+#define OTA_REQ_SUPPRESS_MS 2500 // after overhearing a peer's REQ for a block, don't also request it -
#endif // its DATA is broadcast and will fill our hole too (swarm de-dup)
#ifndef OTA_SERVE_SUPPRESS_MS
#define OTA_SERVE_SUPPRESS_MS 1500 // don't re-serve a block whose DATA we just overheard another holder send
@@ -106,10 +106,10 @@ typedef bool (*ServeReadFn)(void* ctx, uint32_t off, uint8_t* buf, uint32_t len)
// nRF52 note: a flash page-erase halts the CPU (~85 ms, code runs from flash) and starves the LoRa RX,
// so writing to flash on every received packet drops in-flight DATA and the transfer stalls. The SD-safe
// driver (Adafruit flash_nrf5x) always erases on flush, so there is no erase-free write; instead
-// OtaStoreFlashNrf52 COALESCES to the 4 KB page (the erase unit) and writes each page once — RAM stays
+// OtaStoreFlashNrf52 COALESCES to the 4 KB page (the erase unit) and writes each page once - RAM stays
// O(one page), never O(mota). It pins flash page 0 (header+manifest+merkle leaves, which update all
// transfer long) in RAM and streams the payload through one sliding page buffer, flushing page 0 and the
-// last page at finalize() (radio idle). Flash is then touched ~once per 4 KB (≈1 per 4 blocks), not per
+// last page at finalize() (radio idle). Flash is then touched ~once per 4 KB (~1 per 4 blocks), not per
// packet; a small delta that fits page 0 does ZERO flash I/O until COMPLETE. (Pacing alone is not enough.)
class OtaManager {
@@ -160,9 +160,9 @@ public:
bool serve_self(const uint8_t* manifest, uint16_t mfl, const uint8_t* leaves, uint32_t block_count,
uint8_t* proof_scratch, uint32_t proof_scratch_sz, ServeReadFn read, void* ctx);
// Attach an external "folder" of `.mota` images (USB-serial daemon, BLE, WiFi URLs, NFS/samba, ...).
- // The node then advertises + RELAYS them transparently alongside its own fw — peers just see more mOTAs.
+ // The node then advertises + RELAYS them transparently alongside its own fw - peers just see more mOTAs.
// Re-enumerates the source into the serve registry. Returns false if no slots remain. (Trustless: the
- // fetcher verifies merkle+signature, so the source is never trusted — see OtaSource.h.)
+ // fetcher verifies merkle+signature, so the source is never trusted - see OtaSource.h.)
bool add_source(MotaSource* src);
// Re-read every attached source's catalog (call when the folder's contents change). Rebuilds entries
// [1..] from the sources; entry 0 (our own fw) is preserved.
@@ -176,7 +176,7 @@ public:
void servedDigest(uint8_t out[4]) const { setDigest(out); }
// Broadcast the tiny per-node BEACON (OTA_ADV): seeder_id + count + set-digest of everything we serve.
- // Constant size regardless of how many mOTAs — peers ask for the catalog via OTA_QUERY only on interest.
+ // Constant size regardless of how many mOTAs - peers ask for the catalog via OTA_QUERY only on interest.
void announce();
// --- fetch --- Provide the staging store; fetching starts on a matching OTA_ADV.
@@ -212,7 +212,7 @@ public:
void pull(const uint8_t* mid, uint32_t target, bool validate = false) {
want(target); want_mid(mid); startFetch(mid, target, validate);
}
- // Ask every known source for its catalog (populates `ota neighbors`). Async — rows arrive via OTA_HAVE.
+ // Ask every known source for its catalog (populates `ota neighbors`). Async - rows arrive via OTA_HAVE.
void queryAll();
// Coarse clock for source/catalog ages + LRU (the Mesh adapter feeds millis; 0 in host tests is fine).
void set_clock(uint32_t ms) { _now_ms = ms; }
@@ -280,7 +280,7 @@ public:
uint32_t target_id, fw_version;
uint8_t codec, flags;
uint8_t seeders[OTA_CAT_SEEDERS][4]; // distinct sources advertising this mid (deduped; capped)
- uint8_t n_seeders; // count of the above (capped at OTA_CAT_SEEDERS) — "N+ nodes have it"
+ uint8_t n_seeders; // count of the above (capped at OTA_CAT_SEEDERS) - "N+ nodes have it"
uint32_t have_max; // best block-count any source reported (== total when a full copy exists)
uint32_t last_ms;
};
@@ -332,7 +332,7 @@ private:
ServeView _srcv;
uint8_t _srcv_mid[4] = {0};
SrcReadCtx _srcv_rdctx = {nullptr, 0, 0};
- ServeEntry _serve[OTA_MAX_SERVE]; // catalog (what we advertise) — entry 0 is view0
+ ServeEntry _serve[OTA_MAX_SERVE]; // catalog (what we advertise) - entry 0 is view0
uint8_t _n_serve = 0;
MotaSource* _src_list[OTA_MAX_SOURCE_OBJ] = {nullptr};
uint8_t _n_src_obj = 0;
@@ -393,7 +393,7 @@ private:
CatRow _catalog[OTA_MAX_CATALOG];
uint8_t _n_cat = 0;
uint32_t _now_ms = 0; // coarse clock (fed by set_clock; for ages/LRU/jitter)
- // pending catalog query (jittered + suppressed on overhearing a matching QUERY/HAVE — anti-storm)
+ // pending catalog query (jittered + suppressed on overhearing a matching QUERY/HAVE - anti-storm)
bool _pq_active = false;
uint8_t _pq_seeder[4] = {0};
uint8_t _pq_digest[4] = {0};
diff --git a/src/helpers/ota/OtaProtocol.h b/src/helpers/ota/OtaProtocol.h
index fddb81c2..623ca222 100644
--- a/src/helpers/ota/OtaProtocol.h
+++ b/src/helpers/ota/OtaProtocol.h
@@ -4,7 +4,7 @@
#include
#include "OtaFormat.h"
-// Encode/decode for the OTA LoRa messages (docs/ota_protocol.md §8). Each message is a packet payload:
+// Encode/decode for the OTA LoRa messages (docs/ota_protocol.md Section 8). Each message is a packet payload:
// [0]=ota_msg_type, then a fixed body. Portable + allocation-free; unit-tested on the host.
//
// manifest_id == the manifest's merkle_root (4 bytes), a compact content id.
@@ -13,7 +13,7 @@ namespace mesh {
namespace ota {
// ---- OTA_ADV: tiny per-NODE beacon (flood, periodic). CONSTANT size regardless of how many mOTAs a
-// node serves — it just says "I'm a source, here's how many + a digest of my set". A peer that's
+// node serves - it just says "I'm a source, here's how many + a digest of my set". A peer that's
// interested asks for the catalog via OTA_QUERY. (Replaces the old per-mOTA advert so a folder node with
// N images costs one 10-byte beacon, not N adverts.) ----
struct AdvMsg {
@@ -22,7 +22,7 @@ struct AdvMsg {
uint8_t set_digest[4]; // sha2-256:4 over the sorted set of served mids; "did my offering change?"
};
-// ---- OTA_QUERY: "list what you serve" — addressed to a source by seeder_id, FLOODED so neighbours
+// ---- OTA_QUERY: "list what you serve" - addressed to a source by seeder_id, FLOODED so neighbours
// overhear it (storm suppression). set_digest identifies the offering being asked about (so an overhearer
// can suppress its own pending query for the same {source,digest}). filter_target=0 = everything. ----
struct QueryMsg {
@@ -62,7 +62,7 @@ struct ManifestMsg {
// ---- OTA_REQ: request specific fragments of ONE block (direct) ----
// body: manifest_id(4) block_idx(2) want_mask(2). want_mask bit k = "send fragment k" (the slice at byte
// offset k*OTA_FRAG_DATA). A fetcher requests the full mask ((1<size &&
esp_partition_read(p, body_len, tr, ENDF_LEN) == ESP_OK) {
@@ -118,7 +118,7 @@ static uint32_t parse_fw_version(const char* s) {
else break;
}
if (dots >= 1) return FwVersion{ (uint8_t)a, (uint8_t)b, (uint8_t)d, 0 }.pack();
- s = p - 1; // a bare number, no dots — keep scanning
+ s = p - 1; // a bare number, no dots - keep scanning
}
return 0;
}
@@ -126,7 +126,7 @@ static uint32_t parse_fw_version(const char* s) {
bool ota_serve_self(OtaContext& c, uint32_t fw_version) {
// Derive our version from the build string when the caller didn't supply one, so the mOTA we advertise
// carries a real version (was hard-coded 0 -> peers saw "v0.0.0"). A dev build with no dotted number
- // still reads 0 — the self-describing EndF identity (docs) is the durable fix for that.
+ // still reads 0 - the self-describing EndF identity (docs) is the durable fix for that.
#ifdef FIRMWARE_VERSION
if (fw_version == 0) fw_version = parse_fw_version(FIRMWARE_VERSION);
#endif
@@ -161,7 +161,7 @@ bool ota_serve_self(OtaContext& c, uint32_t fw_version) {
uint8_t image_hash[32]; sha.finalize(image_hash, 32);
uint8_t root[4]; merkle_root(root, c.serve_self_leaves, bc);
- // Prefer the SELF-DESCRIBING identity embedded in our own EndF (docs §2) over build flags / the param —
+ // Prefer the SELF-DESCRIBING identity embedded in our own EndF (docs Section 2) over build flags / the param -
// it's correct regardless of how the firmware was built (build.sh injection, IDE, etc.).
uint32_t out_target = fi.target_id ? fi.target_id : c.manager.target();
uint32_t out_ver = fi.fw_version ? fi.fw_version : fw_version;
diff --git a/src/helpers/ota/OtaSelf.h b/src/helpers/ota/OtaSelf.h
index df855c6f..24575e2e 100644
--- a/src/helpers/ota/OtaSelf.h
+++ b/src/helpers/ota/OtaSelf.h
@@ -5,7 +5,7 @@
// Device-side accessor for the running firmware's own image (to read its EndF trailer).
// Per-platform: ESP32 memory-maps the running app partition; other platforms TBD (nRF52 uses the
// bootloader-apply path, so its app-region wiring lands with that work). Not compiled on the native
-// host — the portable scan logic in FirmwareInfo.{h,cpp} is what gets unit-tested there.
+// host - the portable scan logic in FirmwareInfo.{h,cpp} is what gets unit-tested there.
namespace mesh {
namespace ota {
diff --git a/src/helpers/ota/OtaSource.h b/src/helpers/ota/OtaSource.h
index 64c8fce6..88731a69 100644
--- a/src/helpers/ota/OtaSource.h
+++ b/src/helpers/ota/OtaSource.h
@@ -4,16 +4,16 @@
#include
#include "OtaFormat.h"
-// Transport-agnostic "folder of firmware" abstraction (docs/ota_protocol.md §9). A node can RELAY
-// `.mota` images it does not hold in flash — a user drops several `.mota` (different architectures) into
+// Transport-agnostic "folder of firmware" abstraction (docs/ota_protocol.md Section 9). A node can RELAY
+// `.mota` images it does not hold in flash - a user drops several `.mota` (different architectures) into
// some external store and the node advertises + serves them as if it held them. Peers just see "this node
// has N mOTAs"; the node knows they are external. The store is reached through a MotaSource, so the SAME
-// serve code drives a USB-serial host daemon, BLE, a WiFi URL list, an NFS/samba mount, ... — only the
+// serve code drives a USB-serial host daemon, BLE, a WiFi URL list, an NFS/samba mount, ... - only the
// `read()` plumbing differs per transport.
//
// The relay is TRUSTLESS: the fetcher verifies every block against the signed merkle root, so a source is
// never trusted. A wrong descriptor or wrong bytes simply makes the fetch fail its merkle/signature check
-// — a malicious or buggy source cannot forge firmware, only deny it.
+// - a malicious or buggy source cannot forge firmware, only deny it.
namespace mesh {
namespace ota {
diff --git a/src/helpers/ota/OtaStore.h b/src/helpers/ota/OtaStore.h
index 8fb0563b..17d1bd94 100644
--- a/src/helpers/ota/OtaStore.h
+++ b/src/helpers/ota/OtaStore.h
@@ -5,10 +5,10 @@
#include
#include "OtaFormat.h" // MOTA_MAGIC (resume: detect a persisted partial container)
-// Staging backend for an in-transit `.mota` (docs/ota_protocol.md §7). Blocks may arrive out of order
+// Staging backend for an in-transit `.mota` (docs/ota_protocol.md Section 7). Blocks may arrive out of order
// and progress must survive reboots, so the store is random-access. The transfer/verify logic is
// written against this interface; concrete impls are per-platform (RAM for tests/bring-up; persistent
-// flash — ESP32 OTA slot / nRF52 raw region — for production, dropped in behind the same interface).
+// flash - ESP32 OTA slot / nRF52 raw region - for production, dropped in behind the same interface).
namespace mesh {
namespace ota {
@@ -25,8 +25,8 @@ public:
virtual void clear() = 0;
// Optional: declare the size of the leading metadata (header + manifest + merkle leaves, i.e.
- // everything before the payload). A flash-backed store keeps that region — which is updated
- // throughout the transfer (a leaf is committed per block) — pinned in one RAM page, so it can
+ // everything before the payload). A flash-backed store keeps that region - which is updated
+ // throughout the transfer (a leaf is committed per block) - pinned in one RAM page, so it can
// flush the bulk payload page-by-page without re-erasing the leaves' page on every block.
// Returns false if the metadata won't fit the store's pinned region (transfer is then refused).
virtual bool set_meta_size(uint32_t meta_bytes) { (void)meta_bytes; return true; }
@@ -62,8 +62,8 @@ public:
}
};
-// Fixed-capacity RAM store — for native tests and device bring-up of the transfer/verify path.
-// (Does NOT survive reboot; a persistent flash store replaces it for production — see D1.)
+// Fixed-capacity RAM store - for native tests and device bring-up of the transfer/verify path.
+// (Does NOT survive reboot; a persistent flash store replaces it for production - see D1.)
template
class OtaStoreRam : public OtaStore {
uint8_t _buf[CAP] = {}; // zero-init so a never-written store's reopen() finds no MOTA_MAGIC
@@ -88,7 +88,7 @@ public:
uint32_t capacity() const override { return CAP; }
uint32_t staged_size() const override { return _total; }
void clear() override { _total = 0; }
- // RAM doesn't survive a real reboot, but the buffer persists within a process — enough to exercise the
+ // RAM doesn't survive a real reboot, but the buffer persists within a process - enough to exercise the
// manager's resume path in native tests. Recover `total` from the stored header so read() bounds work.
bool reopen() override {
if (memcmp(_buf, MOTA_MAGIC, 4) != 0) return false;
diff --git a/src/helpers/ota/OtaStoreFlashEsp32.cpp b/src/helpers/ota/OtaStoreFlashEsp32.cpp
index 0fae329c..1be20563 100644
--- a/src/helpers/ota/OtaStoreFlashEsp32.cpp
+++ b/src/helpers/ota/OtaStoreFlashEsp32.cpp
@@ -197,7 +197,7 @@ void OtaStoreFlashEsp32::checkpoint() {
}
// Re-attach to a container already staged in the slot (after a reboot), WITHOUT erasing. The header
-// (MOTA_MAGIC + total) sits at a sector boundary (meta_part for full, write_start for delta — both
+// (MOTA_MAGIC + total) sits at a sector boundary (meta_part for full, write_start for delta - both
// sector-aligned), so scan sector starts from the bottom up. A candidate is accepted only if: magic +
// plausible total, the manifest parses, the container geometry is self-consistent with the total, AND the
// recomputed placement lands the meta exactly where we found the magic. Any miss -> false (fetch fresh),
diff --git a/src/helpers/ota/OtaStoreFlashNrf52.h b/src/helpers/ota/OtaStoreFlashNrf52.h
index 9e5f7f46..fd235379 100644
--- a/src/helpers/ota/OtaStoreFlashNrf52.h
+++ b/src/helpers/ota/OtaStoreFlashNrf52.h
@@ -7,12 +7,12 @@
// Persistent flash-backed OtaStore for nRF52840. Stages the received `.mota` in the free flash
// below the primary LittleFS (FS_START), bottom-aligned so its trailer ends at FS_START and the
-// bootloader can scan for it. Survives reboot — the whole point — so the bootloader can apply the
+// bootloader can scan for it. Survives reboot - the whole point - so the bootloader can apply the
// staged delta on the next boot.
//
// RAM is bounded to O(one flash page), NEVER O(mota): a 100 KB+ delta must not live in RAM.
// - On nRF52 the flash *erase* unit is one 4 KB page and the only SoftDevice-safe writer
-// (Adafruit `flash_nrf5x`) erases the whole page on every flush (~85 ms, CPU stalled → LoRa RX
+// (Adafruit `flash_nrf5x`) erases the whole page on every flush (~85 ms, CPU stalled -> LoRa RX
// starved). Writing to flash per received packet therefore drops in-flight DATA and the transfer
// stalls. The fix: coalesce to the *page*, the hardware-natural unit, and write each page once.
// - `_meta_page` pins flash page 0 (header + manifest + the merkle-leaf progress markers, which are
@@ -23,9 +23,9 @@
// monotonically with the (mostly in-order) block stream and flushes the page it leaves behind.
// Rare out-of-order writes to an already-flushed page go straight to flash as a safe read-modify-
// write (flash_nrf5x erases before programming, so re-touching a page never violates the
-// writes-per-word limit — it just costs one extra erase).
+// writes-per-word limit - it just costs one extra erase).
// - The 5-byte trailer is buffered and written at finalize().
-// Net: flash is touched ~once per 4 KB page (≈ 1 per 4 blocks at 1 KB), off the per-packet path; page
+// Net: flash is touched ~once per 4 KB page (~ 1 per 4 blocks at 1 KB), off the per-packet path; page
// 0 and the last page are written at finalize() with the radio idle. For a small delta (whole .mota
// in page 0) there is ZERO flash I/O during the transfer.
@@ -47,7 +47,7 @@ class OtaStoreFlashNrf52 : public OtaStore {
// Bytes from `pos` that stay in one store region (a single flash page, or the trailer tail).
uint32_t run(uint32_t pos, uint32_t remain) const;
- // RAM home of byte `pos`: read_slot always resolves (flushed pages → memory-mapped flash); write_slot
+ // RAM home of byte `pos`: read_slot always resolves (flushed pages -> memory-mapped flash); write_slot
// opens/advances the sliding payload page and returns nullptr if `pos` is in an already-flushed page.
const uint8_t* read_slot(uint32_t pos) const;
uint8_t* write_slot(uint32_t pos);
@@ -66,7 +66,7 @@ public:
void checkpoint() override; // persist page 0 (leaves) + the open payload page so a reboot can resume
bool reopen() override; // re-attach to a container already staged in flash (scan for it)
- // Contiguous view (flash is memory-mapped). VALID ONLY AFTER finalize() — before that, page 0 and the
+ // Contiguous view (flash is memory-mapped). VALID ONLY AFTER finalize() - before that, page 0 and the
// tail are still in RAM. OtaManager/OtaCli/verify use this only once the transfer is COMPLETE.
const uint8_t* data() const { return (_flushed && _io_ok) ? (const uint8_t*)(uintptr_t)_write_start : nullptr; }
uint32_t write_start() const { return _write_start; }
diff --git a/src/helpers/ota/OtaTargets.h b/src/helpers/ota/OtaTargets.h
index b37c7d2e..300c4c45 100644
--- a/src/helpers/ota/OtaTargets.h
+++ b/src/helpers/ota/OtaTargets.h
@@ -1,7 +1,7 @@
#pragma once
#include
-// AUTO-GENERATED by tools/mota/gen_targets.py — do not edit by hand.
+// AUTO-GENERATED by tools/mota/gen_targets.py - do not edit by hand.
// 542 OTA-capable PlatformIO envs. Maps target_id (= sha2-256:4 of the env name, LE uint32)
// to the human-readable env name, so a node/tool can name a target seen over the air WITHOUT
// transmitting the string in the .mota / LoRa protocol. Regenerate when the OTA env set changes.
diff --git a/src/helpers/ota/OtaVerify.h b/src/helpers/ota/OtaVerify.h
index d706827c..1e54f341 100644
--- a/src/helpers/ota/OtaVerify.h
+++ b/src/helpers/ota/OtaVerify.h
@@ -4,7 +4,7 @@
#include "SignerAllowlist.h"
// Full verification of a staged `.mota` (device-side: uses Ed25519 via mesh::Identity, so NOT compiled
-// on the native host — the portable integrity checks live in MotaContainer and are unit-tested there).
+// on the native host - the portable integrity checks live in MotaContainer and are unit-tested there).
namespace mesh {
namespace ota {
diff --git a/src/helpers/ota/SignerAllowlist.h b/src/helpers/ota/SignerAllowlist.h
index 726644fd..f169a588 100644
--- a/src/helpers/ota/SignerAllowlist.h
+++ b/src/helpers/ota/SignerAllowlist.h
@@ -4,7 +4,7 @@
#include
#include
-// Runtime-managed allowlist of trusted Ed25519 firmware-signer public keys (docs/ota_protocol.md §9,
+// Runtime-managed allowlist of trusted Ed25519 firmware-signer public keys (docs/ota_protocol.md Section 9,
// decision D2 + Q1: no key embedded in firmware; only allowlist-signed firmware may auto-apply).
// Portable + fixed-capacity (no dynamic allocation). Persistence (load/save) is layered on per-platform.
diff --git a/src/helpers/radiolib/RadioLibWrappers.cpp b/src/helpers/radiolib/RadioLibWrappers.cpp
index f0b0b726..31be6de7 100644
--- a/src/helpers/radiolib/RadioLibWrappers.cpp
+++ b/src/helpers/radiolib/RadioLibWrappers.cpp
@@ -550,7 +550,7 @@ bool RadioLibWrapper::setRxPowerSaving(bool enabled, uint32_t rx_us, uint32_t sl
int RadioLibWrapper::recvRaw(uint8_t* bytes, int sz) {
int len = 0;
if (state & STATE_INT_READY) {
- last_radio_interrupt_millis = millis(); // ISR fired → radio hardware is alive
+ last_radio_interrupt_millis = millis(); // ISR fired -> radio hardware is alive
if (isPacketReady()) {
len = _radio->getPacketLength();
if (len > 0) {
diff --git a/src/helpers/sensors/EnvironmentSensorManager.cpp b/src/helpers/sensors/EnvironmentSensorManager.cpp
index 6c53b307..f6096678 100644
--- a/src/helpers/sensors/EnvironmentSensorManager.cpp
+++ b/src/helpers/sensors/EnvironmentSensorManager.cpp
@@ -273,14 +273,14 @@ static void scanI2CBus(TwoWire* wire, bool found[128]) {
// ============================================================
// Per-sensor init and query functions
//
-// init(wire, address) — called only when the address was seen
+// init(wire, address) - called only when the address was seen
// on the bus. Returns 0 on failure, or the number of
// telemetry channels the sensor will consume (1 for all
// single-output sensors; INA3221 returns one per enabled
// hardware channel; MLX90614 and RAK12035+calibration
// return 2).
//
-// query(channel, sub_channel, lpp) — called once per active
+// query(channel, sub_channel, lpp) - called once per active
// sensor entry during querySensors(). sub_channel is always
// 0 for single-output sensors.
// ============================================================
@@ -658,7 +658,7 @@ static const SensorDef SENSOR_TABLE[] = {
#if ENV_INCLUDE_RAK12035
{ TELEM_RAK12035_ADDRESS,"RAK12035", init_rak12035, query_rak12035 },
#endif
- { 0, nullptr, nullptr, nullptr } // sentinel — keeps the array non-empty
+ { 0, nullptr, nullptr, nullptr } // sentinel - keeps the array non-empty
};
static const size_t SENSOR_TABLE_SIZE = (sizeof(SENSOR_TABLE) / sizeof(SENSOR_TABLE[0])) - 1;
@@ -676,7 +676,7 @@ static const size_t SENSOR_TABLE_SIZE = (sizeof(SENSOR_TABLE) / sizeof(SENSOR_TA
#endif
// ============================================================
-// begin() — scan the I2C bus, then initialize only what was
+// begin() - scan the I2C bus, then initialize only what was
// found. A sensor whose address does not ACK during the scan
// is never touched by a library call, preventing hangs or
// crashes caused by absent or misbehaving hardware.
@@ -729,7 +729,7 @@ bool EnvironmentSensorManager::begin() {
}
// ============================================================
-// querySensors() — GPS stays on channel 1; each active sensor
+// querySensors() - GPS stays on channel 1; each active sensor
// gets the next available channel in the order it was
// initialized.
// ============================================================
diff --git a/src/helpers/sensors/LPPDataHelpers.h b/src/helpers/sensors/LPPDataHelpers.h
index 70a036c4..f75a392b 100644
--- a/src/helpers/sensors/LPPDataHelpers.h
+++ b/src/helpers/sensors/LPPDataHelpers.h
@@ -9,7 +9,7 @@
#define LPP_GENERIC_SENSOR 100 // 4 bytes, unsigned
#define LPP_LUMINOSITY 101 // 2 bytes, 1 lux unsigned
#define LPP_PRESENCE 102 // 1 byte, bool
-#define LPP_TEMPERATURE 103 // 2 bytes, 0.1°C signed
+#define LPP_TEMPERATURE 103 // 2 bytes, 0.1degC signed
#define LPP_RELATIVE_HUMIDITY 104 // 1 byte, 0.5% unsigned
#define LPP_ACCELEROMETER 113 // 2 bytes per axis, 0.001G
#define LPP_BAROMETRIC_PRESSURE 115 // 2 bytes 0.1hPa unsigned
@@ -24,11 +24,11 @@
#define LPP_ENERGY 131 // 4 byte, 0.001kWh, unsigned
#define LPP_DIRECTION 132 // 2 bytes, 1deg, unsigned
#define LPP_UNIXTIME 133 // 4 bytes, unsigned
-#define LPP_GYROMETER 134 // 2 bytes per axis, 0.01 °/s
+#define LPP_GYROMETER 134 // 2 bytes per axis, 0.01 deg/s
#define LPP_COLOUR 135 // 1 byte per RGB Color
-#define LPP_GPS 136 // 3 byte lon/lat 0.0001 °, 3 bytes alt 0.01 meter
+#define LPP_GPS 136 // 3 byte lon/lat 0.0001 deg, 3 bytes alt 0.01 meter
#define LPP_SWITCH 142 // 1 byte, 0/1
-#define LPP_POLYLINE 240 // 1 byte size, 1 byte delta factor, 3 byte lon/lat 0.0001° * factor, n (size-8) bytes deltas
+#define LPP_POLYLINE 240 // 1 byte size, 1 byte delta factor, 3 byte lon/lat 0.0001deg * factor, n (size-8) bytes deltas
// Multipliers
#define LPP_DIGITAL_INPUT_MULT 1
diff --git a/src/helpers/sensors/RAK12035_SoilMoisture.cpp b/src/helpers/sensors/RAK12035_SoilMoisture.cpp
index 8b291306..44046e34 100644
--- a/src/helpers/sensors/RAK12035_SoilMoisture.cpp
+++ b/src/helpers/sensors/RAK12035_SoilMoisture.cpp
@@ -5,10 +5,10 @@
* *
* Ingo Fischer 11Nov2015 *
* https://github.com/Apollon77/I2CSoilMoistureSensor *
- * *
+ * *
* Ken Privitt 8Feb2026 *
* Adapted for MeshCore Firmware Stack *
- * *
+ * *
* MIT license *
* *
* This file contains a collection of routines to access the *
@@ -30,7 +30,7 @@
// Initializes the I2C to null (will be setup later in Wire.Begin()
//
// No hardware is touched in the constructor.
- // I2C communication is deferred until begin() is called.
+ // I2C communication is deferred until begin() is called.
//------------------------------------------------------------------------------
RAK12035_SoilMoisture::RAK12035_SoilMoisture(uint8_t addr)
@@ -39,17 +39,17 @@ RAK12035_SoilMoisture::RAK12035_SoilMoisture(uint8_t addr)
_i2c = nullptr; // Bus not assigned yet; must be set in begin()
}
-//------------------------------------------------------------------------------
-// setup()
-//------------------------------------------------------------------------------
-// setup(TwoWire &i2c)
-//
-// Assigns the I2C bus that this driver instance will use. This allows the
-// application to choose between Wire, Wire1, or any other TwoWire instance
-// supported by the platform.
-//
-// No I2C communication occurs here; setup() simply stores the pointer so that
-// begin() and all register‑level operations know which bus to use.
+//------------------------------------------------------------------------------
+// setup()
+//------------------------------------------------------------------------------
+// setup(TwoWire &i2c)
+//
+// Assigns the I2C bus that this driver instance will use. This allows the
+// application to choose between Wire, Wire1, or any other TwoWire instance
+// supported by the platform.
+//
+// No I2C communication occurs here; setup() simply stores the pointer so that
+// begin() and all register-level operations know which bus to use.
//------------------------------------------------------------------------------
void RAK12035_SoilMoisture::setup(TwoWire &i2c)
@@ -58,12 +58,12 @@ void RAK12035_SoilMoisture::setup(TwoWire &i2c)
_i2c->begin(); // Initialize the bus to Wire or Wire1
}
-//------------------------------------------------------------------------------
-// RAK12035 Soil Moisture begin()
-//------------------------------------------------------------------------------
-//
-// Performs initialization of the RAK12035 soil‑moisture sensor. This
-// routine assumes that the application has already selected the I2C bus via
+//------------------------------------------------------------------------------
+// RAK12035 Soil Moisture begin()
+//------------------------------------------------------------------------------
+//
+// Performs initialization of the RAK12035 soil-moisture sensor. This
+// routine assumes that the application has already selected the I2C bus via
// setup() and that the bus has been initialized externally (Wire.begin()).
// It uses the passed in I2C Address (default 0x20)
//
@@ -74,14 +74,14 @@ void RAK12035_SoilMoisture::setup(TwoWire &i2c)
// and currently only supports one address 0x20 (the default).
// To support three sensors, EnvironmentSensorManager would need to be modified
// to support multiple instances of the RAK12035_SoilMoisture class,
-// each with a different address. (0x20, 0x21, 0x22)
+// each with a different address. (0x20, 0x21, 0x22)
// The begin() function would need to be modified to loop through the three addresses
//
// DEBUG STATEMENTS: Can be enabled by uncommenting or adding:
// File: variants/rak4631 platformio.ini
-// Section example: [env:RAK_4631_companion_radio_ble]
+// Section example: [env:RAK_4631_companion_radio_ble]
// Enable Debug statements: -D MESH_DEBUG=1
-//
+//
//------------------------------------------------------------------------------
bool RAK12035_SoilMoisture::begin(uint8_t addr)
{
@@ -105,36 +105,36 @@ bool RAK12035_SoilMoisture::begin(uint8_t addr)
* USE the Build Flag: -D ENABLE_RAK12035_CALIBRATION = 1
* OR
* Change the value to 1 in the RAK12035_SoilMoisture.h file
- *
+ *
* Calibration Procedure:
* 1) Flash the Calibration version of the firmware.
* 2) Leave the sensor dry, power up the device.
* 3) After detecting the RAK12035 this firmware will display calibration data on Channel 3
- *
+ *
* Frequency = Current Capacitance Value
* Temperature = Current Wet calibration value
* Power = Current Dry calibration value
- *
+ *
* 4) Click refresh several times. This will take a capacitance reading and if it is
* greater than the current Dry value it will store it in the sensor
* The value will bounce a little as you click refresh, but it eventually settles down (a few clicks)
* the stored value will stabalize at it's Maximum value.
- *
- * 5) Put the sensor in water.
- *
+ *
+ * 5) Put the sensor in water.
+ *
* 6) Click refresh several times. This will take a capacitance reading and if it is
* less than the current Wet value it will store it in the sensor
* The value will bounce a little as you click refresh, but it eventually settles down (a few clicks)
* the stored value will stabalize at it's Minimum value.
- *
+ *
* 7) The Sensor is now calibrated, turn off the device.
- *
+ *
* 8) Reflash the device with the non-Calibration Firmware, Data will be shown on Channel 2
- *
+ *
*------------------------------------------------------------------------------------------
-*/
+*/
-#if ENABLE_RAK12035_CALIBRATION
+#if ENABLE_RAK12035_CALIBRATION
uint16_t _wet = 2000; // A high value the should be out of the normal Wet range
set_humidity_full(_wet);
@@ -143,7 +143,7 @@ bool RAK12035_SoilMoisture::begin(uint8_t addr)
#endif
/*--------------------------------------------------------------------------------
- *
+ *
* Check if a sensor is present and return true if found, false if not present
*
*--------------------------------------------------------------------------------
@@ -162,7 +162,7 @@ bool RAK12035_SoilMoisture::begin(uint8_t addr)
*
* Below are all the routines to execute the various I2C commands supported
* by the RAK12035 sensor
- *
+ *
*--------------------------------------------------------------------------------*/
uint16_t RAK12035_SoilMoisture::get_sensor_capacitance() //Command 01 - (r) 2 byte
@@ -222,7 +222,7 @@ float RAK12035_SoilMoisture::get_sensor_temperature() //Command 05
MESH_DEBUG_PRINTLN("Function 5: get_temperature() FAIL: Bad data returned = %02X %02X", buf[0], buf[1]);
return (buf[0] << 8) | buf[1]; // raw data returned for debugging 0XFFFF is error
}
- // Sensor returns a 16-bit signed integer (°C * 10)
+ // Sensor returns a 16-bit signed integer (degC * 10)
int16_t raw = (buf[0] << 8) | buf[1];
float tempC = raw / 10.0f;
MESH_DEBUG_PRINTLN("Function 5: get_temperature() SUCCESS: Raw=%04X Temp=%.1f C", raw, tempC);
@@ -283,7 +283,7 @@ bool RAK12035_SoilMoisture::set_humidity_zero(uint16_t zero) //Command
uint8_t RAK12035_SoilMoisture::get_sensor_moisture() //Command 09 - (r) 1 byte
{
-// Load calibration values from sensor
+// Load calibration values from sensor
_wet_cal = get_humidity_full();
_dry_cal = get_humidity_zero();
@@ -390,26 +390,26 @@ bool RAK12035_SoilMoisture::getEvent(uint8_t *humidity, uint16_t *temp)
uint8_t moist = get_sensor_moisture();
if (moist == 0xFF) //error indicator
return false;
- MESH_DEBUG_PRINTLN("getEvent() - Humidity = %d", moist);
+ MESH_DEBUG_PRINTLN("getEvent() - Humidity = %d", moist);
*humidity = moist;
//Read temperature (degrees C)
uint16_t t = get_sensor_temperature();
if (t == 0XFFFF) // error indicator
return false;
-
- *temp = t;
- MESH_DEBUG_PRINTLN("getEvent() - Temperature = %d", t);
+
+ *temp = t;
+ MESH_DEBUG_PRINTLN("getEvent() - Temperature = %d", t);
return true;
}
/*------------------------------------------------------------------------------------------*
* Sensor Power Management and Reset Routines
- *
- * These routines manage the power and reset state of the sensor. The sensor_on() routine is
- * designed to power on the sensor and wait for it to become responsive, while the reset()
+ *
+ * These routines manage the power and reset state of the sensor. The sensor_on() routine is
+ * designed to power on the sensor and wait for it to become responsive, while the reset()
* routine toggles the reset pin and waits for the sensor to respond with a valid version.
- *
+ *
* They are for a future sensor power management function.
*------------------------------------------------------------------------------------------*/
@@ -432,7 +432,7 @@ bool RAK12035_SoilMoisture::sensor_on()
*/
uint8_t v = 0;
uint32_t timeout = millis();
- while ((!query_sensor())) //Wait for sensor to respond to I2C commands,
+ while ((!query_sensor())) //Wait for sensor to respond to I2C commands,
{ //indicating it is ready
if ((millis() - timeout) > 50){ //0.5 second timeout for sensor to respond
MESH_DEBUG_PRINTLN("reset() - Timeout, no response from I2C commands");
@@ -452,11 +452,11 @@ bool RAK12035_SoilMoisture::reset()
//
// The Atmel 8495 Microcoltroller: Reset input. A low level on this pin for longer than
// the minimum pulse length will generate a reset, even if the clock is not
-// running and provided the reset pin has not been disabled. The minimum pulse length is
+// running and provided the reset pin has not been disabled. The minimum pulse length is
// given in Table 25-5 on page 240. 2000ns = .002mS
// Shorter pulses are not guaranteed to generate a reset.
//
-// Power is never removed so the Sensor reset was removed and is not needed,
+// Power is never removed so the Sensor reset was removed and is not needed,
// But might be needed if power is ever switched off. Here is tested code.
// This has been commented out due to a pin name conflict with the Heltec v3
@@ -477,12 +477,12 @@ bool RAK12035_SoilMoisture::reset()
const uint32_t timeout_ms = 500; // Wait for 0.5 seconds
uint32_t start = millis();
-
+
while (true) {
if (query_sensor()) {
MESH_DEBUG_PRINTLN("reset() - First Pass, Sensor responded with valid version");
uint32_t stop_time = millis();
- MESH_DEBUG_PRINTLN("reset() - Timeout, Stop Time: %d mS", stop_time);
+ MESH_DEBUG_PRINTLN("reset() - Timeout, Stop Time: %d mS", stop_time);
MESH_DEBUG_PRINTLN("reset() - Timeout, Duration: %d mS", (stop_time - start_time));
return true;
@@ -490,7 +490,7 @@ bool RAK12035_SoilMoisture::reset()
if (millis() - start > timeout_ms) {
MESH_DEBUG_PRINTLN("reset() - Timeout waiting for valid sensor version");
uint32_t stop_time = millis();
- MESH_DEBUG_PRINTLN("reset() - Timeout, Stop Time: %d mS", stop_time);
+ MESH_DEBUG_PRINTLN("reset() - Timeout, Stop Time: %d mS", stop_time);
MESH_DEBUG_PRINTLN("reset() - Timeout, Duration: %d mS", (stop_time - start_time));
return false;
}
diff --git a/src/helpers/ui/DisplayDriver.h b/src/helpers/ui/DisplayDriver.h
index dcc5fe03..dccdc518 100644
--- a/src/helpers/ui/DisplayDriver.h
+++ b/src/helpers/ui/DisplayDriver.h
@@ -51,7 +51,7 @@ public:
if (c >= 32 && c <= 126) {
dest[j++] = c; // ASCII printable
} else if (c >= 0x80) {
- dest[j++] = '\xDB'; // CP437 full block █
+ dest[j++] = '\xDB'; // CP437 full block #
while (src[i+1] && (src[i+1] & 0xC0) == 0x80)
i++; // skip UTF-8 continuation bytes
}
diff --git a/src/helpers/ui/U8g2Display.h b/src/helpers/ui/U8g2Display.h
index 73c58936..1741ab8a 100644
--- a/src/helpers/ui/U8g2Display.h
+++ b/src/helpers/ui/U8g2Display.h
@@ -17,7 +17,7 @@
#endif
class U8g2Display : public DisplayDriver {
- // U8g2 constructor for SSD1306/SSD1315 72×40 panel — handles all
+ // U8g2 constructor for SSD1306/SSD1315 72x40 panel - handles all
// GDDRAM column/page offsets, SETMULTIPLEX, SETDISPLAYOFFSET internally
U8G2_SSD1306_72X40_ER_F_HW_I2C _u8g2;
bool _isOn;
diff --git a/test/test_mesh_tables/test_simple_mesh_tables.cpp b/test/test_mesh_tables/test_simple_mesh_tables.cpp
index aff2fcd9..72c2ce08 100644
--- a/test/test_mesh_tables/test_simple_mesh_tables.cpp
+++ b/test/test_mesh_tables/test_simple_mesh_tables.cpp
@@ -45,7 +45,7 @@ static Packet makeMultipartAckPacket(uint32_t crc, uint8_t remaining = 1, bool d
return p;
}
-// ── wasSeen: pure query ───────────────────────────────────────────────────────
+// -- wasSeen: pure query -------------------------------------------------------
TEST(SimpleMeshTables, WasSeen_ReturnsFalseForUnseen) {
SimpleMeshTables t;
@@ -61,7 +61,7 @@ TEST(SimpleMeshTables, WasSeen_IsPureQuery_DoesNotInsert) {
EXPECT_FALSE(t.wasSeen(&p));
}
-// ── markSeen + wasSeen ───────────────────────────────────────────────────────
+// -- markSeen + wasSeen -------------------------------------------------------
TEST(SimpleMeshTables, MarkSeen_MakesWasSeenReturnTrue) {
SimpleMeshTables t;
@@ -100,7 +100,7 @@ TEST(SimpleMeshTables, QueryThenMark_WorksCorrectly) {
EXPECT_TRUE(t.wasSeen(&p));
}
-// ── dup stats ────────────────────────────────────────────────────────────────
+// -- dup stats ----------------------------------------------------------------
TEST(SimpleMeshTables, WasSeen_IncrementsFloodDupStat) {
SimpleMeshTables t;
@@ -120,7 +120,7 @@ TEST(SimpleMeshTables, WasSeen_IncrementsDirectDupStat) {
EXPECT_EQ(1u, t.getNumDirectDups());
}
-// ── clear ────────────────────────────────────────────────────────────────────
+// -- clear --------------------------------------------------------------------
TEST(SimpleMeshTables, Clear_RemovesSeenPacket) {
SimpleMeshTables t;
diff --git a/test/test_ota/mota_vectors.h b/test/test_ota/mota_vectors.h
index 48a1e205..b9885946 100644
--- a/test/test_ota/mota_vectors.h
+++ b/test/test_ota/mota_vectors.h
@@ -1,4 +1,4 @@
-// AUTO-GENERATED by tools/mota/gen_vectors.py — do not edit by hand.
+// AUTO-GENERATED by tools/mota/gen_vectors.py - do not edit by hand.
// Cross-check vectors: a real .mota from the reference packager (motalib.py).
#pragma once
#include
diff --git a/test/test_ota/test_ota_core.cpp b/test/test_ota/test_ota_core.cpp
index 5b45c9cc..9f2d08ac 100644
--- a/test/test_ota/test_ota_core.cpp
+++ b/test/test_ota/test_ota_core.cpp
@@ -195,7 +195,7 @@ TEST(OtaMerkle, BinaryCounterMatchesLevelByLevel) {
}
// Verify every block's proof for several tricky counts, using proofs generated by the Python
-// reference (the oracle) — covers deep promotion chains (100, 255, 256, ...).
+// reference (the oracle) - covers deep promotion chains (100, 255, 256, ...).
TEST(OtaMerkle, ReferenceProofsAllIndices) {
for (int c = 0; c < N_PROOF_CASES; c++) {
const ProofCase& pc = PROOF_CASES[c];
@@ -285,7 +285,7 @@ TEST(OtaFirmwareInfo, ParsesIdentity) {
TEST(OtaFirmwareInfo, IgnoresStagedMotaHigherInRegion) {
// The firmware's own EndF must win even when a staged .mota (which embeds its own EndF) sits
- // above it in the same region — the body_len == offset check disambiguates.
+ // above it in the same region - the body_len == offset check disambiguates.
std::vector body(2000);
for (size_t i = 0; i < body.size(); i++) body[i] = (uint8_t)(i ^ 0x5A);
std::vector img = make_image(body);
@@ -499,7 +499,7 @@ static void pump(OtaManager& client, int guard_max = 200000) {
}
}
-// A test MotaSource backing an external "folder" with one or more complete `.mota` images held in RAM —
+// A test MotaSource backing an external "folder" with one or more complete `.mota` images held in RAM -
// the simplest concrete transport (a real device uses serial/BLE/WiFi/FS, same interface). describe()
// parses each container for the catalog + region offsets; read() is a bounds-checked memcpy.
class RamMotaSource : public mesh::ota::MotaSource {
@@ -653,7 +653,7 @@ TEST(OtaTransfer, MultiFragmentBlocks) {
// Multi-mota folder serve: a node serves its OWN fw (view0) PLUS an external folder (RamMotaSource) of
// other `.mota`. Peers discover BOTH via the tiny beacon -> query -> broadcast HAVE catalog, then fetch an
-// external mota end-to-end. The relaying node never holds the folder image in RAM — it streams the
+// external mota end-to-end. The relaying node never holds the folder image in RAM - it streams the
// manifest/leaves/blocks from the source on demand (loadSource + srcReadTramp + proof-gen from read
// leaves). The fetched bytes must equal the original `.mota` (proves the trustless relay is byte-exact).
TEST(OtaFolder, ServesSelfPlusFolderAndFetchesExternal) {
@@ -701,7 +701,7 @@ TEST(OtaFolder, ServesSelfPlusFolderAndFetchesExternal) {
}
// Fetch-resume across a reboot: a client commits some blocks, "reboots" (a fresh OtaManager on the SAME
-// persisted store), and resumeStaged() re-adopts the partial container and finishes the remaining blocks —
+// persisted store), and resumeStaged() re-adopts the partial container and finishes the remaining blocks -
// without re-fetching the manifest or the blocks already present.
TEST(OtaTransfer, ResumeAfterReboot) {
g_q.clear();
diff --git a/test/test_ota/test_ota_flashplan.cpp b/test/test_ota/test_ota_flashplan.cpp
index 2a501740..5d47447e 100644
--- a/test/test_ota/test_ota_flashplan.cpp
+++ b/test/test_ota/test_ota_flashplan.cpp
@@ -5,8 +5,8 @@
using namespace mesh::ota;
// These lock down the nRF52 single-slot staging geometry that OtaStoreFlashNrf52::begin()/
-// reopen() rely on. A received `.mota` is placed bottom-aligned below the filesystem region — ExtraFS
-// (0xD4000) / InternalFS (0xED000), where the node's user preferences live — and above the running image.
+// reopen() rely on. A received `.mota` is placed bottom-aligned below the filesystem region - ExtraFS
+// (0xD4000) / InternalFS (0xED000), where the node's user preferences live - and above the running image.
// The prefs region is assumed IMMUTABLE (its bytes are outside the served/hashed self-image), so staging
// or an in-place apply must never reach into it. If a layout constant or the placement math is edited
// inconsistently, these fail here instead of silently corrupting prefs / the app on real hardware.
@@ -62,7 +62,7 @@ TEST(OtaFlashPlan, RejectsUndersizedContainer) {
}
// The user-preferences filesystems (ExtraFS @ 0xD4000, InternalFS @ 0xED000) are entirely ABOVE any
-// staged container AND above the in-place apply workspace — the immutability the shared-firmware hashes
+// staged container AND above the in-place apply workspace - the immutability the shared-firmware hashes
// depend on. Hard-code the FS addresses here (independent of the layout header) so a future edit to
// FS_START that drifts into a filesystem is caught.
TEST(OtaFlashPlan, PrefsRegionNeverStaged) {
@@ -89,7 +89,7 @@ TEST(OtaFlashPlan, SupportsS140V7RuntimeBase) {
EXPECT_EQ(start + CAP_V7, MOTA_NRF52_FS_START);
}
-// out_start is only written on success — a rejected plan must not clobber the caller's variable.
+// out_start is only written on success - a rejected plan must not clobber the caller's variable.
TEST(OtaFlashPlan, LeavesOutputUntouchedOnReject) {
uint32_t start = 0x1234ABCD;
EXPECT_FALSE(mota_nrf52_stage_plan(CAP_V6 + 1, APP_V6, APP_V6, start));
diff --git a/tools/mota/README.md b/tools/mota/README.md
index f57e9808..ac51aecc 100644
--- a/tools/mota/README.md
+++ b/tools/mota/README.md
@@ -1,7 +1,7 @@
-# `tools/mota/` — OTA Python reference library & build/test tooling
+# `tools/mota/` - OTA Python reference library & build/test tooling
The Python side of MeshCore's `.mota` OTA system. It is the **reference implementation** of the wire
-spec ([`docs/ota_protocol.md`](../../docs/ota_protocol.md)) and the **build + test infrastructure** — it is
+spec ([`docs/ota_protocol.md`](../../docs/ota_protocol.md)) and the **build + test infrastructure** - it is
no longer a user-facing CLI.
> **Want to build / verify / inspect / serve `.mota` from the command line?** Use the standalone
@@ -24,8 +24,8 @@ Uses the repo's Python venv (`meshcore/`). Dependencies: `detools` (delta), `cry
|---|---|
| `motalib.py` | Core logic: multihash, EndF, merkle tree+proofs, manifest/container build/parse/verify. The **reference implementation** of the spec and the unit-test oracle. Imported by everything below. |
| `pio_endf.py` | **PlatformIO post-build hook** (wired in `platformio.ini`) that injects the `EndF` self-identity trailer into the flashed firmware (`-D ENABLE_OTA`). |
-| `gen_vectors.py` | Generates `test/test_ota/mota_vectors.h` — the cross-check vectors the native C++ tests run against. |
-| `gen_targets.py` | Generates `src/helpers/ota/OtaTargets.h` — the `target_id → env-name` table (every `ENABLE_OTA` env, resolved from `pio project config`). Shared by the firmware and `motatool` so a node can name a target seen over the air without sending the string. Regenerate when the OTA env set changes. |
+| `gen_vectors.py` | Generates `test/test_ota/mota_vectors.h` - the cross-check vectors the native C++ tests run against. |
+| `gen_targets.py` | Generates `src/helpers/ota/OtaTargets.h` - the `target_id -> env-name` table (every `ENABLE_OTA` env, resolved from `pio project config`). Shared by the firmware and `motatool` so a node can name a target seen over the air without sending the string. Regenerate when the OTA env set changes. |
| `test_mota.py` | Unit tests for `motalib` (run directly or via pytest). |
## Tests
@@ -45,7 +45,7 @@ firmware and matches a delta's `base_hash` against its own `EndF`. Wiring (handl
- **ESP32 / RP2040** (emit `firmware.bin`): `post:tools/mota/pio_endf.py` in the env's `extra_scripts` plus
`-D ENABLE_OTA=1`. The hook appends the 56-byte `EndF` (with `target_id`/`fw_version`/`hw_id`) to the app
`.bin` before merge.
-- **nRF52 / STM32** (emit `.hex` → `.uf2`): the same hook rewrites the `.hex` with the trailer at the image
+- **nRF52 / STM32** (emit `.hex` -> `.uf2`): the same hook rewrites the `.hex` with the trailer at the image
end. The byte logic is `motalib.ensure_endf`, used everywhere.
`target_id` = `sha2-256:4(pio_env_name)`, `hw_id` = explicit `-D MOTA_HW_ID` or a role-stripped hardware
diff --git a/tools/mota/gen_targets.py b/tools/mota/gen_targets.py
index a6a0423d..d30267fc 100644
--- a/tools/mota/gen_targets.py
+++ b/tools/mota/gen_targets.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-"""Generate src/helpers/ota/OtaTargets.h — the target_id -> env-name table for OTA-capable envs.
+"""Generate src/helpers/ota/OtaTargets.h - the target_id -> env-name table for OTA-capable envs.
A target_id is sha2-256:4(pio_env_name); it travels in beacons / the .mota manifest as 4 bytes. This
table lets a node (and motatool) print the human-readable env name for a target seen over the air, WITHOUT
@@ -51,7 +51,7 @@ def main():
rows = [(ml.target_id_for_env(e), e) for e in envs]
seen = {}
- for tid, e in rows: # sha2-256:4 over ~300 names — verify no collisions
+ for tid, e in rows: # sha2-256:4 over ~300 names - verify no collisions
if tid in seen and seen[tid] != e:
print(f"WARNING: target_id collision 0x{tid:08x}: {seen[tid]!r} vs {e!r}", file=sys.stderr)
seen[tid] = e
@@ -61,7 +61,7 @@ def main():
"#pragma once",
"#include ",
"",
- "// AUTO-GENERATED by tools/mota/gen_targets.py — do not edit by hand.",
+ "// AUTO-GENERATED by tools/mota/gen_targets.py - do not edit by hand.",
f"// {len(rows)} OTA-capable PlatformIO envs. Maps target_id (= sha2-256:4 of the env name, LE uint32)",
"// to the human-readable env name, so a node/tool can name a target seen over the air WITHOUT",
"// transmitting the string in the .mota / LoRa protocol. Regenerate when the OTA env set changes.",
diff --git a/tools/mota/gen_vectors.py b/tools/mota/gen_vectors.py
index 35b1a702..70227e56 100644
--- a/tools/mota/gen_vectors.py
+++ b/tools/mota/gen_vectors.py
@@ -70,7 +70,7 @@ def main():
m.merkle_root, len(leaves))
lines = [
- "// AUTO-GENERATED by tools/mota/gen_vectors.py — do not edit by hand.",
+ "// AUTO-GENERATED by tools/mota/gen_vectors.py - do not edit by hand.",
"// Cross-check vectors: a real .mota from the reference packager (motalib.py).",
"#pragma once",
"#include ",
diff --git a/tools/mota/motalib.py b/tools/mota/motalib.py
index a6e18411..1d6bcaf0 100644
--- a/tools/mota/motalib.py
+++ b/tools/mota/motalib.py
@@ -1,5 +1,5 @@
"""
-motalib — build/parse/verify MeshCore ``.mota`` firmware-update containers.
+motalib - build/parse/verify MeshCore ``.mota`` firmware-update containers.
Pure logic, no CLI. Implements docs/ota_protocol.md (format_ver=2, fixed layout).
@@ -136,10 +136,10 @@ def hardware_id_for_env(env_name: str) -> str:
@dataclass
class FwIdent:
- """Self-describing firmware identity carried in the EndF trailer (docs/ota_protocol.md §2) so a node /
+ """Self-describing firmware identity carried in the EndF trailer (docs/ota_protocol.md Section 2) so a node /
the packaging tool reads it straight from the firmware instead of relying on build flags or filenames."""
fw_version: int = 0 # packed MAJOR<<24 | MINOR<<16 | PATCH<<8 | pre
- target_id: int = 0 # sha2-256:4(pio_env) as uint32 LE — hw + role + partition (fetch routing)
+ target_id: int = 0 # sha2-256:4(pio_env) as uint32 LE - hw + role + partition (fetch routing)
hw_id: str = "" # readable hardware tag (brick-safety), e.g. "RAK4631"
diff --git a/tools/mota/pio_endf.py b/tools/mota/pio_endf.py
index b45eedcd..b61294b7 100644
--- a/tools/mota/pio_endf.py
+++ b/tools/mota/pio_endf.py
@@ -1,6 +1,6 @@
"""
PlatformIO post-build extra-script: append the MeshCore ``EndF`` trailer to the
-firmware image so a running node can self-locate its size/identity (docs/ota_protocol.md §2).
+firmware image so a running node can self-locate its size/identity (docs/ota_protocol.md Section 2).
Wire it (ONLY for OTA-enabled builds) from a variant/env, e.g.:
@@ -60,7 +60,7 @@ def _version_from_headers():
"""FIRMWARE_VERSION is a header ``#define`` in the example (upstream MeshCore convention), not a -D, so
_cppdef() can't see it and the EndF version would otherwise default to 0. Read it from the source WITHOUT
changing where MeshCore authors it: prefer the example this env actually builds (from build_src_filter),
- else fall back to the repo-wide value when it's unambiguous. Purely additive — no MeshCore file changes,
+ else fall back to the repo-wide value when it's unambiguous. Purely additive - no MeshCore file changes,
and a -D override (checked first in _firmware_ident) still wins for release builds."""
import re, glob
proj = env["PROJECT_DIR"] # noqa: F821
@@ -102,7 +102,7 @@ def _version_from_headers():
def _firmware_ident():
- """Self-describing identity to embed in EndF (docs/ota_protocol.md §2): target_id is computed from the
+ """Self-describing identity to embed in EndF (docs/ota_protocol.md Section 2): target_id is computed from the
PlatformIO env name (so it's correct even without build.sh's -D MOTA_TARGET_ID), hw_id from MOTA_HW_ID,
fw_version from FIRMWARE_VERSION (a -D if set, else the example's header #define)."""
import re
diff --git a/tools/mota/test_mota.py b/tools/mota/test_mota.py
index 9458493b..ceae01fc 100644
--- a/tools/mota/test_mota.py
+++ b/tools/mota/test_mota.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
"""
-Tests for motalib — run with the meshcore venv:
+Tests for motalib - run with the meshcore venv:
./meshcore/bin/python tools/mota/test_mota.py
diff --git a/variants/generic-e22/variant.h b/variants/generic-e22/variant.h
index 6c850af2..4fe6b7b9 100644
--- a/variants/generic-e22/variant.h
+++ b/variants/generic-e22/variant.h
@@ -12,7 +12,7 @@
#define BATTERY_PIN 35 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage
#define ADC_CHANNEL ADC1_GPIO35_CHANNEL
#define ADC_MULTIPLIER 1.85 // (R1 = 470k, R2 = 680k)
-#define EXT_PWR_DETECT 4 // Pin to detect connected external power source for LILYGO® TTGO T-Energy T18 and other DIY boards
+#define EXT_PWR_DETECT 4 // Pin to detect connected external power source for LILYGO(R) TTGO T-Energy T18 and other DIY boards
#define EXT_NOTIFY_OUT 12 // Overridden default pin to use for Ext Notify Module (#975).
#define LED_PIN 2 // add status LED (compatible with core-pcb and DIY targets)
diff --git a/variants/heltec_t096/platformio.ini b/variants/heltec_t096/platformio.ini
index 132a6612..ee18d291 100644
--- a/variants/heltec_t096/platformio.ini
+++ b/variants/heltec_t096/platformio.ini
@@ -2,7 +2,7 @@
; nRF52840 (same SoftDevice S140 v6 + flash geometry as the RAK4631), so it reuses the shared
; rak4631_hw OTA recipe (ENABLE_OTA + OtaFlashLayout_nrf52.h). With the OTAFIX bootloader (oltaco
; PR #42 adds the T096 board) it can apply in place; without it the node still discovers/relays/serves
-; OTA and just fetch-refuses apply (graceful) — a T096 is a valuable OTA relay/seeder either way.
+; OTA and just fetch-refuses apply (graceful) - a T096 is a valuable OTA relay/seeder either way.
extends = rak4631_hw
board = heltec_t096
board_build.ldscript = boards/nrf52840_s140_v6.ld
diff --git a/variants/heltec_tracker/platformio.ini b/variants/heltec_tracker/platformio.ini
index a20058f3..43b91794 100644
--- a/variants/heltec_tracker/platformio.ini
+++ b/variants/heltec_tracker/platformio.ini
@@ -33,7 +33,7 @@ build_flags =
-D PIN_TFT_LEDA_CTL=21 ; LEDK (switches on/off via mosfet to create the ground)
-D PIN_GPS_RX=33
-D PIN_GPS_TX=34
- -D PIN_GPS_EN=35 ; N-ch MOSFET Q2 drives P-ch high-side switch → active HIGH (default)
+ -D PIN_GPS_EN=35 ; N-ch MOSFET Q2 drives P-ch high-side switch -> active HIGH (default)
-D PIN_GPS_RESET=36
-D PIN_GPS_RESET_ACTIVE=LOW
-D GPS_BAUD_RATE=115200
diff --git a/variants/heltec_tracker/target.cpp b/variants/heltec_tracker/target.cpp
index 6140298c..46d3dc06 100644
--- a/variants/heltec_tracker/target.cpp
+++ b/variants/heltec_tracker/target.cpp
@@ -16,7 +16,7 @@ WRAPPER_CLASS radio_driver(radio, board);
ESP32RTCClock fallback_clock;
AutoDiscoverRTCClock rtc_clock(fallback_clock);
-// GPS_EN (GPIO35) drives N-ch MOSFET → P-ch high-side switch; GPS_RESET (GPIO36) active LOW
+// GPS_EN (GPIO35) drives N-ch MOSFET -> P-ch high-side switch; GPS_RESET (GPIO36) active LOW
MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1, &rtc_clock, GPS_RESET, GPS_EN, &board.periph_power);
HWTSensorManager sensors = HWTSensorManager(nmea);
diff --git a/variants/heltec_v4/LoRaFEMControl.cpp b/variants/heltec_v4/LoRaFEMControl.cpp
index 168185c3..25fbc8b4 100644
--- a/variants/heltec_v4/LoRaFEMControl.cpp
+++ b/variants/heltec_v4/LoRaFEMControl.cpp
@@ -5,7 +5,7 @@
void LoRaFEMControl::init(void)
{
- // Power on FEM LDO — set registers before releasing RTC hold for
+ // Power on FEM LDO - set registers before releasing RTC hold for
// atomic transition (no glitch on deep sleep wake).
pinMode(P_LORA_PA_POWER, OUTPUT);
digitalWrite(P_LORA_PA_POWER, HIGH);
@@ -17,8 +17,8 @@ void LoRaFEMControl::init(void)
}
// Auto-detect FEM type via shared GPIO2 default pull level.
- // GC1109 CSD: internal pull-down → reads LOW
- // KCT8103L CSD: internal pull-up → reads HIGH
+ // GC1109 CSD: internal pull-down -> reads LOW
+ // KCT8103L CSD: internal pull-up -> reads HIGH
rtc_gpio_hold_dis((gpio_num_t)P_LORA_KCT8103L_PA_CSD);
pinMode(P_LORA_KCT8103L_PA_CSD, INPUT);
delay(1);
diff --git a/variants/lilygo_tbeam_1w/TBeam1WBoard.h b/variants/lilygo_tbeam_1w/TBeam1WBoard.h
index d999dfd4..670f9d40 100644
--- a/variants/lilygo_tbeam_1w/TBeam1WBoard.h
+++ b/variants/lilygo_tbeam_1w/TBeam1WBoard.h
@@ -8,16 +8,16 @@
//
// Power architecture (LDO is separate chip on T-Beam board, not inside XY16P35):
//
-// VCC (+4.0~+8.0V) ──┬──────────────────► XY16P35 VCC pin 5 (PA direct)
-// (USB or Battery) │
-// │ ┌───────────┐
-// └──►│ LDO Chip │──► +3.3V ──► XY16P35 (SX1262 + LNA)
-// │ EN=GPIO40 │
-// └───────────┘
+// VCC (+4.0~+8.0V) --+------------------> XY16P35 VCC pin 5 (PA direct)
+// (USB or Battery) |
+// | +-----------+
+// +-->| LDO Chip |--> +3.3V --> XY16P35 (SX1262 + LNA)
+// | EN=GPIO40 |
+// +-----------+
// LDO_EN (GPIO 40): H @ +1.2V~VIN, active high, not floating
//
// Control signals:
-// - LDO_EN (GPIO 40): HIGH enables LDO → powers SX1262 + LNA
+// - LDO_EN (GPIO 40): HIGH enables LDO -> powers SX1262 + LNA
// - TCXO_EN (DIO3): HIGH enables TCXO (set to 1.8V per Meshtastic)
// - CTL (GPIO 21): HIGH=RX (LNA on), LOW=TX (LNA off)
// - DIO2: AUTO via SX126X_DIO2_AS_RF_SWITCH (TX path)
diff --git a/variants/lilygo_tbeam_1w/platformio.ini b/variants/lilygo_tbeam_1w/platformio.ini
index d3245fe0..2d2de8bd 100644
--- a/variants/lilygo_tbeam_1w/platformio.ini
+++ b/variants/lilygo_tbeam_1w/platformio.ini
@@ -21,7 +21,7 @@ build_flags =
; RF switch configuration:
; DIO2 controls TX path (PA enable) via SX126X_DIO2_AS_RF_SWITCH
; GPIO21 controls RX path (LNA enable) via SX126X_RXEN
- ; Truth table: DIO2=1,RXEN=0 → TX | DIO2=0,RXEN=1 → RX
+ ; Truth table: DIO2=1,RXEN=0 -> TX | DIO2=0,RXEN=1 -> RX
-D SX126X_DIO2_AS_RF_SWITCH=true
-D SX126X_RXEN=21
-D SX126X_DIO3_TCXO_VOLTAGE=3.0
@@ -43,8 +43,8 @@ build_flags =
-D PIN_BOARD_SCL=9
; GPS - L76K module
- ; GNSS_TXD (IO5) = GPS transmits → MCU RX
- ; GNSS_RXD (IO6) = GPS receives → MCU TX
+ ; GNSS_TXD (IO5) = GPS transmits -> MCU RX
+ ; GNSS_RXD (IO6) = GPS receives -> MCU TX
-D PIN_GPS_TX=5
-D PIN_GPS_RX=6
-D PIN_GPS_EN=16
diff --git a/variants/lilygo_tbeam_1w/variant.h b/variants/lilygo_tbeam_1w/variant.h
index f6807e56..d8608059 100644
--- a/variants/lilygo_tbeam_1w/variant.h
+++ b/variants/lilygo_tbeam_1w/variant.h
@@ -8,8 +8,8 @@
#define I2C_SCL 9
// GPS - Quectel L76K
-// GNSS_TXD (IO5) = GPS transmits → MCU RX (setPins rxPin)
-// GNSS_RXD (IO6) = GPS receives → MCU TX (setPins txPin)
+// GNSS_TXD (IO5) = GPS transmits -> MCU RX (setPins rxPin)
+// GNSS_RXD (IO6) = GPS receives -> MCU TX (setPins txPin)
#define PIN_GPS_TX 5 // MCU receives from GPS TX
#define PIN_GPS_RX 6 // MCU transmits to GPS RX
#define PIN_GPS_PPS 7 // GPS PPS output
diff --git a/variants/lilygo_techo_lite/TechoBoard.h b/variants/lilygo_techo_lite/TechoBoard.h
index 1e5651e7..055beac8 100644
--- a/variants/lilygo_techo_lite/TechoBoard.h
+++ b/variants/lilygo_techo_lite/TechoBoard.h
@@ -5,7 +5,7 @@
#include
// ============================================================
-// T-Echo Lite battery pins — hardcoded from LilyGo t_echo_lite_config.h
+// T-Echo Lite battery pins - hardcoded from LilyGo t_echo_lite_config.h
// NOT using any defines from variant.h for battery measurement
// ============================================================
#define PIN_VBAT_READ _PINNUM(0, 2) // BATTERY_ADC_DATA
diff --git a/variants/lilygo_techo_lite/variant.h b/variants/lilygo_techo_lite/variant.h
index 702ab334..8505c5d7 100644
--- a/variants/lilygo_techo_lite/variant.h
+++ b/variants/lilygo_techo_lite/variant.h
@@ -149,11 +149,11 @@ extern const int SCK;
#define PIN_DISPLAY_BUSY DISP_BUSY
////////////////////////////////////////////////////////////////////////////////
-// GPS — per LilyGo t_echo_lite_config.h
+// GPS - per LilyGo t_echo_lite_config.h
// PIN_GPS_TX/RX named from GPS module's perspective
-#define PIN_GPS_TX _PINNUM(0, 29) // GPS UART TX → MCU RX
-#define PIN_GPS_RX _PINNUM(1, 10) // GPS UART RX ← MCU TX
+#define PIN_GPS_TX _PINNUM(0, 29) // GPS UART TX -> MCU RX
+#define PIN_GPS_RX _PINNUM(1, 10) // GPS UART RX <- MCU TX
#define GPS_EN _PINNUM(1, 11) // GPS RT9080 power enable
#define PIN_GPS_STANDBY _PINNUM(1, 13) // GPS wake-up
#define PIN_GPS_PPS _PINNUM(1, 15) // GPS 1PPS
\ No newline at end of file
diff --git a/variants/lilygo_tlora_v2_1/platformio.ini b/variants/lilygo_tlora_v2_1/platformio.ini
index 24681fd6..c2860d23 100644
--- a/variants/lilygo_tlora_v2_1/platformio.ini
+++ b/variants/lilygo_tlora_v2_1/platformio.ini
@@ -1,4 +1,4 @@
-; Pins, radio, OLED — shared by all TLora envs. Does not include sensor_base (saves flash for MQTT observer).
+; Pins, radio, OLED - shared by all TLora envs. Does not include sensor_base (saves flash for MQTT observer).
[LilyGo_TLora_V2_1_1_6_core]
extends = esp32_base
board = ttgo-lora32-v1 ; LILYGO T-LoRa V2.1-1.6 ESP32 with SX1276
diff --git a/variants/meshadventurer/variant.h b/variants/meshadventurer/variant.h
index 356a3e17..2c34d3fb 100644
--- a/variants/meshadventurer/variant.h
+++ b/variants/meshadventurer/variant.h
@@ -12,7 +12,7 @@
#define BATTERY_PIN 35 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage
#define ADC_CHANNEL ADC1_GPIO35_CHANNEL
#define ADC_MULTIPLIER 2
-#define EXT_PWR_DETECT 4 // Pin to detect connected external power source for LILYGO® TTGO T-Energy T18 and other DIY boards
+#define EXT_PWR_DETECT 4 // Pin to detect connected external power source for LILYGO(R) TTGO T-Energy T18 and other DIY boards
#define EXT_NOTIFY_OUT 12 // Overridden default pin to use for Ext Notify Module (#975).
#define LED_PIN 2 // add status LED (compatible with core-pcb and DIY targets)
diff --git a/variants/rak3401/RAK3401Board.cpp b/variants/rak3401/RAK3401Board.cpp
index cbf7c108..40bcb991 100644
--- a/variants/rak3401/RAK3401Board.cpp
+++ b/variants/rak3401/RAK3401Board.cpp
@@ -47,7 +47,7 @@ void RAK3401Board::begin() {
// PIN_3V3_EN (WB_IO2, P0.34) controls the 3V3_S switched peripheral rail
// AND the 5V boost regulator (U5) on the RAK13302 that powers the SKY66122 PA.
- // Must stay HIGH during radio operation — do not toggle for power saving.
+ // Must stay HIGH during radio operation - do not toggle for power saving.
pinMode(PIN_3V3_EN, OUTPUT);
digitalWrite(PIN_3V3_EN, HIGH);
diff --git a/variants/rak3401/variant.h b/variants/rak3401/variant.h
index 98827886..66c1e47e 100644
--- a/variants/rak3401/variant.h
+++ b/variants/rak3401/variant.h
@@ -158,7 +158,7 @@ static const uint8_t AREF = PIN_AREF;
// SKY66122-11 FEM control on the RAK13302 module:
// CSD + CPS are tied together on the PCB, routed to WisBlock IO3 (P0.21).
// Setting IO3 HIGH enables the FEM (LNA for RX, PA path for TX).
-// CTX is connected to SX1262 DIO2 — the radio handles TX/RX switching
+// CTX is connected to SX1262 DIO2 - the radio handles TX/RX switching
// in hardware via SetDIO2AsRfSwitchCtrl (microsecond-accurate, no GPIO needed).
// The 5V boost for the PA is enabled by WB_IO2 (P0.34 = PIN_3V3_EN).
#define SX126X_POWER_EN (21) // P0.21 = IO3 -> SKY66122 CSD+CPS (FEM enable)
diff --git a/variants/xiao_nrf52/variant.h b/variants/xiao_nrf52/variant.h
index 25619b9e..63f1d97a 100644
--- a/variants/xiao_nrf52/variant.h
+++ b/variants/xiao_nrf52/variant.h
@@ -83,11 +83,11 @@ static const uint8_t D10 = 10;
#define PWRMGT_LPCOMP_AIN 7 // AIN7 = P0.31 = PIN_VBAT
// IMPORTANT: The XIAO exposes battery via a resistor divider (ADC_MULTIPLIER = 3.0).
// LPCOMP measures the divided voltage, not the battery voltage directly.
-// Vpin = VDD * (REFSEL fraction), and VBAT ≈ Vpin * ADC_MULTIPLIER.
+// Vpin = VDD * (REFSEL fraction), and VBAT ~ Vpin * ADC_MULTIPLIER.
//
// Using 3/8 VDD gives a wake threshold above the boot protection point:
-// - If VDD ≈ 3.0V: VBAT ≈ (3.0 * 3/8) * 3 ≈ 3375mV
-// - If VDD ≈ 3.3V: VBAT ≈ (3.3 * 3/8) * 3 ≈ 3712mV
+// - If VDD ~ 3.0V: VBAT ~ (3.0 * 3/8) * 3 ~ 3375mV
+// - If VDD ~ 3.3V: VBAT ~ (3.3 * 3/8) * 3 ~ 3712mV
#define PWRMGT_LPCOMP_REFSEL 2 // 3/8 VDD (~3.38-3.71V)
static const uint8_t A0 = PIN_A0;
diff --git a/webui/index.html b/webui/index.html
index 80e63851..7447f374 100644
--- a/webui/index.html
+++ b/webui/index.html
@@ -304,7 +304,7 @@ canvas{width:100%;height:56px;display:block}