Adafruit nRF52 Bootloader: getting UF2 and DFU onto Nordic BLE boards
USB-enabled bootloaders for the nRF52 BLE SoC chips
At a glance
- What is it?
- Adafruit's nRF52 Bootloader gives nRF52 boards a USB mass storage bootloader, so firmware is loaded by copying a .uf2 file to a drive that appears on your computer, with serial DFU and over-the-air update as alternatives. The pin table at reset is the part you have to get right, and the bootloader's own release history tells you when to distrust it.
- Who is it for?
- Adopt this bootloader if you are developing on an nRF52840 board and want firmware loading that does not depend on a debug probe, since the UF2 path needs only a USB cable and a file copy. Do not adopt it if you need signed firmware enforcement or dual bank updates out of the box, since both are disabled by default, or if you are shipping a product and cannot accept a bootloader that is publicly documented and freely downgradeable.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 155 days ago.
- What is it written in?
- Mainly C, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
UF2 turns firmware flashing into a file copy
The repository describes itself as a CDC/DFU/UF2 bootloader for the Nordic nRF52 microcontroller, and the three acronyms mark the three ways in. The one that changed how people work with these boards is UF2.
UF2 appears as a flash drive when you plug the board in, and you copy a `.uf2` formatted application image onto it to load new firmware. The README links to Microsoft's uf2 project for more background on the format. There is no bootloader tool to install, no probe to attach, and no driver beyond the one your operating system already has for a USB mass storage device.
The other two paths are DFU over serial and CDC using the adafruit-nrfutil tool, and OTA, meaning over-the-air update. Serial DFU requires adafruit-nrfutil, described as a modified version of Nordic's pc-nrfutil, and the README gives the install command:
$ pip3 install --user adafruit-nrfutilThe choice between them is a choice about failure recovery rather than about convenience. A board that enumerates as a drive is a board that can be reflashed from any machine with a USB port and a file manager, which is exactly what you want when the firmware on it has bricked the USB stack.
Two pins decide what the bootloader does on reset
The mode selection is entirely hardware driven. There are two pins, `DFU` and `FRST`, and the bootloader checks their levels upon reset or power on. The table is short enough to memorize and specific enough that a wiring mistake sends you somewhere unexpected.
With `DFU = LOW` and `FRST = HIGH`, the board enters the bootloader with UF2 and CDC support. With `DFU = LOW` and `FRST = LOW`, it enters the bootloader in OTA mode, which is the path for upgrading from a mobile application such as Nordic nrfConnect or Toolbox. With `DFU = HIGH` and `FRST = HIGH`, the board runs the application code if it is present and otherwise enters DFU with UF2.
A fourth combination, `DFU = HIGH` and `FRST = LOW`, is the factory reset mode that erases the firmware application and its data, and the README strikes that line through, which reads as a mode that exists in the code but is not offered as a supported entry path.
There is also a software entry. The `GPREGRET` register can be set to force the bootloader into any of those modes, plus a CDC-only mode for Arduino, and the application sets it before performing a soft reset. The example the README gives is short:
#include "nrf_nvic.h"
void reset_to_uf2(void) {
NRF_POWER->GPREGRET = 0x57; // 0xA8 OTA, 0x4e Serial
NVIC_SystemReset(); // or sd_nvic_SystemReset();
}The magic values in that comment are the difference between a device that can talk to a desktop tool and one that can only be updated from a phone, and they are the kind of detail that is easy to get backwards.
Double reset gets you into DFU on nRF52840 hardware
There is a fourth way in that needs no pins at all: double reset. Resetting twice within 500 ms enters DFU with UF2 and CDC support, and the README notes this only works with the nRF52840.
That restriction is worth pausing on, because it is a different silicon generation from the nRF52832. The other nRF52 parts do not offer the same magic-button path, which means the pin wiring is not a convenience for them but the primary mechanism. Any board design for an nRF52832 has to give the boot pins a deliberate treatment at the hardware level.
The named board examples make the same point concretely. On the Nordic PCA10056 DK board, `DFU` is connected to Button1 and `FRST` to Button2, so holding Button1 while clicking RESET puts the board into USB bootloader mode with UF2 and CDC support, and holding Button2 while clicking RESET puts it into OTA bootloader mode. On the PCA10059 Dongle, `DFU` is connected to the white button and `FRST` to pin 1.10, with an adjacent ground pad so you can pull `FRST` low by grounding it as if you had pushed the button.
For any other board, the README says to check the board definition for details, which points at the per-board files in the source tree rather than at a generic instruction.
Converting firmware to UF2 needs the right family id and start address
Making your own UF2 image means running Microsoft's Python conversion script, uf2conv.py, on a .bin or .hex file, and the family id is the part people get wrong.
For a hex file the command is short, and the family is specified as 0xADA52840 for the nRF52840 or 0x621E937A for the nRF52833:
nRF52840
uf2conv.py firmware.hex -c -f 0xADA52840
nRF52833
uf2conv.py firmware.hex -c -f 0x621E937AA .bin file additionally needs the application address passed with the -b switch, and that address depends on the SoftDevice size and version. The README gives 0x26000 for S140 v6 and 0x27000 for v7, with the matching family ids:
nRF52840
uf2conv.py firmware.bin -c -b 0x26000 -f 0xADA52840
nRF52833
uf2conv.py firmware.bin -c -b 0x27000 -f 0x621E937ABootloader images use a separate family, 0xd663823c, converted with `uf2conv.py bootloader.hex -c -f 0xd663823c`.
Getting the application address wrong is the failure mode here. A hex file carries its own addresses, so the -b switch is unnecessary and the family id is the only thing that matters. A raw binary carries none, so if the address is wrong the image lands in the wrong place in flash and the board will not boot it.
Pre-built binaries exist, and they can be downgraded freely
You do not need to compile anything to put this bootloader on a board. The README describes burning or upgrading the bootloader with a J-link or with serial DFU to a specific pre-built binary version, without the hassle of installing a toolchain, and it says this is preferred if you are not developing or customizing the bootloader itself. The binaries are on the GitHub releases page.
The same section contains the sentence that should shape any product decision. The bootloader can be downgraded, and because the binary release is a merged version of both the bootloader and the Nordic SoftDevice, you can freely upgrade or downgrade to any version you like.
That is convenient for a hobbyist and a problem for a product. The bootloader is a merged image that includes the SoftDevice, so the two are versioned as one artifact and there is no way to hold the bootloader fixed while updating the radio stack. It also means the bootloader binary is a fully known, publicly downloadable artifact that anyone with physical access can flash, and the README's pin table documents every way to get into it.
For your own board, this is a feature: you can always get back to a known state. For a shipped product, it is a decision to make deliberately, and it interacts with the two features that are off by default.
Dual bank and signed firmware exist but ship disabled
The feature list names six capabilities, and two of them are switched off in the default build. Supports dual bank firmware updates is marked disabled by default, and so is supports signed firmware updates.
Dual bank is the mechanism for surviving a failed update: with two banks, a new image is written to the inactive one and the old one is only discarded once the new one has been confirmed to run. The Makefile documents the corresponding build variables, with `DUALBANK_FW` described as the flag for implementing the dual bank feature to allow autorecover from failed updates.
Signed firmware is the other side of the same coin. The Makefile has `SIGNED_FW` for a bootloader that will only accept signed firmware, plus `SIGNED_FW_QX` and `SIGNED_FW_QY` for the verification key coordinates, and a `FORCE_UF2` variable whose comment spells out the catch. If `SIGNED_FW` is 1, UF2 support is forced back in, and the comment says plainly that this is UNSECURE because UF2 does not validate the signature.
So a signed-firmware configuration still accepts unsigned images over the USB drive path, and a secure product needs that path closed, which means wiring DFU so that the drive-enumerating mode is not reachable, or removing it. That is a hardware decision, and it is not something the README explains how to do.
One more constraint is documented for OTA. The Packet Receipt Notification must be 8 or less, configurable in nRF DFU, otherwise the bootloader runs out of memory. The number is small enough to be easy to miss when you configure the tool.
Building it yourself takes ARM GCC, the Nordic tools and a J-Link
The README is direct about who should continue past this point. You should only continue if you are looking to develop a bootloader for your own, and you must have a J-Link available to unbrick your device.
The prerequisites are ARM GCC, Nordic's nRF5x Command Line Tools, and the Python IntelHex package. The clone needs submodules, because the source tree references the SoftDevice and other vendored pieces:
git clone --recurse-submodules https://github.com/adafruit/Adafruit_nRF52_BootloaderFor git versions before 2.13.0 the README says to do that manually, cloning without the flag and then running the submodule update in the directory.
The build itself is a make with a board variable, for example:
make BOARD=feather_nrf52840_express allRunning `make` with no board gives you the list of supported targets and stops, which is a quick way to confirm your board is in the tree. The CMake path is the alternative, and the two differ in how the board is passed:
mkdir build
cd build
cmake .. -DBOARD=feather_nrf52840_express followed by `make` in the build directory. The README notes you can use the generator of your choice, for example adding `-GNinja` and invoking `ninja build`, and that `cmake --build . --target help` lists all supported targets.
The Makefile itself shows how much board variation is in play. It includes a local `Makefile.user` for customization and a per-board `src/boards/$(BOARD)/board.mk`, then derives paths for the SDK components, an older SDK 11 tree, TinyUSB, TinyCrypt and nrfx. The SoftDevice version defaults to 7.3.0 for the nrf52833 sub-variant and 6.1.1 otherwise, and the board file can override it.
Where this sits against building your own bootloader
The alternative for a product team is to write the boot logic yourself against the Nordic SDK, or to use the vendor's own bootloader, and the difference in approach shows up in three places.
First, merge and versioning. This bootloader ships as a merged image of bootloader and SoftDevice, so the two are upgraded and downgraded together. A custom bootloader can keep the radio stack separate and versioned on its own, at the cost of owning the integration.
Second, the security posture. Dual bank and signed firmware are implemented here but disabled in the default build, and UF2 accepts unsigned images by design. A bootloader written for a product can require a signature on every path, including the mass storage path, and can ship with dual bank enabled rather than optional. The price is that you write it, test it, and keep it patched.
Third, the update channel. OTA here means the bootloader is updatable from a mobile application such as nRF Connect or Toolbox, and the feature list also covers self-upgrade of the bootloader itself over serial and OTA. A custom design can define its own update trigger, including the `GPREGRET` soft reset mechanism, and can restrict the OTA path to a signed manifest.
For a maker, the trade is clear and favorable. This bootloader removes toolchain setup and probe dependency from day one and gives a documented recovery path when firmware goes wrong. For a product, it removes control, and the README's own notes about downgradeability and the unsigned UF2 path are the two places where that shows.
Editorial conclusion
Adopt this bootloader if you are developing on an nRF52840 board and want firmware loading that does not depend on a debug probe, since the UF2 path needs only a USB cable and a file copy. Do not adopt it if you need signed firmware enforcement or dual bank updates out of the box, since both are disabled by default, or if you are shipping a product and cannot accept a bootloader that is publicly documented and freely downgradeable. Verify first that your board appears in supported_boards.md, because the officially supported list is Adafruit boards and a handful of community additions, and note that the double reset entry path is documented as working only on nRF52840 hardware.
Frequently asked questions
How do I flash firmware onto an nRF52 board with this bootloader?
Connect the board over USB so it appears as a flash drive, then copy a `.uf2` file onto it. To create the file, run uf2conv.py on your .hex or .bin with the family id 0xADA52840 for the nRF52840 or 0x621E937A for the nRF52833, and for a .bin also pass the application address with -b.
How do I get an nRF52 board into bootloader mode?
The bootloader checks two pins on reset. `DFU = LOW` with `FRST = HIGH` gives UF2 and CDC support, `DFU = LOW` with `FRST = LOW` gives OTA mode, and `DFU = HIGH` with `FRST = HIGH` runs the application if present. On nRF52840 hardware a double reset within 500 ms also enters DFU with UF2 and CDC support.
Which boards does the Adafruit nRF52 Bootloader officially support?
The README lists the Adafruit CLUE, Circuit Playground Bluefruit, Feather nRF52832, Feather nRF52840 Express, Feather nRF52840 Sense, ItsyBitsy nRF52840 Express, LED Glasses Driver nRF52840, Metro nRF52840 Express, and the Raytac MDBT50Q-RX Dongle. Other third-party boards are added by community makers, with a complete list in supported_boards.md.
Do I need to compile the bootloader to use it?
No. Pre-built binaries are on the GitHub releases page and can be burned with a J-Link or DFU over serial, which the README says is preferred if you are not customizing the bootloader. You need ARM GCC, the Nordic nRF5x Command Line Tools and Python IntelHex only if you are building it yourself.
Can the Adafruit nRF52 Bootloader verify signed firmware?
The signed firmware update support is disabled by default, and the Makefile notes that forcing UF2 support in a signed build is UNSECURE because UF2 does not validate the signature. A secure product therefore has to close or remove the USB drive path, since the standard UF2 route accepts unsigned images.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/adafruit-adafruit-nrf52-bootloader)Was this useful?