Skip to content

zykeco/react-native-dfu-nitro

Repository files navigation

react-native-dfu-nitro

npm version License: MIT

A high-performance React Native library for Nordic Semiconductor DFU (Device Firmware Update) built on Nitro Modules.

Originally developed for Zyke Band — a fitness and health tracker created by a small team.

Features

  • High Performance: Built on Nitro Modules with JSI for zero-overhead native communication
  • Promise-Based API: Clean async/await interface with progress callbacks
  • Full DFU Support: Legacy DFU, Secure DFU (nRF5 SDK), and buttonless DFU
  • Pause / Resume / Abort: Full control over firmware updates in progress
  • Type-Safe: Comprehensive TypeScript definitions for all options, states, and errors
  • Expo Ready: Works with Expo (prebuild required)
  • New Architecture: Full support for React Native's new architecture
  • Zero Bridge: Direct JSI communication eliminates bridge bottlenecks

Companion Library

This library handles firmware updates only and is intended to be used together with react-native-ble-nitro for BLE device scanning, connection, and communication. Use react-native-ble-nitro to discover and connect to your device, then use react-native-dfu-nitro to perform the firmware update.

Quick Start

Installation

npm install react-native-nitro-modules react-native-ble-nitro react-native-dfu-nitro

iOS

npx pod-install

Android

No additional setup required — the library auto-links.

Expo

Prebuild and run:

npx expo prebuild
npx expo run:ios
# or
npx expo run:android

Usage

Basic DFU

import { DfuNitro, DfuState } from 'react-native-dfu-nitro';

const dfu = DfuNitro.instance();

try {
  await dfu.startDfu({
    deviceId: 'AA:BB:CC:DD:EE:FF',
    filePath: 'file:///path/to/firmware.zip',
    onStateChange: (state) => {
      console.log('State:', DfuState[state]);
    },
    onProgress: ({ progress, currentSpeed, part, totalParts }) => {
      console.log(`Part ${part}/${totalParts}: ${progress}% (${currentSpeed} kB/s)`);
    },
  });
  console.log('DFU completed successfully!');
} catch (error) {
  console.error('DFU failed:', error);
}

Using a Custom Manager Instance

import { DfuNitroManager } from 'react-native-dfu-nitro';

const dfu = new DfuNitroManager();

await dfu.startDfu({
  deviceId: 'AA:BB:CC:DD:EE:FF',
  filePath: 'file:///path/to/firmware.zip',
});

Pause, Resume, and Abort

const dfu = DfuNitro.instance();

// Start DFU (non-blocking — store the promise)
const dfuPromise = dfu.startDfu({
  deviceId: 'AA:BB:CC:DD:EE:FF',
  filePath: 'file:///path/to/firmware.zip',
  onProgress: ({ progress }) => console.log(`${progress}%`),
});

// Pause
dfu.pause();

// Resume
dfu.resume();

// Abort — the promise will reject with a DfuException
dfu.abort();

Error Handling

import { DfuNitro, DfuException, DfuError } from 'react-native-dfu-nitro';

const dfu = DfuNitro.instance();

try {
  await dfu.startDfu({
    deviceId: 'AA:BB:CC:DD:EE:FF',
    filePath: 'file:///path/to/firmware.zip',
  });
} catch (error) {
  if (error instanceof DfuException) {
    switch (error.code) {
      case DfuError.DeviceDisconnected:
        console.error('Device disconnected during DFU');
        break;
      case DfuError.FileInvalid:
        console.error('Firmware file is invalid');
        break;
      case DfuError.CrcError:
        console.error('CRC mismatch — retry the update');
        break;
      default:
        console.error(`DFU error [${error.code}]: ${error.message}`);
    }
  }
}

Advanced Configuration

await dfu.startDfu({
  deviceId: 'AA:BB:CC:DD:EE:FF',
  filePath: 'file:///path/to/firmware.zip',

  // Force legacy DFU mode (for older bootloaders)
  forceDfu: true,

  // Packet receipt notification (0 = disabled, default varies by platform)
  packetReceiptNotificationParameter: 12,

  // Number of retries on transient failures
  numberOfRetries: 3,

  // Enable buttonless DFU for devices without a physical button
  enableUnsafeExperimentalButtonlessDfu: true,

  // Scan for new address after DFU in legacy mode
  forceScanningForNewAddressInLegacyDfu: true,

  // Disable resume (start from scratch if interrupted)
  disableResume: true,

  onStateChange: (state) => { /* ... */ },
  onProgress: (progress) => { /* ... */ },
});

API Reference

DfuNitro

Singleton wrapper around DfuNitroManager.

import { DfuNitro } from 'react-native-dfu-nitro';

const dfu = DfuNitro.instance();

DfuNitroManager

The main class for performing DFU operations.

Method Returns Description
startDfu(options) Promise<void> Start a DFU process. Resolves on completion, rejects on error/abort.
pause() void Pause the current DFU process.
resume() void Resume a paused DFU process.
abort() void Abort the current DFU process. The startDfu promise will reject.

DfuManagerOptions

Extends DfuOptions with callback handlers.

Property Type Description
onStateChange (state: DfuState) => void Called when the DFU state changes.
onProgress (progress: DfuProgress) => void Called on upload progress updates.

DfuOptions

