Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down
4 changes: 2 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
22 changes: 11 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -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

Expand Down Expand Up @@ -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)).
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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.

Expand All @@ -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`).

Expand All @@ -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
Expand Down Expand Up @@ -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`.

---

Expand All @@ -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/)

Expand Down
1 change: 1 addition & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -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: |

Expand Down
8 changes: 5 additions & 3 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
Loading