Clarify 1.18.0.1 changes and firmware download choices

This commit is contained in:
mikecarper committed 2026-10-24 18:41:29 -07:00
1 parent 06edf9aa8a
commit da08008893
8 files changed
+428 -210

No files matched your search

+35 -5
View File
@@ -73,8 +73,38 @@ release title/body, and the final asset inventory after publication. Preserve
existing assets when making a documentation-only release edit.
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.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.
## Release notes
Write one public release page at `docs/releases/VERSION.md`. Make it the
starting point from the documentation home page and every GitHub download page.
Use the same format for each revision:
1. **Status and purpose:** stable or testing, version, region, and one sentence
explaining the user benefit.
2. **New:** a short list of actual additions since the previous stable release.
Say who each change helps; link setup instructions beside the feature.
3. **Fixed:** name affected boards and the behavior corrected.
4. **Choose a download:** picker first, then a short table of role-specific
GitHub pages. Explain Full/Reduced sensors and included bootloaders once.
5. **Before updating:** only required actions, changed defaults and compatibility
limits. Link the installation guides instead of copying them.
6. **Tested and limitations:** separate build checks from physical tests. Name
important remaining limitations and link source-specific evidence.
Compare the actual previous release's source and notes. A long commit list can
include old work imported by merges; do not present it all as new. Do not relist
existing features as additions. Keep tuning commands, build counts, memory
layouts and implementation details in the reference guides or test reports.
The main GitHub release should summarize New, Fixed and Tested in a few bullets
and link the public release page. Other download pages should lead with their
purpose and link the same notes. Use readable link names such as "ESP32
Repeater, Room Server and Sensor" rather than internal keys like `full-profiles`.
Label split pages "part 1 of 3" and explain that they contain the same version,
not different feature levels. The picker should find the exact board for users.
Keep required bootloader guidance linked to the exact board/storage
[OTAFIX release](https://github.com/mikecarper/Adafruit_nRF52_Bootloader_OTAFIX/releases/tag/v0.11.0-OTAFIX2.4.12).
A notes-only correction may update public titles, bodies and documentation.
Preserve published tags, binaries, attached build notes and checksum files;
verify release status, Latest and the asset inventory remain unchanged.
+13 -5
View File
@@ -4,14 +4,21 @@
# MeshCore documentation
Choose firmware with the [firmware picker](firmware_picker.md). For help with a device already in service, start with the [FAQ](faq.md).
## Start here
## Current release
**Current stable release: [1.18.0.1 USA Cascade](releases/1.18.0.1.md).**
Read what changed, which boards benefit and the known limitations in one place.
- [1.18.0.1 USA Cascade release notes](releases/1.18.0.1.md)
- [Test Results](test-results/index.md) - source-specific validation archive
| I want to... | Start here |
| --- | --- |
| See what is new or fixed | [1.18.0.1 release notes](releases/1.18.0.1.md) |
| Download firmware for my board | [Firmware picker](firmware_picker.md) |
| Install an update | [Update guide](ota_user_guide.md) |
| Turn a feature on or off | [Feature controls by role](role_feature_switches.md) |
| Find a command | [CLI reference](cli_commands.md) |
| Troubleshoot a device | [FAQ](faq.md) |
## Documentation
## Detailed guides
- [SolarXiao Base/Advanced/MPPT detection and battery controls](solarxiao_power.md)
@@ -32,6 +39,7 @@ The navigation groups the remaining guides under Documentation, including board-
## Engineering records
- [Test Results](test-results/index.md) - hardware and build evidence, with the tested versions
- [Research](research/index.md) - design notes, diagnostics, and historical update-chain studies
- [Old Releases](old-releases/index.md) - historical release notes and supporting files
+108 -90
View File
@@ -4,13 +4,15 @@
# Updating your node over the air (OTA) - user guide
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)
for nRF52 OTAFIX installations. New internal-flash hybrid receivers require
its 64 KiB retained-RAM handoff; QSPI and microSD targets require their own
matching bootloader profiles. Earlier preview versions mentioned below
describe compatibility/migration history, not the current recommended download.
Full Companion is a MOTA source and normally updates itself over USB.
For **[1.18.0.1 USA Cascade](releases/1.18.0.1.md)**, choose the exact board,
role and storage layout in the [firmware picker](firmware_picker.md). Supported
nRF52 Repeater and Room Server images can include the matching OTAFIX 2.4.12
bootloader; see the [embedded bootloader guide](nrf52_embedded_bootloader.md).
Ordinary application images preserve the installed bootloader. Standalone
bootloader downloads require an exact board/storage match from
[OTAFIX 2.4.12](https://github.com/mikecarper/Adafruit_nRF52_Bootloader_OTAFIX/releases/tag/v0.11.0-OTAFIX2.4.12).
Full Companion sends LoRa updates to other nodes and normally updates itself
over USB. Older versions mentioned below describe compatibility history.
Before a Companion upgrade, export its contacts when possible. ESP32, RP2040
and STM32 can retain complete legacy records ahead of an interrupted final
@@ -60,90 +62,19 @@ preserves the RX benefit without risking missed packets from a legacy
> application image alone is not enough. An intermediate repeater only relays packets and needs neither an
> install-capable image nor OTAFIX. Check the bootloader release for an exact board match before attempting an update.
The following nRF52 repeater families gained firmware-side LoRa OTA targets in
this release. Their ordinary repeater keeps its complete declared sensor recipe;
the install-capable `lora_ota_no_external_sensors` sibling is smaller:
- Heltec Mesh Solar, T1, and Tower V2
- Keepteen LT1, LilyGo T-Impulse Plus, Mesh Pocket, and Nano G2 Ultra
- Minewsemi ME25LS01, RAK3401, SenseCAP Solar, and Wio WM1110
The RAK3401 `RAK_3401_repeater_lora_ota_no_external_sensors` image trims selected
optional environmental/ranging drivers but does not disable I2C. It retains the
INA219, INA226, INA260, and INA3221 voltage/current monitors, board display and
RTC support, and both RAK12500 I2C and RAK12501/L76K UART GPS paths. The four
INA drivers are only the retained voltage/current entries in the optional
environmental-sensor table; they are not the only I2C users. The SSD1306 OLED,
autodiscovered DS3231/RV3028/PCF8563/RX8130CE RTCs, and RAK12500 GPS use I2C as
separate board peripherals. Install one GPS module in sensor slot A. Slot D's
reset/PPS lines conflict with the RAK13302 radio's BUSY/DIO1 lines.
The firmware-configured INA3221 address and the RAK12500 address are both
`0x42`, so those devices cannot share one bus at those addresses. To install
both, leave the RAK12500 at `0x42`, strap INA3221 A0 to SCL for `0x43`, and use
a firmware build with `-DTELEM_INA3221_ADDRESS=0x43`.
The reduced RAK4631 profiles likewise retain the four INA monitors. The plain
repeater and Serial2 bridge retain GPS: a RAK12501 can use sensor slot A or D,
and a RAK12500 can use slot A or C. The runtime RS-232 bridge defaults to
Serial2 so the UART RAK12501 can retain Serial1. The merged runtime image always
reserves Serial1: a bounded silent probe cannot prove that a cold RAK12501 is
absent, and turning the GPS setting off cannot stop a fitted module from driving
the shared UART while WB_IO2/3V3_S remains powered. Use Serial2. Serial1 requires
an explicit no-GPS/dedicated image. The reservation remains fail-closed even
when an I2C RAK12500 is detected. The explicitly compiled
`RAK_4631_repeater_bridge_rs232_serial1_lora_ota_no_external_sensors` target
omits the combined GPS provider because its bridge owns the UART used by
RAK12501; consequently that legacy image does not expose the I2C RAK12500 path
either, even though RAK12500 itself does not use the UART.
Selected nRF52 repeaters with dedicated external QSPI can now stage the
complete package off-chip, so their normal full-sensor repeater build can
install a full image or an in-place delta. The current matched families are
XIAO nRF52840 and its XIAO-module derivatives, original LilyGo T-Echo,
ThinkNode M1/M6, Wio Tracker L1, SenseCAP Solar, and the dedicated RAK4631 +
RAK15001 slot-C target. The list also includes a separately wired W25Q16JV on
RAK19007 with either RAK4631 or RAK3401 + RAK13302. These require the
corresponding exact QSPI-aware OTAFIX bootloader; see
[the nRF52 QSPI guide](ota_nrf52_qspi.md).
The W25Q16 option uses one permanent RAK19007 wiring for either core: CLK/MISO/
MOSI tap IO-connector pins 26/27/28, flash CS uses J11 AIN1, and power uses J12
VDD (3.3 V) and GND. Add an approximately 10 kOhm CS-to-VDD pull-up and never
power the flash from VBAT. The three SPI signals require underside pads or an
IO-connector interposer; the easy J10/J11/J12 headers do not expose them. The
flash consumes no WisBlock slot, so a supported GPS can remain in slot A.
The unified RAK repeater images additionally support a header-only W25Q16
layout, using J10 `TX1`/`RX1` and J11 `IO1`/`AIN1`. This layout needs the
matching compatible OTAFIX 2.4.10 bootloader and gives up UART1 GPS/PPS; I2C
sensors remain available. See the [pin table](ota_nrf52_qspi.md#header-only-w25q16-wiring-for-the-unified-rak-images).
The ordinary full-sensor `RAK_4631_repeater` image remains too large for the
safe internal in-place update limit. Without external flash, use
`RAK_4631_repeater_lora_ota_no_external_sensors`; it trims selected optional
environmental/ranging drivers while retaining generic I2C, battery monitoring,
the four INA monitors, and target-compatible GPS. A RAK4631 fitted with
RAK15001 in sensor slot C can instead use
`RAK_4631_repeater_rak15001_slot_c_lora_ota` to retain the full sensor/GPS set
and stage full images or deltas off-chip.
For the external Winbond breakout, use
`RAK_4631_repeater_w25q16_lora_ota` on RAK4631 or
`RAK_3401_repeater_rak13302_w25q16_lora_ota` on RAK3401 + RAK13302. They retain
the full sensor/GPS recipe and require exactly a 2 MiB `EF4015` W25Q16 plus the
bootloader for that same core/radio/flash combination. Install
`wiscore_rak4631_w25q16` for RAK4631 or
`wiscore_rak3401_rak13302_w25q16` for RAK3401 + RAK13302 as a one-time
prerequisite. The physical wiring is shared, but the two firmware identities
are deliberately different and cannot be substituted for one another.
For nRF52 storage and wiring requirements, see the
[QSPI hardware guide](ota_nrf52_qspi.md). Older image names and board-specific
constraints are collected in the [hardware reference](#hardware-and-older-image-layouts)
at the end of this page; use the picker for current downloads.
---
## The important part first: it's safe
## Before you start
- **Nothing installs by itself.** Your node can *discover* and download an update, but
it only **installs** when you say so (unless you deliberately turn on auto-install - see below).
- **Application updates require installation approval** unless you enable
auto-install. Images labelled **with bootloader** have a separate automatic
bootloader check after 70 seconds; read the [bootloader guide](nrf52_embedded_bootloader.md)
before choosing one.
- **Bad downloads can't sneak in.** Every piece of the firmware is checked against a cryptographic
fingerprint as it arrives, and the whole image is verified again before install. A corrupt or tampered
download is rejected, not installed.
@@ -152,8 +83,9 @@ are deliberately different and cannot be substituted for one another.
- **Discovery stays quiet; a transfer is deliberate.** Periodic update discovery uses background priority.
Once a download starts, its transfer packets are primary traffic across every relay hop, so use
TempRadio as an OTA maintenance window when delaying unrelated mesh traffic would matter.
- **It can recover.** If an install ever fails, the node falls back to a safe recovery mode (you can
re-flash a known-good firmware over USB) - it won't be left bricked.
- **Know the recovery route.** Keep local USB/DFU or SWD access available where
practical. Recovery depends on the board, installed bootloader and failure;
a bootloader update can require a debugger if interrupted.
---
@@ -402,7 +334,8 @@ complete target inventory, storage layouts, and safety contract.
without valid live `EndF`, they refuse every internal pull before erase
because their normal application can extend through `0xED000`. Recover one
of those builds over USB/BLE DFU or SWD.
- If an **install** fails, the node won't boot a broken image - it lands in **recovery mode**:
- If an **install** fails, recovery depends on the board and bootloader. When
its recovery mode remains available:
- **nRF52:** it appears as a USB drive; drag a known-good firmware `.uf2` for that exact board onto it
to recover.
- **ESP32:** it keeps the previous firmware in the other slot and rolls back.
@@ -710,3 +643,88 @@ that only contains what changed). You get them by:
For the full technical details (the file format and the radio protocol), see
[the OTA protocol spec](ota_protocol.md).
## Hardware and older image layouts
Use the [firmware picker](firmware_picker.md) for current downloads. These
notes retain hardware constraints and older image names for existing nodes.
The 1.18.0.1 RAK release has separately checked Full/Reduced sensor images;
older size limits below are not instructions to choose a Reduced build today.
Older firmware used separate `lora_ota_no_external_sensors` images on the
following families. These names explain older installations; use the current
picker's Full/Reduced sensor choices for 1.18.0.1:
- Heltec Mesh Solar, T1, and Tower V2
- Keepteen LT1, LilyGo T-Impulse Plus, Mesh Pocket, and Nano G2 Ultra
- Minewsemi ME25LS01, RAK3401, SenseCAP Solar, and Wio WM1110
The RAK3401 `RAK_3401_repeater_lora_ota_no_external_sensors` image trims selected
optional environmental/ranging drivers but does not disable I2C. It retains the
INA219, INA226, INA260, and INA3221 voltage/current monitors, board display and
RTC support, and both RAK12500 I2C and RAK12501/L76K UART GPS paths. The four
INA drivers are only the retained voltage/current entries in the optional
environmental-sensor table; they are not the only I2C users. The SSD1306 OLED,
autodiscovered DS3231/RV3028/PCF8563/RX8130CE RTCs, and RAK12500 GPS use I2C as
separate board peripherals. Install one GPS module in sensor slot A. Slot D's
reset/PPS lines conflict with the RAK13302 radio's BUSY/DIO1 lines.
The firmware-configured INA3221 address and the RAK12500 address are both
`0x42`, so those devices cannot share one bus at those addresses. To install
both, leave the RAK12500 at `0x42`, strap INA3221 A0 to SCL for `0x43`, and use
a firmware build with `-DTELEM_INA3221_ADDRESS=0x43`.
The reduced RAK4631 profiles likewise retain the four INA monitors. The plain
repeater and Serial2 bridge retain GPS: a RAK12501 can use sensor slot A or D,
and a RAK12500 can use slot A or C. The runtime RS-232 bridge defaults to
Serial2 so the UART RAK12501 can retain Serial1. The merged runtime image always
reserves Serial1: a bounded silent probe cannot prove that a cold RAK12501 is
absent, and turning the GPS setting off cannot stop a fitted module from driving
the shared UART while WB_IO2/3V3_S remains powered. Use Serial2. Serial1 requires
an explicit no-GPS/dedicated image. The reservation remains fail-closed even
when an I2C RAK12500 is detected. The explicitly compiled
`RAK_4631_repeater_bridge_rs232_serial1_lora_ota_no_external_sensors` target
omits the combined GPS provider because its bridge owns the UART used by
RAK12501; consequently that legacy image does not expose the I2C RAK12500 path
either, even though RAK12500 itself does not use the UART.
Selected nRF52 repeaters with dedicated external QSPI can stage the
complete package off-chip, so their normal full-sensor repeater build can
install a full image or an in-place delta. The current matched families are
XIAO nRF52840 and its XIAO-module derivatives, original LilyGo T-Echo,
ThinkNode M1/M6, Wio Tracker L1, SenseCAP Solar, and the dedicated RAK4631 +
RAK15001 slot-C target. The list also includes a separately wired W25Q16JV on
RAK19007 with either RAK4631 or RAK3401 + RAK13302. These require the
corresponding exact QSPI-aware OTAFIX bootloader; see
[the nRF52 QSPI guide](ota_nrf52_qspi.md).
The W25Q16 option uses one permanent RAK19007 wiring for either core: CLK/MISO/
MOSI tap IO-connector pins 26/27/28, flash CS uses J11 AIN1, and power uses J12
VDD (3.3 V) and GND. Add an approximately 10 kOhm CS-to-VDD pull-up and never
power the flash from VBAT. The three SPI signals require underside pads or an
IO-connector interposer; the easy J10/J11/J12 headers do not expose them. The
flash consumes no WisBlock slot, so a supported GPS can remain in slot A.
The unified RAK repeater images additionally support a header-only W25Q16
layout, using J10 `TX1`/`RX1` and J11 `IO1`/`AIN1`. This layout needs the
matching compatible OTAFIX 2.4.10 bootloader and gives up UART1 GPS/PPS; I2C
sensors remain available. See the [pin table](ota_nrf52_qspi.md#header-only-w25q16-wiring-for-the-unified-rak-images).
Older full-sensor `RAK_4631_repeater` images exceeded the safe internal
in-place update limit. Their smaller sibling,
`RAK_4631_repeater_lora_ota_no_external_sensors`, trims selected optional
environmental/ranging drivers while retaining generic I2C, battery monitoring,
the four INA monitors, and target-compatible GPS. A RAK4631 fitted with
RAK15001 in sensor slot C can instead use
`RAK_4631_repeater_rak15001_slot_c_lora_ota` to retain the full sensor/GPS set
and stage full images or deltas off-chip.
For the external Winbond breakout, use
`RAK_4631_repeater_w25q16_lora_ota` on RAK4631 or
`RAK_3401_repeater_rak13302_w25q16_lora_ota` on RAK3401 + RAK13302. They retain
the full sensor/GPS recipe and require exactly a 2 MiB `EF4015` W25Q16 plus the
bootloader for that same core/radio/flash combination. Install
`wiscore_rak4631_w25q16` for RAK4631 or
`wiscore_rak3401_rak13302_w25q16` for RAK3401 + RAK13302 as a one-time
prerequisite. The physical wiring is shared, but the two firmware identities
are deliberately different and cannot be substituted for one another.
+65 -86
View File
@@ -2,105 +2,84 @@
<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
# MeshCore 1.18.0.1 - USA Cascade
Firmware source: `03214a16df2bd09d3272dfb001d2d322c6740f60`.
**Stable release.** Meshtext chat, simpler ESP32 administration and new repeater
controls. Changes below are relative to **1.17.1.9**.
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).
[Choose your firmware](../firmware_picker.md) | [Update instructions](../ota_user_guide.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.
## New
## Changes since 1.17.1.9
- **Meshtext chat on supported Full Companions**, including T1000-E.
Use channels such as `#mt-longfast`; see [Meshtext setup](../radio_profiles.md#meshtext-channel-chat).
- **WiFi repeater bridge:** connect up to eight peers by IP on an existing
network. [WiFi setup](../udp_bridge_setup.md).
[ESP-NOW](../espnow_bridge_setup.md) and [RS232](../rs232_bridge_setup.md)
also gain status, counters and link tests.
- **One ESP32 admin password** for device setup, browser administration and
WiFi firmware uploads. Router WiFi passwords and Bluetooth PINs stay separate.
[Password details](../WiFi.md)
- **Telemetry choices:** turn individual packet types on or off. All types
are selected by default; sending telemetry still requires enabling it.
[Telemetry controls](../cli_commands.md#read-repeater-telemetry-history)
- **Low-battery forwarding limits:** optionally stop repeating below 10% and
flooding below 20%, or set your own thresholds. Both limits default off.
[Battery controls](../cli_commands.md#pause-forwarding-when-the-battery-is-low)
- **Radio2 setup warns about overlapping channels** and suggests a clearer
center frequency. [Channel spacing](../radio_profiles.md#channel-spacing).
Supported boards also gain [faster switching and saved tuning controls](../radio_profiles.md#saved-switching-controls).
- **T114 automatic screen detection:** one image for boards with or without
the supported display.
- **Optional nRF52 bootloader update included** in supported Repeater and
Room Server images. These carry the exact board's OTAFIX 2.4.12 bootloader.
[Installation requirements](../nrf52_embedded_bootloader.md#install-and-activate)
- 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.
## Fixed
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.
- G3 startup and radio pin/slot handling.
- XIAO nRF52 external-flash startup, storage alignment and Bluetooth handling.
- V4 USB logging idle/reopen failures and ESP32 Bluetooth/power-saving recovery.
## Choose an installation
## Choose a download
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.
Use the **[firmware picker](../firmware_picker.md)** to match your exact board
and role. The GitHub pages below contain the **same version**, split to fit
GitHub's download limits.
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.
| What you run | Download page |
| --- | --- |
| A radio connected to your phone or computer | [Companion](https://github.com/mikecarper/MeshCore/releases/tag/v1.18.0.1-halo-keymind-cascade-dev-03214a16) |
| An ESP32 Repeater, Room Server or Sensor | [ESP32 infrastructure](https://github.com/mikecarper/MeshCore/releases/tag/full-profiles-v1.18.0.1-halo-keymind-cascade-dev-03214a16) |
| An nRF52 Repeater, Room Server or Sensor | [Part 1](https://github.com/mikecarper/MeshCore/releases/tag/lora-ota-v1.18.0.1-halo-keymind-cascade-dev-03214a16), [Part 2](https://github.com/mikecarper/MeshCore/releases/tag/lora-ota-2-v1.18.0.1-halo-keymind-cascade-dev-03214a16), [Part 3](https://github.com/mikecarper/MeshCore/releases/tag/lora-ota-3-v1.18.0.1-halo-keymind-cascade-dev-03214a16) - use the picker to find your board |
| A partition migration or separate bootloader update | [Update utilities](https://github.com/mikecarper/MeshCore/releases/tag/utility-v1.18.0.1-halo-keymind-cascade-dev-03214a16) |
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).
**Full Companion** combines supported connections and features in one image.
**Full / Reduced sensors** selects optional sensor drivers on nRF52 infrastructure.
**With bootloader** includes the matched bootloader update.
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.
## Before updating
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.
- Match the exact board and storage layout. ESP32 USB installs use a merged
image; WiFi updates use an app-only image and a compatible partition layout.
[Update guide](../ota_user_guide.md)
- An included nRF52 bootloader checks for a compatible older version after
70 seconds. It is not a recovery tool for an unrecognized bootloader.
[Bootloader guide](../nrf52_embedded_bootloader.md)
- Fresh USA Cascade radio defaults: **910.525 MHz / BW62.5 / SF7 / CR5**.
Existing saved radio settings take precedence.
## Verification scope
## Tested and limitations
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 complete release passed build, memory and package checks, and
[release-source CI](https://github.com/mikecarper/MeshCore/actions/runs/40101490838).
[Hardware reports](../test-results/index.md) cover USB, Bluetooth, contact sync,
LoRa and updates at the revisions recorded. The final build has not been
physically tested on every board.
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.
- **T1000-E:** Bluetooth pauses while connected to a USB data host and resumes
after unplugging if enabled. Bluetooth works on battery or a charge-only supply.
- **Radio2:** two profiles share one radio; simultaneous reception is not guaranteed.
- **Morse receive:** noise can still produce false letters.
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.
Firmware source: [03214a16](https://github.com/mikecarper/MeshCore/commit/03214a16df2bd09d3272dfb001d2d322c6740f60).
+3 -2
View File
@@ -2,9 +2,10 @@
<p class="meshcore-hosted-doc-link"><a href="https://mikecarper.github.io/MeshCore/role_feature_switches/">View this page on MeshCore Docs</a>.</p>
<!-- meshcore-hosted-doc-link:end -->
# Feature switches by role - 1.17.1.5 USA Cascade
# Feature controls by role
Use these settings with the exact board's canonical release image. Former
Use these settings with the exact board's image from the
[current release](releases/1.18.0.1.md). Former
logging, power-saving, FEM-gain, and rotated-display variants are now runtime
choices where the hardware supports them. A setting cannot add missing radio
hardware, MQTT code, storage, or an OTA partition. Check the download's
+4 -4
View File
@@ -104,12 +104,12 @@ nav:
- docs.md
- Research: research/index.md
- Releases:
- Current release:
- 1.18.0.1 USA Cascade: releases/1.18.0.1.md
- Current - 1.18.0.1 USA Cascade: releases/1.18.0.1.md
- Previous releases:
- 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
- Old Releases: old-releases/index.md
- Older releases: old-releases/index.md
- Test Results: test-results/index.md
theme:
name: material
+95 -18
View File
@@ -345,6 +345,65 @@ def category(record):
return "repeater-room"
def joined_labels(values, conjunction="and"):
if len(values) < 2:
return "".join(values)
return ", ".join(values[:-1]) + " " + conjunction + " " + values[-1]
def release_group_description(name, records, extras=()):
"""Describe actual page contents without assuming every matrix is current."""
manifests = [record["manifest"] for record in records]
platforms = {manifest["platform"] for manifest in manifests}
platform = ({"NRF52_PLATFORM": "nRF52", "ESP32_PLATFORM": "ESP32"}
.get(next(iter(platforms)), "") if len(platforms) == 1 else "")
role_names = []
for pattern, label in ((r"_repeater(?:[_-]|$)", "Repeater"),
(r"_room_(?:server|svr)(?:[_-]|$)", "Room Server"),
(r"_sensor(?:[_-]|$)", "Sensor"),
(r"_terminal(?:[_-]|$)", "Terminal")):
if any(re.search(pattern, manifest["target"], re.I) for manifest in manifests):
role_names.append(label)
roles = joined_labels(role_names) or "Radio"
operating_roles = joined_labels(["a " + role.lower() for role in role_names], "or")
radio = ("an " if platform == "ESP32" else "a ") + platform + " radio" if platform else "a radio"
if platform == "nRF52":
radio = "an nRF52 radio"
if name == "companion":
return "Companion firmware", "Connect a radio to a phone or computer using the Companion mode supported by its hardware."
if name == "lora-ota":
lora = bool(manifests) and all("lora" in manifest.get("ota_update_methods", []) for manifest in manifests)
title = platform + " " + roles + " firmware" if platform else (
"LoRa OTA firmware" if lora else "Wireless update firmware")
purpose = ("Run " + radio + " as " + operating_roles if operating_roles
else "Firmware for your exact radio")
return title, purpose + (" with LoRa OTA reception." if lora else " with wireless updates.")
if name == "full-profiles":
title = (platform + " " if platform else "Full ") + roles + " firmware"
return title, ("Full firmware for " + radio + " running as " + operating_roles + "." if operating_roles
else "Choose the Full firmware for your exact radio and role.")
if name == "logging":
return "USB packet logging firmware", "Use these images when the chosen radio role needs USB packet logging."
if name == "utility":
names = [path.name.lower() for path in extras]
migration = any(filename.endswith("-migration.zip") or filename.startswith("esp32-partition-migration-")
for filename in names)
bootloader = any("bootloader" in filename for filename in names)
tools = joined_labels((["migration"] if migration else []) + (["bootloader"] if bootloader else []))
if records:
title = roles + " firmware" + (" and upgrade tools" if extras else "")
return title, "Applications for " + (operating_roles or "your radio") + (
", plus exact-board " + (tools or "upgrade") + " tools." if extras else ".")
return (tools.capitalize() if tools else "Upgrade") + " tools", (
"Use the exact board/role migration package or board/storage bootloader file for an update or recovery."
if migration and bootloader else
"Use the exact board and role package to migrate an ESP32 partition layout." if migration else
"Use the exact board/storage bootloader file for an update or recovery." if bootloader else
"Use the exact hardware package and follow its included instructions.")
return roles + " firmware", ("Run " + radio + " as " + operating_roles + "." if operating_roles
else "Firmware for the exact radio hardware and role selected in the picker.")
def sha256(path):
digest = hashlib.sha256()
with path.open("rb") as stream:
@@ -554,10 +613,8 @@ def main():
raise ValueError("staging directory must be empty; existing releases are never overwritten")
base_tag = version
groups = []
titles = {"companion": "Companion builds", "repeater-room": "Repeater and Room Server builds",
"utility": "Sensor and Terminal utilities", "logging": "USB packet logging builds",
"lora-ota": "LoRa OTA builds", "full-profiles": "Expanded FULL ESP32 profiles"}
for name in titles:
descriptions = {}
for name in ("companion", "repeater-room", "utility", "logging", "lora-ota", "full-profiles"):
current = []
count = 0
chunks = []
@@ -572,16 +629,25 @@ def main():
for index, chunk in enumerate(chunks):
key = name + (f"-{index + 1}" if index else "")
tag = base_tag if key == "companion" else f"{key}-{base_tag}"
title = f"MeshCore {args.version}{'' if args.stable else ' Dev'} - {titles[name]}"
if index:
title += f" (part {index + 1})"
label, purpose = release_group_description(name, chunk, extras if key == "utility" else [])
if len(chunks) > 1:
label += f" (part {index + 1} of {len(chunks)})"
title = f"MeshCore {args.version}{'' if args.stable else ' Dev'} - {label}"
descriptions[key] = {"label": label, "purpose": purpose, "parts": len(chunks)}
groups.append({"key": key, "tag": tag, "title": title, "prerelease": not args.stable, "records": chunk})
if extras and not any(g['key'] == 'utility' for g in groups):
label, purpose = release_group_description("utility", [], extras)
groups.append({'key': 'utility', 'tag': 'utility-' + base_tag,
'title': f'MeshCore {args.version} - Migration utilities',
'title': f'MeshCore {args.version}{"" if args.stable else " Dev"} - {label}',
'prerelease': not args.stable, 'records': []})
links = "\n".join(f"- [{g['key']}](https://github.com/{args.repo}/releases/tag/{g['tag']})" for g in groups)
descriptions["utility"] = {"label": label, "purpose": purpose, "parts": 1}
links = "\n".join(f"- [{descriptions[g['key']]['label']}](https://github.com/{args.repo}/releases/tag/{g['tag']})"
for g in groups)
source_url = f"https://github.com/{args.repo}/blob/{args.commit}"
owner, repository = args.repo.split("/", 1)
docs_url = f"https://{owner}.github.io/" + ("" if repository.lower() == owner.lower() + ".github.io"
else quote(repository, safe="") + "/")
release_guide = docs_url + f"releases/{args.version}/"
rows = []
for group in groups:
destination = args.output / group["key"]
@@ -656,15 +722,26 @@ def main():
partial_note = (f"**Partial matrix:** {len(failures)} build attempt(s) failed and await repair. "
f"Only individually qualified firmware is published. [Missing builds]({report_url}).\n\n")
channel = 'Stable release' if args.stable else 'Development prerelease'
body = (f"> **{channel}.** Firmware identifier: **{base_tag}**.\n\n"
f"# MeshCore {args.version} - USA Cascade\n\n"
f"Source: `{args.commit}`. USA/Canada: **{radio['frequency']} MHz, BW{radio['bandwidth']}, SF{radio['sf']}, CR{radio['cr']}**; Cascade defaults.\n\n"
f"This page contains **{len(summaries)} qualified firmware profiles**. Full Companions send MOTA and normally update over USB. Infrastructure has a verified wireless update path; see each capability manifest.\n\n"
f"[Feature on/off and update directions]({source_url}/docs/full_companion_features.md) | "
f"[Release details]({source_url}/docs/releases/{args.version}.md)\n\n"
"Match the exact board, radio, display, and storage variant. ESP32 WiFi updates use the application `.bin`; the merged image installs boot/partition data over USB. nRF52 firmware `.zip` files are native application DFU packages. Separate `with-bootloader-2.4.12` firmware embeds its exact-board OTAFIX image and checks it for installation after 70 seconds; ordinary firmware preserves the installed bootloader. Standalone bootloader downloads are separate utilities. LoRa MOTA installation requires an exact destination package and matching bootloader. Full Companions do not LoRa-install onto themselves.\n\n"
"Firmware and capability checks passed in the build matrix. Hardware update testing across every board was not performed.\n\n"
+ partial_note + portable_note + links + "\n")
description = descriptions[group["key"]]
split_note = ("These pages are split for download limits and contain the same firmware version; "
"the picker finds the correct page for your board.\n\n" if description["parts"] > 1 else "")
body = (f"[Open the firmware picker]({docs_url}firmware_picker/) | "
f"[What's new in {args.version}]({release_guide})\n\n"
f"# {group['title']}\n\n"
f"> **{channel}.** USA Cascade.\n\n"
+ description["purpose"] + "\n\n"
"Select the exact board, radio, storage and role in the picker.\n\n"
+ split_note + partial_note + portable_note
+ f"[Installation and upgrade guide]({release_guide}) | "
f"[Feature controls]({docs_url}role_feature_switches/) | "
f"[Full Companion guide]({docs_url}full_companion_features/)\n\n"
"**Testing:** Published images passed build, capability and memory checks. "
"Physical coverage varies by board and update route; "
f"see the [release guide]({release_guide}).\n\n"
"Other firmware and tools:\n\n" + links + "\n\n"
f"USA Cascade defaults: **{radio['frequency']} MHz, BW{radio['bandwidth']}, "
f"SF{radio['sf']}, CR{radio['cr']}**. Saved radio settings take precedence.\n\n"
f"Firmware identifier: `{base_tag}`. Source: `{args.commit}`.\n")
(args.output / (group["key"] + "-notes.md")).write_text(body)
(destination / "BUILD-NOTES.txt").write_text(body)
picker = ("<!doctype html><meta charset=utf-8><meta name=viewport content='width=device-width'>"
+105
View File
@@ -326,6 +326,111 @@ class ReleasePublicationTest(unittest.TestCase):
self.assertRaisesRegex(ValueError, "another source"):
package.main()
def test_page_descriptions_follow_actual_platforms_roles_and_tool_types(self):
def record(target, platform="ESP32_PLATFORM", methods=("lora",)):
return {"manifest": {"target": target, "platform": platform,
"ota_update_methods": list(methods)}}
nrf = [record("sample_repeater_lora_ota", "NRF52_PLATFORM"),
record("sample_room_server_lora_ota", "NRF52_PLATFORM"),
record("sample_sensor_lora_ota", "NRF52_PLATFORM")]
label, purpose = package.release_group_description("lora-ota", nrf)
self.assertEqual(label, "nRF52 Repeater, Room Server and Sensor firmware")
self.assertIn("LoRa OTA reception", purpose)
# Historical matrices also had ESP32 and mixed-platform LoRa pages.
esp = [record("legacy_repeater_lora_ota")]
self.assertEqual(package.release_group_description("lora-ota", esp)[0], "ESP32 Repeater firmware")
self.assertEqual(package.release_group_description("lora-ota", esp + nrf)[0], "LoRa OTA firmware")
self.assertNotIn("LoRa", package.release_group_description("lora-ota", [
record("legacy_repeater_lora_ota", methods=("wifi",))])[1])
full = [record("sample_repeater"), record("sample_room_server"), record("sample_sensor")]
self.assertEqual(package.release_group_description("full-profiles", full)[0],
"ESP32 Repeater, Room Server and Sensor firmware")
for files, title in (([Path("board-migration.zip")], "Migration tools"),
([Path("board_bootloader.hex")], "Bootloader tools"),
([Path("board-migration.zip"), Path("board_bootloader.hex")],
"Migration and bootloader tools")):
with self.subTest(title=title):
self.assertEqual(package.release_group_description("utility", [], files)[0], title)
utility = [record("sample_sensor"), record("sample_terminal_chat")]
self.assertEqual(package.release_group_description("utility", utility)[0], "Sensor and Terminal firmware")
self.assertEqual(package.release_group_description("utility", utility, [Path("board-migration.zip")])[0],
"Sensor and Terminal firmware and upgrade tools")
def test_staged_notes_lead_with_picker_and_link_human_role_choices(self):
with tempfile.TemporaryDirectory() as temp:
base = Path(temp)
companion, companion_files = self.record(base / "input", "Station_G2_companion_radio_full")
repeater, repeater_files = self.record(base / "input", "Station_G2_repeater", build_profile="full")
records = [{"manifest": companion, "files": companion_files},
{"manifest": repeater, "files": repeater_files}]
extras = [base / "board-migration.zip", base / "board_bootloader.hex"]
for path in extras:
path.write_bytes(b"exact-board tool fixture\n")
before = {path: path.read_bytes() for path in companion_files + repeater_files + extras}
output = base / "stage"
argv = ["package", "--input", str(base / "input"), "--output", str(output),
"--local-release", "--stable", "--version", "1.17.1.9", "--commit", SOURCE]
with patch.object(sys, "argv", argv), patch.object(package, "collect_local_release",
return_value=(records, extras,
{"frequency": "910.525", "bandwidth": "62.5", "sf": "7", "cr": "5"})):
package.main()
plan = json.loads((output / "release-plan.json").read_text())
self.assertEqual([group["key"] for group in plan["groups"]], ["companion", "full-profiles", "utility"])
guide = "https://mikecarper.github.io/MeshCore/releases/1.17.1.9/"
for group in plan["groups"]:
notes = (output / (group["key"] + "-notes.md")).read_text(encoding="ascii")
self.assertTrue(notes.startswith("[Open the firmware picker](https://mikecarper.github.io/MeshCore/firmware_picker/)"))
self.assertIn("[What's new in 1.17.1.9](" + guide + ")", notes)
self.assertIn("[Installation and upgrade guide](" + guide + ")", notes)
self.assertIn("[Companion firmware](https://github.com/mikecarper/MeshCore/releases/tag/" + FAMILY + ")", notes)
self.assertIn("[ESP32 Repeater firmware](https://github.com/mikecarper/MeshCore/releases/tag/full-profiles-" + FAMILY + ")", notes)
self.assertIn("[Migration and bootloader tools](https://github.com/mikecarper/MeshCore/releases/tag/utility-" + FAMILY + ")", notes)
self.assertNotIn("[full-profiles]", notes)
self.assertNotIn("qualified firmware profiles", notes)
self.assertIn("**Testing:**", notes)
self.assertIn("Physical coverage varies by board and update route", notes)
self.assertIn("Saved radio settings take precedence", notes)
self.assertIn(SOURCE, notes)
self.assertIn(FAMILY, notes)
self.assertEqual((output / group["key"] / "BUILD-NOTES.txt").read_text(), notes)
for line in (output / group["key"] / "SHA256SUMS.txt").read_text().splitlines():
expected, name = line.split(" ", 1)
self.assertEqual(hashlib.sha256((output / group["key"] / name).read_bytes()).hexdigest(), expected)
self.assertEqual({path: path.read_bytes() for path in before}, before)
def test_split_pages_keep_tags_and_explain_one_version_with_human_links(self):
with tempfile.TemporaryDirectory() as temp:
base = Path(temp)
records = []
for index in range(3):
target = f"board_{index}_repeater_lora_ota"
row, files = self.record(base / "input", target, platform="NRF52_PLATFORM")
# Exercise the real 900-file split with many tiny report assets.
for number in range(447):
path = base / "input" / f"{target}-report-{number}.txt"
path.write_bytes(b"qualification report fixture\n")
files.append(path)
self.assertEqual(len(files), 451)
records.append({"manifest": row, "files": files})
output = base / "stage"
argv = ["package", "--input", str(base / "input"), "--output", str(output),
"--local-release", "--stable", "--version", "1.17.1.9", "--commit", SOURCE]
with patch.object(sys, "argv", argv), patch.object(package, "collect_local_release",
return_value=(records, [],
{"frequency": "910.525", "bandwidth": "62.5", "sf": "7", "cr": "5"})):
package.main()
plan = json.loads((output / "release-plan.json").read_text())
self.assertEqual([group["key"] for group in plan["groups"]], ["lora-ota", "lora-ota-2", "lora-ota-3"])
for number, group in enumerate(plan["groups"], 1):
self.assertEqual(group["tag"], group["key"] + "-" + FAMILY)
self.assertEqual(group["title"], f"MeshCore 1.17.1.9 - nRF52 Repeater firmware (part {number} of 3)")
notes = (output / (group["key"] + "-notes.md")).read_text(encoding="ascii")
self.assertIn("split for download limits and contain the same firmware version", notes)
for other in range(1, 4):
self.assertIn(f"[nRF52 Repeater firmware (part {other} of 3)]", notes)
self.assertEqual(group["asset_count"], 457)
def test_local_release_preserves_selected_profiles_and_verifies_complete_inventory(self):
with tempfile.TemporaryDirectory() as temp:
directory = Path(temp)