From 327ecdb159c0ad256618b6f2b4f47306c5451965 Mon Sep 17 00:00:00 2001 From: James Rich <2199651+jamesarich@users.noreply.github.com> Date: Wed, 28 Oct 2026 21:34:34 -0500 Subject: [PATCH] docs: trim the README to current behavior Drop release-by-release comparisons and repeated history; state current behavior with minimum versions once. --- README.md | 249 ++++++++++++++++-------------------------------------- 1 file changed, 74 insertions(+), 175 deletions(-) diff --git a/README.md b/README.md index b2cc8325..a9c59dfc 100644 --- a/README.md +++ b/README.md @@ -3,83 +3,23 @@ [![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), 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** (tag `0.9.2-OTAFIX2.5`). `master` carries the unreleased **2.6.0**; releases from 2.6.0 on are tagged plain `x.y.z`. See [changelog.md](changelog.md) for version history. Where this page says "2.6.0 and later", the behavior is on `master` and in every release from 2.6.0. - -## Contents - -- [How this fits together](#how-this-fits-together) -- [Boards supported](#boards-supported) -- [BLE advertising names](#ble-advertising-names) -- [Installation](#installation) - - [Entering UF2 mode](#entering-uf2-mode) - - [The UF2 drive](#the-uf2-drive) - - [Updating the bootloader](#updating-the-bootloader) - - [Backing up the installed firmware](#backing-up-the-installed-firmware) - - [Factory erase](#factory-erase) -- [DFU screens](#dfu-screens) -- [Bootloader upgrade from the Meshtastic Android app](#bootloader-upgrade-from-the-meshtastic-android-app) -- [Troubleshooting](#troubleshooting) -- [Recommended OTA DFU settings](#recommended-ota-dfu-settings) -- [Notes on Xiao NRF52840 BLE](#notes-on-xiao-nrf52840-ble) -- [Notes on RAK4631 bootloader](#notes-on-rak4631-bootloader) -- [Notes on single-button boards: T1000-E and MeshTracker X1](#notes-on-single-button-boards-t1000-e-and-meshtracker-x1) -- [Contributing](#contributing) -- [Getting help](#getting-help) -- [License](#license) +Adafruit nRF52 bootloader with enhanced OTA DFU, forked for [Meshtastic](https://meshtastic.org) from [oltaco's bootloader](https://github.com/oltaco/Adafruit_nRF52_Bootloader_OTAFIX) and [Adafruit's](https://github.com/adafruit/Adafruit_nRF52_Bootloader). Formerly the OTAFIX bootloader. Several nRF52 Meshtastic devices ship with it, and the [Meshtastic Android app](https://github.com/meshtastic/Meshtastic-Android) can upgrade it in-app. ---- +Current release: **OTAFIX 2.5** (tag `0.9.2-OTAFIX2.5`). `master` carries the unreleased **2.6.0**; later releases are tagged `x.y.z`. "2.6.0 and later" below means `master` and every release from 2.6.0. See [changelog.md](changelog.md). ## How this fits together -This repo builds **only the bootloader**, the small program that runs -before anything else on the device. It does not contain the Meshtastic -application (the LoRa mesh, BLE, and display code); that's built entirely -separately in [`meshtastic/firmware`](https://github.com/meshtastic/firmware). -This bootloader's job is just to start that application, or, when asked, -replace it (or itself) with a new version. - -On every power-up or reset, a tiny fixed piece of code at the very start of -flash, Nordic's **MBR**, hands off to this bootloader. From there, the -bootloader either: - -- boots straight into the installed Meshtastic application, or -- if no valid application is installed, or the user has asked for it, waits - for a new one. - -A new application image, or occasionally the bootloader itself, can be -installed three different ways: - -| Method | Transport | Typical use | -|---|---|---| -| **UF2 drag-and-drop** | USB, appears as a drive | Manual flashing, see [Installation](#installation) | -| **Serial DFU** | USB, appears as a serial port | Recovery, and flashing a full bootloader+SoftDevice package with `adafruit-nrfutil`, see [Installation](#installation) | -| **BLE OTA DFU** | Bluetooth, no cable needed | The Meshtastic Android app's firmware/bootloader update, and any Nordic DFU app, see [below](#bootloader-upgrade-from-the-meshtastic-android-app) and [recommended settings](#recommended-ota-dfu-settings) | - -There is also one thing the UF2 drive accepts that is *not* an image: the -[factory-erase file](#factory-erase), which wipes the application's saved -data in place and leaves everything else alone. - -BLE OTA DFU is the only *wireless* path, and it is the bootloader's fallback -when nothing else applies: since **OTAFIX 2.1**, a device with no valid -application waits in BLE OTA mode rather than risk getting stuck. From -**2.6.0** that device first checks for a USB cable: on a host it comes up as -a UF2 drive showing NO FIRMWARE, and only on battery, or on a charger with no -host, does it go to the BLE OTA wait. The full routing is under -[The UF2 drive](#the-uf2-drive). - -The application also relies on Nordic's **SoftDevice**, a closed-source, -precompiled Bluetooth stack (vendored here as a hex blob under -`lib/softdevice/`) that sits in flash alongside it. The bootloader and the -application both call into it for BLE, so bootloader and SoftDevice -versions are usually flashed together as a matched pair (that's why the -[Installation](#installation) instructions below mention flashing "a full -bootloader and SoftDevice zip package"). Each board builds against one -SoftDevice version, S140 6.1.1 or S140 7.3.0, listed in the table below. A -UF2 built for the other SoftDevice is rejected (2.6.0 and later), and the -`SoftDevice:` line in `INFO_UF2.TXT` on the UF2 drive shows which one a -device has. +This repo builds only the bootloader, the small program that runs before anything else on the device. The Meshtastic application is built separately in [`meshtastic/firmware`](https://github.com/meshtastic/firmware). On every power-up or reset, Nordic's **MBR** hands off to the bootloader, which either boots the installed application or, when there is none or the user asks, waits for a new one. + +An application image, or the bootloader itself, installs three ways: + +- **UF2 drag-and-drop** over USB, where the device appears as a drive. See [Installation](#installation). +- **Serial DFU** over USB, for recovery and for flashing a full bootloader+SoftDevice package with `adafruit-nrfutil`. +- **BLE OTA DFU**, with no cable: the Meshtastic Android app's update and any Nordic DFU app. See [recommended settings](#recommended-ota-dfu-settings). + +The UF2 drive also accepts the [factory-erase file](#factory-erase), which wipes the application's saved data and nothing else. + +The application uses Nordic's **SoftDevice**, a precompiled Bluetooth stack vendored under `lib/softdevice/`. Each board builds against one version, S140 6.1.1 or 7.3.0, listed below. A UF2 built for the other SoftDevice is rejected (2.6.0 and later); `INFO_UF2.TXT` shows which one a device has. --- @@ -110,15 +50,15 @@ device has. | Seeed Studio XIAO nRF52840 BLE ([See note](#notes-on-xiao-nrf52840-ble)) | `xiao_nrf52840_ble` | S140 7.3.0 | I2C OLED | | Seeed Studio XIAO nRF52840 BLE SENSE | `xiao_nrf52840_ble_sense` | S140 7.3.0 | I2C OLED | -The `BOARD=` name is the directory under `src/boards/` and the prefix of the release asset file names (`update-_bootloader-_nosd.uf2`, `_bootloader-_s140_.zip`). The DFU screen column says which boards show a screen while in DFU mode; see [DFU screens](#dfu-screens). +The `BOARD=` name is the directory under `src/boards/` and the prefix of the release asset names (`update-_bootloader-_nosd.uf2`, `_bootloader-_s140_.zip`). See [DFU screens](#dfu-screens) for the last column. -**Meshtastic Generic** (2.6.0 and later) is for DIY nRF52840 boards that have no board-specific bootloader. Pick the variant whose SoftDevice your firmware build expects. It drives no LED, and its only button is the reset pin (P0.18), so it claims no GPIO: enter UF2 mode by double-pressing reset, or from the application. The two variants report Board-IDs `nRF52840-Meshtastic-Generic-S140v6` and `nRF52840-Meshtastic-Generic-S140v7` in `INFO_UF2.TXT` and share USB ID `239A:0029` with the RAK4631, RAK3401, T-Echo, WisMesh Tag and MX25LE01, so their `update-` UF2 installs over those boards' bootloaders and the reverse (see [Updating the bootloader](#updating-the-bootloader)). These two builds have not been run on hardware by the maintainers. +**Meshtastic Generic** (2.6.0 and later) is for DIY nRF52840 boards with no board-specific bootloader. Pick the variant whose SoftDevice your firmware expects. It drives no LED and its only button is reset (P0.18), so enter UF2 mode by double-pressing reset or from the application. The Board-IDs are `nRF52840-Meshtastic-Generic-S140v6` and `nRF52840-Meshtastic-Generic-S140v7`. Neither build has been run on hardware by the maintainers. -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. +To request another board, [raise an issue](https://github.com/meshtastic/nRF52_Bootloader/issues/new/choose) or see [Adding a new board](./CONTRIBUTING.md#adding-a-new-board). ## BLE advertising names -When in OTA DFU mode, devices advertise using a board-specific name rather than the generic `AdaDFU`. +In OTA DFU mode, devices advertise a board-specific name instead of the generic `AdaDFU`. | Board | OTA DFU advertising name | | ---------------------------- | ------------------------ | @@ -143,64 +83,63 @@ When in OTA DFU mode, devices advertise using a board-specific name rather than | Seeed WioTracker L1 Pro 1W | `WTLP_DFU` | | XIAO NRF52 BLE / SENSE | `XIAO_DFU` | -The device advertises at the application's BLE address plus one, and exposes its bootloader version through the Device Information Service (firmware revision, `0x2A26`). +The device advertises at the application's BLE address plus one and exposes its bootloader version through the Device Information Service (firmware revision, `0x2A26`). --- ## 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/nRF52_Bootloader/releases) with filenames beginning with `update-`), enter UF2 mode and copy the UF2 file across. The board installs it and reboots into the application; see [Updating the bootloader](#updating-the-bootloader) for what the bootloader checks first. +The recommended way to install the bootloader is the UF2 file. Download the `update-` UF2 for your board from the [releases](https://github.com/meshtastic/nRF52_Bootloader/releases), enter UF2 mode and copy it across. The board installs it and reboots into the application; see [Updating the bootloader](#updating-the-bootloader). -If an incorrect bootloader has been flashed to the device, a full bootloader and SoftDevice zip package will need to be flashed using `adafruit-nrfutil` over serial DFU: +If an incorrect bootloader was flashed, flash a full bootloader and SoftDevice zip package over serial DFU: ```sh adafruit-nrfutil dfu serial --package _bootloader-_s140_.zip --port -b 115200 ``` -That package replaces the SoftDevice too, which marks the installed application invalid: the device does not boot the application afterwards. On a USB host it comes up as a UF2 drive showing NO FIRMWARE (2.6.0 and later; OTAFIX 2.x goes to the BLE OTA wait instead), so copy the firmware `.uf2` across next. +The package replaces the SoftDevice, which marks the installed application invalid. On a USB host the device comes up as a UF2 drive showing NO FIRMWARE (2.6.0 and later); copy the firmware `.uf2` across next. ### Entering UF2 mode -- **Double-press reset** within half a second. The drive comes up with no host timeout and stays until you copy a file, eject, or reset (or, from 2.6.0, until 6 minutes pass with no activity on a board with a valid application). +- **Double-press reset** within half a second. The drive stays until you copy a file, eject, or reset. On a board with a valid application it also closes after 6 minutes with no activity (2.6.0 and later). - **Hold the button through power-on** on the single-button boards, see the [single-button note](#notes-on-single-button-boards-t1000-e-and-meshtracker-x1). -- **From the application**, with no button: `meshtastic --enter-dfu` over USB, or the Meshtastic Android app's firmware-update screen on a USB connection. A board sent to UF2 mode this way needs a USB host within 3 seconds, otherwise it boots the application again. -- **A device with no valid application** goes to the drive on its own whenever a USB host is attached (2.6.0 and later). +- **From the application:** `meshtastic --enter-dfu` over USB, or the Meshtastic Android app's firmware-update screen on a USB connection. The board needs a USB host within 3 seconds, otherwise it boots the application again. +- **No valid application:** the device goes to the drive on its own whenever a USB host is attached (2.6.0 and later). -Holding the DFU button while the board resets enters **BLE OTA mode**, not UF2 mode, on every board except the single-button boards. UF2 mode comes from double reset, the button hold on those boards, or the application. +Holding the DFU button while the board resets enters BLE OTA mode, not UF2 mode, on every board except the single-button boards. ### The UF2 drive The drive is named after the board (`RAK4631`, `XIAO-BOOT`, `T1000-E`, and so on) and holds three files: -- `INFO_UF2.TXT`: the bootloader version, board model and `Board-ID:`, the installed `SoftDevice:` version, a `Factory-Erase:` line on bootloaders that accept the [factory-erase file](#factory-erase), and (2.6.0 and later) a `Last-Fault:` line if the bootloader recorded a fault, see [Troubleshooting](#troubleshooting). +- `INFO_UF2.TXT`: the bootloader version, board model and `Board-ID:`, the installed `SoftDevice:` version, a `Factory-Erase:` line if the bootloader accepts the [factory-erase file](#factory-erase), and a `Last-Fault:` line if the bootloader recorded a fault (2.6.0 and later), see [Troubleshooting](#troubleshooting). - `CURRENT.UF2`: a dump of the installed application, see [Backing up](#backing-up-the-installed-firmware). - `INDEX.HTM`: a redirect to the board vendor's page (the firmware repo on the generic boards). -Copy one `.uf2` file onto the drive at a time. What happens next depends on the file: +Copy one `.uf2` file at a time. What happens next depends on the file: -- **A firmware image** is written as it arrives, and on boards with a screen a progress bar shows how far along it is (2.6.0 and later). Once the whole file has arrived the drive detaches and the board boots the new application. From 2.6.0 the old application is marked invalid as soon as the first application block is written, so a copy interrupted by a pulled cable or a power loss leaves the board with no application, which brings the drive back up for a second try, instead of a half-written image that boots. -- **A firmware image built for the other SoftDevice** (2.6.0 and later) is rejected: nothing is written, the drive stays up, and a board with a screen shows WRONG FILE. The check assumes the host writes the file front to back, which is what Linux does; copies from macOS and Windows have not been exercised. Images that carry their own SoftDevice, and `CURRENT.UF2` restores, pass the check. -- **A bootloader update** (`update-*.uf2`) is checked and installed as described under [Updating the bootloader](#updating-the-bootloader). -- **The factory-erase file** erases the application's data, see [Factory erase](#factory-erase). -- **A UF2 of an unknown family**, or a bootloader update for a board with a different USB ID, is rejected, and a board with a screen shows WRONG FILE. A rejected bootloader update ends the session: the drive detaches and the board boots the application if it has one. Files that are not UF2 are ignored. The bootloader cannot tell one board's firmware image from another's on the same SoftDevice, so check the file name before copying it. +- **A firmware image** is written as it arrives, with a progress bar on boards with a screen. When the file is complete the drive detaches and the board boots the new application. The old application is marked invalid when the first block is written, so an interrupted copy leaves no application and the drive comes back for a retry (2.6.0 and later). +- **A firmware image for the other SoftDevice** is rejected (2.6.0 and later): nothing is written, the drive stays up, and a board with a screen shows WRONG FILE. The check assumes the host writes front to back, as Linux does; macOS and Windows copies have not been exercised. Images that carry their own SoftDevice, and `CURRENT.UF2` restores, pass. +- **A bootloader update** (`update-*.uf2`): see [Updating the bootloader](#updating-the-bootloader). +- **The factory-erase file**: see [Factory erase](#factory-erase). +- **A UF2 of an unknown family**, or a bootloader update for a different USB ID, is rejected with WRONG FILE on a board with a screen. A rejected bootloader update ends the session: the drive detaches and the board boots the application if it has one. Files that are not UF2 are ignored. The bootloader cannot tell one board's firmware from another's on the same SoftDevice, so check the file name. Routing when the device has **no valid application** (2.6.0 and later): - On a USB host it comes up as the UF2 drive showing NO FIRMWARE. - On battery (no VBUS) it goes straight to the BLE OTA wait. -- On a charger or power bank (VBUS, but no host enumerating it within 3 seconds) it falls through to the BLE OTA wait. +- On a charger or power bank (VBUS, no host enumerating within 3 seconds) it goes to the BLE OTA wait. - Pulling the cable while the drive is up sends it to the BLE OTA wait. -- Plugging a cable into a device that is waiting for BLE OTA, with no phone connected, brings the drive up. A cable that was already present when the wait began, or a phone that is connected, leaves the wait alone. -- A rejected or aborted copy returns it to the drive, as does an eject on the [single-button boards](#notes-on-single-button-boards-t1000-e-and-meshtracker-x1). +- Plugging a cable into a device in the BLE OTA wait, with no phone connected, brings the drive up. A cable already present when the wait began, or a connected phone, leaves the wait alone. +- A rejected or aborted copy, or an eject on a [single-button board](#notes-on-single-button-boards-t1000-e-and-meshtracker-x1), returns it to the drive. -A device **with** a valid application that is left in DFU mode boots the application after 6 minutes with no activity (2.6.0 and later): a BLE connection or DFU transfer, a read or write of the drive, a USB host enumerating it, or serial DFU traffic restarts the 6 minutes, and an idle BLE link does not. Before 2.6.0 the device stays in DFU until reset. +A device **with** a valid application left in DFU mode boots it after 6 minutes with no activity (2.6.0 and later). A BLE connection or DFU transfer, a read or write of the drive, a USB host enumerating, or serial DFU traffic restarts the 6 minutes; an idle BLE link does not. ### Updating the bootloader Copying `update-_bootloader-_nosd.uf2` onto the drive stages the new bootloader, checks it, and installs it over the running one; the board then reboots into the application. Boards with a screen show BOOTLOADER while it is staged. -The bootloader accepts an update whose USB vendor and product ID match its own and rejects any other. Several boards share an ID, so the bootloader cannot tell their update files apart and installs the other board's file without complaint: +The bootloader accepts an update whose USB vendor and product ID match its own. Several boards share an ID, so it installs the other board's file without complaint: | USB ID | Boards that share it | | --- | --- | @@ -211,37 +150,33 @@ The bootloader accepts an update whose USB vendor and product ID match its own a | `2886:0057` | T1000-E, MeshTracker X1 | | `2886:1667` | Wio Tracker L1, L1 Pro 1W | -Check the file name against your board before copying it. `Board-ID:` in `INFO_UF2.TXT` tells you which bootloader is installed now. +Check the file name against your board before copying. `Board-ID:` in `INFO_UF2.TXT` tells you which bootloader is installed. -From 2.6.0, if the installed application is large enough that the staged bootloader would overwrite its tail (larger than 744 KB on S140 6.1.1 or 740 KB on 7.3.0), the application is marked invalid before staging and the device comes up on the drive showing NO FIRMWARE, instead of booting a corrupted application. Copy the firmware `.uf2` across again. Current Meshtastic firmware images are smaller than this. +An installed application larger than 744 KB on S140 6.1.1 or 740 KB on 7.3.0 would be overwritten by the staged bootloader, so it is marked invalid first and the drive shows NO FIRMWARE (2.6.0 and later); copy the firmware across again. Current Meshtastic firmware is smaller than this. ### Backing up the installed firmware -`CURRENT.UF2` on the UF2 drive is a dump of the application currently in flash. Copy it off to keep a backup, and copy it back onto the drive to reinstall that application. Since OTAFIX 2.3-BP1.6 the dump is sized from the application start, so it is byte-for-byte the image that was installed; a dump taken with BP1.5 or older after a serial or BLE OTA update could be silently truncated, so re-take any backup made that way. Verified on a RAK4631: restore boots, and the next dump matches byte for byte. - -If the bootloader does not know the application's size (after a debug-probe flash, for example), the dump covers the whole application region instead. Such a dump still restores and boots. +`CURRENT.UF2` is a byte-for-byte dump of the application in flash. Copy it off to keep a backup, and back onto the drive to reinstall it (verified on a RAK4631: restore boots and the next dump matches). If the application's size is unknown (after a debug-probe flash, for example), the dump covers the whole application region and still restores and boots. ### 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/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. Boards with a screen show ERASE while it runs. +To wipe the device's saved data (config, node DB, keys, BLE bonds), enter UF2 mode and copy [`meshtastic_factory_erase.uf2`](./tools/meshtastic_factory_erase.uf2) (also on every [release](https://github.com/meshtastic/nRF52_Bootloader/releases)) onto the drive. The bootloader erases the application's data region, then reboots into UF2 mode. The application stays 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 (or when you eject) and returns a second or two later. Boards with a screen show ERASE. -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 OTAFIX 2.4 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`). +The file is the same for every board: one UF2 block with family ID `0x4D455348` and no payload. OTAFIX 2.4 and later accept it, shown by a `Factory-Erase:` line in `INFO_UF2.TXT`. Regenerate it with `tools/make_factory_erase_uf2.py` (or `make factory-erase-uf2`). --- ## DFU screens -Boards marked in the [board table](#boards-supported) show a screen while in DFU mode: the Heltec T096, T1 and T114 on their SPI TFT, and (2.6.0 and later) the RAK4631, RAK3401, Wio Tracker L1 and L1 Pro 1W, ProMicro nRF52840 and both XIAO nRF52840 boards on a 128x64 I2C OLED at address 0x3C or 0x3D. The OLED path reads the controller type (SSD1306 or SH1106) off the panel; a board with no panel attached shows nothing and otherwise behaves the same. On the RAK4631 and RAK3401 the bootloader powers the switched 3V3_S rail that feeds the WisBlock slots while in DFU, and turns it off again if no panel answers. +Boards marked in the [board table](#boards-supported) show a screen in DFU mode: the SPI TFT on the Heltec T096, T1 and T114, and (2.6.0 and later) a 128x64 I2C OLED at 0x3C or 0x3D on the others. The OLED controller (SSD1306 or SH1106) is detected; a board with no panel shows nothing and otherwise behaves the same. On the RAK4631 and RAK3401 the bootloader powers the switched 3V3_S rail that feeds the WisBlock slots while in DFU, and turns it off if no panel answers. -The OLED shows the board name, the mode (`USB UF2` or `BLE OTA`), the bootloader version, and a banner line; the TFT shows the same first and last lines with a drag-and-drop picture (`firmware.uf2` to the drive's volume label) or `BLE OTA` between them: +The OLED shows the board name, the mode (`USB UF2` or `BLE OTA`), the bootloader version, and a banner line that becomes a progress bar during a transfer. The TFT shows the same first and last lines with a drag-and-drop picture or `BLE OTA` between them. | | | | --- | --- | | ![RAK4631 OLED in UF2 mode](docs/dfu_screen_oled_uf2.png) | ![RAK4631 OLED in BLE OTA mode](docs/dfu_screen_oled_ble.png) | -(These two are rendered from the OLED drawing code, not photographed, and show the version stamp of a 2.5 build.) - -From 2.6.0 the first line changes to say what the bootloader is doing, and the banner line becomes a progress bar during a UF2 copy, a serial DFU or a BLE OTA transfer: +The first line says what the bootloader is doing (2.6.0 and later): | Title | Meaning | | --- | --- | @@ -254,9 +189,7 @@ From 2.6.0 the first line changes to say what the bootloader is doing, and the b ## Bootloader upgrade from the Meshtastic Android app -The [Meshtastic Android app](https://github.com/meshtastic/Meshtastic-Android) can flash this bootloader directly, with no manual UF2 drag-and-drop needed. - -With the radio connected over **USB/serial** (not Bluetooth), open the connected radio's configuration, go to **Advanced → Firmware Update**, and where an upgraded bootloader is published for your board you'll see an **Upgrade bootloader** option alongside **Erase and reinstall**. The app reads `INFO_UF2.TXT` from the device's update drive first to confirm the board and Bluetooth stack before writing anything, and will ask you to select the update drive twice: once for the bootloader image, once for the firmware. See the app's [Firmware Updates guide](https://github.com/meshtastic/Meshtastic-Android/blob/main/docs/en/user/firmware.md) for the full flow. +With the radio connected over **USB/serial** (not Bluetooth), open the [Meshtastic Android app](https://github.com/meshtastic/Meshtastic-Android)'s radio configuration, go to **Advanced → Firmware Update**, and where an upgraded bootloader is published for your board choose **Upgrade bootloader**. The app reads `INFO_UF2.TXT` to confirm the board and Bluetooth stack first, and asks you to select the update drive twice: once for the bootloader, once for the firmware. See its [Firmware Updates guide](https://github.com/meshtastic/Meshtastic-Android/blob/main/docs/en/user/firmware.md). --- @@ -264,64 +197,49 @@ With the radio connected over **USB/serial** (not Bluetooth), open the connected ### Device does not appear as a USB drive or serial port -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.1** and above, OTA DFU is the default state when no valid application is present. From **2.6.0**, a device with no valid application takes the UF2 drive instead whenever a USB host is attached, and reaches OTA DFU only on battery or on a charger; the full routing is under [The UF2 drive](#the-uf2-drive). - -In OTA DFU mode: -- No UF2 drive is exposed -- No serial port is available -- The device is waiting for an OTA firmware update over BLE, advertising as its [board-specific name](#ble-advertising-names) +The device may be **waiting in OTA DFU mode**: no UF2 drive or serial port, advertising as its [board-specific name](#ble-advertising-names). See [The UF2 drive](#the-uf2-drive) for when a device with no valid application lands here. -**What to do:** -- Perform an OTA update using a supported DFU app, **or** -- Explicitly request UF2/serial mode using **double-reset** (or the button hold on a [single-button board](#notes-on-single-button-boards-t1000-e-and-meshtracker-x1)), **or** -- On 2.6.0 and later, plug in a USB cable with no phone connected: the device leaves the wait and comes up as the drive. If the cable was already plugged in when the wait began, unplug it and plug it back in. +What to do: +- Run an OTA update with a supported DFU app, **or** +- Double-reset (or hold the button on a [single-button board](#notes-on-single-button-boards-t1000-e-and-meshtracker-x1)) to get UF2/serial mode, **or** +- Plug in a USB cable with no phone connected and the device comes up as the drive (2.6.0 and later). If the cable was already in when the wait began, replug it. -The OTA fallback is intentional and prevents devices from getting stuck in UF2 mode after failed OTA updates. - -A device with a valid application that sits in DFU for 6 minutes with no activity (no BLE connection or DFU traffic, no drive access) boots its application (2.6.0 and later). A device with no valid application stays in DFU. - -If the bootloader recorded a fault since the last UF2 drive session, `INFO_UF2.TXT` shows a `Last-Fault:` line with the fault id, program counter, and info word (2.6.0 and later). It appears only if the next boot reaches the drive before an application boots, since the application's RAM use wipes the record. Include the line in a bug report. +If the bootloader recorded a fault, `INFO_UF2.TXT` shows a `Last-Fault:` line with the fault id, program counter and info word (2.6.0 and later). It appears only if the next boot reaches the drive before an application boots. Include it in a bug report. --- ### OTA update fails with `Error: Operation Failed` -If an OTA update consistently fails early with `Error: Operation Failed`, this is often caused by BLE stack incompatibilities when **Request High MTU** is enabled. - -**What to try:** -- Experiment with different PRN settings: try 12, 8, 1, or off altogether. -- Disable **Request High MTU** in the DFU app. +An OTA update that consistently fails early with `Error: Operation Failed` is often a BLE stack incompatibility with **Request High MTU** enabled. -While high MTU significantly improves performance on supported devices, it is not required for a successful OTA update. +What to try: +- Try different PRN settings: 12, 8, 1, or off. +- Disable **Request High MTU** in the DFU app. It improves speed but is not required. --- ### OTA update stops partway, or finishes and the device stays in the bootloader -If the phone loses the BLE link in the middle of a transfer, the bootloader goes back to advertising. On 2.6.0 and later the phone can reconnect and start the transfer over without the device being reset; OTAFIX 2.x refuses the new transfer until the device is reset or power-cycled. +If the phone loses the BLE link mid-transfer, the bootloader goes back to advertising and the phone can reconnect and start the transfer over without a reset (2.6.0 and later). -If an OTA update reports success but the device keeps advertising as `_DFU`, or comes up on the UF2 drive showing NO FIRMWARE, one known cause is a flash timeout while the bootloader saves the result (the SoftDevice could not fit the flash write between radio events). On 2.6.0 and later the write is retried, every 100 ms for up to 2 seconds, before the bootloader gives up, and if it does give up the drive's `INFO_UF2.TXT` shows `Last-Fault: id=0x4001 pc=0x0 info=0xd`; on OTAFIX 2.x the first timeout ends the update. The transfer itself was fine, so running the update once more completes it. +If an update reports success but the device keeps advertising as `_DFU`, or shows NO FIRMWARE on the drive, one known cause is a flash timeout while the bootloader saves the result. The write is retried every 100 ms for up to 2 seconds; if it still fails, `INFO_UF2.TXT` shows `Last-Fault: id=0x4001 pc=0x0 info=0xd` (2.6.0 and later). The transfer was fine, so running the update again completes it. --- ### The screen says WRONG FILE -The last UF2 block was not for this board: a UF2 of a family the bootloader does not know, a bootloader update for a board with a different USB ID, or a firmware image for the other SoftDevice. Nothing was written by that block. A firmware image for another board on the same SoftDevice is not caught, so check the file name. Copy the right file for your board; the drive is still up. See [The UF2 drive](#the-uf2-drive) and [Updating the bootloader](#updating-the-bootloader) for what the bootloader checks. +The last UF2 block was not for this board: an unknown family, a bootloader update for a board with a different USB ID, or a firmware image for the other SoftDevice. Nothing was written by that block. A firmware image for another board on the same SoftDevice is not caught, so check the file name. Copy the right file; the drive is still up. See [The UF2 drive](#the-uf2-drive) and [Updating the bootloader](#updating-the-bootloader). --- ## Recommended OTA DFU settings -To perform the OTA update, use **nRF Device Firmware Update** -([Android](https://play.google.com/store/apps/details?id=no.nordicsemi.android.dfu&hl=en&gl=US) / [iOS](https://apps.apple.com/sa/app/device-firmware-update/id1624454660)) -or **nRF Connect** +Use **nRF Device Firmware Update** +([Android](https://play.google.com/store/apps/details?id=no.nordicsemi.android.dfu&hl=en&gl=US) / [iOS](https://apps.apple.com/sa/app/device-firmware-update/id1624454660)), +the recommended app, or **nRF Connect** ([Android](https://play.google.com/store/apps/details?id=no.nordicsemi.android.mcp&hl=en&gl=US) / [iOS](https://apps.apple.com/gb/app/nrf-connect-for-mobile/id1054362403)). -**nRF Device Firmware Update** is the recommended app of the two. - -For **OTAFIX 2.1** and later, including 2.6.0, the following settings are recommended (these may change; feel free to experiment and report findings via a GitHub issue): +Recommended settings (these may change; feel free to experiment and report findings via a GitHub issue): @@ -339,63 +257,44 @@ For **OTAFIX 2.1** and later, including 2.6.0, the following settings are recomm **External MCU DFU:** OFF **Notes:** -- Some Android devices and BLE stacks do not behave well with **Request high MTU** enabled. - If the transfer fails early with `ERROR: Operation Failed`, retry with **Request high MTU turned OFF**. -- For maximum speed, Packet Receipt Notification can be disabled, and the number of packets increased. - Android is generally more tolerant of higher values; on iOS and other small-packet hosts, values above ~60 are not recommended. +- If the transfer fails early with `ERROR: Operation Failed`, retry with **Request high MTU** OFF. +- For maximum speed, disable PRN and raise the packet count. On iOS and other small-packet hosts, values above ~60 are not recommended.
-[Recommended settings for versions prior to 2.0 can be found here](docs/oldsettings.md). - -**IMPORTANT:** -On older versions of the bootloader, performing an OTA update while the device was connected to a computer USB host would complete successfully but **would not automatically boot into the new application firmware**, requiring a manual reset. -This issue is fixed in **OTAFIX 2.1**. - --- ## Notes on Xiao NRF52840 BLE -Many of these boards are shipped with the Sense version of the bootloader installed. If your board has the Sense version installed you must use the Sense version when updating via UF2. - -You can look at the INFO_UF2.TXT file on the UF2 drive to check what version is currently installed. +Many of these boards ship with the Sense version of the bootloader installed. If yours does, use the Sense version when updating via UF2. -To check: -1. Enter UF2 DFU mode (double-press reset) -2. Open the `INFO_UF2.TXT` file on the mounted drive - -If the file shows: "Board-ID: nRF52840-SeeedXiaoSense-v1" then the ***SENSE*** variant must be used if updating via UF2 file. +To check, enter UF2 mode (double-press reset) and open `INFO_UF2.TXT` on the drive. If it shows `Board-ID: nRF52840-SeeedXiaoSense-v1`, use the **Sense** variant. The two variants have different USB product IDs (`2886:0044` and `2886:0045`), so the bootloader rejects the other variant's `update-` file. ## Notes on RAK4631 bootloader -This version of the RAK4631 bootloader is based on a much newer version (0.9.2) of the Adafruit nRF52 bootloader than what RAK Wireless uses on their official bootloader (0.6.2-11). It has been tested with no problems found; whether RAK's own patches to the Adafruit bootloader introduce any behavioral difference has not been investigated. A variant of the official RAK bootloader with these patches included instead is available [here](https://github.com/oltaco/WisCore_RAK4631_Bootloader/releases). +This bootloader is based on Adafruit 0.9.2; RAK Wireless's official one is based on 0.6.2-11. Testing found no problems; whether RAK's own patches make a behavioral difference has not been investigated. A variant of the official RAK bootloader with these patches is [available](https://github.com/oltaco/WisCore_RAK4631_Bootloader/releases). ## Notes on single-button boards: T1000-E and MeshTracker X1 -The SenseCAP Card Tracker T1000-E and the SenseCAP MeshTracker X1 each have a single user button, and their RESET line is wired to the MCU reset pin rather than a second button, so **double-press reset does not work on these boards**. From **OTAFIX 2.5** both use the same hold-to-enter scheme instead: with USB connected, hold the button while the device boots and keep holding for about 3 seconds. The bootloader enters UF2 DFU mode and mounts the mass-storage drive a few seconds later. Releasing the button before then boots the application as normal. - -Because a momentary press belongs to the application, a short press will *not* enter DFU; it has to be an uninterrupted hold through boot. A hold while the application is already running belongs to the application too (on the T1000-E a long press powers the device off). To make the device boot with the button held, either power it on with the button down, or reboot it from a client while holding the button. The power-on case is deliberate: the application sets `DFU_MAGIC_SKIP` before powering off, and a board using the hold scheme keeps checking the button through that flag rather than skipping straight to the application. +The SenseCAP Card Tracker T1000-E and MeshTracker X1 have a single user button, and RESET is wired to the MCU reset pin rather than a second button, so **double-press reset does not work**. From **OTAFIX 2.5** both enter UF2 mode by hold: with USB connected, hold the button while the device boots and keep holding for about 3 seconds. The drive mounts a few seconds later. Releasing the button before then boots the application as normal. -To leave DFU mode, eject the mounted drive: the bootloader exits DFU and boots the application. There is no RESET button to fall back on, so this is the way out if you entered DFU and no longer want to copy firmware across. (Copying a `.uf2` across also reboots the board, as on any other board.) It has to be a real eject (`eject`, `udisksctl power-off`, or Finder/Explorer "Safely Remove"), since the bootloader only sees an eject as a SCSI START STOP UNIT. With no valid application, an eject brings the drive back instead (2.6.0 and later). +A short press does not enter DFU; the hold has to run through boot. A hold while the application is running belongs to the application (on the T1000-E a long press powers off). To boot with the button held, power on with the button down, or reboot from a client while holding it. -Unplugging is not a way out on OTAFIX 2.x: 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. From 2.6.0 a board with a valid application boots it after 6 minutes without activity, and a board with no application goes to the BLE OTA wait when the cable is pulled; ejecting the drive (or copying a `.uf2` across) is still the quick way out. +To leave DFU mode, eject the drive (`eject`, `udisksctl power-off`, or Finder/Explorer "Safely Remove"): the bootloader boots the application. Copying a `.uf2` across also reboots the board. With no valid application, an eject brings the drive back (2.6.0 and later). Unplugging does not leave DFU; on battery the board stays in the bootloader until the 6-minute inactivity timeout boots a valid application (2.6.0 and later). -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, which exposes no drive and has no eject. On 2.6.0 and later the 6-minute inactivity timer applies to it too, so a board with a valid application boots on its own; that path has not been exercised on hardware. -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. On OTAFIX 2.x the only way out is completing a serial DFU with `adafruit-nrfutil`. On 2.6.0 and later the 6-minute inactivity timer also applies to serial-only DFU, so a board with a valid application boots it on its own; that path has not been exercised on hardware. +To get the button path on a board with an older bootloader, enter UF2 mode from the application (`meshtastic --enter-dfu` or the Meshtastic Android app) and install `update-t1000_e_bootloader-*.uf2` (T1000-E) or `update-mesh_tracker_x1_bootloader-*.uf2` (X1) from the [releases](https://github.com/meshtastic/nRF52_Bootloader/releases). The boards share a USB ID, so the bootloader accepts the wrong board's file. --- ## Contributing -Want to build from source, add a board, or submit a fix? See -[`CONTRIBUTING.md`](./CONTRIBUTING.md) for the development setup and PR -process, and [`AGENTS.md`](./AGENTS.md) for how the codebase is put -together. +Want to build from source, add a board, or submit a fix? See [`CONTRIBUTING.md`](./CONTRIBUTING.md) for the development setup and PR process, and [`AGENTS.md`](./AGENTS.md) for how the codebase is put together. ## Getting help