Fix auto nRF52 Companion OTA builds
This commit is contained in:
1 parent
cb27fa8539
commit
249c3680b3
6 files changed
+90
-12
No files matched your search
@@ -245,6 +245,10 @@ init_project_context() {
|
||||
if [ -z "$env_name" ] || [ -z "$env_platform" ]; then
|
||||
continue
|
||||
fi
|
||||
# Native Windows Python writes CRLF even when its output is consumed by
|
||||
# Git Bash. `read` removes LF but leaves CR on the final field, which can
|
||||
# make exact board checks (for example heltec-rc32) silently fail.
|
||||
env_board=${env_board%$'\r'}
|
||||
SUPPORTED_PIO_ENVS+=("$env_name")
|
||||
PIO_ENV_PLATFORM_BY_NAME["$env_name"]=$env_platform
|
||||
PIO_ENV_BOARD_BY_NAME["$env_name"]=$env_board
|
||||
@@ -2720,6 +2724,16 @@ is_lora_ota_build() {
|
||||
return 1
|
||||
fi
|
||||
|
||||
# A single-target auto build promises to preserve the capabilities declared
|
||||
# by its resolved PlatformIO environment and fail if they do not fit. Keep
|
||||
# that contract for nRF52 Companion and other non-repeater roles which
|
||||
# deliberately inherit the shared OTA recipe. The standard/release profile
|
||||
# continues through the opt-in policy below and may select its documented
|
||||
# portable reductions.
|
||||
if [ "${BUILD_PROFILE_FOR_TARGET:-$BUILD_PROFILE_EFFECTIVE}" = "auto" ]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
# The OTA manager, staging store, and self-install path are deliberately
|
||||
# opt-in. Most boards use the constrained no-external-sensors sibling. A
|
||||
# purpose-built external-QSPI target may retain the full board feature set.
|
||||
@@ -2746,13 +2760,17 @@ is_lora_ota_build() {
|
||||
esac
|
||||
}
|
||||
|
||||
is_esp32_companion_build() {
|
||||
is_companion_build() {
|
||||
case "${1,,}" in
|
||||
*companion*|*comp_radio*) return 0 ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
is_esp32_companion_build() {
|
||||
is_companion_build "$1"
|
||||
}
|
||||
|
||||
requires_esp32_portable_app_slot() {
|
||||
[ "${PIO_ENV_PLATFORM_BY_NAME[$1]:-}" = "ESP32_PLATFORM" ] \
|
||||
&& ! is_esp32_companion_build "$1"
|
||||
@@ -2910,8 +2928,7 @@ declare_build_capability_contract() {
|
||||
record_build_expectation "companion.ble_mota_source" \
|
||||
"Bluetooth mOTA source"
|
||||
fi
|
||||
elif [ "$env_platform" = "ESP32_PLATFORM" ] \
|
||||
&& is_esp32_companion_build "$env_name" \
|
||||
elif is_companion_build "$env_name" \
|
||||
&& is_lora_ota_build "$env_name"; then
|
||||
record_build_expectation "companion.temp_radio" "ERR usage: tempradio"
|
||||
record_build_expectation "companion.ota_cli" "OTA: status"
|
||||
@@ -3323,8 +3340,7 @@ apply_lora_ota_override() {
|
||||
append_platformio_build_unflags "-UENABLE_OTA -DDISABLE_LORA_OTA=1"
|
||||
export PLATFORMIO_BUILD_FLAGS="${PLATFORMIO_BUILD_FLAGS} -UDISABLE_LORA_OTA -DENABLE_OTA=1 -DOTA_FLASH_STORE=1 -DOTA_FOLDER_SERIAL"
|
||||
fi
|
||||
if [ "${PIO_ENV_PLATFORM_BY_NAME[$env_name]:-}" = "ESP32_PLATFORM" ] \
|
||||
&& is_esp32_companion_build "$env_name"; then
|
||||
if is_companion_build "$env_name"; then
|
||||
# Do not link an OTA manager into a Companion while compiling out the
|
||||
# terminal controls needed to operate it over LoRa.
|
||||
export PLATFORMIO_BUILD_FLAGS="${PLATFORMIO_BUILD_FLAGS} -DCOMPANION_FEATURE_TEMP_RADIO=1 -DCOMPANION_FEATURE_OTA_CLI=1"
|
||||
|
||||
@@ -28,7 +28,7 @@ and partition policy:
|
||||
|
||||
| Selection | Behavior |
|
||||
| --- | --- |
|
||||
| `auto` | For one explicit target, pass 1 builds the complete supported LoRa-OTA-capable recipe. ESP32 boards with a qualified expanded profile use it; other repeaters attempt the complete recipe in their current application region. A measured flash/partition overflow starts the standard `no_external_sensors` LoRa OTA pass. Internal-flash nRF52 repeaters publish that reduced pass even when the complete image fits, because the smaller running image leaves more room to stage a delta; matched QSPI/SD repeaters do not need the redundant artifact. Compiler errors and missing-capability checks never trigger or conceal a reduced build. Canonical bulk commands keep their established standard partition contract. |
|
||||
| `auto` | For one explicit target, pass 1 builds the complete supported LoRa-OTA-capable recipe. ESP32 boards with a qualified expanded profile use it; other repeaters attempt the complete recipe in their current application region. A non-repeater keeps every capability declared by its resolved PlatformIO recipe, including LoRa OTA, and fails instead of silently removing one that does not fit. A measured repeater flash/partition overflow starts the standard `no_external_sensors` LoRa OTA pass. Internal-flash nRF52 repeaters publish that reduced pass even when the complete image fits, because the smaller running image leaves more room to stage a delta; matched QSPI/SD repeaters do not need the redundant artifact. Compiler errors and missing-capability checks never trigger or conceal a reduced build. Canonical bulk commands keep their established standard partition contract. |
|
||||
| `standard` | Immediately uses the deployed/portable partition contract and its documented reductions. This is useful when the operator already knows the expanded or complete image is unsuitable. |
|
||||
| `full` | Requires a qualified ESP32 expanded-partition target (or an explicitly named Full Companion). Install a matching merged image when this changes the partition table. |
|
||||
|
||||
@@ -38,7 +38,8 @@ logical OTA target, actual PlatformIO base, artifact name target, promised
|
||||
capabilities, and every reduction selected by the script. The build
|
||||
fails if a promised linked marker is absent. Current invariants include
|
||||
`retry.preset` for repeater/room-server roles, WebConfig for the Indicator and
|
||||
ESP32 Full Companion, TempRadio/mOTA controls for Full Companion, and the
|
||||
ESP32 Full Companion, TempRadio/OTA controls for install-capable Companions,
|
||||
host-mOTA controls for Full Companion, and the
|
||||
advanced flood-rule engine for Full room servers. Resume mode will not accept
|
||||
an old or failed artifact without a verified sidecar.
|
||||
|
||||
|
||||
@@ -235,7 +235,7 @@ catalog; bytes `0x2C`-`0x31` are parked and `0x35` is unused.
|
||||
| `0x3F` / `0x40` | `CMD_SET_DEFAULT_FLOOD_SCOPE` / `CMD_GET_DEFAULT_FLOOD_SCOPE` | Write or read the default flood scope. |
|
||||
| `0x41` | `CMD_SEND_RAW_PACKET` | Queue a fully encoded raw mesh packet. |
|
||||
| `0x42` | `CMD_RUN_CLI_COMMAND` | Run a local CLI command (protocol v14+). |
|
||||
| `0x4A` | `CMD_EXEC_LOCAL_OTA_CONTROL` | Run one bounded local TempRadio or OTA command on a Full Companion. |
|
||||
| `0x4A` | `CMD_EXEC_LOCAL_OTA_CONTROL` | Run one bounded local TempRadio or OTA command when the Companion includes the OTA CLI. |
|
||||
| `0x4B` | `CMD_BLE_MOTA_SOURCE` | Query, start, or stop an nRF52 Full Companion's Bluetooth-backed LoRa mOTA source. |
|
||||
| `0x78`-`0x7F` | Deprecated hardware-setting aliases | Receive-only compatibility for clients shipped before command `0x42` became the canonical settings path. |
|
||||
|
||||
@@ -326,14 +326,16 @@ custom name is limited to 31 valid UTF-8 bytes and takes effect after reboot.
|
||||
|
||||
### Bluetooth LoRa mOTA source
|
||||
|
||||
Protocol v14 lets a phone use an nRF52 Full Companion as the source for a
|
||||
Protocol v14 lets a phone control an install-capable Companion's own LoRa OTA
|
||||
session. An nRF52 Full Companion can additionally act 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:
|
||||
bytes. A Companion built with the OTA CLI accepts only these local command
|
||||
families:
|
||||
|
||||
```text
|
||||
tempradio <freq_kHz>,<bw_kHz>,<sf>,<cr>,<minutes>
|
||||
@@ -348,7 +350,7 @@ 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`.
|
||||
without the OTA CLI feature returns `ERR_CODE_UNSUPPORTED_CMD`.
|
||||
|
||||
`CMD_BLE_MOTA_SOURCE` (`0x4B`) has one action byte:
|
||||
|
||||
|
||||
@@ -4572,7 +4572,7 @@ void MyMesh::handleCmdFrame(size_t len) {
|
||||
}
|
||||
} else if (cmd_frame[0]
|
||||
== mesh::companion::CMD_EXEC_LOCAL_OTA_CONTROL) {
|
||||
#if defined(COMPANION_RADIO_FULL)
|
||||
#if COMPANION_FEATURE_OTA_CLI
|
||||
const size_t command_len = len - 1;
|
||||
if (!mesh::companion::isBleOtaControlCommandAllowed(
|
||||
&cmd_frame[1], command_len)) {
|
||||
|
||||
@@ -319,6 +319,51 @@ require(rak_usb, "build_flags", "FORCE_GPS_ALIVE")
|
||||
# replace each with that exact board's Full target.
|
||||
init_project_context >/dev/null
|
||||
|
||||
# Auto means that an explicitly selected target keeps the capabilities in its
|
||||
# resolved PlatformIO recipe. T1000-E BLE/USB both inherit nRF52 LoRa OTA; the
|
||||
# release builder must not silently replace ENABLE_OTA with -UENABLE_OTA merely
|
||||
# because Companion is not a repeater role. Standard keeps the existing
|
||||
# portable-release reduction policy.
|
||||
for t1000_env in \
|
||||
t1000e_companion_radio_ble \
|
||||
t1000e_companion_radio_usb; do
|
||||
[ "${PIO_ENV_OTA_BY_NAME[$t1000_env]:-0}" = 1 ] \
|
||||
|| fail "$t1000_env resolved recipe no longer declares LoRa OTA"
|
||||
(
|
||||
BUILD_PROFILE_FOR_TARGET=auto
|
||||
is_lora_ota_build "$t1000_env" \
|
||||
|| fail "$t1000_env auto profile stripped declared LoRa OTA"
|
||||
|
||||
PLATFORMIO_BUILD_FLAGS=""
|
||||
PLATFORMIO_BUILD_UNFLAGS=""
|
||||
apply_lora_ota_override "$t1000_env"
|
||||
[[ "$PLATFORMIO_BUILD_FLAGS" == *"-DENABLE_OTA=1"* ]] \
|
||||
|| fail "$t1000_env auto overlay omitted ENABLE_OTA"
|
||||
[[ "$PLATFORMIO_BUILD_FLAGS" != *"-UENABLE_OTA"* ]] \
|
||||
|| fail "$t1000_env auto overlay still disables LoRa OTA"
|
||||
[[ "$PLATFORMIO_BUILD_FLAGS" == *"-DCOMPANION_FEATURE_TEMP_RADIO=1"* ]] \
|
||||
|| fail "$t1000_env auto overlay omitted temporary-radio control"
|
||||
[[ "$PLATFORMIO_BUILD_FLAGS" == *"-DCOMPANION_FEATURE_OTA_CLI=1"* ]] \
|
||||
|| fail "$t1000_env auto overlay omitted OTA CLI control"
|
||||
|
||||
BUILD_CAPABILITIES=()
|
||||
BUILD_REDUCTIONS=()
|
||||
BUILD_EXPECTATIONS=()
|
||||
declare_build_capability_contract "$t1000_env" NRF52_PLATFORM
|
||||
expectations=" ${BUILD_EXPECTATIONS[*]} "
|
||||
[[ "$expectations" == *"companion.temp_radio=ERR usage: tempradio"* ]] \
|
||||
|| fail "$t1000_env auto contract omitted temporary-radio verification"
|
||||
[[ "$expectations" == *"companion.ota_cli=OTA: status"* ]] \
|
||||
|| fail "$t1000_env auto contract omitted OTA CLI verification"
|
||||
)
|
||||
(
|
||||
BUILD_PROFILE_FOR_TARGET=standard
|
||||
if is_lora_ota_build "$t1000_env"; then
|
||||
fail "$t1000_env standard profile bypassed portable OTA policy"
|
||||
fi
|
||||
)
|
||||
done
|
||||
|
||||
# Arduino-ESP32 3.x OTA-only targets that have always declared larger A/B
|
||||
# slots must be checked against their real partition table, not the legacy
|
||||
# 1.25 MiB portable ceiling. Keep that exception OTA-only so ordinary RC32
|
||||
|
||||
@@ -100,6 +100,20 @@ assert 'memcmp(command + noun_start, "fold", 4) == 0' in owner_policy
|
||||
assert 'memcmp(command + pos, "on", 2) == 0' in owner_policy
|
||||
assert 'memcmp(command + pos, "off", 3) == 0' in owner_policy
|
||||
|
||||
# The bounded framed/BLE OTA-control command belongs to the OTA features, not
|
||||
# only to the synthetic Full profile. Auto builds of a declared nRF52 OTA
|
||||
# Companion need this route to select TempRadio and drive receive/apply.
|
||||
ota_frame_start = source.index(
|
||||
"== mesh::companion::CMD_EXEC_LOCAL_OTA_CONTROL"
|
||||
)
|
||||
ota_frame_end = source.index(
|
||||
"} else if (cmd_frame[0] == mesh::companion::CMD_BLE_MOTA_SOURCE)",
|
||||
ota_frame_start,
|
||||
)
|
||||
ota_frame = source[ota_frame_start:ota_frame_end]
|
||||
assert "\n#if COMPANION_FEATURE_OTA_CLI\n" in ota_frame
|
||||
assert "#if defined(COMPANION_RADIO_FULL)" not in ota_frame
|
||||
|
||||
# This compile-time contract prevents either the implementation or help entry
|
||||
# from leaking onto a non-ESP32 build merely because a recipe set the flag.
|
||||
assert re.search(
|
||||
|
||||
Reference in new issue
Block a user