Harden marathon-tested OTA and hardware paths

Add durable TempRadio, ACL, clock, bridge, GPS/I2C, USB logging, and Indicator recovery safeguards together with expanded automation, build/profile contracts, release metadata, and the hardware validation ledger.
This commit is contained in:
mikecarper committed 2026-08-30 19:26:15 -07:00
1 parent e26d48e4ca
commit 9fd580c89e
109 files changed
+18697 -1094

No files matched your search

+56 -7
View File
@@ -501,6 +501,26 @@
});
}
function applyMergedRak4631RepeaterCapabilities(profiles) {
return (profiles || []).map(function (profile) {
const target = String(profile && profile.target || "").toLowerCase();
if (
target !== "rak_4631_repeater" &&
target !== "rak_4631_repeater_lora_ota_no_external_sensors"
) {
return profile;
}
// The canonical RAK4631 repeater contains both the ordinary interface
// and the runtime-selectable RS232 bridge. Legacy Serial1/Serial2 target
// rows remain exact OTA compatibility assets; do not attach their files
// to this profile merely to expose the merged image's second mode.
profile.mode = "standard";
profile.connectionModes = ["standard", "rs232"];
return profile;
});
}
function applyMergedStandardUsbLoggingCapabilities(profiles) {
return (profiles || []).map(function (profile) {
const safeRole = [
@@ -619,8 +639,10 @@
return !isHiddenLegacyProfile(profile);
});
const profiles = applyMergedStandardUsbLoggingCapabilities(
omitTransportsReplacedByFull(
applyFullCompanionCapabilities(visibleProfiles)
applyMergedRak4631RepeaterCapabilities(
omitTransportsReplacedByFull(
applyFullCompanionCapabilities(visibleProfiles)
)
)
).sort(function (a, b) {
return a.target.localeCompare(b.target, undefined, {
@@ -655,6 +677,9 @@
}
function profileFieldValues(profile, field) {
if (field === "mode" && Array.isArray(profile.connectionModes)) {
return profile.connectionModes;
}
if (field === "logging" && Array.isArray(profile.loggingModes)) {
return profile.loggingModes;
}
@@ -714,10 +739,27 @@
rak15001: "RAK15001",
qspi: "QSPI",
};
return value.split("-").map(function (token) {
return tokenLabels[token] ||
token.charAt(0).toUpperCase() + token.slice(1);
}).join(" ");
const tokens = value.split("-");
const labels = [];
for (let index = 0; index < tokens.length; index += 1) {
if (
tokens[index] === "no" &&
tokens[index + 1] === "external" &&
tokens[index + 2] === "sensors"
) {
// The legacy target token is an OTA identity, not a literal statement
// that the I2C bus and every external peripheral have been removed.
labels.push("Reduced optional environmental/ranging drivers");
index += 2;
continue;
}
const token = tokens[index];
labels.push(
tokenLabels[token] ||
token.charAt(0).toUpperCase() + token.slice(1)
);
}
return labels.join(" ");
}
function labelFor(field, value) {
@@ -860,7 +902,12 @@
facts.className = "firmware-picker-facts";
const factRows = [
["Target", profile.target],
["Connection / mode", labelFor("mode", profile.mode)],
[
"Connection / mode",
profileFieldValues(profile, "mode").map(function (mode) {
return labelFor("mode", mode);
}).join(" / "),
],
["Logging", labelFor("logging", profile.logging)],
["OTA", labelFor("ota", profile.ota)],
["Feature profile", labelFor("feature", profile.feature)],
@@ -1203,6 +1250,8 @@
parseFirmwareAsset: parseFirmwareAsset,
parseTargetProfile: parseTargetProfile,
applyFullCompanionCapabilities: applyFullCompanionCapabilities,
applyMergedRak4631RepeaterCapabilities:
applyMergedRak4631RepeaterCapabilities,
applyMergedStandardUsbLoggingCapabilities:
applyMergedStandardUsbLoggingCapabilities,
applyDualCdcFullCompanionCapabilities: applyFullCompanionCapabilities,
+13 -2
View File
@@ -68,13 +68,24 @@ retain 50 because their MQTT discovery tables are constrained by internal DRAM.
| Standard non-MQTT repeater or room server | Keeps the normal role CLI and, where USB is a safe plaintext console, embeds debug/packet logging behind persistent `get/set usb.logging`. The explicitly selected portable policy can omit WebConfig and browser WiFi OTA, so those commands are unavailable and the omission is recorded in the capability manifest. |
| Legacy standard logging | No longer emitted separately. Its behavior is compiled into the ordinary artifact. Size-constrained STM32 targets embed packet logging without verbose `MESH_DEBUG`. |
| LoRa-OTA (`-ota-`) | LoRa OTA adds the `ota ...` commands; it does not otherwise reduce the role CLI. ESP32 `no_external_sensors` artifacts retain the compact browser WiFi uploader, the complete CLI, and a 254-entry neighbor table. |
| Internal-flash nRF52 repeater auto pair | `full-ota` retains the board's external-sensor drivers; `reduced-ota` omits the declared optional sensors to leave additional internal-flash staging room. RAK3401 and RAK4631 reduced builds retain INA219, INA226, INA260, and INA3221 I2C voltage/current monitors at a measured 4,808-byte flash cost. Both artifacts carry the same stable OTA target identity and are checked for `ota ...` and `retry.preset`; RAK artifacts also verify the retained monitor drivers. |
| Internal-flash nRF52 repeater auto pair | `full-ota` retains the board's external-sensor drivers; `reduced-ota` omits the declared optional sensors to leave additional internal-flash staging room. RAK3401 and RAK4631 reduced builds retain INA219, INA226, INA260, and INA3221 I2C voltage/current monitors at a measured cost below 5 KiB. Both artifacts carry the same stable OTA target identity and are checked for `ota ...` and `retry.preset`; RAK artifacts also verify the retained monitor drivers. |
| ESP32 MQTT observer or ESP-NOW bridge | Always uses the expanded FULL partition profile. The build never substitutes a reduced CLI to fit the legacy application slot. |
| FULL ESP32 USB + WiFi | Uses the matching MQTT target with packet logging on, verbose debug off, and the complete command surface supported by that role and hardware. `get/set logging.output off\|usb\|wifi\|both` selects and persists the active output paths. |
| FULL ESP32 logging fallback | Uses the matching non-MQTT target only when no WiFi MQTT sibling exists, with debug and packet logging enabled and the complete command surface supported by that role and hardware. Its persistent USB gate also covers output-off operation, avoiding a second FULL ESP-NOW image. |
| nRF52 dual-CDC Full Companion | Fresh installs expose only interface `00`; it starts as an ASCII terminal and automatically hands a complete `<` frame to framed Companion. The same interface also carries exclusive serial mOTA traffic. Enabling logging and rebooting adds interface `02` for plaintext logs. BLE and source-only LoRa OTA remain available. `get/set usb.logging` persistently controls whether the logging interface is present. |
| ESP32 single-TTY Full Companion | Every ESP32 Full image starts with the ASCII terminal on its one USB TTY and automatically hands a complete `<` frame to framed Companion. `set usb.logging on` switches that TTY to an input-capable plaintext logging terminal and makes framed Companion unavailable on USB; `set usb.logging off` stops logging but leaves the TTY in normal ASCII mode. The terminal stop token or a valid incoming framed probe then performs the ordinary switch to Binary Companion. A saved logging-on setting starts directly in that logging terminal and disables automatic frame detection. BLE, WiFi, and source-only LoRa OTA remain available. ESP32 Full uses Arduino-ESP32 2.x where supported; RC32 and ESP32-C6 keep their board-required Arduino 3.x platform but still expose only one TTY. |
| `no_external_sensors` | Removes optional external-sensor drivers and their settings; it does not remove core repeater discovery, routing, or runtime RS-232 commands. RAK3401 and RAK4631 profiles retain the four common INA I2C voltage/current monitors. GPS-preserving RAK nRF52 OTA profiles retain their GPS commands and provider; RAK4631 defaults the bridge to UART 2 because GPS uses UART 1. Legacy target suffixes remain stable for OTA identity compatibility. |
| `no_external_sensors` | Trims selected optional environmental/ranging drivers and their settings; it does not remove generic I2C, core repeater discovery, routing, or runtime RS-232 commands. RAK3401 and RAK4631 profiles retain the four common INA I2C voltage/current monitors. GPS-preserving RAK nRF52 OTA profiles retain their GPS commands and provider; RAK4631 defaults the bridge to UART 2 because RAK12501/L76K GPS uses UART 1. Legacy target suffixes remain stable for OTA identity compatibility. |
The four retained INA drivers are entries in the optional environmental-sensor
table, not the complete set of RAK I2C consumers. Compatible reduced profiles
also retain the SSD1306 OLED, supported autodiscovered RTCs, and RAK12500 I2C
GPS as separate board peripherals. RAK12501/L76K GPS uses UART Serial1. The
explicit RAK4631 Serial1 bridge omits the combined GPS provider, including the
otherwise non-UART RAK12500 path.
The firmware-configured INA3221 and RAK12500 addresses are both `0x42`. To use
both devices on one bus, leave RAK12500 at `0x42`, strap INA3221 A0 to SCL for
`0x43`, and build with `-DTELEM_INA3221_ADDRESS=0x43`.
`logging`, `OTA`, and `FULL` describe independent build features in historical
filenames. Current standard artifacts use no `-logging-` infix because their
+21 -8
View File
@@ -15,8 +15,16 @@ Build columns mean:
filename marker. Safe plaintext-USB roles embed runtime USB logging.
- **Logging** - the legacy `-logging-` profile, now represented by the same
ordinary artifact and retained as a comparison column only.
- **LoRa OTA** - the explicit `-ota-` repeater or repeater-bridge artifact. Its
optional external-sensor drivers are removed, but onboard GPS is retained.
- **LoRa OTA** - the explicit `-ota-` repeater or repeater-bridge artifact. A
legacy `no_external_sensors` profile trims selected optional environmental
and ranging drivers; it does not disable the generic I2C bus or unrelated
board-integrated peripherals. Reduced RAK3401 and RAK4631 profiles retain
INA219/INA226/INA260/INA3221 as voltage/current entries in the optional sensor
table. The SSD1306 OLED, supported autodiscovered RTCs, and RAK12500 GPS are
separate I2C peripherals in compatible recipes. RAK12501/L76K GPS instead
uses UART Serial1. The plain RAK profiles and RAK4631 Serial2 bridge retain
the combined GPS provider; the explicit RAK4631 Serial1 bridge omits that
provider, including RAK12500, because the bridge owns the RAK12501 UART.
- **FULL unified** - the expanded-partition ESP32 artifact with LoRa OTA, the
complete parser, USB packet logging, and direct WiFi MQTT. Its saved output
mode can be off, USB, WiFi, or both. MQTT observers use this profile.
@@ -24,6 +32,11 @@ Build columns mean:
where there is no MQTT sibling. It has the complete parser and a saved
USB-logging on/off gate. ESP-NOW bridges use this profile.
The firmware-configured INA3221 and RAK12500 addresses are both `0x42`; they
cannot coexist on one bus at those addresses. Leave RAK12500 at `0x42`, strap
INA3221 A0 to SCL for `0x43`, and use a build with
`-DTELEM_INA3221_ADDRESS=0x43` when both are installed.
Cell values mean:
- **Yes** - the parser includes the command. The Scope column still applies.
@@ -165,11 +178,11 @@ should use command `0x42`. See [Companion radio binary protocol](companion_proto
| GPS | [`gps setloc`](cli_commands.md#set-this-nodes-location-based-on-the-gps-coordinates) | Compiled GPS | Feature | Feature | Feature |
| GPS | [`gps advert [none/share/prefs]`](cli_commands.md#view-or-change-the-gps-advert-policy) | Compiled GPS | Feature | Feature | Feature |
| Sensors | [`get/set telemetry.access`](cli_commands.md#view-or-change-telemetry-access-mode) | Sensor-capable role | Feature | Feature | Limited |
| Sensors | [`sensor list [start]`](cli_commands.md#view-the-list-of-sensors-on-this-node) | Compiled sensor manager; OTA omits external sensors | Feature | Feature | Limited |
| Sensors | [`sensor get`; `sensor set`](cli_commands.md#view-or-change-the-value-of-a-sensor) | Compiled sensor setting; OTA omits external sensors | Feature | Feature | Limited |
| Sensors | [`sensor list [start]`](cli_commands.md#view-the-list-of-sensors-on-this-node) | Compiled sensor manager; reduced OTA trims target-selected optional environmental/ranging drivers | Feature | Feature | Limited |
| Sensors | [`sensor get`; `sensor set`](cli_commands.md#view-or-change-the-value-of-a-sensor) | Compiled sensor setting; reduced OTA trims target-selected optional environmental/ranging drivers | Feature | Feature | Limited |
| Sensors | [`io [r/s/t]<hex>`](#sensor-io) | Simple sensor role with board GPIO support | Feature | Feature | No |
| Bridge | [`get bridge.type`](cli_commands.md#view-the-compiled-bridge-type) | Compiled bridge | Feature | Feature | Feature |
| Bridge | [`get/set bridge.enabled`](cli_commands.md#view-or-change-the-bridge-enabled-flag) | Compiled bridge | Feature | Feature | Feature |
| Bridge | [`get/set bridge.enabled`; `get bridge.running`](cli_commands.md#view-or-change-the-bridge-enabled-flag) | Compiled bridge | Feature | Feature | Feature |
| Bridge | [`get/set bridge.delay`](cli_commands.md#add-a-delay-to-packets-routed-through-this-bridge) | Compiled bridge | Feature | Feature | Feature |
| Bridge | [`get/set bridge.source`](cli_commands.md#view-or-change-the-source-of-packets-bridged-to-the-external-interface) | Compiled bridge | Feature | Feature | Feature |
| Bridge | [`get/set bridge.baud`](cli_commands.md#view-or-change-the-speed-of-the-bridge-rs-232-only) | RS-232 bridge | Feature | Feature | Feature |
@@ -332,11 +345,11 @@ should use command `0x42`. See [Companion radio binary protocol](companion_proto
| GPS | [`gps setloc`](cli_commands.md#set-this-nodes-location-based-on-the-gps-coordinates) | Compiled onboard GPS | Feature | Feature | Feature | Feature | Feature |
| GPS | [`gps advert [none/share/prefs]`](cli_commands.md#view-or-change-the-gps-advert-policy) | Compiled onboard GPS | Feature | Feature | Feature | Feature | Feature |
| Sensors | [`get/set telemetry.access`](cli_commands.md#view-or-change-telemetry-access-mode) | Sensor-capable full parser | Feature | Feature | Limited | Feature | Feature |
| Sensors | [`sensor list [start]`](cli_commands.md#view-the-list-of-sensors-on-this-node) | Compiled sensor manager; `no_external_sensors` omits optional drivers | Feature | Feature | Limited | Feature | Feature |
| Sensors | [`sensor get`; `sensor set`](cli_commands.md#view-or-change-the-value-of-a-sensor) | Compiled sensor setting; `no_external_sensors` omits optional drivers | Feature | Feature | Limited | Feature | Feature |
| Sensors | [`sensor list [start]`](cli_commands.md#view-the-list-of-sensors-on-this-node) | Compiled sensor manager; `no_external_sensors` trims target-selected environmental/ranging drivers, not generic I2C | Feature | Feature | Limited | Feature | Feature |
| Sensors | [`sensor get`; `sensor set`](cli_commands.md#view-or-change-the-value-of-a-sensor) | Compiled sensor setting; `no_external_sensors` trims target-selected environmental/ranging drivers, not generic I2C | Feature | Feature | Limited | Feature | Feature |
| Sensors | [`io [r/s/t]<hex>`](#sensor-io) | Simple sensor role with board GPIO support | Feature | Feature | No | No | Feature |
| Bridge | [`get bridge.type`](cli_commands.md#view-the-compiled-bridge-type) | Compiled bridge | Feature | Feature | Feature | Yes | Feature |
| Bridge | [`get/set bridge.enabled`](cli_commands.md#view-or-change-the-bridge-enabled-flag) | Compiled bridge | Feature | Feature | Feature | Yes | Feature |
| Bridge | [`get/set bridge.enabled`; `get bridge.running`](cli_commands.md#view-or-change-the-bridge-enabled-flag) | Compiled bridge | Feature | Feature | Feature | Yes | Feature |
| Bridge | [`get/set bridge.delay`](cli_commands.md#add-a-delay-to-packets-routed-through-this-bridge) | Compiled bridge | Feature | Feature | Feature | Yes | Feature |
| Bridge | [`get/set bridge.source`](cli_commands.md#view-or-change-the-source-of-packets-bridged-to-the-external-interface) | Compiled bridge | Feature | Feature | Feature | Yes | Feature |
| Bridge | [`get/set bridge.baud`](cli_commands.md#view-or-change-the-speed-of-the-bridge-rs-232-only) | RS-232 bridge | Feature | Feature | Feature | No | Feature |
+19 -6
View File
@@ -3427,12 +3427,16 @@ still overrides the first-boot default after an update.
#### View or change the bridge enabled flag
**Usage:**
- `get bridge.enabled`
- `get bridge.running`
- `set bridge.enabled <state>`
**Parameters:**
- `state`: `on`|`off`
**Default:** `off`
`bridge.enabled` is the saved intent. `bridge.running` reports whether the
bridge actually started in this boot; they can differ after a hardware conflict,
missing credentials, or a transient initialization failure. Normal merged
repeater images default to `off`; dedicated bridge images may default to `on`.
---
@@ -3455,10 +3459,10 @@ still overrides the first-boot default after an update.
**Parameters:**
- `source`:
- `logRx`: bridges received packets
- `logTx`: bridges transmitted packets
- `rx`: bridges received packets
- `tx`: bridges transmitted packets
**Default:** `logTx`
**Default:** `tx`
> **Note:** For MQTT bridges, use `mqtt.rx` and `mqtt.tx` instead of `bridge.source`. These provide independent per-direction control and support both RX and TX simultaneously. `bridge.source` still works as a convenience alias for MQTT (setting `bridge.source rx` sets `mqtt.rx on` + `mqtt.tx off`, and vice versa), but `mqtt.rx`/`mqtt.tx` are preferred.
@@ -3579,11 +3583,20 @@ Requires WiFi connected and the MQTT bridge running.
**Parameters:**
- `port`: Hardware UART number compiled for the board. Most boards expose one
fixed UART. RAK4631 accepts `1` or `2`; UART 2 is the default so UART 1 can
remain available to GPS.
remain available to the RAK12501/L76K GPS. RAK12500 GPS uses I2C rather than
this UART, but the explicit legacy Serial1 bridge omits the combined GPS
provider and therefore does not expose either GPS path.
The setting is persistent and restarts an enabled bridge immediately. Normal
repeater artifacts start with `bridge.enabled off`; configure the UART and baud
rate before running `set bridge.enabled on`.
rate before running `set bridge.enabled on`. On the canonical RAK4631 runtime
image, UART 1 is reserved even if the bounded boot probe hears no RAK12501.
Silence cannot prove that a cold L76K is physically absent, and that module
remains powered by the shared WB_IO2/3V3_S rail. Use UART 2. UART 1 requires an
explicit no-GPS/dedicated Serial1 bridge image. This fail-closed reservation
also applies when the detected GPS is an I2C RAK12500; that receiver does not
electrically use UART 1, but its presence cannot rule out another silent UART
module.
---
+23
View File
@@ -787,6 +787,29 @@ the PIN page until Bluetooth connects or the two-minute pairing window expires.
8. The device is erased and newest firmware is installed
9. You might need to remove the pairing in Bluetooth System Settings in order to re-pair the app again.
An erase application or ordinary application ZIP/UF2 does **not** repair a
damaged or partially overwritten SoftDevice. This can happen after flashing an
nRF52 image for a different board or SoftDevice layout. A characteristic
failure is that the application can boot and read its filesystem, but reboots
or hangs when it writes or formats internal flash. Repeating `Erase Flash` is
not a recovery for that condition.
Recover it with a combined **bootloader + SoftDevice** package for the exact
board (including XIAO Sense versus non-Sense), installed through serial/BLE DFU
or SWD. With `adafruit-nrfutil`, the serial form is:
```bash
adafruit-nrfutil dfu serial -p <port> \
-pkg <exact-board-bootloader-with-softdevice>.zip -sb -t 1200
```
Then install the exact-board MeshCore application. Do not use an application-
only UF2, filesystem formatter, or a similarly named package from another
nRF52 board as a substitute. See the confirmed XIAO recovery in
[issue #3284](https://github.com/meshcore-dev/MeshCore/issues/3284) and obtain
the matching package from the
[OTAFIX releases](https://github.com/oltaco/Adafruit_nRF52_Bootloader_OTAFIX/releases).
Separately, starting in firmware version 1.7.0, there is a CLI Rescue mode. If your device has a user button (e.g. some RAK, T114), you can activate the rescue mode by holding down the user button of the device within 8 seconds of boot. Then you can use the 'Console' on <https://flasher.meshcore.io>
### 6.8. Q: WebFlasher fails on Linux with failed to open
+15 -4
View File
@@ -257,10 +257,21 @@ Hardware families with multiple released targets get a second hardware-variant
menu. It separates revisions, display type, expansion kit, radio/PA layout,
pin map, and other physical differences without crowding the first menu. The
firmware-variant menu separately exposes choices that still require different
code or wiring, such as serial port or no-external-sensors. Companion power
saving, controllable FEM receive gain, and radio-chip receive gain are saved
settings rather than separate recommended firmware files. Do not substitute a
similarly named physical target.
code or wiring, such as serial port or the legacy `no_external_sensors` target
suffix. The picker labels that suffix **Reduced optional environmental/ranging drivers**:
it does not disable generic I2C or unrelated board-integrated peripherals.
Reduced RAK3401 and RAK4631 targets retain INA219/INA226/INA260/INA3221 as
voltage/current entries in the optional sensor table. They are not the only I2C
users: the SSD1306 OLED, supported autodiscovered RTCs, and RAK12500 GPS remain
separate I2C peripherals in compatible recipes. RAK12501/L76K GPS uses Serial1
instead. The explicit RAK4631 Serial1 bridge omits the combined GPS provider
because its bridge owns the RAK12501 UART, so that legacy image does not expose
RAK12500 either. The firmware-configured INA3221 and RAK12500 addresses are both
`0x42`; to install both, keep RAK12500 at `0x42`, strap INA3221 A0 to SCL for
`0x43`, and use a build with `-DTELEM_INA3221_ADDRESS=0x43`. Companion power saving,
controllable FEM receive gain, and radio-chip receive gain are saved settings
rather than separate recommended firmware files. Do not substitute a similarly
named physical target.
The picker recommends one Full Companion image instead of separate USB, BLE,
ordinary WiFi, and USB-logging images. On ESP32, logging is off by default so
File diff suppressed because it is too large. Load diff
+79 -20
View File
@@ -200,6 +200,9 @@ reported bootloader ABI and codec mask against the selected package. If the
version command is unavailable on older firmware, the script warns and falls
back to the legacy `ota self` platform marker. If an nRF52 bootloader lacks
the required capabilities, install the exact-board OTAFIX bootloader first.
`ota stats` is only an optional EndF version probe. It uses one bounded retry
cycle; unsupported firmware or a lost reply falls back directly to the
required `ver` command instead of entering an operator continuation loop.
The default TempRadio tuple is:
@@ -212,6 +215,30 @@ North American example: choose a legal frequency supported by every
participating radio and appropriate to your location. Older radios that do not
support SF5 require a complete replacement tuple passed with `--temp-radio`.
Before that long window is allowed, the live runner performs a mandatory
independent three-minute rehearsal. Its exact normal-channel `ota status` and
`ota self` identity proof gets one shared four-minute, read-only budget before
any schedule, radio override, seeder, or transfer is armed; this tolerates a
marginal link without consuming or extending a live lease. It advances a stale
managed controller with exact readback and treats a source terminal's
whole-minute display as an uncertainty window, never as an exact epoch. A
source minute overlapping the host is preserved; a completely stale minute is
pinned to a guarded future value and read back once. Neither clock is moved
backward, and the runner stops if a managed clock cannot be proven within the
ten-minute limit. Each remote participant must expose an empty `tempradioat`
schedule. The runner projects one fixed start/end interval into each
participant's independently sampled RTC, sends each mutation once, proves
every exact identity on the temporary tuple, waits for natural expiry, and
proves the complete normal path again. A lost schedule reply is resolved by
those on-air proofs; it is never blindly replayed with a fresh duration.
The published RAK3401 `v1.16.7-c1caa5ad` LoRa-OTA image includes
`get/set/del tempradioat` and can take this safe first step. A truly older or
reduced build without fixed TempRadio scheduling is rejected before mutation;
bootstrap it locally or with another explicitly controlled maintenance path.
The automation does not substitute an immediate `tempradio` command whose
first delivery could remain queued after cleanup.
### RXPS handling during TempRadio
RXPS improves receive performance per unit of radio-on time, so the runner
@@ -265,8 +292,11 @@ moving back to a slower tuple returns to the operator's saved level.
The OTA source has a stricter policy than the destination. For every source
with a managed serial or TCP CLI, the runner reads and retains its exact RXPS
preference, using the legacy fixed-period query only when the detailed query is
unavailable. It then disables RXPS and verifies the readback before any target
discovery or TempRadio change. Source RXPS stays off through catalog serving,
unavailable. It builds and verifies the package, completes the read-only target
checks, and obtains confirmation while that preference remains unchanged.
Immediately before the first radio mutation, it reads the source RXPS state
again, disables RXPS, and verifies the readback. Source RXPS stays off through
catalog serving,
download, installation, and post-install identity verification. Cleanup first
proves that the source has returned to its normal radio, then restores and
verifies the exact saved level/preamble or fixed-period state once. A source
@@ -487,11 +517,15 @@ Useful controls:
downloading or staged on the target. Without it, that update is preserved.
- `--source-shares-controller` is for a Full Companion whose USB Binary API is
the controller while its TCP port `5001` is the source. It verifies that the
source's port-`5000` public key equals the controller key. Port `5002` uses a
bounded local `tempradio` override to move the shared physical radio without
overwriting its saved normal tuple; the Binary API remains the authenticated
transport. Cleanup sends local `normalradio`, proves that override inactive,
and then reasserts the saved Binary tuple. It cannot be combined with
source's port-`5000` public key equals the controller key, then binds the
fresh port-`5002` Full Terminal banner's complete public key to that Binary
identity and challenges it with the supported `ver` command. It does not
assume the Full Terminal implements repeater-only `get public.key`. Port
`5002` uses a bounded local `tempradio` override to move the shared physical
radio without overwriting its saved normal tuple; the Binary API remains the
authenticated transport. Cleanup sends local `normalradio`, proves that
override inactive, and then reasserts the saved Binary tuple. It cannot be
combined with
`--leave-controller-radio`,
because exact source RXPS restoration requires that shared physical radio to
be back on its verified normal tuple.
@@ -505,6 +539,12 @@ Useful controls:
restores both values before the relay leaves TempRadio.
- `--work-dir PATH` chooses a new, non-existent work directory.
- `--meshcli PATH` and `--motatool PATH` select binaries not on `PATH`.
- `--package-build-timeout SECONDS` bounds local mOTA generation. It defaults
to 3600 seconds. Run detools, delta/compression, and raw-firmware package
generation on a workstation or build VM, then transfer the completed,
hash-verified `.mota` to a Pi-class radio host. The Pi should perform only
lightweight identity-gated hardware I/O and serve that finished artifact;
preparation still occurs before any radio state changes.
- `--debug` prints redacted child commands, timeouts, process status, stdout,
and stderr. Admin passwords are never printed, but node names, addresses,
paths, and command replies can still be sensitive; share the log carefully.
@@ -527,30 +567,41 @@ the destination.
## What happens during a run
1. Validate the input paths and host tools, then prove the source is either an
OTA-enabled raw CLI or a source-only full Companion control interface.
2. Authenticate to the target and query its target ID, hardware, running body
OTA-enabled raw CLI or a source-only full Companion control interface. Read
and durably save a managed source's exact RXPS preference without changing
it.
2. Before sending any remote packet, prove the managed source has no active,
pending, or fixed TempRadio work, then gate the source and controller
clocks. Advance a stale exact clock to host time; for a minute-resolution
source, preserve an overlapping minute or use one guarded forward value
with readback. Preserve a small future lead and fail closed above the
ten-minute drift limit. Then
authenticate to the target, query its target ID, hardware, running body
hash, firmware version, bootloader version, and nRF52 bootloader
capabilities.
capabilities, and save the controller's normal radio tuple.
3. Select or build one compatible **v2 application** mOTA and verify all block
hashes, Merkle root, full-image hash where applicable, identity fields,
signature, codec, base, and the firmware's 1024-byte maximum block size.
Version-3 bootloader packages are refused before any target state changes.
4. Read and save a managed source's exact RXPS preference, disable and verify
source RXPS, save the controller's normal radio tuple, read every
participant's version, save the destination's RXPS state, select the
qualified destination policy, and show the confirmation prompt.
5. Apply and verify the destination RXPS policy, then start TempRadio on the
4. Read every participant's version, save the destination's RXPS state,
select the qualified destination policy, and show the confirmation prompt.
5. Run the fixed-window three-minute rehearsal described above. This phase
does not disable RXPS, start a seeder, request a pull, or install anything;
the long transfer remains completely unarmed until temporary and normal
reachability both pass.
6. Fresh-read, disable, and verify source RXPS; apply and verify the
destination RXPS policy; then start TempRadio on the
target, far-to-near relays, and source. A separate controller is moved and
read back through Binary; a shared Full Companion instead schedules its
bounded local override while Binary remains the transport. The runner
rejects a TempRadio window that cannot cover setup, seeder startup,
discovery, the transfer timeout, final polling, and install checks.
6. Start `motatool serve`, discover the exact eight-hex manifest ID, request
7. Start `motatool serve`, discover the exact eight-hex manifest ID, request
`ota pull <id> flash`, and poll until that same ID reports ready. A seeder
process exit stops the run immediately. For `--no-install`, schedule all
script-controlled nodes back to their normal radios before restoring the
controller, unless `--leave-controller-radio` was requested.
7. Recheck that exact ID, give the target a short final TempRadio safety window,
8. Recheck that exact ID, give the target a short final TempRadio safety window,
and request `ota install`. Then shorten each relay's TempRadio window so the
normal multi-hop route returns, stop the seeder, shorten the source window,
restore the controller, and probe `ota self` every 10 seconds through the
@@ -573,9 +624,17 @@ status for another manifest ID is an error, never permission to install it.
Read-only and replay-safe transmissions retry up to three times. Three retries
or 90 seconds, whichever comes first, opens a 10-second stop-or-continue
prompt. Continue is the default on timeout, Enter, and unattended input, so a
temporary outage does not silently abandon a resumable transfer. Enter `s` or
`stop` to end the run; Ctrl-C also remains immediate.
prompt when stdin is an interactive terminal. Continue remains the default on
timeout or Enter, so an operator can persist through a temporary outage; enter
`s` or `stop` to end the run, and Ctrl-C remains immediate. With non-interactive
stdin, the runner stops after that finite automatic retry cycle instead of
silently starting another cycle forever. Optional participant-version probes
always use a bounded cycle and degrade to `unknown`; optional destination
`ota stats` falls back directly to `ver`. The mandatory normal-channel
destination baseline is the narrow exception: it may make nine total read-only
attempts but is stopped by one hard four-minute deadline before any TempRadio
lease or OTA mutation exists. Proofs inside the live rehearsal retain the
smaller four-attempt limit.
Commands that change OTA state are reconciled before replay:
+23 -8
View File
@@ -19,7 +19,8 @@ complete container stays off-chip. See [nRF52 repeater OTA with external QSPI](o
[MeshTower V2 microSD LoRa OTA](ota_meshtower_v2_sdcard.md).
MeshTower V2 microSD application and bootloader containers must be Ed25519-signed by a key in the device
allowlist; pass `--sign-key` when building one. Its BLM2-capable SD-aware bootloader is also required before
allowlist; pass `--sign` to `motatool build` (or `--sign-key` to the end-to-end
runner) when building one. Its BLM2-capable SD-aware bootloader is also required before
ordinary application or bootloader OTA can use the reset-retained authorization record. Upgrade preview.12
locally over USB/BLE DFU or SWD first; there is no raw-card compatibility handoff.
@@ -85,13 +86,25 @@ payload. If any required window closes, the transfer stops making progress and c
overlapping window.
`build.sh` provides a `*_repeater_lora_ota_no_external_sensors` build for standalone ESP32 and nRF52 repeater
targets that need a smaller internal update workspace. Those siblings omit optional external I2C
environmental sensors while retaining board-native features such as displays, buttons, battery monitoring,
targets that need a smaller internal update workspace. Those siblings omit selected optional environmental
and ranging drivers while retaining board-native features such as displays, buttons, battery monitoring,
and GPS where the target uses the GPS-preserving lean profile. RAK3401 and RAK4631 reduced builds also
retain INA219, INA226, INA260, and INA3221 I2C voltage/current monitors; together these drivers add 4,808
bytes over the otherwise reduced image. The RAK3401 OTA repeater retains RAK12501 support in sensor slot A;
slot D conflicts with the RAK13302 radio's BUSY/DIO1 lines. The RAK4631 OTA repeater retains GPS and defaults
its runtime RS-232 bridge to Serial2; Serial1 and GPS use the same UART and must not be enabled together.
retain INA219, INA226, INA260, and INA3221 I2C voltage/current monitors; together these drivers cost less
than 5 KiB in the measured profile. Those four monitor drivers are not the only retained I2C support: the
board's SSD1306 OLED, supported autodiscovered RTCs, and the RAK12500 GPS remain separate I2C peripherals in
GPS-compatible RAK recipes. RAK12501/L76K GPS instead uses UART Serial1. The RAK3401 OTA repeater supports
either GPS module in sensor slot A; slot D conflicts with the RAK13302 radio's BUSY/DIO1 lines. The RAK4631
OTA repeater defaults its runtime RS-232 bridge to Serial2 so the UART RAK12501 can retain Serial1. The
runtime guard always reserves Serial1 because a bounded silent probe cannot prove a cold RAK12501 is absent;
the shared WB_IO2/3V3_S rail keeps a fitted module powered and driving the UART. Use Serial2. Serial1 requires
an explicit no-GPS/dedicated image. The merged image remains fail-closed even when it detects an I2C RAK12500.
The explicit legacy Serial1 bridge omits the combined GPS provider, including the otherwise non-UART
RAK12500 path.
The firmware-configured INA3221 address and the RAK12500 address are both `0x42`; they cannot coexist on one
bus at those addresses. Keep RAK12500 at `0x42`, strap INA3221 A0 to SCL for `0x43`, and use a build with
`-DTELEM_INA3221_ADDRESS=0x43` when both are installed.
Selected nRF52 boards with matched external
QSPI application and bootloader support can instead make the normal full-sensor
repeater install-capable; those targets do not need to reserve internal flash
@@ -283,8 +296,10 @@ running application. The hash check in the next step proves that it is the right
On RAK4631 repeaters, use the
`RAK_4631_repeater_lora_ota_no_external_sensors` environment. It retains built-in battery monitoring but
omits optional external environmental sensor packages so the delta fits the safe in-place workspace. INA219,
omits selected optional environmental/ranging drivers so the delta fits the safe in-place workspace. INA219,
INA226, INA260, and INA3221 I2C voltage/current monitors remain supported despite the legacy target name.
That trim does not remove the SSD1306 OLED, autodiscovered I2C RTCs, or GPS-compatible RAK12500 I2C path.
The `0x42` RAK12500/INA3221 addressing rule above still applies.
If the device has a RAK15001 installed in sensor slot C and the matching
RAK15001 OTAFIX bootloader, use
`RAK_4631_repeater_rak15001_slot_c_lora_ota` instead. That target retains the
+2 -2
View File
@@ -46,7 +46,7 @@ trailer is `0xC7000` bytes (815,104 bytes). To package a full self-update:
```bash
motatool build --fw ./Heltec_tower_v2_sdcard-new.hex \
--sign-key ./trusted-signer.key --out-dir ./motas
--sign ./trusted-signer.key --out-dir ./motas
motatool verify ./motas/*.mota
```
@@ -60,7 +60,7 @@ motatool build \
--fw ./Heltec_tower_v2_sdcard-new.hex \
--patch-type in-place \
--inplace-memory 0xC7000 \
--sign-key ./trusted-signer.key \
--sign ./trusted-signer.key \
--out-dir ./motas
motatool verify ./motas/*.mota
```
+16 -4
View File
@@ -142,10 +142,22 @@ stable release uses `0xFF`, and low-byte zero or all-ones values are rejected.
There is no remote rollback/migration override; intentional rollback uses
USB/BLE DFU or SWD.
The `no_external_sensors` profiles omit optional/add-on sensor packages to
preserve flash headroom. A board recipe can retain GPS. The RAK3401 OTA
repeater retains RAK12501 GPS in sensor slot A, and the RAK4631 OTA repeater
retains GPS except where a Serial1 RS232 bridge owns the same UART.
The legacy `no_external_sensors` profiles trim selected optional environmental
and ranging drivers to preserve flash headroom; they do not generally disable
the I2C bus. Reduced RAK3401 and RAK4631 recipes retain INA219, INA226, INA260,
and INA3221 voltage/current monitors. Those are entries in the optional sensor
table, not the complete set of I2C consumers: the SSD1306 OLED, supported
autodiscovered RTCs, and RAK12500 GPS remain separate I2C peripherals where the
exact board recipe enables them. RAK3401 retains its compatible RAK12500 I2C
and RAK12501/L76K UART GPS paths in sensor slot A. The plain RAK4631 repeater
and Serial2 bridge retain GPS. Its RAK12501 path uses Serial1; the explicitly
compiled Serial1 RS232 bridge therefore omits the combined GPS provider,
including RAK12500, even though RAK12500 itself is I2C rather than UART.
The firmware-configured INA3221 and RAK12500 addresses are both `0x42`. They
cannot coexist on the same bus at those addresses. Leave RAK12500 at `0x42`,
strap INA3221 A0 to SCL for `0x43`, and use firmware built with
`-DTELEM_INA3221_ADDRESS=0x43` when both modules are installed.
Boards with onboard external flash are not silently redirected to internal
staging. Mesh Solar, Nano G2 Ultra, T-Impulse Plus, ThinkNode M8, T-Echo
+17 -7
View File
@@ -128,13 +128,23 @@ established target-specific 1920 KiB or larger A/B app layout and are checked ag
partition. For standalone ESP32 and nRF52 repeaters that need a lean staging
profile, `build.sh` also exposes an explicit `*_lora_ota_no_external_sensors`
artifact: the ordinary repeater remains sensor-enabled, while that sibling
disables optional external environmental-sensor drivers for LoRa distribution. SolarXiao 30S and 33S use
trims selected optional environmental/ranging drivers for LoRa distribution. SolarXiao 30S and 33S use
matched external QSPI staging, so their ordinary full-sensor repeater is already install-capable and no
redundant lean sibling is generated. Integrated GPS and other
board-native telemetry remain enabled where the target selects the GPS-preserving lean profile. The RAK3401
OTA repeater also retains RAK12501 GPS support; install that module in sensor slot A because slot D conflicts
with the RAK13302 radio's BUSY/DIO1 lines. RAK4631 OTA repeaters retain GPS and default the runtime RS-232
bridge to UART 2; do not enable GPS and an explicitly selected UART 1 bridge at the same time.
board-native telemetry remain enabled where the target selects the GPS-preserving lean profile. The legacy
suffix describes a driver trim, not removal of the generic I2C bus. Reduced RAK3401 and RAK4631 profiles
retain INA219, INA226, INA260, and INA3221 voltage/current monitors. These are retained optional-sensor-table
drivers, not the only I2C consumers: SSD1306 display, supported autodiscovered RTCs, and RAK12500 GPS remain
separate I2C peripherals where selected by the board recipe. The RAK3401 OTA repeater also retains RAK12500
I2C and RAK12501/L76K UART GPS support; install either GPS module in sensor slot A because slot D conflicts
with the RAK13302 radio's BUSY/DIO1 lines. The plain RAK4631 OTA repeater and its Serial2 bridge retain GPS.
RAK12501 uses Serial1. Its explicitly compiled Serial1 bridge therefore omits the combined GPS provider,
including RAK12500, even though RAK12500 itself does not use the UART.
The firmware-configured INA3221 address and RAK12500 address are both `0x42`, so those devices cannot share
one bus at those addresses. Keep RAK12500 at `0x42`, strap INA3221 A0 to SCL for `0x43`, and use firmware
built with `-DTELEM_INA3221_ADDRESS=0x43` when both are installed.
ESP32 siblings retain the compact browser WiFi updater and use the full
254-entry neighbor table. RP2040 and STM32 targets are not offered because
those platforms do not yet have a safe bootloader/apply path.
@@ -149,8 +159,8 @@ promotes every ESP32 MQTT observer and ESP-NOW bridge to the expanded FULL parti
retain the complete role CLI, WebConfig where supported, display and optional sensor support, full timezone
and TLS behavior, and the board's normal power-management implementation. The compact CLI is not compiled
into any build. Ordinary repeater builds remain sensor-enabled; only explicitly named
`*_lora_ota_no_external_sensors` siblings omit sensors for LoRa distribution, and those siblings retain the
complete CLI.
`*_lora_ota_no_external_sensors` siblings trim selected optional environmental/ranging drivers for LoRa
distribution, and those siblings retain the complete CLI and target-declared I2C peripherals.
MQTT observer radio and bridge preferences use verified temporary files plus a recoverable backup. A reset
during a settings save restores the last committed common preference image or publishes the completed new
+33 -13
View File
@@ -42,22 +42,41 @@ preserves the RX benefit without risking missed packets from a legacy
> install-capable image nor OTAFIX. Check the bootloader release for an exact board match before attempting an update.
The following nRF52 repeater families gained firmware-side LoRa OTA targets in
this release. Their ordinary repeater remains the full-sensor build; the
install-capable `lora_ota_no_external_sensors` sibling is smaller:
this release. Their ordinary repeater keeps its complete declared sensor recipe;
the install-capable `lora_ota_no_external_sensors` sibling is smaller:
- Heltec Mesh Solar, T1, and Tower V2
- Keepteen LT1, LilyGo T-Impulse Plus, Mesh Pocket, and Nano G2 Ultra
- Minewsemi ME25LS01, RAK3401, SenseCAP Solar, and Wio WM1110
The RAK3401 `RAK_3401_repeater_lora_ota_no_external_sensors` image retains
RAK12501 GPS while omitting the other optional environmental sensors. Install
the GPS in sensor slot A. Slot D's reset/PPS lines conflict with the RAK13302
radio's BUSY/DIO1 lines.
The RAK3401 `RAK_3401_repeater_lora_ota_no_external_sensors` image trims selected
optional environmental/ranging drivers but does not disable I2C. It retains the
INA219, INA226, INA260, and INA3221 voltage/current monitors, board display and
RTC support, and both RAK12500 I2C and RAK12501/L76K UART GPS paths. The four
INA drivers are only the retained voltage/current entries in the optional
environmental-sensor table; they are not the only I2C users. The SSD1306 OLED,
autodiscovered DS3231/RV3028/PCF8563/RX8130CE RTCs, and RAK12500 GPS use I2C as
separate board peripherals. Install one GPS module in sensor slot A. Slot D's
reset/PPS lines conflict with the RAK13302 radio's BUSY/DIO1 lines.
The RAK4631 internal-flash OTA repeater likewise retains GPS. A RAK12501 can
use sensor slot A or D; a RAK12500 can use slot A or C. Its runtime RS-232
bridge defaults to Serial2 so GPS can retain Serial1. If you explicitly select
Serial1 with `set bridge.uart 1`, turn GPS off before enabling the bridge.
The firmware-configured INA3221 address and the RAK12500 address are both
`0x42`, so those devices cannot share one bus at those addresses. To install
both, leave the RAK12500 at `0x42`, strap INA3221 A0 to SCL for `0x43`, and use
a firmware build with `-DTELEM_INA3221_ADDRESS=0x43`.
The reduced RAK4631 profiles likewise retain the four INA monitors. The plain
repeater and Serial2 bridge retain GPS: a RAK12501 can use sensor slot A or D,
and a RAK12500 can use slot A or C. The runtime RS-232 bridge defaults to
Serial2 so the UART RAK12501 can retain Serial1. The merged runtime image always
reserves Serial1: a bounded silent probe cannot prove that a cold RAK12501 is
absent, and turning the GPS setting off cannot stop a fitted module from driving
the shared UART while WB_IO2/3V3_S remains powered. Use Serial2. Serial1 requires
an explicit no-GPS/dedicated image. The reservation remains fail-closed even
when an I2C RAK12500 is detected. The explicitly compiled
`RAK_4631_repeater_bridge_rs232_serial1_lora_ota_no_external_sensors` target
omits the combined GPS provider because its bridge owns the UART used by
RAK12501; consequently that legacy image does not expose the I2C RAK12500 path
either, even though RAK12500 itself does not use the UART.
Selected nRF52 repeaters with dedicated external QSPI can now stage the
complete package off-chip, so their normal full-sensor repeater build can
@@ -69,9 +88,10 @@ RAK15001 slot-C target. These require the corresponding QSPI-aware OTAFIX bootlo
The ordinary full-sensor `RAK_4631_repeater` image remains too large for the
safe internal in-place update limit. Without external flash, use
`RAK_4631_repeater_lora_ota_no_external_sensors`; it removes optional external
environmental/GPS packages but retains battery monitoring. A RAK4631 fitted
with RAK15001 in sensor slot C can instead use
`RAK_4631_repeater_lora_ota_no_external_sensors`; it trims selected optional
environmental/ranging drivers while retaining generic I2C, battery monitoring,
the four INA monitors, and target-compatible GPS. A RAK4631 fitted with
RAK15001 in sensor slot C can instead use
`RAK_4631_repeater_rak15001_slot_c_lora_ota` to retain the full sensor/GPS set
and stage full images or deltas off-chip.