From 00a67c34b143cdce3c7e56b1c481e8e467826f8d Mon Sep 17 00:00:00 2001 From: mikecarper Date: Tue, 11 Aug 2026 15:30:21 -0700 Subject: [PATCH] Fix nRF52 full Companion packaging --- build.sh | 16 ++++++++++------ docs/companion_radio_full.md | 4 +++- tools/mota/pio_endf.py | 26 +++++++++++++++++++++++--- 3 files changed, 36 insertions(+), 10 deletions(-) diff --git a/build.sh b/build.sh index b0e87775..0ac16701 100755 --- a/build.sh +++ b/build.sh @@ -2265,15 +2265,19 @@ apply_nrf52_lora_ota_build_recipe() { local env_name=$1 local pio_env_name=$2 - if [ "${PIO_ENV_PLATFORM_BY_NAME[$env_name]:-}" != "NRF52_PLATFORM" ] \ - || ! is_lora_ota_build "$env_name"; then + if [ "${PIO_ENV_PLATFORM_BY_NAME[$env_name]:-}" != "NRF52_PLATFORM" ]; then + return 0 + fi + if ! is_lora_ota_build "$env_name" \ + && ! is_nrf52_companion_radio_full_target "$env_name"; then return 0 fi - # Synthetic OTA aliases build an ordinary repeater PlatformIO environment - # with OTA flags. Most nRF52 repeaters already include this recipe, but a new - # board may not. Add only the missing pieces so ENABLE_OTA never reaches the - # linker without its implementation or the EndF trailer/zip post-build step. + # Synthetic OTA aliases and full Companions build an ordinary PlatformIO + # environment with an OTA overlay. Most nRF52 bases already include this + # recipe, but a new board may not. Add only the missing pieces so ENABLE_OTA + # never reaches the linker without its implementation or the EndF + # trailer/zip post-build step. if ! pio_env_option_contains "$pio_env_name" build_src_filter "helpers/ota/"; then append_platformio_build_src_filter "+" fi diff --git a/docs/companion_radio_full.md b/docs/companion_radio_full.md index 87c2f010..d45e9030 100644 --- a/docs/companion_radio_full.md +++ b/docs/companion_radio_full.md @@ -77,7 +77,9 @@ this combined profile because of internal DRAM limits. The nRF52 target inherits the board's ordinary USB Companion installation format and adds BLE plus the serial mOTA source. It does not enable an SD cache or any other board-specific storage behavior; host files are streamed as they -are requested. +are requested. Its image is bounded by the board's normal application region, +not the smaller OTAFIX in-place workspace reserved for firmware that can update +itself. ## Interfaces diff --git a/tools/mota/pio_endf.py b/tools/mota/pio_endf.py index 5f046784..e7124c0e 100644 --- a/tools/mota/pio_endf.py +++ b/tools/mota/pio_endf.py @@ -56,6 +56,16 @@ def _cppdef(name): # value of a -D= return None +def _board_maximum_size(build_env): + """Return PlatformIO's resolved application-size limit, including per-env overrides.""" + try: + value = build_env.BoardConfig().get("upload.maximum_size") + size = int(str(value), 0) + return size if size > 0 else None + except (AttributeError, TypeError, ValueError): + return None + + def _version_from_headers(): """FIRMWARE_VERSION is a header ``#define`` in the example (upstream MeshCore convention), not a -D, so _cppdef() can't see it and the EndF version would otherwise default to 0. Read it from the source WITHOUT @@ -149,9 +159,19 @@ def _append_endf_hex(source, target, env): # Intel-HEX path (nRF52: app f ident = _firmware_ident() out, h8 = ml.ensure_endf(body, ident) sd_backed = _cppdef("OTA_SD_STORE") is not None - image_limit = ml.NRF52_SD_APP_MEMORY if sd_backed else ml.NRF52_INPLACE_MEMORY - limit_name = "SD application" if sd_backed else "in-place" - if len(out) > image_limit: + seeder_only = _cppdef("OTA_SEEDER_ONLY") is not None + if seeder_only: + # A source-only full Companion never stages or applies an update to + # itself, so it does not need to fit in OTAFIX's in-place workspace. + # It must still fit the board/env's resolved flash application region. + image_limit = _board_maximum_size(env) + if image_limit is None: + raise RuntimeError("nRF52 seeder-only application limit is unavailable") + limit_name = "application" + else: + image_limit = ml.NRF52_SD_APP_MEMORY if sd_backed else ml.NRF52_INPLACE_MEMORY + limit_name = "SD application" if sd_backed else "in-place" + if image_limit is not None and len(out) > image_limit: raise RuntimeError(f"nRF52 OTA image is {len(out)} bytes; {limit_name} limit is " f"{image_limit} bytes") if len(out) == len(body):