NordicSemiconductor/IOS-DFU-Library

Detecting a desired peripheral before starting firmware update

xaviergspintly opened this issue · 5 comments

So the below is where a peripheral is set before initiating the firmware process:
let controller = initiator.start(target: peripheral)
I understand that this is after a device has been put into DFU mode.
If i'm already connected to a device and I know that I want to upload to that device, but after putting in DFU, the device gets disconnected.
Now if there are more that one device in DFU mode while I'm scanning for a device for firmware update, how do I select my device of interest without having a UI selection of a peripheral?
Does the peripheral Id change when in DFU mode as compared to normal mode?

So, 'device put into DFU mode'. I just got back from vacation so I might get things wrong. But, 'DFU Mode' is really that the Device advertises that it supports the Legacy DFU system, which is the one this library addresses. If you're trying to DFU using the more modern McuMgr / Zephyr derived system, this is the wrong library. The correct one is this: https://github.com/NordicSemiconductor/IOS-nRF-Connect-Device-Manager

Yes, the device getting disconnected when DFU starts is a normal thing. This is because to finish DFU, the device must restart into the new uploaded firmware to finish. Therefore, disconnection is expected.

As for 'peripheral id', you mean the Device's UUID or MAC Address ? This is not visible from iOS / Apple libraries. But if memory serves me right, the MAC Address is derived from physical hardware not software, so it should not change. But the ID or UUID Apple exposes through APIs, is not the MAC Address of a device. It's a randomised value that doesn't offer much value, except inform the user whether 'it's the same device', for example, if you want to connect to a device you were previously connected to between app restarts. I presume the surfaced UUID exposed by CoreBluetooth will change if we flash the firmware, or if the user goes to Settings -> Bluetooth and turns off and on Bluetooth, since this clears the device cache.

I understand that this is after a device has been put into DFU mode.
If i'm already connected to a device and I know that I want to upload to that device, but after putting in DFU, the device gets disconnected.

No, it can be also a device in app mode, with Buttonless service. The buttonless service or characteristic have changed over different nRF5 SDK versions, but all are supported. The device can also be connected, no need to disconnect.

Now if there are more that one device in DFU mode while I'm scanning for a device for firmware update, how do I select my device of interest without having a UI selection of a peripheral?
Does the peripheral Id change when in DFU mode as compared to normal mode?

If your device is based on nRF5 SDK v15 or newer it will do a "smart jump". Before sending the command to trigger the jump to bootloader mode the library is sending a random 5-digit ID. The device will start advertising with this ID as Local Name. The library will scan until this name is found. You may customize this behavior using DfuServiceInitiator, here:

/**
In SDK 14.0.0 a new feature was added to the Buttonless DFU for non-bonded
devices which allows to send a unique name to the device before it is switched
to bootloader mode. After jump, the bootloader will advertise with this name
as the Complete Local Name making it easy to select proper device. In this case
you don't have to override the default peripheral selector.
Read more:
http://infocenter.nordicsemi.com/topic/com.nordic.infocenter.sdk5.v14.0.0/service_dfu.html
Setting this flag to false you will disable this feature. iOS DFU Library will
not send the 0x02-[len]-[new name] command prior jumping and will rely on the DfuPeripheralSelectorDelegate just like it used to in previous SDK.
This flag is ignored in Legacy DFU.
**It is recommended to keep this flag set to true unless necessary.**
For more information read:
https://github.com/NordicSemiconductor/IOS-nRF-Connect/issues/16
*/
@objc public var alternativeAdvertisingNameEnabled = true
/**
If `alternativeAdvertisingNameEnabled` is `true` then this specifies the
alternative name to use. If nil (default) then a random name is generated.
The maximum length of the alertnative advertising name is 20 bytes.
Longer name will be trundated. UTF-8 characters can be cut in the middle.
*/
@objc public var alternativeAdvertisingName: String? = nil

The device can also be connected, no need to disconnect.

I mean, the library will automatically send the "jump to bootlaoder" mode command and the optional name, so you don't have to worry about it.

Just start the library with the device you already have selected and connected.
If your device is based on an SDK older than 15 (or 14?), which doesn't support this alternative name, the library will use a DfuDeviceSelector to choose the right device.

You set it here:

/**
The selector object is used when the device needs to disconnect and start
advertising with a different address to avoid caching problems, for example
after switching to the Bootloader mode, or during sending a firmware containing
a Softdevice (or Softdevice and Bootloader) and the Application.
After flashing the first part (containing the Softdevice), the device restarts
in the DFU Bootloader mode and may (since SDK 8.0.0) start advertising with an
address incremented by 1. The peripheral specified in the `init` may no longer
be used as there is no device advertising with its address.
The DFU Service will scan for a new device and connect to the first device
returned by the selector.
The default selecter returns the first device with the required DFU Service
UUID in the advertising packet (Secure or Legacy DFU Service UUID).
Ignore this property if not updating Softdevice and Application from one ZIP
file.
*/
@objc public var peripheralSelector: DFUPeripheralSelectorDelegate

By default if uses the first found device advertising the correct DFU Service UUID, but you assign your own selector based on advertising data or RSSI.

@philips77 So the way we currently do it in Android is that we connect to a device in normal mode. We send it a command to put it into DFU (bootloader) mode. In Android the device shows up as the mac id of the device in normal mode +1 bit. So XX:XX:XX:XX:AB will show up as XX:XX:XX:XX:AC. We specifically connect to XX:XX:XX:XX:AC and start firmware update using nRF DFU library, since we know that it is the device we put into DFU. Now iOS doesn't have MAC IDs. All we have is peripheral IDs. How do we move through the landscape for iOS. Does the peripheral ID remain same for the same device in normal and DFU mode? Or does it increment? Or there is some other method to accurately target the device we are interested in (which we put into DFU using a command)?

In Android the device shows up as the mac id of the device in normal mode +1 bit. So XX:XX:XX:XX:AB will show up as XX:XX:XX:XX:AC. We specifically connect to XX:XX:XX:XX:AC and start firmware update using nRF DFU library, since we know that it is the device we put into DFU.

Do you use your custom way of switching to DFU bootloader mode? The library can do this on its own if you're using the DFU Buttonless Service. This works for both Legacy and Secure DFU, where in Secure DFU there's a variant with and without bonding.

Does the peripheral ID remain same for the same device in normal and DFU mode? Or does it increment?

No, it's a random one each time a device changes MAC.

Or there is some other method to accurately target the device we are interested in (which we put into DFU using a command)?

I replied here: #508 (comment)

If you're using own implementation for jumping to the bootloader mode, you may also have similar feature implemented, depending on your nRF5 SDK version and how did you implement buttonless feature. In Nordic's impl the client sends 0x02-[random name] to the device:
https://github.com/NordicSemiconductor/IOS-DFU-Library/blob/main/Library/Classes/Implementation/SecureDFU/Services/SecureDFUService.swift#L624-L626
followed by 0x01 - the jump command. After restarting the device will advertising with the random name as Complete Local Name.