diff --git a/AGENTS.md b/AGENTS.md index 003e62fc..7f0cb6d5 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -14,13 +14,14 @@ Canonical guidance for AI coding agents and maintainers working in this repo. 2. The rest of `README.md` — boards supported, installation, the Meshtastic Android in-app upgrade flow, troubleshooting. 3. `CONTRIBUTING.md` — dev setup, PR process, adding a new board. -4. `changelog.md` — OTAFIX version history (2.1–2.6 at the top; everything - below predates the OTAFIX fork). +4. `changelog.md` — version history (`2.6.0` and the `OTAFIX 2.x` entries at + the top; everything below predates the OTAFIX fork). 5. The design invariants and gotchas below — do not violate them. ## What this is -Meshtastic's fork (org-owned since 2026-08) of +The Meshtastic nRF52 Bootloader (formerly the OTAFIX bootloader; repo +`meshtastic/nrf52-bootloader`), org-owned since 2026-08. It is a fork of [oltaco's OTAFIX bootloader](https://github.com/oltaco/Adafruit_nRF52_Bootloader_OTAFIX), itself a fork of `adafruit/Adafruit_nRF52_Bootloader` with OTA DFU enhancements. It's the bootloader several nRF52-based Meshtastic devices @@ -148,6 +149,9 @@ itself; a human has to edit it too. ## Gotchas +- **Release tags are bare `x.y.z`.** The Makefile parses the first + dash-separated `x.y.z` of `git describe` into `MK_BOOTLOADER_VERSION`, so a + `v` prefix breaks the build. - **ARM GCC version matters.** CI pins exactly `12.3.Rel1`; 13.3.Rel1 also compiles clean and is the closest verified-working version if you can't get the exact CI-pinned one. GCC 15 is unverified here. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c029c91b..41d6ae69 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -47,9 +47,9 @@ signals that exist. The [README's board list](./README.md#boards-supported) says to raise an issue for a board you'd like supported — that's still the right first step, -and [#4](https://github.com/meshtastic/Adafruit_nRF52_Bootloader_OTAFIX/issues/4) +and [#4](https://github.com/meshtastic/nrf52-bootloader/issues/4) and -[#5](https://github.com/meshtastic/Adafruit_nRF52_Bootloader_OTAFIX/issues/5) +[#5](https://github.com/meshtastic/nrf52-bootloader/issues/5) are open examples. To actually submit the board yourself: 1. You need the physical hardware to test on — a bootloader that compiles diff --git a/README.md b/README.md index d1d23e14..9ced5d68 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ -# Meshtastic OTAFIX Bootloader +# Meshtastic nRF52 Bootloader -[![Build](https://github.com/meshtastic/Adafruit_nRF52_Bootloader_OTAFIX/actions/workflows/githubci.yml/badge.svg)](https://github.com/meshtastic/Adafruit_nRF52_Bootloader_OTAFIX/actions/workflows/githubci.yml) +[![Build](https://github.com/meshtastic/nrf52-bootloader/actions/workflows/githubci.yml/badge.svg)](https://github.com/meshtastic/nrf52-bootloader/actions/workflows/githubci.yml) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](./LICENSE) -Adafruit nRF52 bootloader with enhanced OTA DFU, forked for [Meshtastic](https://meshtastic.org) from [oltaco's OTAFIX bootloader](https://github.com/oltaco/Adafruit_nRF52_Bootloader_OTAFIX). This is the bootloader several nRF52-based Meshtastic devices ship with, and the one the [Meshtastic Android app](https://github.com/meshtastic/Meshtastic-Android) can upgrade in-app. +Adafruit nRF52 bootloader with enhanced OTA DFU, forked for [Meshtastic](https://meshtastic.org) from [oltaco's OTAFIX bootloader](https://github.com/oltaco/Adafruit_nRF52_Bootloader_OTAFIX), itself a fork of [Adafruit's nRF52 bootloader](https://github.com/adafruit/Adafruit_nRF52_Bootloader). It was previously called the OTAFIX bootloader. This is the bootloader several nRF52-based Meshtastic devices ship with, and the one the [Meshtastic Android app](https://github.com/meshtastic/Meshtastic-Android) can upgrade in-app. -Current release: **OTAFIX 2.5** — see [changelog.md](changelog.md) for version history. +Current release: **OTAFIX 2.5** (tag `0.9.2-OTAFIX2.5`). Releases from 2.6.0 on are tagged plain `x.y.z`. See [changelog.md](changelog.md) for version history. ## Contents @@ -56,7 +56,7 @@ There is also one thing the UF2 drive accepts that is *not* an image: the data in place and leaves everything else alone. BLE OTA DFU is the only *wireless* path, which is what the "OTA" in -"OTAFIX" refers to — and it's also the bootloader's default fallback: since +"OTAFIX" in the bootloader's former name referred to — and it's also the bootloader's default fallback: since **OTAFIX 2.0**, if no valid application is present, the device waits in BLE OTA mode automatically rather than risk getting stuck (see [Troubleshooting](#troubleshooting)). @@ -93,7 +93,7 @@ bootloader and SoftDevice zip package"). - Seeed Studio XIAO nRF52840 BLE ([See note](#notes-on-xiao-nrf52840-ble)) - Seeed Studio XIAO nRF52840 BLE SENSE -If there is another nRF52840-based Meshtastic board you would like to see supported, please [raise a GitHub issue](https://github.com/meshtastic/Adafruit_nRF52_Bootloader_OTAFIX/issues/new/choose) — or see [Adding a new board](./CONTRIBUTING.md#adding-a-new-board) in `CONTRIBUTING.md` if you want to submit it yourself. +If there is another nRF52840-based Meshtastic board you would like to see supported, please [raise a GitHub issue](https://github.com/meshtastic/nrf52-bootloader/issues/new/choose) — or see [Adding a new board](./CONTRIBUTING.md#adding-a-new-board) in `CONTRIBUTING.md` if you want to submit it yourself. ## BLE advertising names @@ -126,7 +126,7 @@ When in OTA DFU mode, devices advertise using a board-specific name rather than ## Installation The recommended way to install the bootloader is using the UF2 file. -Download the UF2 file for your board (they can be found in the [releases](https://github.com/meshtastic/Adafruit_nRF52_Bootloader_OTAFIX/releases) with filenames beginning with `update-`), enter UF2 mode (usually by double pressing the reset button within 0.5s; boards with no reset button hold their one button instead, see the [single-button note](#notes-on-single-button-boards-t1000-e-and-meshtracker-x1)) and copy the UF2 file across. +Download the UF2 file for your board (they can be found in the [releases](https://github.com/meshtastic/nrf52-bootloader/releases) with filenames beginning with `update-`), enter UF2 mode (usually by double pressing the reset button within 0.5s; boards with no reset button hold their one button instead, see the [single-button note](#notes-on-single-button-boards-t1000-e-and-meshtracker-x1)) and copy the UF2 file across. Any board can also be put into UF2 mode from the application, with no button at all: `meshtastic --enter-dfu` over USB, or the Meshtastic Android app's firmware-update screen on a USB connection. @@ -138,7 +138,7 @@ If an incorrect bootloader has been flashed to the device, a full bootloader and ### Factory erase -To wipe the device's saved data — config, node DB, keys, BLE bonds — without a serial terminal or a special erase firmware: enter UF2 mode and copy [`meshtastic_factory_erase.uf2`](./tools/meshtastic_factory_erase.uf2) (also attached to every [release](https://github.com/meshtastic/Adafruit_nRF52_Bootloader_OTAFIX/releases)) onto the drive. The bootloader erases the application's data region, then reboots straight back into UF2 mode so you can copy the firmware across next. The application itself is left in place, so if you unplug instead, it boots factory-fresh. Copy this file on its own: the drive detaches about half a second after the copy finishes (or as soon as you eject it) and comes back a second or two later. +To wipe the device's saved data — config, node DB, keys, BLE bonds — without a serial terminal or a special erase firmware: enter UF2 mode and copy [`meshtastic_factory_erase.uf2`](./tools/meshtastic_factory_erase.uf2) (also attached to every [release](https://github.com/meshtastic/nrf52-bootloader/releases)) onto the drive. The bootloader erases the application's data region, then reboots straight back into UF2 mode so you can copy the firmware across next. The application itself is left in place, so if you unplug instead, it boots factory-fresh. Copy this file on its own: the drive detaches about half a second after the copy finishes (or as soon as you eject it) and comes back a second or two later. The file is the same for every board. It is a single UF2 block with family ID `0x4D455348` and no payload; a bootloader that predates this feature ignores it silently — check `INFO_UF2.TXT` on the drive for a `Factory-Erase:` line to know whether yours supports it. Regenerate it with `tools/make_factory_erase_uf2.py` (or `make factory-erase-uf2`). @@ -158,7 +158,7 @@ With the radio connected over **USB/serial** (not Bluetooth), open the connected If the device does not show up on your computer after flashing the bootloader or performing an OTA update, it may be **waiting in OTA DFU mode**. -In **OTAFIX 2.0** and above, OTA DFU is the default state when no valid application is present. From **OTAFIX 2.6**, a device with no valid application on a USB host comes up as a UF2 drive showing NO FIRMWARE instead, and goes straight to OTA DFU on battery (no VBUS). With VBUS but no host (a charger or power bank) it falls through to OTA DFU after 3 seconds, as it does as soon as the cable is pulled while the drive is up. Plugging a cable into a device with no valid application that is waiting in OTA DFU, with no phone connected, brings the drive up again. +In **OTAFIX 2.0** and above, OTA DFU is the default state when no valid application is present. From **2.6.0**, a device with no valid application on a USB host comes up as a UF2 drive showing NO FIRMWARE instead, and goes straight to OTA DFU on battery (no VBUS). With VBUS but no host (a charger or power bank) it falls through to OTA DFU after 3 seconds, as it does as soon as the cable is pulled while the drive is up. Plugging a cable into a device with no valid application that is waiting in OTA DFU, with no phone connected, brings the drive up again. In OTA DFU mode: - No UF2 drive is exposed - No serial port is available @@ -258,7 +258,7 @@ To leave DFU mode, eject the mounted drive: the bootloader exits DFU and boots t Unplugging is not a way out. The board stays in the bootloader on battery, with no LED on the X1 to show it, and plugging it back in only mounts the drive again. Eject the drive (or copy a `.uf2` across) to boot the application. -Before OTAFIX 2.5 the T1000-E had no button path into DFU at all (the bootloader read the button with the wrong polarity), and an eject did not leave DFU. On those bootloaders, enter UF2 mode from the application instead: `meshtastic --enter-dfu` or the Meshtastic Android app; then install the file for your board from the [releases](https://github.com/meshtastic/Adafruit_nRF52_Bootloader_OTAFIX/releases) to get the button: `update-t1000_e_bootloader-*.uf2` on the T1000-E, `update-mesh_tracker_x1_bootloader-*.uf2` on the X1. The two boards share a USB VID/PID, which is what the bootloader's self-update check keys on, so it accepts the wrong board's file without complaint. Avoid the 1200 bps serial touch on these boards: it selects serial-only DFU (`DFU_MAGIC_SERIAL_ONLY_RESET`), which exposes no drive, has no eject to leave by, and the serial DFU protocol carries no reset request, so the only way out is completing a serial DFU with `adafruit-nrfutil`. +Before OTAFIX 2.5 the T1000-E had no button path into DFU at all (the bootloader read the button with the wrong polarity), and an eject did not leave DFU. On those bootloaders, enter UF2 mode from the application instead: `meshtastic --enter-dfu` or the Meshtastic Android app; then install the file for your board from the [releases](https://github.com/meshtastic/nrf52-bootloader/releases) to get the button: `update-t1000_e_bootloader-*.uf2` on the T1000-E, `update-mesh_tracker_x1_bootloader-*.uf2` on the X1. The two boards share a USB VID/PID, which is what the bootloader's self-update check keys on, so it accepts the wrong board's file without complaint. Avoid the 1200 bps serial touch on these boards: it selects serial-only DFU (`DFU_MAGIC_SERIAL_ONLY_RESET`), which exposes no drive, has no eject to leave by, and the serial DFU protocol carries no reset request, so the only way out is completing a serial DFU with `adafruit-nrfutil`. --- @@ -272,7 +272,7 @@ together. ## Getting help - **Questions or troubleshooting:** [Meshtastic Discussions](https://github.com/orgs/meshtastic/discussions) -- **Bug reports and feature requests:** [open an issue](https://github.com/meshtastic/Adafruit_nRF52_Bootloader_OTAFIX/issues/new/choose) +- **Bug reports and feature requests:** [open an issue](https://github.com/meshtastic/nrf52-bootloader/issues/new/choose) - **Security vulnerabilities:** see [`SECURITY.md`](./SECURITY.md) — please do not open a public issue - **General Meshtastic docs:** [meshtastic.org](https://meshtastic.org/) diff --git a/SECURITY.md b/SECURITY.md index 5d0d5765..3414359c 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -4,6 +4,7 @@ | Bootloader Version | Supported | | ------------------- | ------------------ | +| 2.6.0 and later | :white_check_mark: | | OTAFIX 2.x | :white_check_mark: | | < OTAFIX 2.0 | :x: | diff --git a/changelog.md b/changelog.md index db443f70..905ac8ba 100644 --- a/changelog.md +++ b/changelog.md @@ -1,10 +1,12 @@ # Adafruit nRF52 Bootloader Changelog -OTAFIX 2.1–2.6 are Meshtastic's fork; everything from 0.6.2 down predates -it and is upstream Adafruit history, kept for provenance. +2.6.0 and the OTAFIX 2.x entries below it are Meshtastic's fork; everything +from 0.6.2 down predates it and is upstream Adafruit history, kept for +provenance. -## OTAFIX 2.6 +## 2.6.0 +- The project is renamed the Meshtastic nRF52 Bootloader (repo `meshtastic/nrf52-bootloader`), and releases are tagged plain `x.y.z` from 2.6.0, continuing the OTAFIX numbering. Board-IDs, BLE names and asset file names are unchanged. - New board: Seeed Studio Wio Tracker L1 Pro 1W (OTA DFU advertising name `WTLP_DFU`). - A board with a valid application that sits in DFU for 6 minutes with no activity boots its application. A BLE connection or DFU write, a UF2 drive read or write, USB enumeration, and serial DFU traffic each restart the 6 minutes; a board with no valid application keeps waiting. - A SoftDevice flash timeout during a BLE or serial transfer, or the activation settings save, is retried every 100 ms, up to 20 times, instead of resetting the bootloader.