Xiao Ble Sense - Ota Dfu from Arduino IDE code

Hi all.
Finally I got ota dfu working with the android app Bluefruit Connect and the binaries created in Arduino IDE.
It wasn´t easy, because I didn´t find the solution ready to use in one place, I had to pick up a lot of half-solutions around the forums, just to finally merge everything in something that now is perfectly working, at least for my needings.

My goal was a completely sealed system, battery operated, without any button or external access, just a signalling led and a magnetic charging port.

The system stays most of the time in ultra low power mode, just to be activated by some accelerometer activity.
When active it advertises some ble services, among them the bledfu and bleuart services.
Now the app Bluefruit Connect can connect to the board to exchange some data through the bleuart service and , if necessary, to upload a new custom firmware.
Here comes the difficulties, because the process didn´t work out of the box and needed some adjustment to function.
The Updates feature of the Bluefruit Connect app have a Use Custom Firmwares button, which require the selection of a .hex file and a init file, which I thought were those generated by the arduino ide (export compiled bineries), but not exactly.
The “myapp.ino.hex” file is ok, and can be used as is.
The init file (the “myapp.ino.dat” file contained in the “myapp.ino.zip”) for some reason is not usable and makes the process fail, then another must be created using the following command line.
<adafruit-nrfutil.exe dfu genpkg --dev-type 0x0052 --application myapp.ino.hex dfu_package.zip>
The correct “myapp.ino.dat” must be extracted from dfu_package.zip and can be used in the update process, with the original .hex file.

Here another strange thing happens inside the Bluefruit Connect app, when the update process is started.
The board correctly goes in dfu mode ,but the app cannot complete the updating process because the board resets and its ble name changes to ADA_DFU, so the app is no more able to connect to the board again and complete the process (I thought it should do it automatically, because the mac address is the same, but not…)
Anyway, it is possible to make it work in two ways.
1 - cancel the update process when it displays the connection error, move back in the menu , connect to the now available ADA_DFU board and repeat the custom firmware update process, now it will work.
2 - I preferred not to wait for the app error, I forced the board to enter in dfu mode by sending a command through the bleuart protocol, then calling the enterOTADfu() api function inside my code.
Then , when the ADA_DFU board appears in the list, I connect to it and do the update.

Some system specification is important to make everything function:

  • the board must be the right one among the Seeed Nrf52840 Boards (NOT mbed-enabled boards)
  • the original bootloader was changed with an updated one, available in the adafruit repository ( thanks to Aovestdipaperino for the good job done on it)
  • the bledfu service must be advertised with all your other ble services (someone says must be the first service in the list).
    If the bledfu service is not initialized, the update process will work as well, but for some reason the board cannot restart after the update, and you must press the rst button, which in my case is not an option.

I hope this tutorial can help anyone who like me have been struggling for months attempting to make the ota dfu to work.

Hi there,

And Congrats on getting that Salad mixed and working…It’s a big deal the DFU - OTA area everyone can use it if it worked easier.
One reason I like the MCUBoot Bootloader over Adafruity.

Some stuff to consider…and AI observations.

Key Observations on the User’s Approach

  1. He used Adafruit’s adafruit-nrfutil tool, which is fine — it generates the correct zip DFU packages expected by the bootloader.
  2. He had issues with BLE disconnection and name change to ADA_DFU, which is a known quirk with the Adafruit bootloader. The app doesn’t reconnect automatically unless explicitly designed to do so.
  3. He relied on Bluefruit Connect, which isn’t maintained as actively as Nordic’s tools and doesn’t handle edge cases well (like reconnecting to ADA_DFU).

What He Did Right

  • Replaced the outdated bootloader with an Adafruit UF2-compatible one (from AovestdiPaperino’s GitHub likely — he’s known in the community).
  • Used BLE UART to trigger OTA DFU without buttons — ideal for sealed, headless systems.
  • Created a real-world workaround for OTA reconnection glitches.

What Could Be Improved

1. Use nRF Connect for Mobile Instead

  • It automatically handles reconnection to DFU mode device names.
  • Supports DFU ZIP packages directly (the .dat, .hex, .bin bundled in a .zip).
  • Better error reporting and logging.
  • Actively maintained by Nordic.

