nRF Connect Device Manager
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.
Supported Platforms
- Android:
minSdkVersion 21 - iOS:
13.0 - MacOS:
10.15
Getting Started
Creating a manager
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();
Updating the device
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.
Listening for updates
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");
});
Controlling the update
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();
Killing the manager
After the update is finished, call kill to kill the manager, otherwise it will lead to memory leaks and other issues:
updateManager.kill();
Reading image list
To read the current image list (installed firmware slots) from the device:
List<ImageSlot>? slots = await updateManager.readImageList();
Confirming an image
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.
Reading logs
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);
Settings Manager
The Settings Manager provides functionality to read and write device configuration settings via the MCU Manager protocol.
Creating a Settings Manager
import 'package:mcumgr_flutter/mcumgr_flutter.dart';
final mcumgrSettings = McumgrSettings();
Initializing the Settings Manager
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)
);
Reading Settings
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 type
Writing Settings
To 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);
Decoding Setting Values
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;
}
Disposing the Settings Manager
When you're done using the settings manager:
await mcumgrSettings.dispose();
Complete Example
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();
}