From 36b80bb8cd09668e4963da37c7bd9af83312fa97 Mon Sep 17 00:00:00 2001 From: mikecarper Date: Sun, 23 Aug 2026 07:29:25 +0000 Subject: [PATCH] deploy: 003738766c97effbd601233194c61b11c4d79c61 --- 404.html | 31 + WiFi/index.html | 31 + _javascript/firmware_picker.js | 359 ++++++ _stylesheets/firmware_picker.css | 173 +++ cli_build_matrix/index.html | 31 + cli_command_availability/index.html | 31 + cli_commands/index.html | 31 + companion_offline_queue/index.html | 31 + companion_protocol/index.html | 31 + companion_radio_full/index.html | 31 + docs/index.html | 31 + faq/index.html | 31 + filter_tool/index.html | 33 +- firmware_picker/index.html | 1514 ++++++++++++++++++++++++ flood_filtering/index.html | 33 +- gps_tracking/index.html | 31 + halo_keymind_settings/index.html | 31 + index.html | 32 + kiss_modem_protocol/index.html | 31 + lora_ota_automation/index.html | 31 + mbedtls-tls-footprint/index.html | 31 + nrf52_power_management/index.html | 31 + number_allocations/index.html | 31 + ota_easy/index.html | 31 + ota_meshtower_v2_sdcard/index.html | 31 + ota_nrf52_bootloader_update/index.html | 31 + ota_nrf52_qspi/index.html | 31 + ota_protocol/index.html | 31 + ota_user_guide/index.html | 31 + packet_format/index.html | 31 + payloads/index.html | 31 + qr_codes/index.html | 31 + rak3401_mota_chain/index.html | 31 + search/search_index.json | 2 +- sitemap.xml | 68 +- sitemap.xml.gz | Bin 529 -> 541 bytes stats_binary_frames/index.html | 31 + telemetry_decoder/index.html | 31 + terminal_chat_cli/index.html | 31 + 39 files changed, 3109 insertions(+), 35 deletions(-) create mode 100644 _javascript/firmware_picker.js create mode 100644 _stylesheets/firmware_picker.css create mode 100644 firmware_picker/index.html diff --git a/404.html b/404.html index a24b318c..5f873a32 100644 --- a/404.html +++ b/404.html @@ -46,6 +46,8 @@ + + @@ -515,6 +517,33 @@ +
  • + + + + + + + + Firmware picker + + + + + + + + +
  • + + + + + + + + +
  • @@ -1148,6 +1177,8 @@ + + diff --git a/WiFi/index.html b/WiFi/index.html index 958e5f6a..71626f13 100644 --- a/WiFi/index.html +++ b/WiFi/index.html @@ -52,6 +52,8 @@ + + @@ -700,6 +702,33 @@ +
  • + + + + + + + + Firmware picker + + + + + + + + +
  • + + + + + + + + +
  • @@ -1978,6 +2007,8 @@ diagnostics apply only to a wifi_mqtt companion target.

    + + diff --git a/_javascript/firmware_picker.js b/_javascript/firmware_picker.js new file mode 100644 index 00000000..5b244786 --- /dev/null +++ b/_javascript/firmware_picker.js @@ -0,0 +1,359 @@ +(function (global) { + "use strict"; + + const PURPOSES = Object.freeze({ + "usb-logging": Object.freeze({ + title: "Station G2 FULL USB logging repeater", + summary: "Best for a G2 that normally remains connected to a computer collecting serial packet logs.", + pattern: /^Station_G2_repeater-full-logging-ota-/, + expected: "Station_G2_repeater-full-logging-ota-...", + full: true, + notes: [ + "Back up the node configuration before the first FULL install.", + "Use the merged image once to install the expanded partition table.", + "Reconnect at 115200 baud and leave the USB data connection attached for logging.", + ], + }), + field: Object.freeze({ + title: "Station G2 standard repeater", + summary: "The simplest sensor-enabled repeater for local USB installation and ordinary field use.", + pattern: /^Station_G2_repeater-v/, + expected: "Station_G2_repeater-v...", + full: false, + notes: [ + "Choose this when USB logging, MQTT, ESP-NOW, and remote LoRa updates are not required.", + "This keeps the standard Station G2 partition profile.", + ], + }), + "field-ota": Object.freeze({ + title: "Station G2 lean OTA repeater", + summary: "A standalone repeater with LoRa/WiFi update support and no optional external environmental-sensor drivers.", + pattern: /^Station_G2_repeater_lora_ota_no_external_sensors-ota-/, + expected: "Station_G2_repeater_lora_ota_no_external_sensors-ota-...", + full: false, + notes: [ + "Board-native GPS support remains available; only optional external sensor drivers are removed.", + "Use the exact matching target for every later LoRa package.", + ], + }), + mqtt: Object.freeze({ + title: "Station G2 FULL MQTT observer/repeater", + summary: "For direct radio-to-MQTT forwarding over WiFi with the complete CLI and expanded partitions.", + pattern: /^Station_G2_repeater_observer_mqtt-full-ota-/, + expected: "Station_G2_repeater_observer_mqtt-full-ota-...", + full: true, + notes: [ + "This is an MQTT observer role, not USB packet logging.", + "Use its merged image once before any app-only WiFi or LoRa update.", + ], + }), + espnow: Object.freeze({ + title: "Station G2 FULL ESP-NOW bridge repeater", + summary: "For a repeater that bridges packets through ESP-NOW and needs the complete CLI.", + pattern: /^Station_G2_repeater_bridge_espnow-full-ota-/, + expected: "Station_G2_repeater_bridge_espnow-full-ota-...", + full: true, + notes: ["ESP-NOW bridge builds use the expanded FULL partition profile."], + }), + room: Object.freeze({ + title: "Station G2 room server", + summary: "A standard room-server role without MQTT.", + pattern: /^Station_G2_room_server-v/, + expected: "Station_G2_room_server-v...", + full: false, + notes: ["Do not install this when the node is intended to remain a repeater."], + }), + "room-mqtt": Object.freeze({ + title: "Station G2 FULL MQTT room observer", + summary: "A room server with direct MQTT/WiFi forwarding and expanded partitions.", + pattern: /^Station_G2_room_server_observer_mqtt-full-ota-/, + expected: "Station_G2_room_server_observer_mqtt-full-ota-...", + full: true, + notes: ["Use its merged image once before later same-profile OTA updates."], + }), + "companion-usb": Object.freeze({ + title: "Station G2 USB Companion", + summary: "For a Station G2 controlled as a Companion through a USB serial connection.", + pattern: /^Station_G2_companion_radio_usb-ota-/, + expected: "Station_G2_companion_radio_usb-ota-...", + full: false, + notes: ["This is a Companion, not a standalone repeater or USB packet logger."], + }), + "companion-ble": Object.freeze({ + title: "Station G2 Bluetooth Companion", + summary: "For a Station G2 controlled by a phone or computer through Bluetooth.", + pattern: /^Station_G2_companion_radio_ble-/, + expected: "Station_G2_companion_radio_ble-...", + full: false, + notes: ["Choose USB Companion instead when the data connection is normally wired."], + }), + "companion-wifi": Object.freeze({ + title: "Station G2 WiFi Companion", + summary: "For Companion protocol access over WiFi.", + pattern: /^Station_G2_companion_radio_wifi-ota-/, + expected: "Station_G2_companion_radio_wifi-ota-...", + full: false, + notes: ["This is not the standalone MQTT observer role."], + }), + "companion-full": Object.freeze({ + title: "Station G2 Full Companion", + summary: "A host-backed Companion that can serve LoRa OTA content and exposes the complete transport set.", + pattern: /^Station_G2_companion_radio_full-v/, + expected: "Station_G2_companion_radio_full-...", + full: true, + notes: [ + "Full Companion is an OTA source and host-backed controller, not a self-updating repeater.", + "Install its merged image over USB.", + ], + }), + }); + + function isSafeGithubUrl(value) { + try { + const url = new URL(value); + return url.protocol === "https:" && + (url.hostname === "github.com" || url.hostname === "objects.githubusercontent.com"); + } catch (_) { + return false; + } + } + + function flattenReleases(releases, channel) { + const allowPrerelease = channel === "development"; + const rows = []; + (Array.isArray(releases) ? releases : []).forEach(function (release) { + if (!release || release.draft || (!allowPrerelease && release.prerelease)) return; + (Array.isArray(release.assets) ? release.assets : []).forEach(function (asset) { + if (!asset || typeof asset.name !== "string") return; + rows.push({ + name: asset.name, + url: isSafeGithubUrl(asset.browser_download_url) ? asset.browser_download_url : "", + size: Number(asset.size) || 0, + releaseName: release.name || release.tag_name || "Release", + releaseTag: release.tag_name || "", + releaseUrl: isSafeGithubUrl(release.html_url) ? release.html_url : "", + prerelease: Boolean(release.prerelease), + publishedAt: release.published_at || release.created_at || "", + }); + }); + }); + return rows.sort(function (a, b) { + return String(b.publishedAt).localeCompare(String(a.publishedAt)); + }); + } + + function wantedSuffix(install) { + return install === "first" ? "-merged.bin" : ".bin"; + } + + function selectAsset(rows, purposeKey, install) { + const purpose = PURPOSES[purposeKey]; + if (!purpose) return null; + const suffix = wantedSuffix(install); + const matches = rows.filter(function (row) { + if (!purpose.pattern.test(row.name) || !row.name.endsWith(suffix)) return false; + if (install !== "first" && row.name.endsWith("-merged.bin")) return false; + return true; + }); + matches.sort(function (a, b) { + const byDate = String(b.publishedAt).localeCompare(String(a.publishedAt)); + if (byDate) return byDate; + const byLength = a.name.length - b.name.length; + return byLength || a.name.localeCompare(b.name); + }); + return matches[0] || null; + } + + function formatBytes(value) { + const size = Number(value) || 0; + if (size >= 1024 * 1024) return (size / (1024 * 1024)).toFixed(2) + " MiB"; + if (size >= 1024) return (size / 1024).toFixed(1) + " KiB"; + return size + " bytes"; + } + + function installExplanation(install) { + if (install === "first") { + return { + label: "USB merged-image install", + warning: "This can replace the partition table. Back up configuration first.", + }; + } + if (install === "lora") { + return { + label: "LoRa OTA source image", + warning: "Package the non-merged application for the exact target. The installed partition signature must already match.", + }; + } + return { + label: "Browser/WiFi app-only update", + warning: "Use only when the installed board, role, and partition layout already match.", + }; + } + + function createElement(tag, text) { + const element = document.createElement(tag); + if (text != null) element.textContent = text; + return element; + } + + function replaceFacts(container, facts) { + container.replaceChildren(); + facts.forEach(function (fact) { + container.appendChild(createElement("dt", fact[0])); + const dd = createElement("dd", fact[1]); + if (fact[2]) dd.title = fact[2]; + container.appendChild(dd); + }); + } + + function renderSteps(container, purpose, install) { + container.replaceChildren(); + container.appendChild(createElement("strong", "What to do")); + const list = createElement("ol"); + const common = install === "first" + ? [ + "Back up configuration, keys, and radio settings.", + "Verify that the filename begins with Station_G2 before flashing.", + "Flash the merged image over a data-capable USB cable.", + "Wait for the board to reboot, reconnect, and verify its role and radio settings.", + ] + : install === "lora" + ? [ + "Confirm the running target and partition signature already match this profile.", + "Use the non-merged application as input to the LoRa OTA packaging workflow.", + "Verify the target identity and completed update after reboot.", + ] + : [ + "Confirm the running target and partition signature already match this profile.", + "Upload the non-merged application through the firmware's browser/WiFi updater.", + "Verify the role and version after reboot.", + ]; + common.concat(purpose.notes).forEach(function (text) { + list.appendChild(createElement("li", text)); + }); + container.appendChild(list); + } + + function initPicker(root) { + const repo = root.getAttribute("data-release-repo") || "mikecarper/MeshCore"; + const form = root.querySelector('[data-role="form"]'); + const channelField = root.querySelector('[data-field="channel"]'); + const purposeField = root.querySelector('[data-field="purpose"]'); + const installField = root.querySelector('[data-field="install"]'); + const status = root.querySelector('[data-role="status"]'); + const result = root.querySelector('[data-role="result"]'); + const missing = root.querySelector('[data-role="missing"]'); + const search = root.querySelector('[data-field="asset-search"]'); + const assetResults = root.querySelector('[data-role="asset-results"]'); + let releases = []; + + function currentRows() { + return flattenReleases(releases, channelField.value); + } + + function render() { + const purpose = PURPOSES[purposeField.value]; + const install = installField.value; + const rows = currentRows(); + const asset = selectAsset(rows, purposeField.value, install); + const installInfo = installExplanation(install); + result.hidden = true; + missing.hidden = true; + + if (!asset) { + missing.hidden = false; + const suffix = wantedSuffix(install); + root.querySelector('[data-role="missing-text"]').textContent = + "Expected a recent asset beginning with " + purpose.expected + + " and ending in " + suffix + ". Try the other release channel or open the release collection."; + return; + } + + root.querySelector('[data-role="result-title"]').textContent = purpose.title; + root.querySelector('[data-role="result-summary"]').textContent = purpose.summary; + replaceFacts(root.querySelector('[data-role="result-facts"]'), [ + ["File", asset.name], + ["Release", asset.releaseName], + ["Install path", installInfo.label], + ["Size", formatBytes(asset.size)], + ["Important", installInfo.warning], + ]); + const download = root.querySelector('[data-role="download"]'); + download.href = asset.url || asset.releaseUrl; + download.hidden = !(asset.url || asset.releaseUrl); + const releaseLink = root.querySelector('[data-role="release"]'); + releaseLink.href = asset.releaseUrl || "https://github.com/" + repo + "/releases"; + renderSteps(root.querySelector('[data-role="steps"]'), purpose, install); + result.hidden = false; + } + + function renderSearch() { + assetResults.replaceChildren(); + const query = search.value.trim().toLowerCase(); + if (query.length < 2) return; + const matches = currentRows().filter(function (row) { + return row.name.toLowerCase().includes(query); + }).slice(0, 80); + if (!matches.length) { + assetResults.appendChild(createElement("p", "No recent release filename matched.")); + return; + } + const list = createElement("ul"); + list.className = "firmware-asset-list"; + matches.forEach(function (row) { + const item = createElement("li"); + const link = createElement("a", row.name); + link.href = row.url || row.releaseUrl; + item.appendChild(link); + const meta = createElement("span", row.releaseName + " - " + formatBytes(row.size)); + meta.className = "firmware-asset-meta"; + item.appendChild(meta); + list.appendChild(item); + }); + assetResults.appendChild(list); + } + + form.addEventListener("change", function () { + render(); + renderSearch(); + }); + search.addEventListener("input", renderSearch); + + const endpoint = "https://api.github.com/repos/" + encodeURIComponent(repo.split("/")[0]) + + "/" + encodeURIComponent(repo.split("/")[1]) + "/releases?per_page=20"; + fetch(endpoint, { headers: { Accept: "application/vnd.github+json" } }) + .then(function (response) { + if (!response.ok) throw new Error("GitHub returned HTTP " + response.status); + return response.json(); + }) + .then(function (data) { + releases = Array.isArray(data) ? data : []; + const count = flattenReleases(releases, "development").length; + status.textContent = "Loaded " + count + " firmware assets from the 20 most recent releases."; + render(); + }) + .catch(function (error) { + status.textContent = "Could not load the GitHub release catalog: " + error.message; + missing.hidden = false; + root.querySelector('[data-role="missing-text"]').textContent = + "Open the release collection and look for the expected filename shown by this picker."; + }); + } + + const api = Object.freeze({ + PURPOSES: PURPOSES, + flattenReleases: flattenReleases, + selectAsset: selectAsset, + wantedSuffix: wantedSuffix, + formatBytes: formatBytes, + }); + + if (typeof module !== "undefined" && module.exports) module.exports = api; + global.MeshCoreFirmwarePicker = api; + + if (typeof document !== "undefined") { + document.addEventListener("DOMContentLoaded", function () { + document.querySelectorAll("[data-firmware-picker]").forEach(initPicker); + }); + } +})(typeof globalThis !== "undefined" ? globalThis : this); diff --git a/_stylesheets/firmware_picker.css b/_stylesheets/firmware_picker.css new file mode 100644 index 00000000..ba53b946 --- /dev/null +++ b/_stylesheets/firmware_picker.css @@ -0,0 +1,173 @@ +.firmware-picker { + --picker-accent: #0f766e; + --picker-accent-dark: #115e59; + --picker-border: color-mix(in srgb, currentColor 18%, transparent); + margin: 1.25rem 0 2rem; +} + +.firmware-picker-intro, +.firmware-picker-result, +.firmware-picker-missing, +.firmware-asset-browser { + border: 1px solid var(--picker-border); + border-radius: 0.7rem; + padding: 1rem 1.15rem; +} + +.firmware-picker-intro { + border-left: 0.35rem solid var(--picker-accent); + background: color-mix(in srgb, var(--picker-accent) 8%, transparent); +} + +.firmware-picker-intro p, +.firmware-picker-result p, +.firmware-picker-missing p { + margin-bottom: 0; +} + +.firmware-picker-form { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr)); + gap: 0.9rem; + margin: 1.2rem 0; +} + +.firmware-picker-form label, +.firmware-asset-browser label { + display: grid; + gap: 0.35rem; + font-weight: 650; +} + +.firmware-picker-form select, +.firmware-asset-browser input { + width: 100%; + min-height: 2.7rem; + padding: 0.45rem 0.6rem; + border: 1px solid var(--picker-border); + border-radius: 0.35rem; + color: inherit; + background: var(--md-default-bg-color); + font: inherit; +} + +.firmware-picker-status { + min-height: 1.5rem; + margin: 0.75rem 0; + color: var(--md-default-fg-color--light); +} + +.firmware-picker-result { + border-top: 0.3rem solid var(--picker-accent); +} + +.firmware-picker-eyebrow { + margin: 0 0 0.15rem !important; + color: var(--picker-accent); + font-size: 0.75rem; + font-weight: 750; + letter-spacing: 0.08em; + text-transform: uppercase; +} + +.firmware-picker-result h2, +.firmware-picker-missing h2 { + margin-top: 0.2rem; +} + +.firmware-picker-facts { + display: grid; + grid-template-columns: minmax(7rem, auto) 1fr; + gap: 0.35rem 0.9rem; + margin: 1rem 0; +} + +.firmware-picker-facts dt { + font-weight: 700; +} + +.firmware-picker-facts dd { + min-width: 0; + margin: 0; + overflow-wrap: anywhere; +} + +.firmware-picker-actions { + display: flex; + flex-wrap: wrap; + gap: 0.65rem; + margin: 1rem 0; +} + +.firmware-picker-actions a { + display: inline-block; + padding: 0.55rem 0.8rem; + border: 1px solid var(--picker-accent); + border-radius: 0.4rem; + font-weight: 700; + text-decoration: none; +} + +.firmware-picker-actions .firmware-picker-primary { + color: white; + background: var(--picker-accent); +} + +.firmware-picker-actions .firmware-picker-primary:hover { + background: var(--picker-accent-dark); +} + +.firmware-picker-steps { + border-top: 1px solid var(--picker-border); + margin-top: 1rem; + padding-top: 0.8rem; +} + +.firmware-picker-steps ol { + margin-bottom: 0; +} + +.firmware-picker-missing { + border-left: 0.35rem solid #b45309; +} + +.firmware-asset-browser { + margin-top: 1.2rem; +} + +.firmware-asset-browser summary { + cursor: pointer; + font-weight: 700; +} + +.firmware-asset-list { + max-height: 24rem; + margin-top: 0.8rem; + padding-left: 1.2rem; + overflow: auto; +} + +.firmware-asset-list li { + margin: 0.35rem 0; + overflow-wrap: anywhere; +} + +.firmware-asset-meta { + display: block; + color: var(--md-default-fg-color--light); + font-size: 0.78rem; +} + +@media (max-width: 44rem) { + .firmware-picker-form { + grid-template-columns: 1fr; + } + + .firmware-picker-facts { + grid-template-columns: 1fr; + } + + .firmware-picker-facts dd + dt { + margin-top: 0.35rem; + } +} diff --git a/cli_build_matrix/index.html b/cli_build_matrix/index.html index 9cfa8971..c039f770 100644 --- a/cli_build_matrix/index.html +++ b/cli_build_matrix/index.html @@ -52,6 +52,8 @@ + + @@ -628,6 +630,33 @@ +
  • + + + + + + + + Firmware picker + + + + + + + + +
  • + + + + + + + + +
  • @@ -1488,6 +1517,8 @@ profile, and finally the target's compiled hardware features.

    + + diff --git a/cli_command_availability/index.html b/cli_command_availability/index.html index d752fb18..14564a1e 100644 --- a/cli_command_availability/index.html +++ b/cli_command_availability/index.html @@ -52,6 +52,8 @@ + + @@ -744,6 +746,33 @@ +
  • + + + + + + + + Firmware picker + + + + + + + + +
  • + + + + + + + + +
  • @@ -4487,6 +4516,8 @@ selected by that manifest.

    + + diff --git a/cli_commands/index.html b/cli_commands/index.html index a2668695..442a8628 100644 --- a/cli_commands/index.html +++ b/cli_commands/index.html @@ -52,6 +52,8 @@ + + @@ -2424,6 +2426,33 @@ +
  • + + + + + + + + Firmware picker + + + + + + + + +
  • + + + + + + + + +
  • @@ -7603,6 +7632,8 @@ non-PSRAM variants opt in with MQTT_NEIGHBORS_WITHOUT_PSRAM.

    + + diff --git a/companion_offline_queue/index.html b/companion_offline_queue/index.html index 6838d2a0..da18fe85 100644 --- a/companion_offline_queue/index.html +++ b/companion_offline_queue/index.html @@ -52,6 +52,8 @@ + + @@ -595,6 +597,33 @@ +
  • + + + + + + + + Firmware picker + + + + + + + + +
  • + + + + + + + + +
  • @@ -1353,6 +1382,8 @@ protocol setting to resize it at runtime.

    + + diff --git a/companion_protocol/index.html b/companion_protocol/index.html index 9bcd9154..f192527e 100644 --- a/companion_protocol/index.html +++ b/companion_protocol/index.html @@ -52,6 +52,8 @@ + + @@ -1149,6 +1151,33 @@ +
  • + + + + + + + + Firmware picker + + + + + + + + +
  • + + + + + + + + +
  • @@ -3881,6 +3910,8 @@ response = wait_for_response(PACKET_MSG_SENT) + + diff --git a/companion_radio_full/index.html b/companion_radio_full/index.html index 5d10688f..b0d6ac26 100644 --- a/companion_radio_full/index.html +++ b/companion_radio_full/index.html @@ -52,6 +52,8 @@ + + @@ -667,6 +669,33 @@ +
  • + + + + + + + + Firmware picker + + + + + + + + +
  • + + + + + + + + +
  • @@ -1822,6 +1851,8 @@ selection, nRF52 in-place deltas, relays, trust checks, and recovery behavior. + + diff --git a/docs/index.html b/docs/index.html index 205d1033..cc0446f5 100644 --- a/docs/index.html +++ b/docs/index.html @@ -52,6 +52,8 @@ + + @@ -584,6 +586,33 @@ +
  • + + + + + + + + Firmware picker + + + + + + + + +
  • + + + + + + + + +
  • @@ -1271,6 +1300,8 @@ pip install mkdocs-material + + diff --git a/faq/index.html b/faq/index.html index 1ad91025..1b290f02 100644 --- a/faq/index.html +++ b/faq/index.html @@ -52,6 +52,8 @@ + + @@ -1518,6 +1520,33 @@ +
  • + + + + + + + + Firmware picker + + + + + + + + +
  • + + + + + + + + +
  • @@ -3978,6 +4007,8 @@ Flash one of the Ethernet-enabled firmware variants: + + diff --git a/filter_tool/index.html b/filter_tool/index.html index 8558c26b..50eeafc6 100644 --- a/filter_tool/index.html +++ b/filter_tool/index.html @@ -16,7 +16,7 @@ - + @@ -52,6 +52,8 @@ + + @@ -617,6 +619,33 @@ +
  • + + + + + + + + Firmware picker + + + + + + + + +
  • + + + + + + + + +
  • @@ -2051,6 +2080,8 @@ sized structure for every possible condition and action.

    + + diff --git a/firmware_picker/index.html b/firmware_picker/index.html new file mode 100644 index 00000000..3118dc1d --- /dev/null +++ b/firmware_picker/index.html @@ -0,0 +1,1514 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Firmware picker - MeshCore Docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + Skip to content + + +
    +
    + +
    + + + + + + +
    + + +
    + +
    + + + + + + +
    +
    + + + +
    +
    +
    + + + + + +
    +
    +
    + + + + + + + +
    + +
    + + + + + + + + + + + + + +

    Firmware picker

    +

    Choose what the board will do, then let the picker reduce the release assets +to one recommended firmware. The first guided board is Station G2 because its +standard, logging, FULL, LoRa-OTA, MQTT, bridge, room-server, and Companion +profiles are easy to confuse.

    +

    The picker reads public release metadata from GitHub. It does not upload any +device information.

    +
    +
    + Station G2 shortcut +

    + If the G2 normally stays connected to a computer and you want USB packet + logs, choose USB logging repeater. The recommended image + is the expanded FULL logging profile. The smaller legacy + logging image is mainly a compatibility fallback, not the normal choice + for a computer-powered logger. +

    +
    + +
    + + + + + +
    + +
    + Loading release catalog... +
    + + + + + +
    + Advanced: search all recent release filenames +

    + This search is for uncommon hardware variants and expert recovery. A + filename match is not a board-identity check. +

    + +
    +
    +
    + +

    What FULL means for a USB logger

    +

    A Station G2 that is powered from and monitored by a computer normally should +use the FULL logging profile:

    +
      +
    1. Save its name, radio settings, keys, and other configuration.
    2. +
    3. Download the exact Station G2 full-logging-ota merged image recommended + above.
    4. +
    5. Flash that -merged.bin over USB once. It installs the expanded partition + table as well as the application.
    6. +
    7. Reconnect the serial terminal at 115200 baud and restore any settings that + were not retained.
    8. +
    9. Leave the data-capable USB connection attached to collect logs. Use + set usb.logging off temporarily if the runtime output needs to be quiet.
    10. +
    +

    Choose the standalone or lean OTA repeater instead when the board is normally +battery/solar powered, has no computer collecting USB output, or must retain a +known legacy partition layout.

    +

    Can a Heltec V4 partition be expanded by OTA?

    +

    No. WiFi OTA and LoRa OTA write an application into the inactive application +partition. They do not replace the partition table at flash offset 0x8000. +Allowing a running application to move its own active/inactive partitions +would risk overwriting the running image, staged image, NVS, or filesystem.

    +

    The current Heltec V4 definition already uses its 16 MiB flash efficiently:

    + + + + + + + + + + + + + + + + + + + + + +
    RegionSize
    Application slot A0x640000 (6.25 MiB)
    Application slot B0x640000 (6.25 MiB)
    SPIFFS0x360000 (3.375 MiB)
    +

    That is much larger than current MeshCore V4 applications. A different layout +is possible only as a custom build, and it requires a one-time exact-board +merged-image flash over USB. Expanding the app slots further would shrink or +remove SPIFFS, or remove the second slot and therefore remove safe OTA.

    +

    After installing a new partition table over USB, later WiFi or LoRa updates +must use a non-merged application built for the same board, role, and partition +signature. Never use an app-only FULL image to try to migrate a device that is +still running another partition layout.

    +

    Merged versus non-merged files

    + + + + + + + + + + + + + + + + + + + + + +
    FileUse
    -merged.binFirst install, recovery, role/profile migration, or partition-table change over USB
    .bin without -mergedBrowser/WiFi application update when the installed partition layout already matches
    .motaLoRa OTA package for the exact target identity and installed partition layout
    +

    When uncertain, back up the configuration and use the exact-board merged image +over USB. Do not send a merged image through browser OTA or LoRa OTA.

    + + + + + + + + + + + + + +
    +
    + + + +
    + +
    + + + +
    +
    +
    +
    + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/flood_filtering/index.html b/flood_filtering/index.html index 07cf8fa1..c5f43ef5 100644 --- a/flood_filtering/index.html +++ b/flood_filtering/index.html @@ -13,7 +13,7 @@ - + @@ -52,6 +52,8 @@ + + @@ -524,6 +526,33 @@ + + +
  • + + + + + + + + Firmware picker + + + + + + + + +
  • + + + + + + + @@ -2389,6 +2418,8 @@ inspect or reset those separately when troubleshooting.

    + + diff --git a/gps_tracking/index.html b/gps_tracking/index.html index fc1341d1..1055348b 100644 --- a/gps_tracking/index.html +++ b/gps_tracking/index.html @@ -52,6 +52,8 @@ + + @@ -526,6 +528,33 @@ +
  • + + + + + + + + Firmware picker + + + + + + + + +
  • + + + + + + + + +
  • @@ -1479,6 +1508,8 @@ fresh data is available.

    + + diff --git a/halo_keymind_settings/index.html b/halo_keymind_settings/index.html index a5f38a1b..8fd6aeb7 100644 --- a/halo_keymind_settings/index.html +++ b/halo_keymind_settings/index.html @@ -52,6 +52,8 @@ + + @@ -526,6 +528,33 @@ +
  • + + + + + + + + Firmware picker + + + + + + + + +
  • + + + + + + + + +
  • @@ -2108,6 +2137,8 @@ set flood.retry.path 2 + + diff --git a/index.html b/index.html index 77c729a2..2b16a0ac 100644 --- a/index.html +++ b/index.html @@ -50,6 +50,8 @@ + + @@ -534,6 +536,33 @@ +
  • + + + + + + + + Firmware picker + + + + + + + + +
  • + + + + + + + + +
  • @@ -1143,6 +1172,7 @@

    Below are a few quick start guides.