Publish 1.18.0.1 release catalogs and bootloader picker choices

This commit is contained in:
mikecarper committed 2026-10-24 13:58:25 -07:00
1 parent 03214a16df
commit ec478c30ed
17 files changed
+37893 -8184

No files matched your search

+13 -10
View File
@@ -1,24 +1,25 @@
# Releasing Firmware
For 1.17.1.9 and later, build the complete qualified local release from a
For 1.18.0.1, build the complete qualified local release from a
clean, committed checkout. The builder runs one PlatformIO process at a time:
```bash
python3 scripts/build_local_release.py \
--firmware-version v1.17.1.9-halo-keymind-cascade-dev --pio-jobs 4 --resume
--firmware-version v1.18.0.1-halo-keymind-cascade-dev --pio-jobs 8 --resume
```
Package its completed directory for stable publication:
```bash
python3 scripts/package_cascade_release.py \
--input .releases/v1.17.1.9-halo-keymind-cascade-dev-SOURCE8 \
--input .releases/v1.18.0.1-halo-keymind-cascade-dev-SOURCE8 \
--output /path/to/publication-stage --local-release --stable \
--version 1.17.1.9 --commit FULL_SOURCE_COMMIT
--version 1.18.0.1 --commit FULL_SOURCE_COMMIT
```
The packager rechecks the local checksum inventory, qualified firmware,
full/reduced nRF52 pairs, and every exact ESP32 migration package. It keeps
Full/Reduced nRF52 pairs, separate exact-board OTAFIX 2.4.12 embedded variants,
standalone bootloader files, and every exact ESP32 migration package. It keeps
migration-only application identities inside their migration ZIPs. It does
not upload anything. Review `release-plan.json`, generate the picker controls
as described in [the picker guide](docs/firmware_picker.md), and generate
@@ -39,10 +40,12 @@ names, sizes and checksums before switching the catalogs. Regenerate the
downloadable picker from the same family and update its checksum on every
page. Documentation-only follow-up commits do not move firmware tags.
Include the [1.17.1.9 release notes](docs/releases/1.17.1.9.md) and
[1.17.1.8 WiFi upgrade guide](docs/upgrading_1.17.1.8_to_1.17.1.9.md) in the
publication review. Replace the release notes' final package-validation gate
with the completed inventory only after packaging passes. Keep the firmware
Include the [1.18.0.1 release notes](docs/releases/1.18.0.1.md) and
[embedded bootloader guide](docs/nrf52_embedded_bootloader.md) in the
publication review. The historical
[1.17.1.8 WiFi upgrade guide](docs/upgrading_1.17.1.8_to_1.17.1.9.md) retains
its measured starting-version limits. Record the completed inventory only
after packaging passes. Keep the firmware
source separate from a later documentation-only commit, and retain the
measured hardware scope and old-updater completion limitation.
@@ -72,6 +75,6 @@ The current trigger explicitly excludes `repeater-room-*`.
Use role-specific introductions for Companion, Repeater/Room Server, Sensor,
LoRa OTA Repeater, and expanded Full infrastructure pages. Link the exact
board/storage [OTAFIX 2.4.6 release](https://github.com/mikecarper/Adafruit_nRF52_Bootloader_OTAFIX/releases/tag/0.11.0-OTAFIX2.4.6)
board/storage [OTAFIX 2.4.12 release](https://github.com/mikecarper/Adafruit_nRF52_Bootloader_OTAFIX/releases/tag/v0.11.0-OTAFIX2.4.12)
where nRF52 bootloader requirements apply. Later documentation commits can be
linked from release notes without moving the firmware tag or rebuilding assets.
File diff suppressed because it is too large. Load diff
+26 -2
View File
@@ -1048,6 +1048,17 @@
profile.variant = variantWithoutSensorMarker(profile.variant);
}
}
const embedded = controls.embeddedBootloader;
if (profile.sensorProfile && ["repeater", "room"].includes(profile.role) &&
sensorSourceMatches && controls.embeddedBootloaderSource === sensorSource &&
embedded && /^\d+(?:\.\d+){2}$/.test(embedded.release || "") &&
embedded.automatic_delay_seconds === 70 && embedded.image_bytes === 40960 &&
embedded.activation === "ota bootloader update" &&
/^[0-9a-f]{64}$/.test(embedded.candidate_sha256 || "") &&
/^[0-9a-f]{64}$/.test(embedded.asset_sha256 || "") &&
profile.target.endsWith("-with-bootloader-" + embedded.release + "-" + profile.sensorProfile)) {
profile.embeddedBootloader = embedded;
}
}
function variantWithoutSensorMarker(value) {
@@ -1385,8 +1396,15 @@
function firmwareProfileLabel(profile) {
if (profile.sensorProfile) {
const label = (profile.sensorProfile === "full" ? "Full supported sensors" : "Reduced sensors") + " + LoRa OTA";
const variant = variantWithoutSensorMarker(profile.variant);
return label + (variant !== "default" ? " - " + humanizeVariant(variant, profile.hardware) : "");
let variant = variantWithoutSensorMarker(profile.variant);
const embedded = profile.embeddedBootloader;
if (embedded) {
variant = variant.replace(new RegExp("(?:^|-)with-bootloader-" +
escapeRegExp(embedded.release.replace(/\./g, "-")) + "(?=-|$)"), "")
.replace(/^-+|-+$/g, "") || "default";
}
return label + (variant !== "default" ? " - " + humanizeVariant(variant, profile.hardware) : "") +
(embedded ? " + OTAFIX " + embedded.release + " bootloader included" : "");
}
const parts = [];
if (profile.feature === "full") parts.push("Full");
@@ -2007,6 +2025,12 @@
],
};
const extra = [];
if (profile.embeddedBootloader) {
const embedded = profile.embeddedBootloader;
extra.push("This application includes the exact-board OTAFIX " + embedded.release +
" bootloader. Leave it running for at least " + embedded.automatic_delay_seconds +
" seconds after startup. It checks once when radio and OTA activity are idle, updates a recognized compatible older bootloader, and skips a matching current or newer version. Recovery and unrecognized bootloaders require the documented local migration route. After any reboot, check get bootloader.ver.");
}
if (isExpandedEsp32Infrastructure(profile) && kind === "bin") {
extra.push(
"If the node still has a smaller layout, first flash the matching merged image over USB or use the linked exact board/role migration ZIP, when available. Follow its README to check the installed OTA target and source layout before starting. Never send this app-only image across a partition change."
+13 -6
View File
@@ -4,7 +4,7 @@
# Firmware picker
<div class="firmware-picker" data-firmware-picker data-release-repo="mikecarper/MeshCore" data-controls-url="../_data/firmware_controls.json?v=t114-autodisplay-794f5f7b" data-bootloaders-url="../_data/bootloader_manifest.json?v=20261002-1" data-share-url="https://mikecarper.github.io/MeshCore/p/">
<div class="firmware-picker" data-firmware-picker data-release-repo="mikecarper/MeshCore" data-controls-url="../_data/firmware_controls.json?v=1.18.0.1-03214a16" data-bootloaders-url="../_data/bootloader_manifest.json?v=otafix-2.4.12" data-share-url="https://mikecarper.github.io/MeshCore/p/">
<form class="firmware-picker-form" data-role="form">
<div class="firmware-picker-feedback firmware-picker-wide" data-role="filter-feedback" hidden>
<div aria-live="polite" aria-atomic="true">
@@ -286,7 +286,7 @@ capacity and sensor-profile choices.
Moving an older ESP32 installation to an expanded Full layout requires a
partition migration.
Use the exact-board merged image over USB, or a supported exact board and role
staged migration ZIP from the [utility release](https://github.com/mikecarper/MeshCore/releases/tag/utility-v1.17.1.9-halo-keymind-cascade-dev-88c85108).
staged migration ZIP from the [utility release](https://github.com/mikecarper/MeshCore/releases/tag/utility-v1.18.0.1-halo-keymind-cascade-dev-03214a16).
Read that ZIP's README before choosing the Wi-Fi or LoRa route. Do not send a
loose app-only Full `.bin` directly to an older layout: the running application
cannot move its own active and inactive partitions. Run
@@ -478,7 +478,7 @@ is available. ESP32 and other chip families do not show this section. The web
picker checks the latest stable OTAFIX release; the downloadable picker
includes the bootloader release named in its saved catalog. If a board has no
verified mapping or a file is missing, the picker explains that instead of
offering another board's bootloader. OTAFIX 2.4.11 publishes a combined
offering another board's bootloader. OTAFIX 2.4.12 includes a combined
MeshTower V2 SD/internal image, mapped only to `Heltec_tower_v2_sdcard`.
The historical `Heltec_tower_v2` internal-only profile is explicitly retired:
move to the normal combined image with a one-time local USB/BLE DFU or SWD
@@ -493,8 +493,15 @@ for the source checks and boards still awaiting a dedicated, tested release.
Board mappings and file names, URLs, sizes and SHA-256 hashes come from
`bootloader-manifest.json` in the
[OTAFIX release](https://github.com/mikecarper/Adafruit_nRF52_Bootloader_OTAFIX/releases/latest).
Application UF2 and DFU files do not install a bootloader. Follow the bootloader
release's migration instructions if the installed version needs a recovery bridge.
Ordinary application UF2 and DFU files preserve the installed bootloader.
Separate **OTAFIX 2.4.12 bootloader included** Repeater and Room Server choices
carry their exact-board bootloader inside the application. Leave that application
running for at least 70 seconds after startup; it checks once when radio and OTA
activity are idle, updates a recognized compatible older bootloader, and skips
a matching current or newer version. Check `get bootloader.ver` after reboot.
Recovery and unrecognized bootloaders require the documented migration route;
the embedded updater does not guess a different board identity. See
[embedded bootloader installation](nrf52_embedded_bootloader.md).
nRF52 LoRa OTA requires an OTAFIX bootloader built for the exact board and
storage layout. Select the hardware-matched HEX, Serial DFU ZIP, or
@@ -509,7 +516,7 @@ pin map, and other physical differences without crowding the Hardware dropdown.
that need a different image or wiring, such as serial port or external storage.
The picker skips this step when all available profile choices lead to the
same firmware file.
The 1.17.1.9 nRF52 repeater, room-server and sensor releases publish two separately
The 1.18.0.1 nRF52 repeater, room-server and sensor releases publish two separately
qualified choices: **Reduced sensors + LoRa OTA** and
**Full supported sensors + LoRa OTA**, with `-reduced-ota` and `-full-ota` artifact suffixes. Each keeps
its exact board/role OTA identity and storage layout; the suffix is not a new
+4 -4
View File
@@ -8,8 +8,8 @@ Choose firmware with the [firmware picker](firmware_picker.md). For help with a
## Current release
- [1.17.1.8 USA Cascade release notes](releases/1.17.1.8.md)
- [Test Results](test-results/index.md) — validation archive and 1.17.1.8 Preview 1 qualification evidence
- [1.18.0.1 USA Cascade release notes](releases/1.18.0.1.md)
- [Test Results](test-results/index.md) - source-specific validation archive
## Documentation
@@ -32,7 +32,7 @@ The navigation groups the remaining guides under Documentation, including board-
## Engineering records
- [Research](research/index.md) — design notes, diagnostics, and historical update-chain studies
- [Old Releases](old-releases/index.md) — historical release notes and supporting files
- [Research](research/index.md) - design notes, diagnostics, and historical update-chain studies
- [Old Releases](old-releases/index.md) - historical release notes and supporting files
Found a mistake or missing information? [Edit the documentation](https://github.com/mikecarper/MeshCore/tree/keymindCascade/docs).
+1 -1
View File
@@ -4,7 +4,7 @@
# Old Releases
Historical release notes and their supporting files are kept here for reference. Use the [current 1.17.1.8 release notes](../releases/1.17.1.8.md) for current firmware.
Historical release notes and their supporting files are kept here for reference. Use the [current 1.18.0.1 release notes](../releases/1.18.0.1.md) for current firmware.
- [1.17.1.7 release](1.17.1.7.md)
- [1.17.1.6 release](1.17.1.6.md)
+4 -2
View File
@@ -5,8 +5,10 @@
# nRF52 bootloader updates over LoRa
For an application image that carries its own board-matched 2.4.12 replacement,
see [embedded nRF52 bootloader updates](nrf52_embedded_bootloader.md). That opt-in
bridge uses a separate admin activation command after the application is running.
see [embedded nRF52 bootloader updates](nrf52_embedded_bootloader.md). A separate
`with-bootloader-2.4.12` image checks once after 70 seconds of startup and updates
a recognized compatible older bootloader when radio and OTA activity are idle.
The authenticated admin activation command remains available for manual retry.
For **1.17.1.5**, use the exact board/storage profile from
[OTAFIX 2.4.6](https://github.com/mikecarper/Adafruit_nRF52_Bootloader_OTAFIX/releases/tag/0.11.0-OTAFIX2.4.6)
+106
View File
@@ -0,0 +1,106 @@
<!-- meshcore-hosted-doc-link:start -->
<p class="meshcore-hosted-doc-link"><a href="https://mikecarper.github.io/MeshCore/releases/1.18.0.1/">View this page on MeshCore Docs</a>.</p>
<!-- meshcore-hosted-doc-link:end -->
# MeshCore 1.18.0.1 USA Cascade
Firmware source: `03214a16df2bd09d3272dfb001d2d322c6740f60`.
USA Cascade defaults are **910.525 MHz, 62.5 kHz bandwidth, SF7 and CR5**.
Saved radio settings take precedence. Choose the exact board, radio, storage
layout and role in the [firmware picker](../firmware_picker.md).
Download the [1.18.0.1 release family](https://github.com/mikecarper/MeshCore/releases/tag/v1.18.0.1-halo-keymind-cascade-dev-03214a16)
and its [migration and bootloader utilities](https://github.com/mikecarper/MeshCore/releases/tag/utility-v1.18.0.1-halo-keymind-cascade-dev-03214a16).
The picker, Mesh America and the Keymind choices in meshfirmware select these
same exact release assets.
## Changes since 1.17.1.9
- Supported nRF52 Repeater and Room Server targets have separate application
images carrying their exact-board **OTAFIX 2.4.12** bootloader. Ordinary
applications remain available, together with standalone bootloader files.
- Full Companion includes Meshtext on all supported board families. Message
retries are bounded; repeated traffic does not become a new local message.
- [ESP-NOW](../espnow_bridge_setup.md) and
[RS232](../rs232_bridge_setup.md) bridges provide runtime status, counters
and link probes. The [WiFi UDP bridge](../udp_bridge_setup.md) supports eight
authenticated IP peers per repeater on an existing station network.
- [Telemetry packet selections](../cli_commands.md#read-repeater-telemetry-history)
independently control temperature, battery, external I2C voltage, main-radio
noise and radio2 noise snapshots. All five selections default on; the
telemetry transmitter itself remains off until enabled.
- Optional [battery forwarding thresholds](../cli_commands.md) can stop repeat
and flood forwarding independently. Both controls default off; their
percentages and supported voltage calibration are saved settings.
- Supported radios expose saved `radio.fast` acceleration controls and
`radio.fast.spi` choices. A `9` MHz selection uses approximately 8.889 MHz
where the controller supports that rate. Read `get radio.fast.caps` for the
actual board's options.
- Board profiles consolidate supported display and transport choices, including
T114 automatic display detection. Exact V4 hardware and storage variants
remain distinct where their wiring requires a different image.
- T1000-E Companion gives an enumerated USB data connection priority over
Bluetooth. Bluetooth resumes after USB disconnects if it is enabled;
battery and a charge-only wall supply allow Bluetooth. Pairing and the saved
Bluetooth setting are retained.
- Release capacity fixes retain the qualified feature sets with compact help,
shared buffers and board-specific memory placement. Every image includes its
own capability and memory reports; capacities still differ between boards.
See [feature controls](../role_feature_switches.md), the
[Full Companion guide](../full_companion_features.md) and
[CLI reference](../cli_commands.md) for supported commands and defaults.
## Choose an installation
For ESP32, the merged `.bin` installs bootloader, partitions and application
over USB. An app-only `.bin` requires a compatible existing partition layout.
When expansion is needed over WiFi or LoRa, use the exact board/role migration
ZIP in this release family's utility page and install its supplied Full
application. Follow the included README; an app-only image cannot expand
partitions. `get storage.layout` reports the live layout where supported.
Ordinary nRF52 UF2 and Serial DFU ZIP files install the application and preserve
the installed bootloader. Repeater, Room Server and Sensor retain separately
qualified **Full supported sensors + LoRa OTA** and
**Reduced sensors + LoRa OTA** choices. These keep the exact OTA target and
storage layout; sensor policy is not a different radio identity.
Separate `with-bootloader-2.4.12` Repeater and Room Server applications carry a
board-matched bootloader. Leave the application powered for at least 70 seconds
after installation. It checks once when radio and OTA activity are idle and
updates a recognized compatible older bootloader; matching current or newer
versions are skipped. Recovery or unrecognized bootloaders require their
documented local migration route. Check `get bootloader.ver` afterward. See
[embedded bootloader installation](../nrf52_embedded_bootloader.md).
The utility release also supplies exact-board bootloader-update UF2,
SoftDevice/bootloader DFU ZIP and SWD HEX files for 23 published OTAFIX profiles
covering 40 hardware aliases. The SoftDevice/bootloader DFU package requires
reinstalling the application. Pending, blocked and retired board mappings are
identified rather than replaced by another board's image.
LoRa OTA reception requires the matching bootloader, target identity, storage
layout and radio settings. Full Companion can send a host-supplied update to
another node; it does not install that LoRa transfer onto itself.
## Verification scope
The complete release has **589 qualified firmware packages**: 435 ordinary
packages and 154 embedded-bootloader variants. The latter cover 77 logical
Repeater/Room targets with both sensor policies. It also includes **69
standalone bootloader files**, **96 exact ESP32 board/role migration packages**
and **26 migration utility builds**.
The completed matrix passed exact target inventory, source provenance,
checksums, packaged OTA identity, native DFU, firmware size, runtime RAM,
sensor-pair, bootloader mapping and migration-layout validation. All 14 jobs in
the [source revision's CI run](https://github.com/mikecarper/MeshCore/actions/runs/40101490838)
passed.
Physical results remain tied to the hardware, revision and installation route
recorded in the [test reports](../test-results/index.md). Building every target
does not establish every board's physical USB, Bluetooth, WiFi, RF, sleep or
update behavior. The release does not qualify every starting firmware or
bootloader migration path.
+5 -3
View File
@@ -1,6 +1,6 @@
# Mesh America provider catalogs
The two active provider catalogs select the 1.17.1.9 USA Cascade release family. Each lists 463 unique qualified firmware profiles across 103 hardware cards (464 firmware entries). Both catalogs use the same binaries. The logging catalog remains a separate stable provider URL for existing Mesh America installations.
The two active provider catalogs select the 1.18.0.1 USA Cascade release family. Each lists 589 unique qualified firmware profiles across 101 hardware cards (589 firmware entries). Both catalogs use the same binaries. The logging catalog remains a separate stable provider URL for existing Mesh America installations.
```text
Provider name: Keymind Cascade
@@ -10,10 +10,12 @@ Provider name: Keymind Cascade Logging
Catalog URL: https://raw.githubusercontent.com/mikecarper/MeshCore/keymindCascade/mesh-america/keymind-cascade-logging-v1.16.0-provider.json
```
The `v1.16.0` part of those filenames is a stable provider URL, not the firmware version. Both JSON files contain exact 1.17.1.9 asset URLs, including the exact release page for each qualified Companion, infrastructure, sensor, and terminal profile.
The `v1.16.0` part of those filenames is a stable provider URL, not the firmware version. Both JSON files contain exact 1.18.0.1 asset URLs, including the exact release page for each qualified Companion, infrastructure, sensor, and terminal profile.
Four 256 KiB STM32WL USB Companion images (RAK 3x72, Tiny Relay, Wio-E5 Mini, and Wio-E5) omit packet diagnostics to preserve their 32 KiB LittleFS layout. Their USB Companion transport remains available. Their entries say so explicitly. Other logging controls follow each exact image's capability manifest.
See the [1.17.1.9 release guide](../docs/releases/1.17.1.9.md) and [firmware picker](../docs/firmware_picker.md). An ESP32 `flash-wipe` merged image installs the bootloader, partition table, and application over USB. An app-only `flash-update` image requires an already compatible layout; it cannot expand partitions. If the installed firmware supports it, run `get storage.layout` to inspect the live partition table; firmware version alone cannot confirm the layout. For an existing node that must retain a Wi-Fi or LoRa update path, check the [utility release](https://github.com/mikecarper/MeshCore/releases/tag/utility-v1.17.1.9-halo-keymind-cascade-dev-88c85108) for its exact board and role migration ZIP and follow the README inside. Back up identity and settings, and plan for cable recovery if power fails during the partition-table write. nRF52 application images require a matching OTAFIX bootloader when the installed bootloader is not already compatible.
See the [1.18.0.1 release guide](../docs/releases/1.18.0.1.md) and [firmware picker](../docs/firmware_picker.md). An ESP32 `flash-wipe` merged image installs the bootloader, partition table, and application over USB. An app-only `flash-update` image requires an already compatible layout; it cannot expand partitions. If the installed firmware supports it, run `get storage.layout` to inspect the live partition table; firmware version alone cannot confirm the layout. For an existing node that must retain a Wi-Fi or LoRa update path, check the [utility release](https://github.com/mikecarper/MeshCore/releases/tag/utility-v1.18.0.1-halo-keymind-cascade-dev-03214a16) for its exact board and role migration ZIP and follow the README inside. Back up identity and settings, and plan for cable recovery if power fails during the partition-table write. Ordinary nRF52 application images preserve the installed bootloader. Separate `with-bootloader-2.4.12` Repeater and Room Server choices carry the exact-board OTAFIX image and check it once after 70 seconds; recovery and unrecognized bootloaders require the documented local migration route. Standalone board-matched bootloader utilities remain available for other roles. LoRa OTA requires a compatible exact-board bootloader.
The release includes 435 ordinary applications and 154 embedded-bootloader variants, plus 69 standalone bootloader files and 96 exact ESP32 board/role migration packages in the utility release. Meshfirmware on Linux and Windows reads these stable provider URLs, so it receives the same release without a separate firmware-list update.
Catalogs are generated from the staged release manifest, with every asset URL checked against the staged file and tag. Historical `provider-backup.json` files are retained unchanged.
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+1
View File
@@ -105,6 +105,7 @@ nav:
- Research: research/index.md
- Releases:
- Current release:
- 1.18.0.1 USA Cascade: releases/1.18.0.1.md
- 1.17.1.9 USA Cascade: releases/1.17.1.9.md
- 1.17.1.8 USA Cascade: releases/1.17.1.8.md
- Test Results: test-results/index.md
+16 -2
View File
@@ -18,6 +18,12 @@ ROOT = Path(__file__).resolve().parents[1]
SUFFIXES = {".bin", ".zip", ".uf2", ".hex"}
PLATFORMS = {"ESP32_PLATFORM": "esp32", "NRF52_PLATFORM": "nrf52",
"RP2040_PLATFORM": "noflash", "STM32_PLATFORM": "noflash"}
ADDITIONAL_HARDWARE_CARDS = {
("esp32", "Waveshare_S3_LR1121"): {
"maker": "waveshare", "name": "Waveshare ESP32-S3 LR1121", "type": "esp32"},
("esp32", "Wio_Tracker_L2"): {
"maker": "seeed", "name": "Seeed Wio Tracker L2", "type": "esp32"},
}
def identity(name):
@@ -149,9 +155,17 @@ def generate(catalog, plan, family, records, profiles, repo):
# A download-only card is an installer restriction, not a chip.
# Retain that restriction for new profiles of the same hardware.
indexes.update(by_hardware.get(("noflash", parsed["hardware"]), set()))
card_key = (platform, parsed["hardware"])
if not indexes and card_key in ADDITIONAL_HARDWARE_CARDS:
card = ADDITIONAL_HARDWARE_CARDS[card_key]
if card["maker"] not in catalog["maker"]:
raise ValueError("new hardware card has no curated maker")
indexes = {len(result["device"])}
result["device"].append({**card, "firmware": []})
by_hardware[card_key] = indexes
if len(indexes) != 1:
raise ValueError(f"new profile needs an exact hardware card mapping: {target}")
if any(catalog["device"][i]["type"] not in (platform, "noflash") for i in indexes):
if any(result["device"][i]["type"] not in (platform, "noflash") for i in indexes):
raise ValueError("firmware platform differs from its hardware card")
role, title = {"repeater": ("repeater", "Repeater"),
"room": ("roomServer", "Room Server"),
@@ -204,7 +218,7 @@ def generate(catalog, plan, family, records, profiles, repo):
f"SOURCE - https://github.com/{repo}/tree/{row.get('source_commit', plan['source'])}",
f"RELEASE - https://github.com/{repo}/releases/tag/{tag}"]
for index in sorted(indexes):
install_platform = catalog["device"][index]["type"]
install_platform = result["device"][index]["type"]
card_notes = list(notes)
if install_platform == "noflash":
card_notes.append("MESH AMERICA - Downloads only. In-app flashing is not qualified for this hardware card.")
+10 -1
View File
@@ -10,7 +10,7 @@ import re
from pathlib import Path
from package_esp32_partition_migration import BOARDS
from package_cascade_release import nrf52_sensor_profile
from package_cascade_release import embedded_bootloader_variant, nrf52_sensor_profile
from station_g3_profiles import runtime_metadata as station_g3_metadata
from optional_display_profiles import runtime_metadata as display_metadata
from consolidated_profiles import runtime_metadata as consolidation_metadata
@@ -91,6 +91,15 @@ def runtime_metadata(manifest, mqtt, release_source=None):
if not isinstance(source, str) or not re.fullmatch(r'[0-9a-f]{40}', source):
raise ValueError('Sensor profiles require an exact source commit: ' + manifest['target'])
result.update(sensorProfile=sensor_profile, sensorProfileSource=source)
if (manifest.get('embedded_bootloader') is not None
or 'bootloader.embedded' in capabilities
or '-with-bootloader-' in manifest.get('artifact_target', '')):
embedded_bootloader_variant(manifest)
source = manifest.get('source_commit', release_source)
if not isinstance(source, str) or not re.fullmatch(r'[0-9a-f]{40}', source):
raise ValueError('Embedded bootloader requires an exact source commit: ' + manifest['target'])
result.update(embeddedBootloader=dict(manifest['embedded_bootloader']),
embeddedBootloaderSource=source)
if {'logging.usb.packets', 'logging.usb.control'} <= packaged:
result['loggingModes'] = ['none', 'usb', 'wifi', 'both'] if mqtt else ['none', 'usb']
result['loggingControl'] = 'logging.output' if mqtt else 'usb.logging'
File diff suppressed because it is too large. Load diff
+56 -3
View File
@@ -1173,9 +1173,62 @@ assert(stale.profiles.every(p => !p.controls));
assert(!picker.runtimeDirections({...mqttCompanion, controls: undefined}, {}).some(s => s.title === 'GPS'));
console.log('role-specific runtime directions tests passed');
// Exercise every profile from the current generated catalog while the tests
// above keep the older release's USB and capacity workarounds intact.
const currentControls = require('../docs/_data/firmware_controls.json');
// Every currently published profile must retain its own release-bound controls.
// Bootloader-carrying applications stay separate from ordinary applications.
const publishedControls = require('../docs/_data/firmware_controls.json');
const publishedAssets = Object.entries(publishedControls.profiles).map(([target, info]) =>
asset(target + '-' + publishedControls.familyTag +
(info.platform === 'NRF52_PLATFORM' ? '.uf2' : '.bin')));
const publishedReleases = [release(publishedControls.familyTag, '2026-10-24T00:00:00Z', publishedAssets)];
const publishedCatalog = picker.buildCatalog(publishedReleases, publishedControls);
assert.strictEqual(publishedCatalog.rows.length, publishedAssets.length);
assert(publishedCatalog.profiles.every(profile => profile.controls && profile.chipFamily !== 'unknown'));
const embeddedControls = Object.entries(publishedControls.profiles).filter(([, info]) => info.embeddedBootloader);
assert.strictEqual(embeddedControls.length, 154);
for (const [target, info] of embeddedControls) {
const profile = publishedCatalog.profiles.find(item => item.target === target);
assert(profile, target + ' must remain selectable');
assert.deepStrictEqual(profile.embeddedBootloader, info.embeddedBootloader);
const ordinaryTarget = target.replace('-with-bootloader-' + info.embeddedBootloader.release, '');
const ordinaryAsset = publishedAssets.find(file => file.name.startsWith(ordinaryTarget + '-'));
assert(ordinaryAsset, target + ' must retain its exact ordinary application download');
const ordinary = picker.buildCatalog([
release(publishedControls.familyTag, '2026-10-24T00:00:00Z', [ordinaryAsset]),
], publishedControls).profiles.find(item => item.target === ordinaryTarget);
assert(ordinary, target + ' must retain its ordinary application identity');
assert.strictEqual(ordinary.embeddedBootloader, undefined);
assert.notStrictEqual(picker.firmwareProfileValue(profile), picker.firmwareProfileValue(ordinary));
assert(picker.firmwareProfileLabel(profile).includes('OTAFIX 2.4.12 bootloader included'));
assert(!picker.firmwareProfileLabel(ordinary).includes('bootloader included'));
const instructions = picker.installSteps(profile, 'uf2').join('\n');
assert(instructions.includes('at least 70 seconds'));
assert(instructions.includes('Recovery and unrecognized bootloaders'));
assert(instructions.includes('get bootloader.ver'));
}
const [embeddedTarget] = embeddedControls[0];
for (const fault of ['stale_source', 'invalid_candidate', 'wrong_delay', 'wrong_version', 'ordinary_target']) {
const metadata = JSON.parse(JSON.stringify(publishedControls));
let target = embeddedTarget;
const changed = metadata.profiles[target];
if (fault === 'stale_source') changed.embeddedBootloaderSource = 'b'.repeat(40);
if (fault === 'invalid_candidate') changed.embeddedBootloader.candidate_sha256 = 'invalid';
if (fault === 'wrong_delay') changed.embeddedBootloader.automatic_delay_seconds = 1;
if (fault === 'wrong_version') changed.embeddedBootloader.release = '2.4.11';
if (fault === 'ordinary_target') {
target = embeddedTarget.replace('-with-bootloader-2.4.12', '');
metadata.profiles[target] = changed;
}
const profile = picker.buildCatalog(publishedReleases, metadata).profiles.find(item => item.target === target);
assert(profile, fault);
assert.strictEqual(profile.embeddedBootloader, undefined, fault);
assert(!picker.firmwareProfileLabel(profile).includes('bootloader included'), fault);
assert(!picker.installSteps(profile, 'uf2').join('\n').includes('at least 70 seconds'), fault);
}
console.log('all current release profiles, distinct embedded bootloaders and unsafe metadata rejection passed');
// Keep the published 1.17.1.9 compatibility matrix independent of the newest
// release, which can retire old recovery targets or add new firmware choices.
const currentControls = require('./fixtures/firmware_picker_1_17_1_9_controls.json');
const currentAssets = Object.entries(currentControls.profiles).map(([target, info]) => {
const source = info.loggingSource || info.memorySource || currentControls.source;
const tag = currentControls.familyTag.replace(/-[0-9a-f]{8}$/, '-' + source.slice(0, 8));
+36
View File
@@ -79,6 +79,42 @@ class PickerControlsTests(unittest.TestCase):
with self.assertRaisesRegex(ValueError, 'ambiguous'):
GENERATOR.runtime_metadata(manifest, False, source)
def test_embedded_bootloader_controls_retain_qualified_exact_board_metadata(self):
from nrf52_embedded_bootloader import MANIFEST_SHA256, load_manifest, select_profile
source = 'a' * 40
target = 'RAK_4631_repeater'
profile, _ = select_profile(load_manifest(), target)
metadata = dict(release='2.4.12', profile=profile['id'],
manifest_sha256=MANIFEST_SHA256,
asset_sha256=profile['files']['zip']['sha256'],
candidate_sha256='c' * 64, legacy_sha256=None,
image_bytes=40960, app_offset=32,
activation='ota bootloader update', automatic_delay_seconds=70)
manifest = dict(target=target,
artifact_target=target + '-with-bootloader-2.4.12-full-ota',
platform='NRF52_PLATFORM', verified=True, ota_update_verified=True,
capabilities=['ota.update.lora', 'sensor.profile.full', 'bootloader.embedded'],
ota_update_methods=['lora'], embedded_bootloader=metadata,
verification=[dict(capability='ota.update.lora', present=True, source='linked image'),
dict(capability='bootloader.embedded', present=True,
source='packaged application')])
result = GENERATOR.runtime_metadata(manifest, False, source)
self.assertEqual(result['embeddedBootloader'], metadata)
self.assertIsNot(result['embeddedBootloader'], metadata)
self.assertEqual(result['embeddedBootloaderSource'], source)
for fault in ('wrong_board', 'wrong_delay', 'no_packaged_proof', 'no_metadata'):
changed = json.loads(json.dumps(manifest))
if fault == 'wrong_board':
changed['embedded_bootloader']['profile'] = 'xiao'
elif fault == 'wrong_delay':
changed['embedded_bootloader']['automatic_delay_seconds'] = 1
elif fault == 'no_packaged_proof':
changed['verification'].pop()
else:
del changed['embedded_bootloader']
with self.subTest(fault=fault), self.assertRaises(ValueError):
GENERATOR.runtime_metadata(changed, False, source)
def test_merged_rs232_requires_qualified_linked_and_packaged_proof(self):
source = 'a' * 40
manifest = dict(target='Xiao_nrf52_repeater', source_commit=source, verified=True,