feat: add Bluetooth-controlled LoRa OTA
Add the framed BLE mOTA transport and companion control path, complete the full-profile build and runtime fixes, and document and test the OTA workflow.
This commit is contained in:
1 parent
bd3a94bddb
commit
69ded9d6c7
79 files changed
+3178
-304
No files matched your search
+5
-5
@@ -431,8 +431,8 @@ meaning can change when the service reorders or adds presets.
|
||||
|
||||
| Build profile | WiFi/MQTT behavior |
|
||||
|---|---|
|
||||
| Standard | Uses the selected target's role. Ordinary legacy-slot ESP32 repeater/room-server artifacts omit WebConfig when needed to fit. ESP32 MQTT observer and ESP-NOW bridge targets are automatically promoted to FULL; WiFi-companion targets keep their companion partition profile. |
|
||||
| Logging | Enables USB/debug packet logging and disables the MQTT bridge. CommonCLI roles persist `get/set usb.logging`; logging output itself is not a direct MQTT uplink. |
|
||||
| Standard | Uses the selected target's role. Where USB is a safe plaintext console, the same artifact embeds debug/packet logging behind persistent `get/set usb.logging`. Ordinary legacy-slot ESP32 repeater/room-server artifacts omit WebConfig when needed to fit. ESP32 MQTT observer and ESP-NOW bridge targets are automatically promoted to FULL; WiFi-companion targets keep their companion partition profile. |
|
||||
| Legacy logging | No separate artifact is emitted; USB logging is part of the ordinary image. Logging output itself is not a direct MQTT uplink. |
|
||||
| MQTT | Builds explicit MQTT observer or WiFi-companion-MQTT targets with USB packet logging off. Non-companion ESP32 MQTT observers always use FULL expanded partitions. |
|
||||
| FULL ESP32 USB + WiFi | Uses the board's MQTT target with USB packet logging and direct WiFi MQTT together, expanded dual-OTA partitions, up to 254 neighbors, LoRa OTA, and full-size ESP32 features such as WebConfig where supported. `get/set logging.output off\|usb\|wifi\|both` persists the active paths. Classic T-Beam MQTT observers retain their 50-entry table because their persistent discovery state exhausts internal DRAM at 254. |
|
||||
| FULL ESP32 logging fallback | Uses the board's non-MQTT target only when no matching WiFi MQTT environment exists. It keeps debug and packet logging, expanded dual-OTA partitions, up to 254 neighbors, and LoRa OTA. Persistent `usb.logging off` also provides normal output-off operation, so ESP-NOW FULL roles need no second non-logging image. |
|
||||
@@ -445,9 +445,9 @@ persistent MQTT discovery state leaves insufficient internal-DRAM margin at 254.
|
||||
|
||||
The interactive Option 1 **FULL everything** choice and the standalone FULL
|
||||
command select the unified USB + WiFi image when a matching MQTT target exists;
|
||||
otherwise they select the logging fallback. The build matrix no longer emits a
|
||||
separate standard logging image or non-MQTT FULL twin for a role covered by the
|
||||
unified image. All FULL profiles include LoRa OTA, WebConfig where supported,
|
||||
otherwise they select the logging fallback. The build matrix no longer emits
|
||||
any separate standard logging image, or a non-MQTT FULL twin for a role covered
|
||||
by the unified image. All FULL profiles include LoRa OTA, WebConfig where supported,
|
||||
up to 254 neighbors, and expanded dual-OTA partitions. Target-specific
|
||||
internal-DRAM limits still apply.
|
||||
|
||||
|
||||
@@ -53,6 +53,7 @@
|
||||
full: "Full Companion transports",
|
||||
ble: "Bluetooth LE",
|
||||
usb: "USB",
|
||||
"usb-ble": "USB + Bluetooth LE",
|
||||
wifi: "Wi-Fi",
|
||||
serial: "Serial / UART",
|
||||
ethernet: "Ethernet",
|
||||
@@ -109,6 +110,7 @@
|
||||
// Exact aliases of the unsuffixed Heltec V4 USB/BLE recipes.
|
||||
"heltec_v4_companion_radio_usb_femon",
|
||||
"heltec_v4_companion_radio_ble_femon",
|
||||
"ikoka_handheld_nrf_e22_30dbm_096_rotated_companion_radio_full",
|
||||
]);
|
||||
|
||||
let pickerInstanceCount = 0;
|
||||
@@ -273,6 +275,7 @@
|
||||
const value = String(tail).toLowerCase().replace(/^[_-]+/, "");
|
||||
if (role === "companion") {
|
||||
if (/^full(?:$|[_-])/.test(value)) return "full";
|
||||
if (/^usb_ble(?:$|[_-])/.test(value)) return "usb-ble";
|
||||
if (/^ble(?:$|[_-])/.test(value)) return "ble";
|
||||
if (/^usb(?:$|[_-])/.test(value)) return "usb";
|
||||
if (/^wifi(?:$|[_-])/.test(value)) return "wifi";
|
||||
@@ -291,7 +294,7 @@
|
||||
let value = String(tail);
|
||||
if (role === "companion") {
|
||||
value = value.replace(
|
||||
/^(?:full|ble|usb|wifi|serial|ethernet)(?=$|[_-])/i,
|
||||
/^(?:full|usb_ble|ble|usb|wifi|serial|ethernet)(?=$|[_-])/i,
|
||||
" "
|
||||
);
|
||||
// Power saving and controllable FEM gain are persisted settings. Legacy
|
||||
@@ -414,13 +417,69 @@
|
||||
}).map(function (profile) {
|
||||
return profile.hardware + "\n" + profile.variant;
|
||||
}));
|
||||
const combinedUsbBleKeys = new Set((profiles || []).filter(
|
||||
function (profile) {
|
||||
return profile.role === "companion" && profile.mode === "usb-ble";
|
||||
}
|
||||
).map(function (profile) {
|
||||
return profile.hardware + "\n" + profile.variant;
|
||||
}));
|
||||
const terminalReplacementKeys = new Set((profiles || []).filter(
|
||||
function (profile) {
|
||||
return profile.role === "companion" &&
|
||||
(profile.mode === "full" || profile.mode === "usb" ||
|
||||
profile.mode === "usb-ble");
|
||||
}
|
||||
).map(function (profile) {
|
||||
return profile.hardware + "\n" + profile.variant;
|
||||
}));
|
||||
const targets = new Set((profiles || []).map(function (profile) {
|
||||
return String(profile.target || "").toLowerCase();
|
||||
}));
|
||||
const mergedRs232Targets = {
|
||||
heltec_t096_repeater_bridge_rs232: "heltec_t096_repeater",
|
||||
heltec_t096_repeater_bridge_rs232_lora_ota_no_external_sensors:
|
||||
"heltec_t096_repeater_lora_ota_no_external_sensors",
|
||||
rak_4631_repeater_bridge_rs232_serial1_lora_ota_no_external_sensors:
|
||||
"rak_4631_repeater_lora_ota_no_external_sensors",
|
||||
rak_4631_repeater_bridge_rs232_serial2_lora_ota_no_external_sensors:
|
||||
"rak_4631_repeater_lora_ota_no_external_sensors",
|
||||
rak_4631_repeater_bridge_rs232_serial1: "rak_4631_repeater",
|
||||
rak_4631_repeater_bridge_rs232_serial2: "rak_4631_repeater",
|
||||
promicro_repeater_bridge_rs232_serial1: "promicro_repeater",
|
||||
heltec_t114_without_display_repeater_bridge_rs232:
|
||||
"heltec_t114_without_display_repeater",
|
||||
heltec_t114_repeater_bridge_rs232: "heltec_t114_repeater",
|
||||
rak_3112_repeater_bridge_rs232: "rak_3112_repeater",
|
||||
rak_11310_repeater_bridge_rs232: "rak_11310_repeater",
|
||||
waveshare_rp2040_lora_repeater_bridge_rs232:
|
||||
"waveshare_rp2040_lora_repeater",
|
||||
solarxiao_30s_repeater_bridge_rs232: "solarxiao_30s_repeater",
|
||||
solarxiao_33s_repeater_bridge_rs232: "solarxiao_33s_repeater",
|
||||
heltec_v3_repeater_bridge_rs232: "heltec_v3_repeater",
|
||||
heltec_wsl3_repeater_bridge_rs232: "heltec_wsl3_repeater",
|
||||
lilygo_tlora_v2_1_1_6_repeater_bridge_rs232:
|
||||
"lilygo_tlora_v2_1_1_6_repeater",
|
||||
};
|
||||
|
||||
return (profiles || []).filter(function (profile) {
|
||||
const key = profile.hardware + "\n" + profile.variant;
|
||||
const replacedAttachedTransport = profile.role === "companion" &&
|
||||
(profile.mode === "usb" || profile.mode === "ble" ||
|
||||
profile.mode === "wifi");
|
||||
return !(replacedAttachedTransport && fullKeys.has(key));
|
||||
profile.mode === "wifi" || profile.mode === "serial" ||
|
||||
profile.mode === "ethernet" || profile.mode === "usb-ble");
|
||||
const replacedByUsbBle = profile.role === "companion" &&
|
||||
(profile.mode === "usb" || profile.mode === "ble") &&
|
||||
combinedUsbBleKeys.has(key);
|
||||
const replacedTerminal = profile.role === "terminal" &&
|
||||
terminalReplacementKeys.has(key);
|
||||
const rs232Replacement = mergedRs232Targets[
|
||||
String(profile.target || "").toLowerCase()
|
||||
];
|
||||
const replacedRs232 = Boolean(rs232Replacement &&
|
||||
targets.has(rs232Replacement));
|
||||
return !(replacedAttachedTransport && fullKeys.has(key)) &&
|
||||
!replacedByUsbBle && !replacedTerminal && !replacedRs232;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -440,6 +499,24 @@
|
||||
});
|
||||
}
|
||||
|
||||
function applyMergedStandardUsbLoggingCapabilities(profiles) {
|
||||
return (profiles || []).map(function (profile) {
|
||||
const safeRole = [
|
||||
"companion", "repeater", "room", "sensor", "terminal",
|
||||
].includes(profile.role);
|
||||
const unsafeCompanionMode = profile.role === "companion" &&
|
||||
(profile.mode === "ble" || profile.mode === "full");
|
||||
if (profile.logging !== "none" || profile.ota !== "none" ||
|
||||
!safeRole || unsafeCompanionMode) {
|
||||
return profile;
|
||||
}
|
||||
|
||||
profile.logging = "usb-runtime";
|
||||
profile.loggingModes = ["none", "usb"];
|
||||
return profile;
|
||||
});
|
||||
}
|
||||
|
||||
function hardwareFamilyFor(hardware, hardwareNames) {
|
||||
const value = String(hardware || "");
|
||||
const lowerValue = value.toLowerCase();
|
||||
@@ -539,8 +616,10 @@
|
||||
}).filter(function (profile) {
|
||||
return !isHiddenLegacyProfile(profile);
|
||||
});
|
||||
const profiles = omitTransportsReplacedByFull(
|
||||
applyFullCompanionCapabilities(visibleProfiles)
|
||||
const profiles = applyMergedStandardUsbLoggingCapabilities(
|
||||
omitTransportsReplacedByFull(
|
||||
applyFullCompanionCapabilities(visibleProfiles)
|
||||
)
|
||||
).sort(function (a, b) {
|
||||
return a.target.localeCompare(b.target, undefined, {
|
||||
numeric: true,
|
||||
@@ -738,7 +817,11 @@
|
||||
"With no saved SSID, the setup AP stays available for 30 minutes after each boot, then Wi-Fi powers off automatically until reboot or an explicit start webconfig command. A configured Wi-Fi mode keeps reconnecting instead."
|
||||
);
|
||||
} else if (profile.logging === "usb-runtime") {
|
||||
if (profile.dedicatedUsbLogging) {
|
||||
if (!isFullCompanion(profile)) {
|
||||
extra.push(
|
||||
"This ordinary image includes USB logging. Use get usb.logging and set usb.logging off|on to select and save normal or logging operation."
|
||||
);
|
||||
} else if (profile.dedicatedUsbLogging) {
|
||||
extra.push(
|
||||
"Full Companion starts with USB logging off and only interface 00. Use get usb.logging, or set usb.logging on reboot to add interface 02; set usb.logging off reboot removes it again."
|
||||
);
|
||||
@@ -1118,6 +1201,8 @@
|
||||
parseFirmwareAsset: parseFirmwareAsset,
|
||||
parseTargetProfile: parseTargetProfile,
|
||||
applyFullCompanionCapabilities: applyFullCompanionCapabilities,
|
||||
applyMergedStandardUsbLoggingCapabilities:
|
||||
applyMergedStandardUsbLoggingCapabilities,
|
||||
applyDualCdcFullCompanionCapabilities: applyFullCompanionCapabilities,
|
||||
applyNrf52FullCompanionCapabilities:
|
||||
applyFullCompanionCapabilities,
|
||||
|
||||
@@ -49,7 +49,7 @@ an old or failed artifact without a verified sidecar.
|
||||
| Repeater | Full repeater administration surface, subject to the profile differences below |
|
||||
| Room server | Room-server administration surface, subject to the profile differences below |
|
||||
| Sensor | Sensor command surface; it does not acquire the repeater administration tree |
|
||||
| Serial, USB, BLE, or WiFi companion | Uses the companion protocol; any serial diagnostics are target-specific |
|
||||
| Full, serial, Ethernet, USB, BLE, or WiFi companion | Uses the companion protocol; Full combines every qualified transport for that exact board |
|
||||
| KISS modem | Uses the KISS/TNC frame interface, not the repeater text CLI |
|
||||
| Bridge | Uses its base role plus commands for the bridge transport compiled into that target |
|
||||
|
||||
@@ -65,8 +65,8 @@ retain 50 because their MQTT discovery tables are constrained by internal DRAM.
|
||||
|
||||
| Build/profile | Command availability |
|
||||
|---|---|
|
||||
| Standard non-MQTT repeater or room server | Keeps the normal role CLI. 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. |
|
||||
| Standard logging | Logging does not remove commands by itself. It has the same CLI as the selected role/profile and adds compiled logging behavior. CommonCLI roles persist `get/set usb.logging`. Roles covered by a Full image with runtime logging are not duplicated here. |
|
||||
| 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. |
|
||||
| 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. |
|
||||
@@ -74,10 +74,11 @@ retain 50 because their MQTT discovery tables are constrained by internal DRAM.
|
||||
| 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. |
|
||||
| Dual-CDC Full Companion | nRF52 and qualified native-USB ESP32-S3 Full images use one physical USB connection. Fresh installs expose only interface `00` for framed Companion/terminal/mOTA traffic. Enabling logging and rebooting adds interface `02` for plaintext logs. They also provide BLE and source-only LoRa OTA; ESP32 additionally provides WiFi. `get/set usb.logging` persistently controls whether the logging interface is present. |
|
||||
| Single-TTY Full Companion | ESP32 Full images without dual CDC start with framed Companion on their one TTY. `set usb.logging on` switches it to an input-capable plaintext logging terminal; `set usb.logging off` replies and then restores framed Companion automatically. BLE, WiFi, and source-only LoRa OTA remain available. |
|
||||
| `no_external_sensors` | Removes optional external-sensor drivers and their settings; it does not remove core repeater discovery or routing commands. RAK3401 and RAK4631 profiles retain the four common INA I2C voltage/current monitors. GPS-preserving RAK nRF52 OTA profiles also retain their GPS commands and provider. The RAK4631 Serial1 RS232 bridge remains GPS-off because both features require Serial1. The legacy target suffix is retained for OTA identity compatibility. |
|
||||
| `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. |
|
||||
|
||||
`logging`, `OTA`, and `FULL` describe independent build features. Do not infer
|
||||
that a command is missing merely because `logging` appears in the filename.
|
||||
`logging`, `OTA`, and `FULL` describe independent build features in historical
|
||||
filenames. Current standard artifacts use no `-logging-` infix because their
|
||||
USB logging is runtime controlled.
|
||||
|
||||
## Canonical bulk-build policy
|
||||
|
||||
@@ -94,7 +95,8 @@ available from a canonical image:
|
||||
persisted `radio.rxgain on|off` setting; the G3 alias changed only the
|
||||
advertised default name.
|
||||
- When a board has a Full Companion, that one artifact replaces its
|
||||
separate USB, BLE, ordinary WiFi, and USB packet-logging Companion artifacts.
|
||||
separate USB, BLE, ordinary WiFi, hardware-serial, Ethernet Companion, and
|
||||
USB packet-logging Companion artifacts.
|
||||
It provides BLE and source-only LoRa OTA; ESP32 also provides ordinary WiFi.
|
||||
Dual-CDC builds keep framed traffic on interface `00` and add logging on
|
||||
interface `02` after a reboot. Single-TTY builds switch interface `00` into
|
||||
@@ -108,6 +110,18 @@ available from a canonical image:
|
||||
available as explicit build targets, but are not canonical release artifacts
|
||||
when the qualified Full image fits.
|
||||
|
||||
- Standalone Terminal Chat is omitted when the same exact hardware has either
|
||||
Full Companion or a USB Companion, because their local text terminal supplies
|
||||
the same role. Heltec E290 and T190 similarly publish one combined USB + BLE
|
||||
Companion. RAK4631 repeater and room-server Ethernet builds stay separate;
|
||||
only the RAK4631 Ethernet Companion is folded into Full Companion.
|
||||
- Matching RS-232 bridge roles are compiled into the normal repeater and
|
||||
selected at runtime with `bridge.enabled`, `bridge.baud`, and `bridge.uart`.
|
||||
Historical bridge names remain directly buildable but are omitted from bulk
|
||||
releases. Wio-E5 remains separate because its normal image has only 916
|
||||
bytes free, while the combined image exceeds the fixed 240 KiB application
|
||||
partition by 2,192 bytes.
|
||||
|
||||
The old aliases still work with `build-firmware` and
|
||||
`build-matching-firmwares`. Dedicated repeater LoRa OTA receiver images are not
|
||||
collapsed; they retain their exact storage, bootloader, role, and target
|
||||
|
||||
@@ -11,10 +11,10 @@ own interface, so those build roles are not represented here.
|
||||
|
||||
Build columns mean:
|
||||
|
||||
- **Standard** - the ordinary non-MQTT artifact, without the `logging` or
|
||||
explicit `ota` filename marker.
|
||||
- **Logging** - the ordinary non-MQTT `-logging-` artifact. Logging does not
|
||||
remove commands by itself.
|
||||
- **Standard** - the ordinary non-MQTT artifact, without an explicit `ota`
|
||||
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.
|
||||
- **FULL unified** - the expanded-partition ESP32 artifact with LoRa OTA, the
|
||||
@@ -72,7 +72,7 @@ fix, no WiFi connection, an inactive bridge, or an nRF52 bootloader without
|
||||
| Statistics | [`set telemetry.gps`; `get/set/send telemetry.tx`](cli_commands.md#read-repeater-telemetry-history) | Non-STM32 repeater; GPS setting requires a provider; remote access requires administrator | Yes | Yes | Yes |
|
||||
| Logging | [`log start`; `log stop`; `log erase`](cli_commands.md#logging) | Storage-backed roles retain data; other roles can return empty data | Yes | Yes | Yes |
|
||||
| Logging | [`log`](cli_commands.md#print-the-captured-log-to-the-serial-terminal) | Local serial | Serial | Serial | Serial |
|
||||
| Logging | [`get/set usb.logging`; unified FULL `get/set logging.output`](cli_commands.md#control-live-usb-logging) | Logging artifacts; CommonCLI USB gate is persistent; unified ESP32 FULL selects off/USB/WiFi/both; Full Companion uses either a reboot-controlled second CDC or an input-capable single-TTY logging terminal | No | Yes | No |
|
||||
| Logging | [`get/set usb.logging`; unified FULL `get/set logging.output`](cli_commands.md#control-live-usb-logging) | Ordinary safe-USB artifacts; CommonCLI USB gate is persistent; unified ESP32 FULL selects off/USB/WiFi/both; Full Companion uses either a reboot-controlled second CDC or an input-capable single-TTY logging terminal | Yes | Yes | No |
|
||||
| Radio | [`get radio`; `set radio ...`](cli_commands.md#view-or-change-this-nodes-radio-parameters) | All text CLI roles | Yes | Yes | Yes |
|
||||
| Radio | [`get tx`; `set tx <dbm>`](cli_commands.md#view-or-change-this-nodes-transmit-power) | Board TX-power limits apply | Yes | Yes | Yes |
|
||||
| Radio | [`tempradio ...`; `normalradio`](cli_commands.md#change-the-radio-parameters-for-a-set-duration) | Full parser | Yes | Yes | Yes |
|
||||
@@ -155,6 +155,7 @@ fix, no WiFi connection, an inactive bridge, or an nRF52 bootloader without
|
||||
| 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 |
|
||||
| Bridge | [`get/set bridge.uart`](cli_commands.md#view-or-change-the-uart-used-by-the-bridge-rs-232-only) | RS-232 bridge | Feature | Feature | Feature |
|
||||
| Bridge | [`get/set bridge.channel`](cli_commands.md#view-or-change-the-channel-used-for-bridging-espnow-only) | ESP-NOW is ESP32 only | No | No | No |
|
||||
| Bridge | [`get/set bridge.secret`](cli_commands.md#set-the-esp-now-secret) | ESP-NOW is ESP32 only | No | No | No |
|
||||
| Board | [`get bootloader.ver`](cli_commands.md#view-the-bootloader-version-nrf52-only) | nRF52 bootloader metadata | Yes | Yes | Yes |
|
||||
@@ -235,7 +236,7 @@ fix, no WiFi connection, an inactive bridge, or an nRF52 bootloader without
|
||||
| Statistics | [`set telemetry.gps`; `get/set/send telemetry.tx`](cli_commands.md#read-repeater-telemetry-history) | Non-STM32 repeater; GPS setting requires a provider; remote access requires administrator | Yes | Yes | Yes | Yes | Yes |
|
||||
| Logging | [`log start`; `log stop`; `log erase`](cli_commands.md#logging) | Storage-backed roles retain data | Yes | Yes | Yes | Yes | Yes |
|
||||
| Logging | [`log`](cli_commands.md#print-the-captured-log-to-the-serial-terminal) | Local serial | Serial | Serial | Serial | Serial | Serial |
|
||||
| Logging | [`get/set usb.logging`; unified FULL `get/set logging.output`](cli_commands.md#control-live-usb-logging) | Logging artifacts; CommonCLI USB gate is persistent; unified ESP32 FULL selects off/USB/WiFi/both; Full Companion uses either a reboot-controlled second CDC or an input-capable single-TTY logging terminal | No | Yes | No | No | Yes |
|
||||
| Logging | [`get/set usb.logging`; unified FULL `get/set logging.output`](cli_commands.md#control-live-usb-logging) | Ordinary safe-USB artifacts; CommonCLI USB gate is persistent; unified ESP32 FULL selects off/USB/WiFi/both; Full Companion uses either a reboot-controlled second CDC or an input-capable single-TTY logging terminal | Yes | Yes | No | No | Yes |
|
||||
| Radio | [`get radio`; `set radio ...`](cli_commands.md#view-or-change-this-nodes-radio-parameters) | All text CLI roles | Yes | Yes | Yes | Yes | Yes |
|
||||
| Radio | [`get tx`; `set tx <dbm>`](cli_commands.md#view-or-change-this-nodes-transmit-power) | Board TX-power limits apply | Yes | Yes | Yes | Yes | Yes |
|
||||
| Radio | [`tempradio ...`; `normalradio`](cli_commands.md#change-the-radio-parameters-for-a-set-duration) | Full parser | Yes | Yes | Yes | Yes | Yes |
|
||||
@@ -318,6 +319,7 @@ fix, no WiFi connection, an inactive bridge, or an nRF52 bootloader without
|
||||
| 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 |
|
||||
| Bridge | [`get/set bridge.uart`](cli_commands.md#view-or-change-the-uart-used-by-the-bridge-rs-232-only) | RS-232 bridge | Feature | Feature | Feature | No | Feature |
|
||||
| Bridge | [`get/set bridge.channel`](cli_commands.md#view-or-change-the-channel-used-for-bridging-espnow-only) | ESP-NOW bridge | No | No | No | Feature | Feature |
|
||||
| Bridge | [`get/set bridge.secret`](cli_commands.md#set-the-esp-now-secret) | ESP-NOW bridge | No | No | No | Feature | Feature |
|
||||
| Board | [`get bootloader.ver`](cli_commands.md#view-the-bootloader-version-nrf52-only) | nRF52 only | No | No | No | No | No |
|
||||
|
||||
+41
-6
@@ -562,6 +562,23 @@ set flag bit 0.
|
||||
|
||||
---
|
||||
|
||||
## Set Companion display rotation
|
||||
|
||||
SSD1306 Full Companion builds support a persisted runtime orientation:
|
||||
|
||||
```text
|
||||
get display.rotation
|
||||
set display.rotation 0
|
||||
set display.rotation 90
|
||||
set display.rotation 180
|
||||
set display.rotation 270
|
||||
```
|
||||
|
||||
The values are clockwise degrees. `0` clears the override and restores the
|
||||
board's compiled default. Unsupported display drivers return an error.
|
||||
|
||||
---
|
||||
|
||||
## Logging
|
||||
|
||||
Builds compiled with `MESH_PACKET_LOGGING` emit one `RAW:` line for every
|
||||
@@ -576,8 +593,9 @@ timing, hash, type, route, and payload information. Frames that cannot be
|
||||
decoded still emit their `RAW:` line. Transmitted packets emit the decoded TX
|
||||
summary.
|
||||
|
||||
Ordinary `-logging-` artifacts keep packet logging separate from LoRa OTA.
|
||||
Use the separately named `-ota-` artifact when LoRa OTA is required. A
|
||||
Ordinary non-OTA artifacts compile packet logging into the canonical image and
|
||||
control its live USB output at runtime; no separate `-logging-` artifact is
|
||||
emitted. Use the separately named `-ota-` artifact when LoRa OTA is required. A
|
||||
`-full-usb-wifi-ota-` artifact combines USB packet logging, direct WiFi MQTT,
|
||||
LoRa OTA, and the expanded FULL feature set. A `-full-logging-ota-` artifact is
|
||||
emitted only when that hardware/role has no matching WiFi MQTT environment.
|
||||
@@ -594,10 +612,11 @@ set usb.logging on reboot
|
||||
set usb.logging off reboot
|
||||
```
|
||||
|
||||
These commands are compiled into logging artifacts and every Full Companion.
|
||||
They control live USB debug and packet output. CommonCLI roles save the setting
|
||||
in `/com_prefs`, so it survives reboot; their first boot defaults to on. Full
|
||||
Companion starts off on a fresh installation.
|
||||
These commands are compiled into ordinary USB-loggable artifacts and every
|
||||
Full Companion. They control live USB debug and packet output. CommonCLI roles
|
||||
save the setting in `/com_prefs`, so it survives reboot; their first boot
|
||||
defaults to on. Full Companion and ordinary USB Companion start off on a fresh
|
||||
installation so diagnostics cannot corrupt framed traffic.
|
||||
|
||||
On Full Companion these lines belong to its text terminal, not `meshcli`'s
|
||||
Binary `get/set` parameter namespace. Open interface `00`, send
|
||||
@@ -3446,6 +3465,22 @@ Requires WiFi connected and the MQTT bridge running.
|
||||
|
||||
---
|
||||
|
||||
#### View or change the UART used by the bridge (RS-232 only)
|
||||
**Usage:**
|
||||
- `get bridge.uart`
|
||||
- `set bridge.uart <port>`
|
||||
|
||||
**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.
|
||||
|
||||
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`.
|
||||
|
||||
---
|
||||
|
||||
#### View or change the channel used for bridging (ESPNow only)
|
||||
**Usage:**
|
||||
- `get bridge.channel`
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Companion Protocol
|
||||
|
||||
- **Last Updated**: 2026-08-18
|
||||
- **Protocol Version**: 13 (`FIRMWARE_VER_CODE`)
|
||||
- **Last Updated**: 2026-08-26
|
||||
- **Protocol Version**: 14 (`FIRMWARE_VER_CODE`)
|
||||
|
||||
> The command and response catalogs track
|
||||
> `examples/companion_radio/MyMesh.cpp`. Applications should negotiate the
|
||||
@@ -60,6 +60,22 @@ MeshCore Companion devices expose a BLE service with the following UUIDs:
|
||||
- **RX Characteristic** (App -> Firmware): `6E400002-B5A3-F393-E0A9-E50E24DCCA9E`
|
||||
- **TX Characteristic** (Firmware -> App): `6E400003-B5A3-F393-E0A9-E50E24DCCA9E`
|
||||
|
||||
An nRF52 Full Companion also exposes a separate LoRa mOTA source service. It
|
||||
does not replace or multiplex the normal Companion UART service:
|
||||
|
||||
- **mOTA Service**: `14518FC2-7E7A-4D84-8CAE-6664B0234CF2`
|
||||
- **Device Request** (notify): `2BFAA1EE-7030-459A-B65A-E7CFD5B09735`
|
||||
- **Host Response** (write with response): `ACF38A51-DD58-4DCE-917F-0B1135E41B1A`
|
||||
|
||||
All three mOTA attributes require an encrypted, MITM-authenticated connection
|
||||
using the Companion's six-digit PIN. The source remains inactive until the
|
||||
client subscribes to Device Request and explicitly starts it with command
|
||||
`0x4B`. See [Bluetooth LoRa mOTA source](#bluetooth-lora-mota-source).
|
||||
|
||||
ESP32 and nRF52 Companion UART characteristics require the same PIN-protected,
|
||||
MITM-authenticated link. ESP32 advertises DisplayOnly capability so a central
|
||||
must enter the PIN shown by the Companion; a Just Works bond is insufficient.
|
||||
|
||||
### Connection Steps
|
||||
|
||||
1. **Scan for Devices**
|
||||
@@ -170,7 +186,7 @@ The first byte indicates the packet type (see [Response Parsing](#response-parsi
|
||||
|
||||
## Commands
|
||||
|
||||
The first byte selects the command. This is the current protocol-v13 command
|
||||
The first byte selects the command. This is the current protocol-v14 command
|
||||
catalog; bytes `0x2C`-`0x31` are parked and `0x35` is unused.
|
||||
|
||||
| Byte | Firmware name | Purpose |
|
||||
@@ -222,12 +238,14 @@ catalog; bytes `0x2C`-`0x31` are parked and `0x35` is unused.
|
||||
| `0x44` / `0x45` | `CMD_GET_RADIO_RXGAIN` / `CMD_SET_RADIO_RXGAIN` | Read or set the radio chip's boosted receive-gain mode. |
|
||||
| `0x46` / `0x47` | `CMD_GET_WIFI_POWER_SAVE` / `CMD_SET_WIFI_POWER_SAVE` | Read or set ESP32 Companion WiFi modem sleep. |
|
||||
| `0x48` / `0x49` | `CMD_GET_BLUETOOTH_NAME` / `CMD_SET_BLUETOOTH_NAME` | Read or set the independent Bluetooth device name. |
|
||||
| `0x4A` | `CMD_EXEC_LOCAL_OTA_CONTROL` | Run one bounded local TempRadio or OTA command on a Full Companion. |
|
||||
| `0x4B` | `CMD_BLE_MOTA_SOURCE` | Query, start, or stop an nRF52 Full Companion's Bluetooth-backed LoRa mOTA source. |
|
||||
|
||||
The sections below detail the most common frames. Refer to the source named
|
||||
above for command bodies that are not expanded here.
|
||||
|
||||
The additive hardware-setting commands `0x42`-`0x47` do not change any existing
|
||||
version-13 frame layout. Clients should probe the command they need and treat
|
||||
legacy frame layout. Clients should probe the command they need and treat
|
||||
`ERR_CODE_UNSUPPORTED_CMD` as feature absence.
|
||||
|
||||
Both gain-command pairs can be used over the normal binary Companion
|
||||
@@ -273,6 +291,79 @@ override and restores `MeshCore-<advert name>`. A successful SET replies with
|
||||
control characters, malformed UTF-8, or oversized values return
|
||||
`ERR_CODE_ILLEGAL_ARG`; a storage failure returns `ERR_CODE_BAD_STATE`.
|
||||
|
||||
### Bluetooth LoRa mOTA source
|
||||
|
||||
Protocol v14 lets a phone use an nRF52 Full Companion as the source for a
|
||||
remote repeater update without a USB computer. The normal Companion service
|
||||
still carries contacts, repeater login, CLI messages, and these two control
|
||||
commands. The separate mOTA service carries only host-folder request/response
|
||||
frames.
|
||||
|
||||
`CMD_EXEC_LOCAL_OTA_CONTROL` (`0x4A`) is followed by 1-174 printable ASCII
|
||||
bytes. Full Companion accepts only these local command families:
|
||||
|
||||
```text
|
||||
tempradio <freq_kHz>,<bw_kHz>,<sf>,<cr>,<minutes>
|
||||
normalradio
|
||||
ota ...
|
||||
```
|
||||
|
||||
`ota folder ...` is deliberately rejected because USB and Bluetooth source
|
||||
ownership must not be changed through the wrong transport. Embedded NUL, CR,
|
||||
LF, other control bytes, non-ASCII bytes, empty commands, and oversized frames
|
||||
return `ERR_CODE_ILLEGAL_ARG`. A recognized command replies with
|
||||
`RESP_CODE_OK`, one unsigned reply-length byte, and exactly that many printable
|
||||
result bytes. Shell metacharacters are rejected as well; the text is dispatched
|
||||
only to the in-firmware parser and is never passed to a host shell. Firmware
|
||||
without the Full Companion feature returns `ERR_CODE_UNSUPPORTED_CMD`.
|
||||
|
||||
`CMD_BLE_MOTA_SOURCE` (`0x4B`) has one action byte:
|
||||
|
||||
| Action | Meaning |
|
||||
| ---: | --- |
|
||||
| `0` | Read status without changing it. |
|
||||
| `1` | Attach and enumerate the subscribed Bluetooth host's `.mota` catalog. |
|
||||
| `2` | Detach the Bluetooth source. |
|
||||
|
||||
Current firmware returns eleven bytes (legacy protocol-v14 previews returned
|
||||
the seven-byte prefix only):
|
||||
|
||||
```text
|
||||
00 action flags offered_le16 advertised_le16 source_packets_sent_le32
|
||||
```
|
||||
|
||||
Flag bit `0x01` means the encrypted GATT channel is connected and Device
|
||||
Request notifications are enabled. Bit `0x02` means the Bluetooth catalog is
|
||||
attached. Bit `0x04` means USB or another folder transport currently owns the
|
||||
source slot. Start without a ready subscription, or while another source link
|
||||
owns the slot, returns `ERR_CODE_BAD_STATE`. A non-nRF52 Full Companion returns
|
||||
`ERR_CODE_UNSUPPORTED_CMD`. `source_packets_sent` is a per-attachment count of
|
||||
OTA packets accepted by the Companion's LoRa transmit adapter, including
|
||||
catalog/manifest traffic, data, proofs, and retries. It wraps as an unsigned
|
||||
32-bit value. Clients should accept the legacy seven-byte response and display
|
||||
the packet counter as unavailable.
|
||||
|
||||
After a successful start, the device sends the same bounded seeder frames used
|
||||
by `motatool serve` on Device Request:
|
||||
|
||||
```text
|
||||
device -> host: 'M' 'S' op args... xor(op || args)
|
||||
host -> device: 'm' 's' op status payload... xor(all prior bytes)
|
||||
```
|
||||
|
||||
Device requests are at most 11 bytes. A source response is at most 197 bytes.
|
||||
The host may split one response across multiple write-with-response operations
|
||||
when the negotiated ATT payload is smaller; it must preserve byte order and
|
||||
must not interleave another response. Bad checksums, partial frames, overflow,
|
||||
unsubscribe, loss of encryption, or disconnect fail closed. The firmware then
|
||||
detaches the catalog and stops advertising its entries. USB and Bluetooth
|
||||
folder sources are mutually exclusive.
|
||||
|
||||
A Linux reference controller and seeder is provided at
|
||||
`tools/ble_mota/ble_mota_seeder.py`. It verifies every input with `motatool`
|
||||
before offering it. A mobile implementation should apply the same complete
|
||||
container verification before serving files.
|
||||
|
||||
### 1. App Start
|
||||
|
||||
**Purpose**: Initialize communication with the device. Must be sent first after connection.
|
||||
@@ -305,7 +396,7 @@ Byte 1: Highest companion protocol version understood by the app
|
||||
|
||||
**Example** (hex):
|
||||
```
|
||||
16 0D
|
||||
16 0E
|
||||
```
|
||||
|
||||
**Response**: `PACKET_DEVICE_INFO` (0x0D) with device information
|
||||
|
||||
@@ -12,8 +12,10 @@ firmware as an mOTA image.
|
||||
| BLE Binary Companion | Yes | Yes |
|
||||
| USB ASCII terminal | Yes | Yes |
|
||||
| Dedicated USB plaintext logging | Qualified native-USB ESP32-S3 profiles | Yes |
|
||||
| Host-backed LoRa mOTA source | WiFi TCP 5001 | Exclusive USB mode |
|
||||
| Host-backed LoRa mOTA source | WiFi TCP 5001 | Exclusive USB mode or encrypted BLE |
|
||||
| WiFi Companion/WebConfig | Yes | No - nRF52840 has no WiFi |
|
||||
| Hardware serial Companion | On targets with assigned serial pins | On targets with assigned serial pins |
|
||||
| Ethernet Companion | On targets with an Ethernet module | On RAK4631 with RAK13800 |
|
||||
| LoRa self-update | No | No |
|
||||
|
||||
## Build and install
|
||||
@@ -63,8 +65,11 @@ Canonical Companion bulk builds also omit legacy `_ps` and `_femoff` aliases.
|
||||
Power saving and controllable FEM receive gain are persisted runtime settings;
|
||||
the old names remain available through an explicit `build-firmware` command
|
||||
for compatibility. Full Companion replaces separate USB, BLE, ordinary WiFi,
|
||||
and USB-only packet-logging release artifacts whenever the exact board supports
|
||||
the combined profile. Dual-CDC builds separate framed traffic and logs;
|
||||
hardware-serial, Ethernet Companion, Terminal Chat, and USB-only packet-logging
|
||||
release artifacts whenever the exact board supports those combined transports.
|
||||
Direct builds of the legacy targets remain available. RAK4631 repeater and room
|
||||
server Ethernet builds remain separate because they are different standalone
|
||||
roles, not Companion transports. Dual-CDC builds separate framed traffic and logs;
|
||||
single-TTY builds make those modes mutually exclusive. In WebConfig, use the
|
||||
**FEM RX boost** switch. From the text terminal (USB, or TCP 5002 on ESP32), use:
|
||||
|
||||
@@ -85,6 +90,22 @@ commands control the external receive and transmit paths. The selected states
|
||||
are applied immediately and retained after reboot. FEM TX gain is reported as
|
||||
unsupported on boards without software-selectable PA gain.
|
||||
|
||||
SSD1306 display builds also persist a runtime orientation. This replaces the
|
||||
separate rotated Full Companion release image:
|
||||
|
||||
```text
|
||||
get display.rotation
|
||||
set display.rotation 90
|
||||
set display.rotation 180
|
||||
set display.rotation 270
|
||||
set display.rotation 0
|
||||
```
|
||||
|
||||
`0` resets the screen to that board's compiled default orientation.
|
||||
|
||||
Heltec E290 and T190 use one `usb_ble` Companion artifact for simultaneous USB
|
||||
and BLE rather than publishing separate USB-only and BLE-only images.
|
||||
|
||||
Device power saving is separate from LoRa RXPS. It can be changed in WebConfig
|
||||
with the **Device power saving** switch or from the text terminal:
|
||||
|
||||
@@ -219,6 +240,7 @@ itself.
|
||||
| ESP32 | TCP 5001 | Host `.mota` folder from `motatool serve --tcp` |
|
||||
| ESP32 | TCP 5002 | Full Companion text terminal; same role commands as the USB terminal |
|
||||
| nRF52 | USB mOTA mode | Host `.mota` folder from `motatool serve --serial` |
|
||||
| nRF52 | Encrypted BLE mOTA service | Paired phone/tablet/Linux host `.mota` catalog |
|
||||
|
||||
Delivery-required replies are returned only to the interface which supplied the
|
||||
latest command. A contact-list stream keeps that route locked from
|
||||
@@ -356,6 +378,10 @@ only one may own the terminal at a time. Entering USB terminal mode closes an
|
||||
active TCP terminal session. Disconnecting TCP clears pending terminal-only
|
||||
state without cancelling Binary Companion delivery or radio retries.
|
||||
|
||||
Both terminal transports accept `reboot`. The reply is sent first and the
|
||||
device reboots one second later, so a script can distinguish an accepted reboot
|
||||
from an abruptly lost connection.
|
||||
|
||||
Port 5002 is plaintext and has no device-local login gate. A remote-admin
|
||||
password entered with `login` is sent across the LAN connection as typed even
|
||||
though the terminal does not echo it. Use port 5002 only on a trusted LAN or a
|
||||
@@ -459,6 +485,50 @@ While mOTA mode owns USB:
|
||||
|
||||
No manual mode token or modified `motatool` build is required.
|
||||
|
||||
## nRF52 Bluetooth mOTA source
|
||||
|
||||
Protocol v14 also lets a phone, tablet, or Bluetooth-capable Linux host feed
|
||||
the `.mota` catalog to an nRF52 Full Companion. Normal Companion commands stay
|
||||
on the Nordic UART service. Firmware data uses a separate GATT service, so
|
||||
binary app traffic cannot be mistaken for a firmware block.
|
||||
|
||||
The client must pair with the Companion PIN, subscribe to the mOTA Device
|
||||
Request characteristic, and send `CMD_BLE_MOTA_SOURCE` action `start` over the
|
||||
normal Binary Companion connection. The source is available only while that
|
||||
encrypted MITM-authenticated connection remains active. Disconnecting,
|
||||
unsubscribing, overflowing a frame, or receiving malformed data automatically
|
||||
detaches the catalog. USB and BLE source modes are mutually exclusive.
|
||||
|
||||
The included Raspberry Pi reference client validates each `.mota` with
|
||||
`motatool`, schedules the local TempRadio window, serves until interrupted,
|
||||
then detaches and restores the normal radio tuple:
|
||||
|
||||
```bash
|
||||
python3 tools/ble_mota/ble_mota_seeder.py \
|
||||
--device MeshCore-MyCompanion \
|
||||
--dir ./motas \
|
||||
--local 'tempradio 909.950,250,5,5,120'
|
||||
```
|
||||
|
||||
Prefer this relative `tempradio` form when the phone/Pi and radio clocks may
|
||||
disagree. It starts a duration on the Companion and does not compare their
|
||||
wall clocks. Use the absolute `tempradioat` scheduler only after synchronizing
|
||||
the participating nodes.
|
||||
|
||||
Use `--pair` when the Linux host has not already bonded. BlueZ must have an
|
||||
agent capable of entering or confirming the six-digit PIN. Use `--source
|
||||
status` without `--dir` for a read-only channel/status check. The complete
|
||||
UUID, frame, action, and status definitions are in the
|
||||
[Companion protocol](./companion_protocol.md#bluetooth-lora-mota-source).
|
||||
|
||||
This reference process stands in for the phone application. A mobile app can
|
||||
use the same sequence while retaining its normal contact and Repeater Admin
|
||||
UI: log in to the destination, put each required node on the same bounded
|
||||
TempRadio tuple, start the local Bluetooth catalog, then send the normal remote
|
||||
`ota ls`, `ota pull`, and `ota install` commands. The destination still checks
|
||||
container geometry, hardware identity, hashes, signature policy, and the
|
||||
OTAFIX bootloader before installation.
|
||||
|
||||
## Serve mOTA images manually
|
||||
|
||||
First put the destination, required relays, controller, and source on the same
|
||||
@@ -514,6 +584,10 @@ Ctrl-C to detach the folder. Reopen the terminal and use `normalradio` if the
|
||||
source should return early; otherwise the saved radio settings return when the
|
||||
bounded window expires.
|
||||
|
||||
As a cable-free alternative, keep the normal Companion BLE session open and
|
||||
run the Bluetooth reference client shown in the nRF52 Bluetooth section. Do
|
||||
not run the USB seeder at the same time.
|
||||
|
||||
Both platforms intentionally refuse firmware installation commands such as:
|
||||
|
||||
```text
|
||||
|
||||
+23
-5
@@ -140,8 +140,12 @@ from the published firmware assets.
|
||||
| LoRa OTA source only | Full Companion serving a host-supplied update to another node without self-installing it |
|
||||
|
||||
Connection and bridge choices depend on the selected role. Companion firmware
|
||||
may offer Full, Bluetooth, USB, Wi-Fi, serial, or Ethernet transports.
|
||||
Repeaters may offer standard, ESP-NOW bridge, RS-232 bridge, Ethernet, or MQTT
|
||||
may offer Full, combined USB + Bluetooth, Bluetooth, USB, Wi-Fi, serial, or
|
||||
Ethernet transports.
|
||||
Normal repeater firmware includes runtime-controlled RS-232 support where the
|
||||
board has room; use `set bridge.enabled on` after configuring `bridge.uart` and
|
||||
`bridge.baud`. The Wio-E5 remains the capacity exception and offers a separate
|
||||
RS-232 image. Repeaters may also offer separate ESP-NOW, Ethernet, or MQTT
|
||||
observer modes.
|
||||
|
||||
## FULL versus standard
|
||||
@@ -169,9 +173,23 @@ available as an override. A saved SSID switches to the normal indefinite
|
||||
reconnect behavior instead.
|
||||
|
||||
Full Companion profiles use one binary for USB, BLE, ordinary Wi-Fi on ESP32,
|
||||
source-only LoRa OTA, and optional USB packet logging. The picker therefore
|
||||
omits separate attached-transport and USB-logging choices whenever the exact
|
||||
Full recipe exists. Fresh installs default to logging off.
|
||||
source-only LoRa OTA, Terminal Chat, optional USB packet logging, and any
|
||||
board-qualified serial or Ethernet Companion transport. Bulk builds therefore
|
||||
omit separate attached-transport, Terminal Chat, and USB-logging artifacts
|
||||
whenever the exact Full recipe exists. RAK4631 repeater and room-server
|
||||
Ethernet images remain separate roles. Fresh installs default to logging off.
|
||||
|
||||
When Full Companion does not fit but a matching USB Companion does, that USB
|
||||
artifact also supplies Terminal Chat and replaces its standalone release image.
|
||||
Heltec E290 and T190 publish a combined USB + BLE Companion. SSD1306 Full
|
||||
Companion builds use `set display.rotation 90|180|270`; `0` restores the board
|
||||
default, so a separate rotated release image is not recommended.
|
||||
|
||||
Ordinary non-OTA roles also use one artifact for normal operation and USB
|
||||
logging. Select the saved mode with `set usb.logging off|on`; no `-logging-`
|
||||
artifact is emitted. KISS, BLE-only Companion, and constrained LoRa-OTA
|
||||
receiver images retain their protocol/partition contracts and do not inherit
|
||||
plaintext USB logging.
|
||||
|
||||
Dual-CDC nRF52 and qualified native-USB ESP32-S3 builds keep Binary Companion
|
||||
on interface `00`; `set usb.logging on reboot` adds plaintext interface `02`.
|
||||
|
||||
@@ -21,10 +21,10 @@ example and is disabled unless the endpoint is started with `--allow-reboot`.
|
||||
Text such as `reboot now`, `cpu-temp; reboot`, and embedded newlines is not a
|
||||
command: the endpoint accepts only an exact allowlist match.
|
||||
|
||||
The bridge is included in normal repeater firmware. The specialized Wio-E5
|
||||
RS232 bridge image omits it because the RS232 application already fills its
|
||||
fixed 240 KiB application partition; use the normal Wio-E5 repeater image when
|
||||
the USB/MQTT host service is needed.
|
||||
The bridge is included in normal repeater firmware except on Wio-E5. Its
|
||||
specialized RS232 bridge image omits the host service because the combined
|
||||
application exceeds the fixed 240 KiB partition; use the normal Wio-E5
|
||||
repeater image when the USB/MQTT host service is needed.
|
||||
|
||||
## Run alongside meshcoretomqtt
|
||||
|
||||
|
||||
+17
-2
@@ -90,8 +90,9 @@ environmental sensors while retaining board-native features such as displays, bu
|
||||
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 except for
|
||||
its Serial1 RS232 bridge, which uses the same UART. Selected nRF52 boards with matched external
|
||||
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.
|
||||
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
|
||||
for the downloaded container. SolarXiao 30S and 33S use this matched external-QSPI
|
||||
@@ -156,6 +157,10 @@ An nRF52 `companion_radio_full` starts in USB Binary mode. Use
|
||||
`+++MESHCORE-TERM-STOP`. When `motatool serve --serial` opens the port, its
|
||||
automatic `ota folder on` command selects exclusive mOTA mode; stopping the
|
||||
tool or disconnecting resets USB to Binary. BLE remains available throughout.
|
||||
Protocol v14 can instead take the catalog from a paired phone or Linux host
|
||||
over a separate encrypted BLE mOTA service while Binary Companion remains
|
||||
active. USB and BLE catalog sources are mutually exclusive. See the
|
||||
[Full Companion Bluetooth source guide](./companion_radio_full.md#nrf52-bluetooth-mota-source).
|
||||
|
||||
For an ESP32 WiFi companion or FULL ESP32 source with active WiFi, use its dedicated OTA seeder:
|
||||
|
||||
@@ -356,6 +361,16 @@ Replace `/dev/ttyACM0` with the USB serial device of the source companion select
|
||||
`motatool` attaches the folder to the source, which advertises the update over LoRa while its temporary-radio
|
||||
window is active. KISS modem serial ports cannot be used here.
|
||||
|
||||
For an nRF52 Full Companion and a Bluetooth-capable host, the equivalent
|
||||
cable-free source is:
|
||||
|
||||
```bash
|
||||
python3 tools/ble_mota/ble_mota_seeder.py \
|
||||
--device MeshCore-MyCompanion \
|
||||
--dir ./motas \
|
||||
--local 'tempradio 909.950,250,5,5,120'
|
||||
```
|
||||
|
||||
Leave this command running until the destination finishes downloading.
|
||||
|
||||
### 3. Find and download the update
|
||||
|
||||
+22
-10
@@ -133,8 +133,8 @@ matched external QSPI staging, so their ordinary full-sensor repeater is already
|
||||
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 except for the Serial1 RS232
|
||||
bridge, whose bridge and GPS would contend for the same UART.
|
||||
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.
|
||||
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.
|
||||
@@ -161,8 +161,9 @@ Option 3 in `build.sh` emits one `*-full-usb-wifi-ota-*` ESP32 artifact for each
|
||||
FULL-capable non-companion hardware/role that has a matching MQTT environment.
|
||||
It compiles USB packet logging and direct WiFi MQTT together. A
|
||||
`*-full-logging-ota-*` fallback is emitted only when there is no MQTT sibling;
|
||||
the old separate standard-logging and non-MQTT FULL twins are skipped for
|
||||
covered ESP32 roles. MQTT observers and ESP-NOW bridges are emitted only with
|
||||
ordinary non-OTA roles compile runtime USB logging into their canonical image,
|
||||
so separate standard-logging artifacts are not emitted. Non-MQTT FULL twins
|
||||
are also skipped for covered ESP32 roles. MQTT observers and ESP-NOW bridges are emitted only with
|
||||
expanded FULL partitions. Menu option 8, or `build-full-esp32-firmwares`,
|
||||
builds the unified profiles plus necessary fallbacks. Menu option 9, or
|
||||
`build-full-esp32-logging-firmwares`, builds only those fallbacks.
|
||||
@@ -686,13 +687,15 @@ verify everything). The serve side (`OtaManager`) keeps a lightweight registry o
|
||||
resident "views": `view0` (its own firmware) and one on-demand view loaded from a source when a request
|
||||
targets an external mota. Every fetch message carries `manifest_id`, so dispatch is a registry lookup.
|
||||
|
||||
The same host-folder link is also a **pull destination** (the reverse direction): `ota pull <mid8> folder`
|
||||
The USB/TCP host-folder link can also be a **pull destination** (the reverse direction): `ota pull <mid8> folder`
|
||||
fetches a `.mota` off the mesh and streams it onto the host as `<mid>.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
|
||||
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`
|
||||
let the fetcher recompute and refill only the missing blocks.
|
||||
The phone-oriented BLE link is deliberately source-only and does not register
|
||||
a folder destination.
|
||||
|
||||
### 10.1 The `MotaSource` abstraction (`OtaSource.h`)
|
||||
|
||||
@@ -717,7 +720,8 @@ blocks) and streams payload blocks from the source on demand; proofs are generat
|
||||
### 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 or FULL ESP32 role, over **WiFi (TCP)**. The
|
||||
CLI uses - no extra hardware), on an ESP32 WiFi companion or FULL ESP32 role over **WiFi (TCP)**, or on an
|
||||
nRF52 Full Companion over an encrypted **BLE GATT** service. The
|
||||
host is the
|
||||
standalone Rust tool [`motatool`](https://github.com/vk496/motatool) (`motatool serve --serial <port>` /
|
||||
`--tcp <host[:port]>`, which also builds + verifies + inspects `.mota`). The device only emits request frames *while
|
||||
@@ -761,6 +765,10 @@ deployment runner temporarily uses `0.3` on managed relays.
|
||||
work**: KISS firmware exposes a TNC/KISS frame interface, not the MeshCore CLI and `mota-seeder`
|
||||
request/response transport. An ESP32 WiFi companion or FULL ESP32 role with active WiFi is the alternative
|
||||
source connection: use its dedicated seeder port with `motatool serve --tcp <host>:5001`.
|
||||
An nRF52 Full Companion can instead pair with a phone or Linux host, subscribe
|
||||
to its mOTA request characteristic, and use protocol-v14
|
||||
`CMD_BLE_MOTA_SOURCE`. That BLE path is source-only; it does not expose the
|
||||
reverse `FolderMotaStore` capture operations.
|
||||
|
||||
Device CLI: `ota folder on` (attach + announce), `ota folder` (list), `ota folder off`. Build flag
|
||||
`OTA_FOLDER_SERIAL` (default stream = console `Serial`; override `OTA_FOLDER_SERIAL_STREAM` + define
|
||||
@@ -784,10 +792,14 @@ disappearing. Operators should split a large chain or use a higher-capacity/SD s
|
||||
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
|
||||
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
|
||||
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.
|
||||
`motatool`'s `TcpTransport` mirrors its `SerialTransport`). The nRF52 Full
|
||||
Companion's **BLE GATT** path also reuses the exact frame and checksum. Device
|
||||
requests are notifications on a dedicated characteristic and host responses
|
||||
are ordered write-with-response fragments on a second characteristic. Keeping
|
||||
the same framing makes retries and corruption handling identical across USB,
|
||||
TCP, and GATT. The Linux reference implementation is
|
||||
`tools/ble_mota/ble_mota_seeder.py`; a phone app can implement the same
|
||||
transport-free catalog operations.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -55,9 +55,9 @@ the GPS 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. The RAK4631 Serial1
|
||||
RS232 OTA bridge is the exception because the bridge owns the GPS UART. The
|
||||
Serial2 RS232 OTA bridge retains GPS on Serial1.
|
||||
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.
|
||||
|
||||
Selected nRF52 repeaters with dedicated external QSPI can now stage the
|
||||
complete package off-chip, so their normal full-sensor repeater build can
|
||||
@@ -394,14 +394,17 @@ 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 (<https://github.com/vk496/motatool>) -
|
||||
then point it at your node and the folder - over the node's **USB serial**, or over **WiFi** if it is
|
||||
an ESP32 WiFi companion or FULL ESP32 node:
|
||||
then point it at your node and the folder - over the node's **USB serial**, over **WiFi** if it is
|
||||
an ESP32 WiFi companion or FULL ESP32 node, or over encrypted **Bluetooth** if it is an nRF52 Full Companion:
|
||||
```
|
||||
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: the seeder is on dedicated TCP port 5001:
|
||||
motatool serve --dir ./my_firmware/ --tcp 192.168.1.50:5001 -v
|
||||
# ...or over paired BLE to an nRF52 Full Companion (protocol v14):
|
||||
python3 tools/ble_mota/ble_mota_seeder.py \
|
||||
--device MeshCore-MyCompanion --dir ./my_firmware/
|
||||
```
|
||||
It answers the node's requests; your node then advertises those updates to neighbours, who can
|
||||
`ota get` them like any other. (A WiFi node prints its IP + seeder port to the serial log on connect.
|
||||
|
||||
@@ -107,13 +107,21 @@ GPS idle behavior; it does not change LoRa RXPS or WiFi modem sleep.
|
||||
get usb.logging
|
||||
set usb.logging {on|off} [reboot]
|
||||
```
|
||||
Shows or changes persistent live USB debug and packet output in a Companion
|
||||
logging artifact or Full Companion. Full starts off on a fresh install.
|
||||
Shows or changes persistent live USB debug and packet output in an ordinary
|
||||
USB-loggable Companion or Full Companion. USB Companion and Full start off on
|
||||
a fresh install with logging disabled to protect framed traffic.
|
||||
Dual-CDC Full changes its interface count after a reboot and keeps Companion on
|
||||
interface `00`. Single-TTY Full needs no reboot: logging uses the active text
|
||||
terminal, which continues accepting `set usb.logging off` and automatically
|
||||
restores Binary Companion after that reply.
|
||||
|
||||
```
|
||||
reboot
|
||||
```
|
||||
Sends an acknowledgement, then reboots the Companion one second later. The
|
||||
delay gives either the USB terminal or the Full Companion TCP terminal on port
|
||||
5002 time to deliver the reply before its transport disappears.
|
||||
|
||||
```
|
||||
get radio.rxps
|
||||
get radio.rxps.config
|
||||
|
||||
Reference in new issue
Block a user