nRF Connect Device Manager library is a Flutter plugin (aka "wrapper") around the existing Android and iOS nRF Connect Device Manager libraries. For more concrete documentation, you may also try reaching out into those for specific details.
- Android:
minSdkVersion 21 - iOS:
13.0 - macOS:
10.15 - Web: Chrome 56+ (Web Bluetooth required)
Use UpdateManagerFactory to create an instance of FirmwareUpdateManager:
final managerFactory: UpdateManagerFactory = FirmwareUpdateManagerFactory()
// `deviceId` is a String with the device's MAC address (on Android) or UUID (on iOS)
final updateManager = await managerFactory.getUpdateManager(deviceId);
// call `setup` before using the manager
final updateStream = updateManager.setup();To update the device, call update method on the FirmwareUpdateManager instance:
// `firmware` is a List of Image objects
List<Image> firmwareImages = [];
for (final file in manifest.files) {
final image = Image(
image: file.image,
data: firmwareFileData,
);
firmwareImages.add(image);
}
final configuration = const FirmwareUpgradeConfiguration(
estimatedSwapTime: const Duration(seconds: 0),
byteAlignment: ImageUploadAlignment.fourByte,
eraseAppSettings: true,
pipelineDepth: 1,
);
// `configuration` is an optional parameter. If not provided, default values will be used.
updateManager.update(firmwareImages, configuration: configuration);Alternatively, you can use updateWithImageData to update the device with a single image data:
await updateManager.updateWithImageData(image: fwImage!);Tip
update and updateWithImageData methods are asynchronous, however, they do not return a result of the update process. They only start the update process. To listen for updates, subscribe to the updateStream and progressStream. See also Issue #63 for more information.
To listen for updates, subscribe to the updateStream and progressStream:
updateManager.updateStateStream?.listen((event) {
if (event == FirmwareUpgradeState.success) {
print("Update Success");
} else {
print(event);
}
});
updateManager.progressStream.listen((event) {
print("${event.bytesSent} / ${event.imageSize}} bytes sent");
});To control the update, use FirmwareUpdateManager methods:
/// Pause the update process.
Future<void> pause();
/// Resume the update process.
Future<void> resume();
/// Cancel update.
Future<void> cancel();
/// Check if the progress is in process.
Future<bool> inProgress();
/// Check if the progress is paused.
Future<bool> isPaused();After the update is finished, call kill to kill the manager, otherwise it will lead to memory leaks and other issues:
updateManager.kill();To read the current image list (installed firmware slots) from the device:
List<ImageSlot>? slots = await updateManager.readImageList();When using FirmwareUpgradeMode.testOnly, the new firmware runs without being confirmed. Use confirmImage to permanently mark it as the active image after your own validation:
final slots = await updateManager.readImageList();
final activeSlot = slots!.firstWhere((s) => s.active && !s.confirmed);
await updateManager.confirmImage(activeSlot.hash);If confirmImage is not called before the next reboot, the bootloader will revert to the previous firmware.
To listen for logs, subscribe to the logger.logMessageStream:
updateManager.logger.logMessageStream
.where((log) => log.level.rawValue > 1) // filter out debug messages
.listen((log) {
print(log.message);
});To read logs from the device, use readLog method:
List<McuLogMessage> logs =
await updateManager.logger.readLogs(clearLogs: false);Web support uses the Web Bluetooth API, which is currently supported in Chrome and Chrome-based browsers. It requires a secure context (HTTPS or localhost).
Add the following <script> tag to your app's web/index.html before flutter_bootstrap.js. This installs an intercept that caches the BLE device obtained during scanning so the firmware update flow can reuse it without prompting the user a second time.
<body>
<script src="assets/packages/mcumgr_flutter/lib/src/mcumgr_web/mcumgr_setup.js"></script>
<script src="flutter_bootstrap.js" async></script>
</body>Without this script, the browser will show a second Bluetooth device picker when the firmware update starts.
The Settings Manager provides functionality to read and write device configuration settings via the MCU Manager protocol.
import 'package:mcumgr_flutter/mcumgr_flutter.dart';
final mcumgrSettings = McumgrSettings();Before using the settings manager, you must initialize it with the device address:
await mcumgrSettings.init(
deviceAddress: deviceAddress, // MAC address (Android) or UUID (iOS)
encodeValueToCBOR: true, // Encode values to CBOR format (optional, default: false)
padTo4Bytes: true, // Pad values to 4-byte alignment (optional, default: false)
);You can read all settings or a specific setting:
// Read all settings
final allSettings = await mcumgrSettings.readSettings();
print('All settings: $allSettings');
// Read a specific setting
final rawBytes = await mcumgrSettings.readSetting('config/timeout/value');
// The result is returned as Uint8List, which you need to decode based on the expected typeTo write a setting:
// Write a string value
final result = await mcumgrSettings.writeSetting('device/name', 'MyDevice');
// Write other types of values
await mcumgrSettings.writeSetting('config/interval', 1000);
await mcumgrSettings.writeSetting('feature/enabled', true);Settings are returned as raw bytes (Uint8List). You need to decode them based on their expected type:
// Decode as string (removing first byte which is typically a type indicator)
String decodeStringSettings(Uint8List bytes) {
return String.fromCharCodes(List.from(bytes)..removeAt(0));
}
// Decode as double
double decodeDoubleSetting(Uint8List bytes) {
if (bytes.length != 8) {
throw Exception("Expected 8 bytes for double, got ${bytes.length}");
}
return ByteData.sublistView(bytes).getFloat64(0, Endian.little);
}
// Decode as boolean
bool decodeBoolSetting(Uint8List bytes) {
if (bytes.isEmpty) throw Exception("Empty byte array for bool");
return bytes[0] != 0;
}When you're done using the settings manager:
await mcumgrSettings.dispose();final mcumgrSettings = McumgrSettings();
try {
// Initialize
await mcumgrSettings.init(
deviceAddress: device.remoteId.str,
encodeValueToCBOR: true,
padTo4Bytes: true,
);
// Read a setting
final deviceName = await mcumgrSettings.readSetting('device/name');
final nameString = String.fromCharCodes(List.from(deviceName)..removeAt(0));
print('Device name: $nameString');
// Write a setting
await mcumgrSettings.writeSetting('device/name', 'NewDeviceName');
} catch (e) {
print('Error: $e');
} finally {
await mcumgrSettings.dispose();
}