Property Type Required Description
deviceId string Yes Bluetooth device address (MAC on Android, UUID on iOS).
filePath string Yes URI to the firmware zip file.
firmwareType DfuFirmwareType No Type of firmware being flashed.
packetReceiptNotificationParameter number No PRN value. 0 to disable.
forceDfu boolean No Force legacy DFU mode.
enableUnsafeExperimentalButtonlessDfu boolean No Enable experimental buttonless DFU.
forceScanningForNewAddressInLegacyDfu boolean No Scan for new address after legacy DFU.
numberOfRetries number No Number of retries on failure.
connectionTimeout number No Connection timeout in milliseconds (iOS only).
dataObjectPreparationDelay number No Delay between data objects in ms (iOS only).
alternativeAdvertisingNameEnabled boolean No Use alternative advertising name (iOS only).
disableResume boolean No Disable resume — always restart from scratch.

DfuProgress

Property Type Description
part number Current part number (1-based).
totalParts number Total number of parts.
progress number Upload progress percentage (0–100).
currentSpeed number Current transfer speed (kB/s).
avgSpeed number Average transfer speed (kB/s).

DfuState

enum DfuState {
  Connecting,
  Starting,
  EnablingDfuMode,
  Uploading,
  Validating,
  Disconnecting,
  Completed,
  Aborted,
}

DfuFirmwareType

enum DfuFirmwareType {
  Application,
  Bootloader,
  SoftDevice,
  SoftDeviceBootloader,
  SoftDeviceBootloaderApplication,
}

DfuException

Thrown when a DFU process fails. Extends Error.

Property Type Description
code DfuError Machine-readable error code.
message string Human-readable error description.

DfuError

Unified error codes covering both iOS and Android platforms. See src/specs/NativeDfuNitro.nitro.ts for the full list.

Categories:

Range Category Examples
2–6 Remote Legacy DFU RemoteLegacyDfuCrcError, RemoteLegacyDfuOperationFailed
12–21 Remote Secure DFU RemoteSecureDfuSignatureMismatch, RemoteSecureDfuExtendedError
22–33 Remote Extended RemoteExtendedErrorFwVersionFailure, RemoteExtendedErrorInsufficientSpace
92–97 Remote Buttonless RemoteButtonlessDfuBusy, RemoteButtonlessDfuNotBonded
101–107 File / Firmware FileNotSpecified, FileInvalid, CrcError
201–204 Connection FailedToConnect, DeviceDisconnected, BluetoothDisabled
301–309 Service / Protocol ServiceDiscoveryFailed, DeviceNotSupported, BytesLost
500 Internal InvalidInternalState

Firmware File Preparation

This library expects a DFU zip package created with nRF Util or nrfutil.

Using nRF Util (recommended)

nrfutil pkg generate \
  --hw-version 52 \
  --sd-req 0x00 \
  --application firmware.hex \
  --application-version 1 \
  firmware.zip

Using legacy nrfutil (Python)

pip install nrfutil
nrfutil pkg generate \
  --hw-version 52 \
  --sd-req 0x00 \
  --application firmware.hex \
  --application-version 1 \
  firmware.zip

The resulting firmware.zip can be bundled with your app or downloaded at runtime.

Platform Notes

iOS

  • Uses NordicDFU (CocoaPods, ~> 4.16)
  • deviceId is the Core Bluetooth peripheral UUID (not a MAC address)
  • connectionTimeout, dataObjectPreparationDelay, and alternativeAdvertisingNameEnabled are iOS-only options
  • Requires iOS 16.0+

Android

  • Uses Android DFU Library (2.10.1 — pinned because 2.11.0 requires Kotlin 2.3, which is incompatible with React Native's Kotlin 2.1 compiler)
  • deviceId is the Bluetooth MAC address
  • DFU runs in a foreground service (DfuService) for reliability
  • Requires FOREGROUND_SERVICE and FOREGROUND_SERVICE_CONNECTED_DEVICE permissions (added automatically)
  • Requires BLUETOOTH_CONNECT permission at runtime (API 31+)

Permissions

iOS — Add to your Info.plist:

<key>NSBluetoothAlwaysUsageDescription</key>
<string>This app uses Bluetooth to update device firmware.</string>

Android — Add to your AndroidManifest.xml:

<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />

Troubleshooting

Problem Solution
DfuError.FileNotSpecified Ensure filePath is a valid file URI (file:///...).
DfuError.FileInvalid The zip is not a valid DFU package. Re-generate with nRF Util.
DfuError.DeviceDisconnected Device went out of range or was turned off during DFU. Move closer and retry.
DfuError.CrcError Data corruption during transfer. Retry the DFU.
DfuError.BluetoothDisabled Bluetooth is turned off. Prompt the user to enable it.
Android: DFU stalls at 0% Ensure BLUETOOTH_CONNECT permission is granted at runtime.
iOS: Device not found Verify you're using the Core Bluetooth UUID, not a MAC address.

Architecture

Built on Nitro Modules for direct JSI communication, type-safe native bindings, and high performance.

  • iOS: Swift implementation using NordicDFU delegates
  • Android: Kotlin implementation using Nordic DFU foreground service
  • TypeScript: Promise-based manager layer wrapping native callbacks
react-native-dfu-nitro/
├── src/
│   ├── specs/          # Nitro HybridObject specs & types
│   ├── manager.ts      # DfuNitroManager (Promise-based API)
│   ├── singleton.ts    # DfuNitro (singleton wrapper)
│   └── index.ts        # Public exports
├── ios/                # Swift implementation
├── android/            # Kotlin implementation + DfuService
├── nitrogen/generated/ # Auto-generated Nitro bindings
└── nitro.json          # Nitro Modules configuration

Contributing

Contributions are welcome! Fork the repo, make your changes, and submit a pull request.

git clone https://github.com/zykeco/react-native-dfu-nitro.git
cd react-native-dfu-nitro
npm install
npm run specs   # Regenerate Nitro bindings
npm run typecheck

License

MIT License — see LICENSE file.

Acknowledgments

Support

About

A high performance DFU implementation for react native based on nitro modules

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors