mirror of
https://github.com/mikecarper/MeshCore.git
synced 2026-10-23 13:22:22 +00:00
Add portable LoRa OTA repeater builds
This commit is contained in:
1 parent
371d86c4b1
commit
7abb5fb95b
7 files changed
+1323
-1169
No files matched your search
@@ -5,6 +5,7 @@ SUPPORTED_PIO_ENVS=()
|
||||
declare -A PIO_ENV_PLATFORM_BY_NAME=()
|
||||
declare -A PIO_ENV_MQTT_BY_NAME=()
|
||||
declare -A PIO_ENV_OTA_BY_NAME=()
|
||||
declare -A PIO_ENV_BUILD_BASE_BY_NAME=()
|
||||
PIO_CONFIG_JSON=""
|
||||
MENU_CHOICE=""
|
||||
SELECTED_TARGET=""
|
||||
@@ -31,7 +32,7 @@ PARSED_COMMAND_ARGS=()
|
||||
FIRMWARE_VERSION_EXPLICIT=0
|
||||
OUTPUT_POLICY_EXPLICIT=0
|
||||
|
||||
ENV_VARIANT_SUFFIX_PATTERN='companion_radio_(wifi_mqtt|serial|wifi|usb|ble)(_ps)?(_fem(on|off))?|companion_radio_ethernet|comp_radio_usb|companion_usb|companion_ble|repeater_bridge_rs232_serial1_lora_ota_no_external_sensors|repeater_bridge_rs232_serial2_lora_ota_no_external_sensors|repeater_bridge_rs232_lora_ota_no_external_sensors|room_server_lora_ota_no_external_sensors|repeater_lora_ota_no_external_sensors|repeater_bridge_rs232_serial1|repeater_bridge_rs232_serial2|repeater_bridge_rs232|repeater_bridge_espnow|repeater_observer_mqtt|repeater_ethernet|room_server_observer_mqtt|room_server_ethernet|terminal_chat|room_server|room_svr|kiss_modem|sensor|repeatr|repeater'
|
||||
ENV_VARIANT_SUFFIX_PATTERN='companion_radio_(wifi_mqtt|serial|wifi|usb|ble)(_ps)?(_fem(on|off))?|companion_radio_ethernet|comp_radio_usb|companion_usb|companion_ble|repeater_bridge_rs232_serial1_lora_ota_no_external_sensors|repeater_bridge_rs232_serial2_lora_ota_no_external_sensors|repeater_bridge_rs232_lora_ota_no_external_sensors|repeater_lora_ota_no_external_sensors|repeater_bridge_rs232_serial1|repeater_bridge_rs232_serial2|repeater_bridge_rs232|repeater_bridge_espnow|repeater_observer_mqtt|repeater_ethernet|room_server_observer_mqtt|room_server_ethernet|terminal_chat|room_server|room_svr|kiss_modem|sensor|repeatr|repeater'
|
||||
BOARD_MODIFIER_WITHOUT_DISPLAY="_without_display"
|
||||
BOARD_MODIFIER_LOGGING="_logging"
|
||||
BOARD_MODIFIER_TFT="_tft"
|
||||
@@ -49,6 +50,7 @@ TAG_PREFIX_REPEATER="repeater"
|
||||
TAG_PREFIX_SENSOR="sensor"
|
||||
SUPPORTED_PLATFORM_PATTERN='ESP32_PLATFORM|NRF52_PLATFORM|STM32_PLATFORM|RP2040_PLATFORM'
|
||||
OUTPUT_DIR="out"
|
||||
ESP32_LORA_OTA_APP_LIMIT=$((0x150000 - 0x10000))
|
||||
FALLBACK_VERSION_PREFIX="dev"
|
||||
FALLBACK_VERSION_DATE_FORMAT='+%Y-%m-%d-%H-%M'
|
||||
|
||||
@@ -183,6 +185,35 @@ for section, options in data:
|
||||
print(f"{env_name}\t{platform}\t{1 if mqtt_enabled else 0}\t{1 if ota_enabled and not ota_disabled else 0}")
|
||||
' "$SUPPORTED_PLATFORM_PATTERN" <<<"$PIO_CONFIG_JSON"
|
||||
)
|
||||
|
||||
# Keep each ordinary repeater environment feature-rich (including external
|
||||
# sensors), and expose a separately named no-external-sensors OTA build for
|
||||
# every ESP32/nRF52 repeater role. These two platforms have a complete apply
|
||||
# path; RP2040 and STM32 do not yet have the required bootloader/apply path.
|
||||
local env_name ota_env
|
||||
local -a base_envs=("${SUPPORTED_PIO_ENVS[@]}")
|
||||
for env_name in "${base_envs[@]}"; do
|
||||
case "${PIO_ENV_PLATFORM_BY_NAME[$env_name]}" in
|
||||
ESP32_PLATFORM|NRF52_PLATFORM) ;;
|
||||
*) continue ;;
|
||||
esac
|
||||
# Generate one lean LoRa-OTA image for each board's standalone repeater
|
||||
# role. Observer, Ethernet, and bridge profiles are separate roles; any
|
||||
# purpose-built OTA versions of those remain explicit PlatformIO targets.
|
||||
case "${env_name,,}" in
|
||||
*_repeater|*_repeater_|*_repeatr|*_repeatr_) ;;
|
||||
*) continue ;;
|
||||
esac
|
||||
ota_env="${env_name%_}_lora_ota_no_external_sensors"
|
||||
if [ -n "${PIO_ENV_PLATFORM_BY_NAME[$ota_env]+x}" ]; then
|
||||
continue
|
||||
fi
|
||||
SUPPORTED_PIO_ENVS+=("$ota_env")
|
||||
PIO_ENV_PLATFORM_BY_NAME["$ota_env"]="${PIO_ENV_PLATFORM_BY_NAME[$env_name]}"
|
||||
PIO_ENV_MQTT_BY_NAME["$ota_env"]=0
|
||||
PIO_ENV_OTA_BY_NAME["$ota_env"]=1
|
||||
PIO_ENV_BUILD_BASE_BY_NAME["$ota_env"]="$env_name"
|
||||
done
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -1371,6 +1402,13 @@ is_known_pio_env() {
|
||||
local env_name=$1
|
||||
local env
|
||||
|
||||
# Synthetic LoRa-OTA environments build their recorded base PlatformIO
|
||||
# environment with a constrained flag profile, so they intentionally do not
|
||||
# appear in `pio project config`.
|
||||
if [ -n "${PIO_ENV_BUILD_BASE_BY_NAME[$env_name]+x}" ]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
for env in "${ALL_PIO_ENVS[@]}"; do
|
||||
if [ "$env" == "$env_name" ]; then
|
||||
return 0
|
||||
@@ -1386,6 +1424,11 @@ is_supported_build_env() {
|
||||
[ -n "${PIO_ENV_PLATFORM_BY_NAME[$env_name]+x}" ]
|
||||
}
|
||||
|
||||
get_pio_build_env() {
|
||||
local env_name=$1
|
||||
echo "${PIO_ENV_BUILD_BASE_BY_NAME[$env_name]:-$env_name}"
|
||||
}
|
||||
|
||||
is_mqtt_bridge_target() {
|
||||
[ "${PIO_ENV_MQTT_BY_NAME[$1]:-0}" == "1" ]
|
||||
}
|
||||
@@ -1527,8 +1570,14 @@ is_lora_ota_build() {
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [[ "$env_name_lc" == *mqtt* ]] \
|
||||
|| is_mqtt_bridge_target "$env_name" \
|
||||
# OTA is deliberately opt-in. The standard repeater remains a normal,
|
||||
# sensor-enabled build; its explicit _lora_ota_no_external_sensors sibling
|
||||
# is the constrained image distributed over LoRa.
|
||||
if [[ "$env_name_lc" != *lora_ota_no_external_sensors ]]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
if is_mqtt_bridge_target "$env_name" \
|
||||
|| [ "${MQTT_BRIDGE_OVERRIDE,,}" == "on" ] \
|
||||
|| [ "${MESHDEBUG_OVERRIDE,,}" == "on" ] \
|
||||
|| [ "${PACKET_LOGGING_OVERRIDE,,}" == "on" ] \
|
||||
@@ -1545,12 +1594,81 @@ is_lora_ota_build() {
|
||||
esac
|
||||
}
|
||||
|
||||
is_esp32_companion_build() {
|
||||
case "${1,,}" in
|
||||
*companion*|*comp_radio*) return 0 ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
requires_esp32_portable_app_slot() {
|
||||
[ "${PIO_ENV_PLATFORM_BY_NAME[$1]:-}" = "ESP32_PLATFORM" ] \
|
||||
&& ! is_esp32_companion_build "$1"
|
||||
}
|
||||
|
||||
apply_esp32_lora_ota_size_profile() {
|
||||
local env_name=$1
|
||||
|
||||
if ! requires_esp32_portable_app_slot "$env_name"; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
# All non-companion ESP32 artifacts must remain installable into the legacy
|
||||
# 0x10000..0x150000 app slot. The WebConfig portal is deliberately omitted.
|
||||
# WiFi/MQTT observers retain their small WiFi updater, while radio-only roles
|
||||
# avoid linking WiFi solely for that updater. Companions retain their target
|
||||
# defaults because they are installed over USB.
|
||||
export PLATFORMIO_BUILD_FLAGS="${PLATFORMIO_BUILD_FLAGS} -DWEBCONFIG_DISABLED=1"
|
||||
if is_mqtt_bridge_target "$env_name"; then
|
||||
export PLATFORMIO_BUILD_FLAGS="${PLATFORMIO_BUILD_FLAGS} -DLIGHTWEIGHT_WIFI_OTA=1"
|
||||
else
|
||||
append_platformio_build_unflags "-DLIGHTWEIGHT_WIFI_OTA=1"
|
||||
export PLATFORMIO_BUILD_FLAGS="${PLATFORMIO_BUILD_FLAGS} -ULIGHTWEIGHT_WIFI_OTA -DDISABLE_WIFI_OTA=1"
|
||||
fi
|
||||
|
||||
}
|
||||
|
||||
apply_lora_ota_no_external_sensors_profile() {
|
||||
local env_name=$1
|
||||
|
||||
if ! is_lora_ota_build "$env_name"; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
# The explicit LoRa-OTA sibling additionally drops optional external sensors.
|
||||
# Its ordinary sibling remains sensor-enabled.
|
||||
# Keep board-integrated GPS support. Several target implementations require
|
||||
# their location provider even when optional external I2C sensors are absent.
|
||||
append_platformio_build_unflags "-DENV_INCLUDE_AHTX0=1 -DENV_INCLUDE_BME280=1 -DENV_INCLUDE_BMP280=1 -DENV_INCLUDE_SHTC3=1 -DENV_INCLUDE_SHT4X=1 -DENV_INCLUDE_LPS22HB=1 -DENV_INCLUDE_INA3221=1 -DENV_INCLUDE_INA219=1 -DENV_INCLUDE_INA226=1 -DENV_INCLUDE_INA260=1 -DENV_INCLUDE_MLX90614=1 -DENV_INCLUDE_VL53L0X=1 -DENV_INCLUDE_BME680=1 -DENV_INCLUDE_BMP085=1 -DENV_INCLUDE_RAK12035=1 -DENV_INCLUDE_BME680_BSEC=1"
|
||||
export PLATFORMIO_BUILD_FLAGS="${PLATFORMIO_BUILD_FLAGS} -UENV_INCLUDE_AHTX0 -UENV_INCLUDE_BME280 -UENV_INCLUDE_BMP280 -UENV_INCLUDE_SHTC3 -UENV_INCLUDE_SHT4X -UENV_INCLUDE_LPS22HB -UENV_INCLUDE_INA3221 -UENV_INCLUDE_INA219 -UENV_INCLUDE_INA226 -UENV_INCLUDE_INA260 -UENV_INCLUDE_MLX90614 -UENV_INCLUDE_VL53L0X -UENV_INCLUDE_BME680 -UENV_INCLUDE_BMP085 -UENV_INCLUDE_RAK12035 -UENV_INCLUDE_BME680_BSEC"
|
||||
}
|
||||
|
||||
append_platformio_build_unflags() {
|
||||
local flags=$1
|
||||
local flag
|
||||
local entry
|
||||
|
||||
for flag in $flags; do
|
||||
case "$flag" in
|
||||
-D*) entry="-D ${flag#-D}" ;;
|
||||
*) entry="$flag" ;;
|
||||
esac
|
||||
if [ -n "${PLATFORMIO_BUILD_UNFLAGS:-}" ]; then
|
||||
PLATFORMIO_BUILD_UNFLAGS+=$'\n'
|
||||
fi
|
||||
PLATFORMIO_BUILD_UNFLAGS+="$entry"
|
||||
done
|
||||
export PLATFORMIO_BUILD_UNFLAGS
|
||||
}
|
||||
|
||||
apply_lora_ota_override() {
|
||||
local env_name=$1
|
||||
|
||||
if is_lora_ota_build "$env_name"; then
|
||||
export PLATFORMIO_BUILD_FLAGS="${PLATFORMIO_BUILD_FLAGS} -DENABLE_OTA=1"
|
||||
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"
|
||||
else
|
||||
append_platformio_build_unflags "-DENABLE_OTA=1"
|
||||
export PLATFORMIO_BUILD_FLAGS="${PLATFORMIO_BUILD_FLAGS} -UENABLE_OTA"
|
||||
fi
|
||||
}
|
||||
@@ -1596,6 +1714,7 @@ for section, options in data:
|
||||
break
|
||||
|
||||
env_flags = shlex.split(os.environ.get("PLATFORMIO_BUILD_FLAGS", ""))
|
||||
env_unflags = os.environ.get("PLATFORMIO_BUILD_UNFLAGS", "").splitlines()
|
||||
|
||||
def print_flags(title, flags):
|
||||
print(f" {title}:")
|
||||
@@ -1607,6 +1726,7 @@ def print_flags(title, flags):
|
||||
|
||||
print_flags("platformio.ini build_flags", config_flags)
|
||||
print_flags("PLATFORMIO_BUILD_FLAGS", env_flags)
|
||||
print_flags("PLATFORMIO_BUILD_UNFLAGS", env_unflags)
|
||||
' "$env_name" <<<"$PIO_CONFIG_JSON"
|
||||
}
|
||||
|
||||
@@ -1624,32 +1744,40 @@ copy_build_output() {
|
||||
|
||||
collect_esp32_artifacts() {
|
||||
local env_name=$1
|
||||
local firmware_filename=$2
|
||||
local pio_env_name=$2
|
||||
local firmware_filename=$3
|
||||
local -a size_check_args=(
|
||||
".pio/build/${pio_env_name}/firmware.bin"
|
||||
".pio/build/${pio_env_name}/partitions.bin"
|
||||
)
|
||||
|
||||
python3 scripts/check_esp32_app_size.py \
|
||||
".pio/build/${env_name}/firmware.bin" \
|
||||
".pio/build/${env_name}/partitions.bin" || return $?
|
||||
pio run -t mergebin -e "$env_name" || return $?
|
||||
copy_build_output ".pio/build/${env_name}/firmware.bin" "${OUTPUT_DIR}/${firmware_filename}.bin" || return $?
|
||||
copy_build_output ".pio/build/${env_name}/firmware-merged.bin" "${OUTPUT_DIR}/${firmware_filename}-merged.bin" || return $?
|
||||
if requires_esp32_portable_app_slot "$env_name"; then
|
||||
size_check_args+=("$ESP32_LORA_OTA_APP_LIMIT")
|
||||
fi
|
||||
|
||||
python3 scripts/check_esp32_app_size.py "${size_check_args[@]}" || return $?
|
||||
pio run -t mergebin -e "$pio_env_name" || return $?
|
||||
copy_build_output ".pio/build/${pio_env_name}/firmware.bin" "${OUTPUT_DIR}/${firmware_filename}.bin" || return $?
|
||||
copy_build_output ".pio/build/${pio_env_name}/firmware-merged.bin" "${OUTPUT_DIR}/${firmware_filename}-merged.bin" || return $?
|
||||
|
||||
# Emit the partition-table signature for OTA partition-compatibility checks.
|
||||
# Keyed by env name so the slim-manifest generator can find it; the firmware
|
||||
# computes the same signature at runtime from its flashed table. Best-effort:
|
||||
# local builds without the script's deps just skip it.
|
||||
if [ -f ".pio/build/${env_name}/partitions.bin" ]; then
|
||||
python3 scripts/partition_signature.py ".pio/build/${env_name}/partitions.bin" > "${OUTPUT_DIR}/${env_name}.partsig" 2>/dev/null || true
|
||||
if [ -f ".pio/build/${pio_env_name}/partitions.bin" ]; then
|
||||
python3 scripts/partition_signature.py ".pio/build/${pio_env_name}/partitions.bin" > "${OUTPUT_DIR}/${env_name}.partsig" 2>/dev/null || true
|
||||
fi
|
||||
}
|
||||
|
||||
collect_nrf52_artifacts() {
|
||||
local env_name=$1
|
||||
local firmware_filename=$2
|
||||
local pio_env_name=$2
|
||||
local firmware_filename=$3
|
||||
|
||||
python3 bin/uf2conv/uf2conv.py ".pio/build/${env_name}/firmware.hex" -c -o ".pio/build/${env_name}/firmware.uf2" -f 0xADA52840 || return $?
|
||||
copy_build_output ".pio/build/${env_name}/firmware.uf2" "${OUTPUT_DIR}/${firmware_filename}.uf2" || return $?
|
||||
if [ -f ".pio/build/${env_name}/firmware.zip" ]; then
|
||||
copy_build_output ".pio/build/${env_name}/firmware.zip" "${OUTPUT_DIR}/${firmware_filename}.zip" || return $?
|
||||
python3 bin/uf2conv/uf2conv.py ".pio/build/${pio_env_name}/firmware.hex" -c -o ".pio/build/${pio_env_name}/firmware.uf2" -f 0xADA52840 || return $?
|
||||
copy_build_output ".pio/build/${pio_env_name}/firmware.uf2" "${OUTPUT_DIR}/${firmware_filename}.uf2" || return $?
|
||||
if [ -f ".pio/build/${pio_env_name}/firmware.zip" ]; then
|
||||
copy_build_output ".pio/build/${pio_env_name}/firmware.zip" "${OUTPUT_DIR}/${firmware_filename}.zip" || return $?
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -1702,16 +1830,17 @@ build_artifacts_exist() {
|
||||
collect_build_artifacts() {
|
||||
local env_name=$1
|
||||
local env_platform=$2
|
||||
local firmware_filename=$3
|
||||
local pio_env_name=$3
|
||||
local firmware_filename=$4
|
||||
|
||||
# Post-build outputs differ by platform, so dispatch to the matching
|
||||
# collector after the main firmware build succeeds.
|
||||
case "$env_platform" in
|
||||
ESP32_PLATFORM)
|
||||
collect_esp32_artifacts "$env_name" "$firmware_filename"
|
||||
collect_esp32_artifacts "$env_name" "$pio_env_name" "$firmware_filename"
|
||||
;;
|
||||
NRF52_PLATFORM)
|
||||
collect_nrf52_artifacts "$env_name" "$firmware_filename"
|
||||
collect_nrf52_artifacts "$env_name" "$pio_env_name" "$firmware_filename"
|
||||
;;
|
||||
STM32_PLATFORM)
|
||||
collect_stm32_artifacts "$env_name" "$firmware_filename"
|
||||
@@ -1764,6 +1893,7 @@ restore_platformio_build_flags() {
|
||||
|
||||
build_firmware() {
|
||||
local env_name=$1
|
||||
local pio_env_name
|
||||
local env_platform
|
||||
local commit_hash
|
||||
local firmware_build_date
|
||||
@@ -1774,7 +1904,9 @@ build_firmware() {
|
||||
local mota_target_id
|
||||
local mota_target_flag=""
|
||||
local original_platformio_build_flags
|
||||
local original_platformio_build_unflags
|
||||
local had_platformio_build_flags=0
|
||||
local had_platformio_build_unflags=0
|
||||
local build_status
|
||||
|
||||
env_platform=$(get_platform_for_env "$env_name")
|
||||
@@ -1782,6 +1914,7 @@ build_firmware() {
|
||||
echo "Unsupported or unknown platform for env: $env_name"
|
||||
return 1
|
||||
fi
|
||||
pio_env_name=$(get_pio_build_env "$env_name")
|
||||
|
||||
commit_hash=$(git rev-parse --short HEAD)
|
||||
firmware_build_date=$(date -u '+%d-%b-%Y')
|
||||
@@ -1815,7 +1948,7 @@ build_firmware() {
|
||||
# per-base published-build counter (FIRMWARE_BUILD_NUMBER) as a 4th version
|
||||
# component so `ota check` can show how many builds behind a node is. The
|
||||
# *filename* stays untagged/un-numbered so assets remain <env>-v<base>-<hash>.
|
||||
# OTA_VARIANT is the env name — it selects the slim per-variant manifest
|
||||
# OTA_VARIANT is the env name - it selects the slim per-variant manifest
|
||||
# (<OTA_MANIFEST_BASE>/<OTA_VARIANT>.json) that the observer pull-OTA fetches.
|
||||
local embedded_variant_tag=""
|
||||
case "$env_name" in
|
||||
@@ -1838,6 +1971,12 @@ build_firmware() {
|
||||
else
|
||||
original_platformio_build_flags=""
|
||||
fi
|
||||
if [ "${PLATFORMIO_BUILD_UNFLAGS+x}" ]; then
|
||||
had_platformio_build_unflags=1
|
||||
original_platformio_build_unflags=$PLATFORMIO_BUILD_UNFLAGS
|
||||
else
|
||||
original_platformio_build_unflags=""
|
||||
fi
|
||||
|
||||
export PLATFORMIO_BUILD_FLAGS="${original_platformio_build_flags} -DFIRMWARE_BUILD_DATE='\"${firmware_build_date}\"' -DFIRMWARE_BUILD_EPOCH=${firmware_build_epoch} -DFIRMWARE_VERSION='\"${embedded_version_string}\"' -DOTA_VARIANT='\"${env_name}\"'${mota_target_flag}"
|
||||
disable_debug_flags
|
||||
@@ -1845,18 +1984,25 @@ build_firmware() {
|
||||
apply_mqtt_bridge_override
|
||||
disable_usb_logging_for_mqtt "$env_name"
|
||||
apply_lora_ota_override "$env_name"
|
||||
apply_esp32_lora_ota_size_profile "$env_name"
|
||||
apply_lora_ota_no_external_sensors_profile "$env_name"
|
||||
apply_radio_overrides
|
||||
apply_firmware_profile_overrides
|
||||
|
||||
print_build_flags "$env_name"
|
||||
pio run -e "$env_name"
|
||||
pio run -e "$pio_env_name"
|
||||
build_status=$?
|
||||
if [ "$build_status" -eq 0 ]; then
|
||||
collect_build_artifacts "$env_name" "$env_platform" "$firmware_filename"
|
||||
collect_build_artifacts "$env_name" "$env_platform" "$pio_env_name" "$firmware_filename"
|
||||
build_status=$?
|
||||
fi
|
||||
|
||||
restore_platformio_build_flags "$had_platformio_build_flags" "$original_platformio_build_flags"
|
||||
if [ "$had_platformio_build_unflags" -eq 1 ]; then
|
||||
export PLATFORMIO_BUILD_UNFLAGS="$original_platformio_build_unflags"
|
||||
else
|
||||
unset PLATFORMIO_BUILD_UNFLAGS
|
||||
fi
|
||||
return "$build_status"
|
||||
}
|
||||
|
||||
|
||||
+10
-4
@@ -38,15 +38,21 @@ location and change it when necessary.
|
||||
|
||||
Both paths require:
|
||||
|
||||
- An OTA-enabled repeater build whose artifact filename contains `-ota-` on the source, destination, and
|
||||
every intermediate node. The `-ota-` stamp confirms that LoRa OTA is compiled into the build. Logging,
|
||||
- An OTA-enabled repeater build whose artifact filename contains `-ota-` on the destination and every
|
||||
intermediate repeater. The `-ota-` stamp confirms that LoRa OTA is compiled into the build. Logging,
|
||||
MQTT, and untagged builds cannot perform LoRa OTA; use WiFi or USB to replace them first.
|
||||
- A source node connected to the computer by USB serial.
|
||||
- An ESP32 MeshCore companion connected to the computer by USB serial or WiFi as described below.
|
||||
- Overlapping `tempradio` windows on the source, destination, and every repeater needed between them.
|
||||
|
||||
LoRa OTA packets are generated, received, and relayed only while `tempradio` is active. If any required
|
||||
window closes, the transfer stops making progress and can resume during a later overlapping window.
|
||||
|
||||
`build.sh` provides a `*_repeater_lora_ota_no_external_sensors` build for every standalone ESP32 and nRF52
|
||||
repeater target. The normal repeater build keeps its external-sensor support. The `-ota-` sibling omits
|
||||
optional external I2C environmental sensors to preserve the update workspace, while retaining board-native
|
||||
features such as its display, buttons, battery monitoring, and integrated GPS. RP2040 and STM32 repeaters do
|
||||
not currently have a safe self-apply path and therefore cannot install LoRa firmware updates.
|
||||
|
||||
### Choose the source radio
|
||||
|
||||
Use an ESP32 **MeshCore companion** as the source node. It receives the update folder from the computer,
|
||||
@@ -148,7 +154,7 @@ 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/GPS sensor packages so the delta fits the safe in-place workspace.
|
||||
omits optional external environmental sensor packages so the delta fits the safe in-place workspace.
|
||||
|
||||
### 3. Build and check the in-place delta
|
||||
|
||||
|
||||
@@ -90,8 +90,9 @@ EndF trailer (fixed 56 bytes):
|
||||
off 24 32 hw_id NUL-padded ASCII hardware tag (brick-safety), e.g. "RAK4631" ("" = unknown)
|
||||
```
|
||||
|
||||
- **Self-describing identity.** `pio_endf.py` computes `target_id` from the PlatformIO env name itself (so
|
||||
it's correct even without `build.sh`'s `-D MOTA_TARGET_ID`), `hw_id` from `MOTA_HW_ID`, and `fw_version`
|
||||
- **Self-describing identity.** `pio_endf.py` uses `build.sh`'s `MOTA_TARGET_ID` when present (required for
|
||||
virtual LoRa-OTA build names), otherwise it computes `target_id` from the PlatformIO env name. It reads
|
||||
`hw_id` from `MOTA_HW_ID` and `fw_version`
|
||||
from `FIRMWARE_VERSION`. The device reads them back (`ota_self_firmware()`), so a node's advertised
|
||||
identity is correct regardless of how it was built - and the packaging tool reads them straight from a raw
|
||||
`.bin` (no `--target-env`/`--fw-version` flags, no reliance on filenames; Section 9, Section 13). A dev build with no
|
||||
@@ -109,9 +110,11 @@ The "reconstructed image" referenced by the manifest is the full `BODY || EndF`
|
||||
ESP32 companion firmware is installed over USB and is exempt from the portable-slot limit. Every other
|
||||
ESP32 artifact, including room, sensor, and repeater roles, must fit the legacy slot from `0x10000` up to
|
||||
`0x150000` (`0x140000`, 1,310,720 bytes), including the 56-byte `EndF` trailer. The build checks both that
|
||||
limit and the target's actual app partition. For every ESP32 repeater, `build.sh` also exposes an explicit
|
||||
`*_lora_ota_no_external_sensors` artifact: the ordinary repeater remains sensor-enabled, while that sibling
|
||||
also disables optional external-sensor drivers for LoRa distribution.
|
||||
limit and the target's actual app partition. For every standalone ESP32 and nRF52 repeater, `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. Integrated
|
||||
GPS and other board-native telemetry remain enabled. RP2040 and STM32 targets are not offered because those
|
||||
platforms do not yet have a safe bootloader/apply path.
|
||||
|
||||
> **Implementer note:** the bootloader (and any non-Arduino consumer) MUST locate the body extent by
|
||||
> scanning for `EndF`, never by trusting a stored size - see the bootloader contract in Section 12.
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Fail when an ESP32 app image (including EndF) exceeds its app partition."""
|
||||
"""Fail when an ESP32 app image (including EndF) exceeds its app partition.
|
||||
|
||||
An optional third argument additionally enforces a portable maximum image size.
|
||||
MeshCore's LoRa-OTA ESP32 artifacts use 0x140000 bytes: the legacy app slot
|
||||
starts at 0x10000 and the next OTA slot begins at 0x150000.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -34,12 +39,16 @@ def app_partition_size(table: bytes) -> tuple[str, int]:
|
||||
|
||||
|
||||
def main() -> int:
|
||||
if len(sys.argv) != 3:
|
||||
print(f"usage: {Path(sys.argv[0]).name} FIRMWARE.BIN PARTITIONS.BIN", file=sys.stderr)
|
||||
if len(sys.argv) not in (3, 4):
|
||||
print(
|
||||
f"usage: {Path(sys.argv[0]).name} FIRMWARE.BIN PARTITIONS.BIN [MAX_IMAGE_BYTES]",
|
||||
file=sys.stderr,
|
||||
)
|
||||
return 2
|
||||
|
||||
firmware_path = Path(sys.argv[1])
|
||||
partition_path = Path(sys.argv[2])
|
||||
portable_limit = int(sys.argv[3], 0) if len(sys.argv) == 4 else None
|
||||
try:
|
||||
image_size = firmware_path.stat().st_size
|
||||
label, limit = app_partition_size(partition_path.read_bytes())
|
||||
@@ -55,10 +64,19 @@ def main() -> int:
|
||||
)
|
||||
return 1
|
||||
|
||||
print(
|
||||
f"ESP32 app image fits {label}: {image_size}/{limit} bytes "
|
||||
f"({limit - image_size} bytes free)"
|
||||
)
|
||||
if portable_limit is not None and image_size > portable_limit:
|
||||
print(
|
||||
f"ESP32 app image is {image_size} bytes, exceeding portable LoRa-OTA "
|
||||
f"slot 0x10000..0x150000 ({portable_limit} bytes) by "
|
||||
f"{image_size - portable_limit} bytes",
|
||||
file=sys.stderr,
|
||||
)
|
||||
return 1
|
||||
|
||||
message = f"ESP32 app image fits {label}: {image_size}/{limit} bytes ({limit - image_size} bytes free)"
|
||||
if portable_limit is not None:
|
||||
message += f"; portable LoRa-OTA slot: {portable_limit - image_size} bytes free"
|
||||
print(message)
|
||||
return 0
|
||||
|
||||
|
||||
|
||||
+1105
-1109
File diff suppressed because it is too large.
Load diff
@@ -106,7 +106,11 @@ def _firmware_ident():
|
||||
PlatformIO env name (so it's correct even without build.sh's -D MOTA_TARGET_ID), hw_id from MOTA_HW_ID,
|
||||
fw_version from FIRMWARE_VERSION (a -D if set, else the example's header #define)."""
|
||||
import re
|
||||
target_id = ml.target_id_for_env(env["PIOENV"]) # noqa: F821
|
||||
target_define = (_cppdef("MOTA_TARGET_ID") or "").strip()
|
||||
try:
|
||||
target_id = int(target_define, 0) if target_define else ml.target_id_for_env(env["PIOENV"]) # noqa: F821
|
||||
except ValueError:
|
||||
target_id = ml.target_id_for_env(env["PIOENV"]) # noqa: F821
|
||||
hw_id = (_cppdef("MOTA_HW_ID") or "").replace("\\", "").strip().strip('"').strip("'")
|
||||
if not hw_id:
|
||||
hw_id = ml.hardware_id_for_env(env["PIOENV"]) # noqa: F821
|
||||
|
||||
@@ -224,25 +224,6 @@ build_src_filter = ${rak4631.build_src_filter}
|
||||
+<helpers/ui/SSD1306Display.cpp>
|
||||
+<../examples/simple_room_server>
|
||||
|
||||
; Explicit lean alternative to the full-sensor, non-OTA room server above.
|
||||
[env:RAK_4631_room_server_lora_ota_no_external_sensors]
|
||||
extends = rak4631
|
||||
build_flags =
|
||||
${rak4631.build_flags}
|
||||
${nrf52_no_external_sensors.build_flags}
|
||||
-D DISPLAY_CLASS=SSD1306Display
|
||||
-D ADVERT_NAME='"Test Room"'
|
||||
-D ADVERT_LAT=0.0
|
||||
-D ADVERT_LON=0.0
|
||||
-D ADMIN_PASSWORD='"password"'
|
||||
-D ROOM_PASSWORD='"hello"'
|
||||
build_src_filter = ${rak4631.build_src_filter}
|
||||
+<helpers/ui/SSD1306Display.cpp>
|
||||
+<../examples/simple_room_server>
|
||||
lib_deps =
|
||||
${nrf52_lora_ota.lib_deps}
|
||||
adafruit/Adafruit SSD1306 @ ^2.5.13
|
||||
|
||||
[env:RAK_4631_room_server_ethernet]
|
||||
extends = rak4631
|
||||
build_flags =
|
||||
|
||||
Reference in new issue
Block a user