Publish 1.18.0.1 release catalogs and bootloader picker choices
This commit is contained in:
1 parent
03214a16df
commit
ec478c30ed
17 files changed
+37893
-8184
No files matched your search
+13
-10
@@ -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.
|
||||
+9203
-1726
File diff suppressed because it is too large.
Load diff
@@ -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
@@ -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
@@ -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).
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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.
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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,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'
|
||||
|
||||
+16003
File diff suppressed because it is too large.
Load diff
@@ -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));
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in new issue
Block a user