diff --git a/build_legacy.sh b/build_legacy.sh index ff3d69999..2d70f1c54 100755 --- a/build_legacy.sh +++ b/build_legacy.sh @@ -4120,6 +4120,12 @@ apply_lora_ota_no_external_sensors_profile() { for flag in $voltage_monitor_flags; do retain_overrides+=" -D${flag}=1" done + # Keep only this image's readable OTA name in the reduced RAK profiles. + # A non-OTA-prefixed marker is deliberately not copied from a reduced + # logical recipe into the Full sensor pass by apply_logical_ota_tuning_flags. + retain_overrides+=" -DMESH_OTA_LOCAL_TARGET_NAME_ONLY=1" + record_build_reduction \ + "ota target-name catalog omitted; local target name and numeric OTA matching retained" export PLATFORMIO_BUILD_FLAGS="${PLATFORMIO_BUILD_FLAGS}${omit_overrides}${retain_overrides}" if is_rak_gps_retaining_ota_target "$env_name"; then record_build_reduction \ diff --git a/docs/firmware_picker.md b/docs/firmware_picker.md index f9e1ea8c9..97e783693 100644 --- a/docs/firmware_picker.md +++ b/docs/firmware_picker.md @@ -581,6 +581,9 @@ The legacy `no_external_sensors` profile is labeled **Receives LoRa OTA - Reduced optional sensors** on the same button. On RAK3401 and RAK4631 this compact profile uses internal storage without an external storage board. The reduction does not disable generic I2C or unrelated board-integrated peripherals. +These reduced RAK profiles keep their own readable OTA target name and omit +the all-board name catalog. Updates for other targets display numeric IDs; +OTA compatibility checks and transfers are unchanged. Reduced RAK3401 and RAK4631 targets retain INA219/INA226/INA260/INA3221 as voltage/current entries in the optional sensor table. They are not the only I2C users: the SSD1306 OLED, supported autodiscovered RTCs, and RAK12500 GPS remain diff --git a/scripts/nrf52_flash_trim.py b/scripts/nrf52_flash_trim.py index a7116487f..90a48c3f4 100644 --- a/scripts/nrf52_flash_trim.py +++ b/scripts/nrf52_flash_trim.py @@ -141,6 +141,20 @@ def trim_policy(platform, mcu, env_name, src_filter, flags=(), cppdefines=(), if ota_name_mode != "front-coded": raise ValueError("unsupported nRF52 OTA name trim policy: " + ota_name_mode) definitions = dict(TRIM_DEFINES, OTA_TARGET_NAME_FRONT_CODED=1) + if _enabled(values.get("MESH_OTA_LOCAL_TARGET_NAME_ONLY")): + # Release overlays can compile a different PIOENV from their deployed + # OTA identity. Keep the logical name/ID in that case; direct builds + # use the same PIOENV hash as the ordinary EndF/board identity code. + target_id = values.get("MOTA_TARGET_ID") + if target_id is None: + target_id = "0x%08x" % int.from_bytes( + hashlib.sha256(str(env_name).encode()).digest()[:4], "little") + definitions.update( + OTA_TARGET_NAME_TABLE=0, + OTA_LOCAL_TARGET_ID=target_id, + OTA_LOCAL_TARGET_NAME="OTA_VARIANT" if values.get("OTA_VARIANT") + else '"%s"' % env_name, + ) # Explicit board/user flags retain their value. In particular never undo # an opt-out or a board-specific USB configuration by re-defining it. return {name: value for name, value in definitions.items() if name not in values} @@ -241,6 +255,9 @@ def install(build_env): build_env.get("SRC_FILTER", ()), flags, cppdefines) if not definitions: return + local_name = definitions.get("OTA_LOCAL_TARGET_NAME", "") + if local_name.startswith('"') and local_name.endswith('"'): + definitions["OTA_LOCAL_TARGET_NAME"] = build_env.StringifyMacro(local_name[1:-1]) build_env["MESH_NRF52_FLASH_TRIM_ACTIVE"] = True build_env.AppendUnique(CPPDEFINES=list(definitions.items())) hook_path = build_env.subst("$PROJECT_DIR/scripts/nrf52_vl53l0x_const.py") @@ -249,7 +266,10 @@ def install(build_env): hook_spec.loader.exec_module(hook) hook.install(build_env) if infrastructure: - print("nRF52 infrastructure flash trim: compact crypto, unused USB device classes/GPS cache/vendor splash, compact OTA target names") + names = ("local OTA target name only" if definitions.get("OTA_TARGET_NAME_TABLE") == 0 + or flag_defines(flags, cppdefines).get("OTA_TARGET_NAME_TABLE") == "0" + else "compact OTA target names") + print("nRF52 infrastructure flash trim: compact crypto, unused USB device classes/GPS cache/vendor splash, " + names) else: print("nRF52 Companion flash trim: small crypto table, unused USB device classes/GPS cache/vendor splash; fonts and Reader retained") hook_path = build_env.subst("$PROJECT_DIR/scripts/nrf52_gnss_version_trim.py") diff --git a/test/test_build_profiles.sh b/test/test_build_profiles.sh index 4b62241ce..05fd4c398 100644 --- a/test/test_build_profiles.sh +++ b/test/test_build_profiles.sh @@ -1900,11 +1900,17 @@ verify_reduced_sensor_flags() { [[ "$PLATFORMIO_BUILD_FLAGS" == *-UENV_INCLUDE_BME280* ]] \ || fail "$env_name did not omit environmental sensors" if [ "$expect_ina" = 1 ]; then + [[ "$PLATFORMIO_BUILD_FLAGS" == *-DMESH_OTA_LOCAL_TARGET_NAME_ONLY=1* ]] \ + || fail "$env_name retained the all-board OTA name catalog" + [[ " ${BUILD_REDUCTIONS[*]} " == *"ota target-name catalog omitted"* ]] \ + || fail "$env_name did not declare its OTA name reduction" [[ "$PLATFORMIO_BUILD_FLAGS" == *-DENV_INCLUDE_INA219=1* ]] \ || fail "$env_name did not retain INA monitor support" [[ "$PLATFORMIO_BUILD_FLAGS" != *-UENV_INCLUDE_INA219* ]] \ || fail "$env_name both retained and omitted INA monitor support" else + [[ "$PLATFORMIO_BUILD_FLAGS" != *MESH_OTA_LOCAL_TARGET_NAME_ONLY* ]] \ + || fail "$env_name lost OTA names outside the reduced RAK profile" [[ "$PLATFORMIO_BUILD_FLAGS" == *-UENV_INCLUDE_INA219* ]] \ || fail "$env_name incorrectly retained INA monitor support" fi @@ -1914,6 +1920,10 @@ verify_reduced_sensor_flags \ RAK_3401_repeater_lora_ota_no_external_sensors 1 verify_reduced_sensor_flags \ RAK_3401_room_server_lora_ota_no_external_sensors 1 +verify_reduced_sensor_flags \ + RAK_4631_repeater_lora_ota_no_external_sensors 1 +verify_reduced_sensor_flags \ + RAK_4631_sensor_lora_ota_no_external_sensors 1 verify_reduced_sensor_flags \ Heltec_t114_repeater_lora_ota_no_external_sensors 0 diff --git a/test/test_nrf52_flash_trim.py b/test/test_nrf52_flash_trim.py index 09e8ae260..d1cd69251 100644 --- a/test/test_nrf52_flash_trim.py +++ b/test/test_nrf52_flash_trim.py @@ -67,6 +67,26 @@ class Nrf52FlashTrimTest(unittest.TestCase): self.assertNotIn("SSD1306_NO_SPLASH", defines) self.assertEqual(TRIM.flag_defines("-DMESH_NRF52_FLASH_TRIM=1 -UMESH_NRF52_FLASH_TRIM")["MESH_NRF52_FLASH_TRIM"], None) + def test_local_names_honor_overlay_identity_and_explicit_defines(self): + flags = ('-DMESH_OTA_LOCAL_TARGET_NAME_ONLY=1 ' + '-DMOTA_TARGET_ID=0x29a0da19 ' + '-DOTA_VARIANT=\'"RAK_4631_repeater_lora_ota_no_external_sensors"\'') + defines = self.policy(flags=flags) + self.assertEqual(defines["OTA_TARGET_NAME_TABLE"], 0) + self.assertEqual(defines["OTA_LOCAL_TARGET_ID"], "0x29a0da19") + self.assertEqual(defines["OTA_LOCAL_TARGET_NAME"], "OTA_VARIANT") + explicit = self.policy(flags=flags + ' -DOTA_TARGET_NAME_TABLE=1 ' + '-DOTA_LOCAL_TARGET_ID=7 -DOTA_LOCAL_TARGET_NAME=custom_name') + for name in ("OTA_TARGET_NAME_TABLE", "OTA_LOCAL_TARGET_ID", "OTA_LOCAL_TARGET_NAME"): + self.assertNotIn(name, explicit) + for flags in ("", "-DMESH_OTA_LOCAL_TARGET_NAME_ONLY=0"): + self.assertNotIn("OTA_TARGET_NAME_TABLE", self.policy(flags=flags)) + + def test_local_names_do_not_change_companion_or_unqualified_roles(self): + for options in (dict(name="RAK_4631_companion_radio_full"), + dict(role="custom"), dict(platform="espressif32", mcu="esp32")): + self.assertEqual(self.policy(flags="-DMESH_OTA_LOCAL_TARGET_NAME_ONLY=1", **options), {}) + def test_cc310_archive_is_exact_and_requires_supported_mcu_and_enabled_crypto(self): with tempfile.TemporaryDirectory() as temp_dir: archive = Path(temp_dir) / "libraries/Adafruit_nRFCrypto" / TRIM.CC310_ARCHIVE diff --git a/test/test_ota_target_names.py b/test/test_ota_target_names.py index 4405e8bf0..3e373ec2a 100644 --- a/test/test_ota_target_names.py +++ b/test/test_ota_target_names.py @@ -1,6 +1,8 @@ """All OTA names survive front coding; original Companion API stays unchanged.""" import re import os +import configparser +import importlib.util import shutil import subprocess import sys @@ -237,6 +239,29 @@ class OtaTargetNameTest(unittest.TestCase): f'-DOTA_LOCAL_TARGET_NAME="{local_name}"', ], ids), [local_name, "?", "?"]) + def test_reduced_rak_policy_compiles_with_exact_local_names_and_ids(self): + spec = importlib.util.spec_from_file_location("ota_name_trim", ROOT / "scripts/nrf52_flash_trim.py") + trim = importlib.util.module_from_spec(spec) + spec.loader.exec_module(trim) + for board, suffix in (("3401", ""), ("4631", ""), + ("4631", "_bridge_rs232_serial1"), ("4631", "_bridge_rs232_serial2")): + target = f"RAK_{board}_repeater{suffix}_lora_ota_no_external_sensors" + config = configparser.ConfigParser(interpolation=None, inline_comment_prefixes=(";",)) + config.read(ROOT / f"variants/rak{board}/platformio.ini") + flags = config["env:" + target]["build_flags"] + # Interpolated hardware flags do not influence the name policy. + flags = re.sub(r"\$\{[^}]+\}", "", flags) + target_id = gen_targets.ml.target_id_for_env(target) + definitions = trim.flag_defines(flags) + definitions.update(trim.trim_policy("nordicnrf52", "nrf52840", target, + "+<../examples/simple_repeater>", flags)) + name_flags = [f"-D{name}={value}" for name, value in definitions.items() + if name in ("OTA_TARGET_NAME_TABLE", "OTA_TARGET_NAME_FRONT_CODED", + "OTA_LOCAL_TARGET_ID", "OTA_LOCAL_TARGET_NAME")] + with self.subTest(target=target): + self.assertEqual(self.compile_and_lookup(name_flags, + [target_id, self.rows[0][0], 0]), [target, "?", "?"]) + def test_cpp_decoder_fails_closed_on_invalid_records_and_trailing_bytes(self): from zopfli_compress import raw_deflate_compress rows = gen_targets.target_rows(["hello"]) diff --git a/variants/rak3401/platformio.ini b/variants/rak3401/platformio.ini index aed1e4878..1b87797fd 100644 --- a/variants/rak3401/platformio.ini +++ b/variants/rak3401/platformio.ini @@ -89,7 +89,7 @@ build_flags = -D ENABLE_OTA=1 -D OTA_FLASH_STORE=1 -D OTA_FOLDER_SERIAL - -D OTA_TARGET_NAME_TABLE=0 ; IDs remain authoritative; omit the 540-entry display-name table + -D OTA_TARGET_NAME_TABLE=0 ; IDs remain authoritative; omit the all-board display-name catalog -D OTA_LOCAL_TARGET_ID=0x2fa509c1 -D OTA_LOCAL_TARGET_NAME='"RAK_3401_repeater_lora_ota_no_external_sensors"' -D OTA_FETCH_PIPELINE=4 ; adaptive one-packet flights grow 1 -> 2 -> 3 -> 4 on a clean link diff --git a/variants/rak4631/platformio.ini b/variants/rak4631/platformio.ini index a43f0225d..bf6221781 100644 --- a/variants/rak4631/platformio.ini +++ b/variants/rak4631/platformio.ini @@ -213,6 +213,7 @@ extra_scripts = ${rak4631_repeater_reduced_ota_base.extra_scripts} build_flags = ${rak4631_repeater_reduced_ota_base.build_flags} ${nrf52_rak4631_unified_ota.build_flags} + -D MESH_OTA_LOCAL_TARGET_NAME_ONLY=1 ; keep this target's name without the all-board catalog build_src_filter = ${rak4631_repeater_reduced_ota_base.build_src_filter} lib_deps = ${rak4631_repeater_reduced_ota_base.lib_deps} @@ -302,6 +303,7 @@ extends = rak4631 build_flags = ${rak4631.build_flags} ${nrf52_reduced_sensors_keep_ina.build_flags} + -D MESH_OTA_LOCAL_TARGET_NAME_ONLY=1 -D DISPLAY_CLASS=SSD1306Display -D ADVERT_NAME='"RS232 Bridge"' -D ADVERT_LAT=0.0 @@ -354,6 +356,7 @@ extends = rak4631 build_flags = ${rak4631.build_flags} ${nrf52_reduced_sensors_keep_ina_gps.build_flags} + -D MESH_OTA_LOCAL_TARGET_NAME_ONLY=1 -D DISPLAY_CLASS=SSD1306Display -D ADVERT_NAME='"RS232 Bridge"' -D ADVERT_LAT=0.0