Enable dual WiFi OTA slots for the smaller 4 MiB NimBLE trials

This commit is contained in:
mikecarper
2026-09-08 12:36:43 -07:00
parent 468cc3cf56
commit b68aa1c685
4 changed files with 46 additions and 1 deletions
+8
View File
@@ -140,6 +140,14 @@ OUTPUT_DIR=.releases/contact-cache-v3 bash build.sh build-firmware \
The firmware RAM guard remains enabled. A successful linked budget is distinct
from live free heap and physical Bluetooth/LoRa qualification on each board.
The smaller NimBLE applications let the CT62, XIAO C3 and Generic ESP-NOW
trials use two 1.5 MiB WiFi OTA slots on 4 MiB flash. Their 896 KiB SPIFFS and
coredump stay at the same addresses as the ordinary `huge_app.csv` Full
layout. Install the trial's merged image over USB first to install its
partition table. Subsequent WiFi updates must fit the new slots. An
application-only update from the ordinary single-slot Full layout cannot
change that layout or enable WiFi OTA.
## Regression checks
```sh
+3
View File
@@ -77,6 +77,7 @@ lib_ignore = ${companion_nimble_trial.lib_ignore}
[env:Heltec_ct62_ram_trial_companion_radio_full_nimble]
extends = env:Heltec_ct62_companion_radio_ble
board_build.full_partitions = variants/dual_ota_1536k.csv
build_flags =
${env:Heltec_ct62_companion_radio_ble.build_flags}
${companion_nimble_ram_trial.build_flags}
@@ -87,6 +88,7 @@ lib_ignore = ${companion_nimble_trial.lib_ignore}
[env:Xiao_C3_ram_trial_companion_radio_full_nimble]
extends = env:Xiao_C3_companion_radio_wifi
board_build.full_partitions = variants/dual_ota_1536k.csv
build_flags =
${env:Xiao_C3_companion_radio_wifi.build_flags}
${companion_nimble_ram_trial.build_flags}
@@ -97,6 +99,7 @@ lib_ignore = ${companion_nimble_trial.lib_ignore}
[env:Generic_ESPNOW_ram_trial_companion_radio_full_nimble]
extends = env:Generic_ESPNOW_comp_radio_usb
board_build.full_partitions = variants/dual_ota_1536k.csv
build_flags =
${env:Generic_ESPNOW_comp_radio_usb.build_flags}
${companion_nimble_ram_trial.build_flags}
+9 -1
View File
@@ -12,6 +12,10 @@ PRESERVED_FULL_PARTITION_TABLES = {
"variants/sensecap_indicator-espnow/dual_ota_6400k_preserve_spiffs.csv",
}
# Explicitly selected by the smaller NimBLE capacity trials. The normal
# 4 MiB Full Companion still needs huge_app.csv for its larger application.
OPT_IN_FULL_PARTITION_TABLES = {"variants/dual_ota_1536k.csv"}
def parse_flash_size(value):
text = str(value).strip().upper()
@@ -39,10 +43,14 @@ if os.environ.get("MESHCORE_ESP32_FULL_BUILD") == "1":
flash_size = parse_flash_size(board.get("upload.flash_size", "4MB"))
requested_partitions = os.environ.get(
"MESHCORE_ESP32_FULL_PARTITION_TABLE", ""
).strip()
).strip() or str(board.get("build.full_partitions", "")).strip()
required_partitions = preserved_full_partition_table(
requested_partitions
)
if requested_partitions in OPT_IN_FULL_PARTITION_TABLES:
if flash_size < 4 * 1024 * 1024:
raise ValueError("Full dual-OTA trial requires at least 4 MiB flash")
required_partitions = requested_partitions
if requested_partitions and not required_partitions:
raise ValueError(
"unsupported required Full partition table: "
+26
View File
@@ -170,6 +170,32 @@ class Esp32FullPartitionTest(unittest.TestCase):
)
self.assertEqual(board.values["build.partitions"], "huge_app.csv")
def test_explicit_small_full_trial_uses_dual_ota_with_large_spiffs(self):
table = "variants/dual_ota_1536k.csv"
board, _output = apply_policy(
{
"upload.flash_size": "4MB",
"build.mcu": "esp32c3",
"build.partitions": "huge_app.csv",
"build.full_partitions": table,
}
)
self.assertEqual(board.values["build.partitions"], table)
# Retain the huge_app SPIFFS/coredump placement and enough storage for
# two complete 350-contact snapshots, rather than the 64 KiB table.
partitions = (ROOT / table).read_text()
self.assertIn("spiffs, data, spiffs, 0x310000,0xE0000", partitions)
self.assertIn("app1, app, ota_1, 0x190000,0x180000", partitions)
def test_explicit_small_full_trial_rejects_insufficient_flash(self):
with self.assertRaisesRegex(ValueError, "at least 4 MiB"):
apply_policy(
{
"upload.flash_size": "2MB",
"build.full_partitions": "variants/dual_ota_1536k.csv",
}
)
def test_tbeam_1w_factory_layout_keeps_precedence(self):
board, _output = apply_policy(
{