2. Use a Proper Bootloader with Buttonless DFU Support

  • Either Adafruit’s latest bootloader with buttonless DFU enabled
  • Or MCUboot + Zephyr/NCS (more complex, but truly robust)
  • This solves the “needs manual reset” problem entirely when correctly configured.

3. Simplify With Arduino IDE Tools

  • If the device is always sealed and headless, you can pre-bundle the DFU trigger via:
#include <bluefruit.h>
Bluefruit.begin();
Bluefruit.setTxPower(4);
Bluefruit.setName("SensorNode");
bledfu.begin(); // Needed if using Adafruit bootloader

If doing sealed SensorNodes like my project:

  • :white_check_mark: Stick with Adafruit’s bootloader for UF2 + OTA (just flash it once using SWD).
  • :white_check_mark: Use nrfutil dfu genpkg to generate OTA ZIP from Arduino .hex.
  • :white_check_mark: Trigger DFU in code (enterOTADfu() or NVIC_SystemReset() after setting flags).
  • :white_check_mark: Use nRF Connect for Mobile for smoother updates — especially with multiple SensorNodes.

IMO, Bluefruit Connect can work, but it’s clunky.
nRF Connect for Mobile is more robust, supports reconnections, and should be the go-to for DFU via BLE.

I’m currently working on a Multi-Node connected SensorNode setup
The DFU is the holy grail for this sort of thing.

HTH
GL :slight_smile: PJ :v:



6 SensorNode’s each with TEMP, HUMID, BATTERY, Motion or Digital BLE characteristics. all readable and two Notifies each. All 6 connected to a Xiao ESP32S3 :crossed_fingers:

1 Like

It took me a while to figure out how to perform Bluetooth BLE, over-the-air OTA, device-firmware-upgrade DFU of the Xiao nRF52840. It is actually very easy and so far, robust.

I am using the Arduino framework in the PlatformIO extension for Visual Studio Code

The first thing to do is replace the bootloader of the Xiao nRF52840 with a patched version of the Adafruit bootloader to enable BLE OTA upgrades. The original Adafruit bootloader that the Xiao bootloader is based on had some bugs that prevented OTA working.

Go to the link below and choose the latest bootloader release for your Xiao board - get the update uf2 binary:

e.g. update-xiao_nrf52840_ble_bootloader-0.9.2-OTAFIX2.1-BP1.2_nosd.uf2

Adafruit nRF52 Bootloader with Enhanced OTA DFU

Connect the Xiao board to your PC with a USB cable and place it into bootloader mode by quickly pressing the reset button twice (less than 0.5 seconds!). You should now be able to browse your Xiao board as an external USB device. Copy the patched bootloader uf2 file into the directory of your device. This will automatically overwrite the bootloader and reboot the device. You can put it into bootloader mode again and browse it to check the bootloader did upgrade properly.

With the patched bootloader you can now reboot your device into BLE DFU mode from software and upload new firmware via one of the NRF mobile apps.

In my case, the device is in an IP69 enclosure fixed to my chimney stack. It regularly sends data via LoRa to a hub that returns a short acknowledgement packet. The ack packet includes a control byte that I can use to change sensor settings but also put it into DFU mode so that I can upgrade the firmware without climbing onto the roof.

In PlatformIO to get the firmware binary for uploading: build the working code and look in the hidden .pio folder within the project folder, then the build folder, then the board folder, and find the firmware.zip file - this is what you need to upload using the mobile app. On Android, I have tried both nRF Connect for Mobile, and nRF Device Firmware Update - they both worked.

To reboot the device into DFU mode, issue the following commands in your software:

// The general purpose retention register survives rebooting
// and instructs the bootloader to enter OTA DFU mode,
// so set the register accordingly and reboot
NRF_POWER->GPREGRET = 0xA8; 
NVIC_SystemReset(); // reboot device 

The device will now reboot and start advertising itself via Bluetooth and you can upgrade the firmware using one of the apps. Easy. Note that once in DFU mode, it stays there without timing out, so you have to perform an OTA upgrade, unless there is a way to reboot it which I have not found.