mirror of
https://github.com/mikecarper/MeshCore.git
synced 2026-10-23 19:12:23 +00:00
Resolve nRF52 carrier bootloader links and explain audited coverage gaps
This commit is contained in:
1 parent
bf04fbb2e8
commit
ddcc1de467
6 files changed
+224
-6
No files matched your search
@@ -536,7 +536,9 @@
|
||||
"id": "wio_tracker_l1",
|
||||
"label": "Seeed Wio Tracker L1",
|
||||
"meshcoreHardware": [
|
||||
"WioTrackerL1"
|
||||
"WioTrackerL1",
|
||||
"WioTrackerL1-1W",
|
||||
"WioTrackerL1Eink"
|
||||
],
|
||||
"deviceName": "WTL1_DFU",
|
||||
"storage": "qspi",
|
||||
@@ -653,7 +655,17 @@
|
||||
"id": "xiao_nrf52840_ble",
|
||||
"label": "Seeed XIAO nRF52840 BLE",
|
||||
"meshcoreHardware": [
|
||||
"Xiao_nrf52"
|
||||
"Xiao_nrf52",
|
||||
"ikoka_handheld_nrf_e22_30dbm",
|
||||
"ikoka_handheld_nrf_e22_30dbm_096",
|
||||
"ikoka_nano_nrf_22dbm",
|
||||
"ikoka_nano_nrf_30dbm",
|
||||
"ikoka_nano_nrf_33dbm",
|
||||
"ikoka_stick_nrf_22dbm",
|
||||
"ikoka_stick_nrf_30dbm",
|
||||
"ikoka_stick_nrf_33dbm",
|
||||
"solarxiao_30S",
|
||||
"solarxiao_33S"
|
||||
],
|
||||
"deviceName": "XIAO_DFU",
|
||||
"storage": "qspi",
|
||||
@@ -707,5 +719,98 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"unavailableProfiles": [
|
||||
{
|
||||
"id": "gat562_mesh_watch13",
|
||||
"label": "GAT562 Mesh Watch13",
|
||||
"meshcoreHardware": [
|
||||
"GAT562_Mesh_Watch13"
|
||||
],
|
||||
"reason": "The watch routes a GAT562 bootloader LED output to its vibration motor and has onboard flash. It needs a watch-specific profile."
|
||||
},
|
||||
{
|
||||
"id": "heltec_mesh_solar",
|
||||
"label": "Heltec Mesh Solar",
|
||||
"meshcoreHardware": [
|
||||
"Heltec_mesh_solar"
|
||||
],
|
||||
"reason": "Mesh Solar has different LED and peripheral pins from the supported Heltec boards. It needs its own profile."
|
||||
},
|
||||
{
|
||||
"id": "lilygo_t_impulse_plus",
|
||||
"label": "LilyGo T-Impulse Plus",
|
||||
"meshcoreHardware": [
|
||||
"LilyGo_T_Impulse_Plus"
|
||||
],
|
||||
"reason": "T-Impulse Plus has different flash data pins and LED wiring from T-Echo Lite. It needs its own profile."
|
||||
},
|
||||
{
|
||||
"id": "lilygo_techo_card",
|
||||
"label": "LilyGo T-Echo Card",
|
||||
"meshcoreHardware": [
|
||||
"LilyGo_T-Echo_Card"
|
||||
],
|
||||
"reason": "T-Echo Card has its own NFC configuration, buttons and LED hardware. It needs a Card-specific profile."
|
||||
},
|
||||
{
|
||||
"id": "meshtiny",
|
||||
"label": "Meshtiny",
|
||||
"meshcoreHardware": [
|
||||
"Meshtiny"
|
||||
],
|
||||
"reason": "Meshtiny has its own factory bootloader identity and controls. A dedicated Meshtiny profile has not been qualified."
|
||||
},
|
||||
{
|
||||
"id": "meshtracker_x1",
|
||||
"label": "MeshTracker X1",
|
||||
"meshcoreHardware": [
|
||||
"MeshTracker_X1"
|
||||
],
|
||||
"reason": "MeshTracker X1 has different flash wiring, power control and button polarity from Wio Tracker L1. It needs its own profile."
|
||||
},
|
||||
{
|
||||
"id": "muzi_base",
|
||||
"label": "Muzi Base",
|
||||
"meshcoreHardware": [
|
||||
"muzi_base_duo",
|
||||
"muzi_base_duo_superIO",
|
||||
"muzi_base_uno",
|
||||
"muzi_base_uno_superIO"
|
||||
],
|
||||
"reason": "These four variants share Muzi Base hardware, which needs its own bootloader identity, LED polarity and flash configuration."
|
||||
},
|
||||
{
|
||||
"id": "nano_g2_ultra",
|
||||
"label": "Nano G2 Ultra",
|
||||
"meshcoreHardware": [
|
||||
"Nano_G2_Ultra"
|
||||
],
|
||||
"reason": "Nano G2 Ultra has its own flash and button wiring. It needs a dedicated profile."
|
||||
},
|
||||
{
|
||||
"id": "thinknode_m4",
|
||||
"label": "ThinkNode M4",
|
||||
"meshcoreHardware": [
|
||||
"ThinkNode_M4"
|
||||
],
|
||||
"reason": "ThinkNode M4 has different LED, button and power-control pins from the supported ThinkNodes. It needs its own profile."
|
||||
},
|
||||
{
|
||||
"id": "thinknode_m8",
|
||||
"label": "ThinkNode M8",
|
||||
"meshcoreHardware": [
|
||||
"ThinkNode_M8"
|
||||
],
|
||||
"reason": "ThinkNode M8 shares some flash wiring with M1 but has different buttons and peripheral controls. It needs an M8-specific profile."
|
||||
},
|
||||
{
|
||||
"id": "wio_wm1110",
|
||||
"label": "Seeed Wio WM1110",
|
||||
"meshcoreHardware": [
|
||||
"wio_wm1110"
|
||||
],
|
||||
"reason": "Wio WM1110 is a separate tracker board with different LED and peripheral pins. Its XIAO build setting does not make it XIAO hardware."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -152,7 +152,21 @@
|
||||
});
|
||||
return Object.assign({}, profile, { files: files });
|
||||
}).filter(function (profile) { return Object.keys(profile.files).length === 3; });
|
||||
return { tag: tag, version: tag.split("OTAFIX")[1], releaseUrl: releaseUrl, profiles: profiles };
|
||||
const unavailableProfiles = (manifest.unavailableProfiles || []).map(function (profile) {
|
||||
if (!/^[a-z0-9_]+$/.test(profile.id) || ids.has(profile.id) ||
|
||||
!Array.isArray(profile.meshcoreHardware) || !profile.meshcoreHardware.length ||
|
||||
typeof profile.reason !== "string" || !profile.reason.trim()) {
|
||||
throw new Error("Invalid unavailable bootloader profile");
|
||||
}
|
||||
ids.add(profile.id);
|
||||
profile.meshcoreHardware.forEach(function (hardware) {
|
||||
if (!hardware || aliases.has(hardware)) throw new Error("Ambiguous bootloader hardware mapping");
|
||||
aliases.add(hardware);
|
||||
});
|
||||
return profile;
|
||||
});
|
||||
return { tag: tag, version: tag.split("OTAFIX")[1], releaseUrl: releaseUrl,
|
||||
profiles: profiles, unavailableProfiles: unavailableProfiles };
|
||||
}
|
||||
|
||||
function bootloaderForHardware(catalog, hardware, chipFamily) {
|
||||
@@ -163,6 +177,13 @@
|
||||
return matches.length === 1 ? matches[0] : null;
|
||||
}
|
||||
|
||||
function unavailableBootloaderForHardware(catalog, hardware, chipFamily) {
|
||||
if (!catalog || chipFamily !== "nrf52" || !hardware) return null;
|
||||
return catalog.unavailableProfiles.find(function (profile) {
|
||||
return profile.meshcoreHardware.includes(hardware);
|
||||
}) || null;
|
||||
}
|
||||
|
||||
const HARDWARE_ALIASES = Object.freeze({
|
||||
// Generated Full Companion artifact name. It uses the standard Heltec V4
|
||||
// board definition and auto-detects the V4.2 and V4.3 FEM hardware.
|
||||
@@ -1995,7 +2016,10 @@
|
||||
content.appendChild(actions);
|
||||
content.appendChild(createElement("p", "UF2 updates an existing compatible bootloader. The DFU ZIP includes SoftDevice and bootloader; HEX is for SWD installation or recovery. Application firmware files do not install a bootloader. Follow the release's migration instructions if your installed identity needs a recovery bridge."));
|
||||
} else if (bootloaderCatalog) {
|
||||
content.appendChild(createElement("p", "No verified bootloader download matches this exact hardware in the current catalog. Check the release notes for board support."));
|
||||
const unavailable = unavailableBootloaderForHardware(bootloaderCatalog, hardware, chip);
|
||||
content.appendChild(createElement("p", unavailable ?
|
||||
"No OTAFIX " + bootloaderCatalog.version + " bootloader has been published for this board. " + unavailable.reason :
|
||||
"No verified bootloader download matches this exact hardware in the current catalog. Check the release notes for board support."));
|
||||
}
|
||||
if (bootloaderNotice) content.appendChild(createElement("p", bootloaderNotice));
|
||||
const notes = createElement("a", "Bootloader release notes and migration instructions");
|
||||
@@ -2159,6 +2183,7 @@
|
||||
const api = Object.freeze({
|
||||
buildBootloaderCatalog: buildBootloaderCatalog,
|
||||
bootloaderForHardware: bootloaderForHardware,
|
||||
unavailableBootloaderForHardware: unavailableBootloaderForHardware,
|
||||
FILTER_FIELDS: FILTER_FIELDS,
|
||||
FACET_FIELDS: FACET_FIELDS,
|
||||
PROFILE_FIELDS: PROFILE_FIELDS,
|
||||
|
||||
@@ -20,7 +20,7 @@ The picker reads public release metadata from GitHub. It does not upload device
|
||||
information. Hardware names, target names, and download links come directly
|
||||
from the published firmware assets.
|
||||
|
||||
<div class="firmware-picker" data-firmware-picker data-release-repo="mikecarper/MeshCore" data-controls-url="../_data/firmware_controls.json?v=1.17.1.7-runtime-2" data-bootloaders-url="../_data/bootloader_manifest.json?v=20260929-1" data-share-url="https://mikecarper.github.io/MeshCore/firmware_picker/">
|
||||
<div class="firmware-picker" data-firmware-picker data-release-repo="mikecarper/MeshCore" data-controls-url="../_data/firmware_controls.json?v=1.17.1.7-runtime-2" data-bootloaders-url="../_data/bootloader_manifest.json?v=20260929-2" data-share-url="https://mikecarper.github.io/MeshCore/firmware_picker/">
|
||||
<div class="firmware-picker-intro" role="note">
|
||||
<strong>Current release set</strong>
|
||||
<p data-role="release-set">Loading release information...</p>
|
||||
@@ -398,6 +398,10 @@ 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. MeshTower internal and microSD remain
|
||||
separate; RAK3401 and RAK4631 each use their adaptive storage bootloader.
|
||||
Ikoka Stick, Nano and Handheld plus SolarXiao use the XIAO bootloader;
|
||||
Wio Tracker L1 1W and E-Ink use the L1 bootloader. See the
|
||||
[board coverage audit](https://github.com/mikecarper/Adafruit_nRF52_Bootloader_OTAFIX/blob/feature/ota-delta-apply/docs/meshcore-hardware-coverage.md)
|
||||
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
|
||||
|
||||
+1
-1
@@ -26,7 +26,7 @@ extra_css:
|
||||
|
||||
extra_javascript:
|
||||
- https://unpkg.com/leaflet@1.9.4/dist/leaflet.js
|
||||
- _javascript/firmware_picker.js?v=20260929-3
|
||||
- _javascript/firmware_picker.js?v=20260929-4
|
||||
- _javascript/telemetry_decoder.js
|
||||
- _javascript/management_decoder.js
|
||||
- _javascript/filter_tool.js
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
{
|
||||
"familyTag": "v1.17.1.7-halo-keymind-cascade-dev-2d03e098",
|
||||
"hardware": [
|
||||
"GAT562_30S_Mesh_Kit",
|
||||
"GAT562_Mesh_EVB_Pro",
|
||||
"GAT562_Mesh_Tracker_Pro",
|
||||
"GAT562_Mesh_Watch13",
|
||||
"Heltec_mesh_solar",
|
||||
"Heltec_t096",
|
||||
"Heltec_t1",
|
||||
"Heltec_t114",
|
||||
"Heltec_t114_without_display",
|
||||
"Heltec_tower_v2",
|
||||
"Heltec_tower_v2_sdcard",
|
||||
"KeepteenLT1",
|
||||
"LilyGo_T-Echo",
|
||||
"LilyGo_T-Echo-Lite",
|
||||
"LilyGo_T-Echo-Lite_non_shell",
|
||||
"LilyGo_T-Echo_Card",
|
||||
"LilyGo_T_Impulse_Plus",
|
||||
"MeshTracker_X1",
|
||||
"Mesh_pocket",
|
||||
"Meshtiny",
|
||||
"Minewsemi_me25ls01",
|
||||
"Nano_G2_Ultra",
|
||||
"ProMicro",
|
||||
"R1Neo",
|
||||
"RAK_3401",
|
||||
"RAK_4631",
|
||||
"RAK_WisMesh_Tag",
|
||||
"SenseCap_Solar",
|
||||
"ThinkNode_M1",
|
||||
"ThinkNode_M3",
|
||||
"ThinkNode_M4",
|
||||
"ThinkNode_M6",
|
||||
"ThinkNode_M8",
|
||||
"WioTrackerL1",
|
||||
"WioTrackerL1-1W",
|
||||
"WioTrackerL1Eink",
|
||||
"Xiao_nrf52",
|
||||
"ikoka_handheld_nrf_e22_30dbm",
|
||||
"ikoka_handheld_nrf_e22_30dbm_096",
|
||||
"ikoka_nano_nrf_22dbm",
|
||||
"ikoka_nano_nrf_30dbm",
|
||||
"ikoka_nano_nrf_33dbm",
|
||||
"ikoka_stick_nrf_22dbm",
|
||||
"ikoka_stick_nrf_30dbm",
|
||||
"ikoka_stick_nrf_33dbm",
|
||||
"muzi_base_duo",
|
||||
"muzi_base_duo_superIO",
|
||||
"muzi_base_uno",
|
||||
"muzi_base_uno_superIO",
|
||||
"solarxiao_30S",
|
||||
"solarxiao_33S",
|
||||
"t1000e",
|
||||
"wio_wm1110"
|
||||
]
|
||||
}
|
||||
@@ -20,6 +20,29 @@ assert.strictEqual(picker.bootloaderForHardware(bootloaderCatalog, "Heltec_tower
|
||||
assert.strictEqual(picker.bootloaderForHardware(bootloaderCatalog, "RAK_3401", "nrf52").storage, "adaptive");
|
||||
assert.strictEqual(picker.bootloaderForHardware(bootloaderCatalog, "RAK_4631", "nrf52").id, "wiscore_rak4631_auto");
|
||||
assert.strictEqual(picker.bootloaderForHardware(bootloaderCatalog, "ThinkNode_M8", "nrf52"), null);
|
||||
const nrf52Hardware = require("./fixtures/firmware_picker_nrf52_1_17_1_7.json").hardware;
|
||||
assert.strictEqual(nrf52Hardware.length, 53);
|
||||
let mappedNrf52 = 0;
|
||||
for (const hardware of nrf52Hardware) {
|
||||
const download = picker.bootloaderForHardware(bootloaderCatalog, hardware, "nrf52");
|
||||
const unavailable = picker.unavailableBootloaderForHardware(bootloaderCatalog, hardware, "nrf52");
|
||||
assert.strictEqual(Number(!!download) + Number(!!unavailable), 1, hardware + " needs an explicit coverage decision");
|
||||
if (download) mappedNrf52++;
|
||||
else {
|
||||
assert(unavailable.reason.length > 20);
|
||||
assert.strictEqual(unavailable.files, undefined);
|
||||
}
|
||||
assert.strictEqual(picker.unavailableBootloaderForHardware(bootloaderCatalog, hardware, "esp32"), null);
|
||||
}
|
||||
assert.strictEqual(mappedNrf52, 39);
|
||||
for (const hardware of nrf52Hardware.filter(h => /^(ikoka_|solarxiao_)/.test(h))) {
|
||||
assert.strictEqual(picker.bootloaderForHardware(bootloaderCatalog, hardware, "nrf52").id, "xiao_nrf52840_ble");
|
||||
}
|
||||
for (const hardware of ["WioTrackerL1-1W", "WioTrackerL1Eink"]) {
|
||||
assert.strictEqual(picker.bootloaderForHardware(bootloaderCatalog, hardware, "nrf52").id, "wio_tracker_l1");
|
||||
}
|
||||
assert(picker.unavailableBootloaderForHardware(bootloaderCatalog, "GAT562_Mesh_Watch13", "nrf52").reason.includes("vibration motor"));
|
||||
assert(picker.unavailableBootloaderForHardware(bootloaderCatalog, "wio_wm1110", "nrf52").reason.includes("does not make it XIAO"));
|
||||
const nextBootloaderTag = "v0.11.0-OTAFIX2.4.11";
|
||||
const nextBootloaderRelease = {
|
||||
tag_name: nextBootloaderTag, draft: false, prerelease: false,
|
||||
@@ -44,6 +67,9 @@ assert.strictEqual(picker.bootloaderForHardware(picker.buildBootloaderCatalog(bo
|
||||
const ambiguousMapping = structuredClone(bootloaderManifest);
|
||||
ambiguousMapping.profiles[1].meshcoreHardware.push(ambiguousMapping.profiles[0].meshcoreHardware[0]);
|
||||
assert.throws(() => picker.buildBootloaderCatalog(ambiguousMapping), /Ambiguous bootloader hardware/);
|
||||
const shadowedMapping = structuredClone(bootloaderManifest);
|
||||
shadowedMapping.unavailableProfiles[0].meshcoreHardware.push("ikoka_stick_nrf_33dbm");
|
||||
assert.throws(() => picker.buildBootloaderCatalog(shadowedMapping), /Ambiguous bootloader hardware/);
|
||||
assert.throws(() => picker.buildBootloaderCatalog(bootloaderManifest, Object.assign({}, nextBootloaderRelease, {prerelease: true})), /stable OTAFIX/);
|
||||
console.log("exact bootloader downloads, storage variants, chip gating, latest version and unsafe catalog rejection passed");
|
||||
|
||||
|
||||
Reference in new issue
Block a user