NordicSemiconductor / IOS-DFU-Library

OTA DFU Library for Mac and iOS, compatible with nRF5x SoCs
http://www.nordicsemi.com
BSD 3-Clause "New" or "Revised" License
526 stars 215 forks source link

DFU update cannot be started without turning bluetooth on/off #51

Closed michaello closed 7 years ago

michaello commented 7 years ago

Hey guys,

I'm struggling with this issue. In my app I can't really force user to turn bluetooth on and off. I was looking at some posts on your forums, but no one really stumble upon some reasonable solution.

Is it possible to make DFU service visible without this?

philips77 commented 7 years ago

Hi, as I understand your services are cached by iOS. There are two ways to correctly handle changing services, both involve Service Changed characteristic. First of all, you must make sure this characteristic is in your Generic Attribute service. If your device is bonded, this characteristic should indicate. Ios will automatically enable cccd for it after it gets bonded and will invalidate cache whenever indication is received and automatically perform service discovery. If your device isn't bonded, simple fact that the characteristic is present there, will tell iOS never to cache services. This is what the BLE spec says, and iOS behaves accordingly.

In case your device is bonded, and you would like it to send the indication when it switches to bootloader mode, and after DFU, to app mode again. This requires passing the bond info to the bootloader and then not removing it during DFU. It is not yet implemented in Secure DFU in SDK 12, but was in legacy DFU since SDK 8 (to make sure the bond info is safe after dfu a small change was required - setting number to bytes from app storage to be preserved). Instead, in Secure DFU we do an address trick. When the device switches to dfu bootloader mode it starts advertise with address incremented by 1, so it is visible as a new device for iOS, so it does a new service discovery and there is no caching problem. Then, after a DFU is complete, and your services list has change and you don't have SC characteristic you may increment the address again in your new app. This, however, is a trick, as devices may be produced in bundles of sequential addresses so it may overlap.

michaello commented 7 years ago

Thanks for detailed response. Unfortunately for my device I can't spot the Service Changed service. And in the app that you've provided I can't start the update when device is in DFU mode. I send command in my app to change into DFU mode, then I switch to Nordic app. I can select it from available devices list, it's advertised as Dfu, but the update won't fire(immediately alert shows up). Any ideas?

michaello commented 7 years ago

Anything? :(

mostafaberg commented 7 years ago

So sorry about the delays its the weekend and will get back to you on monday , can you create like an example app till then that reproduces the issue and send me a link to it so i can fix it much faster ? I guess thats the quickest way On Dec 3, 2016 4:34 PM, michaello notifications@github.com wrote:Anything? :(

—You are receiving this because you are subscribed to this thread.Reply to this email directly, view it on GitHub, or mute the thread.

michaello commented 7 years ago

Hey Mostafa, thanks for the reply. I'm just using the example app that is provided here. I've heard from my colleague that Android is working fine. I could just record the actions that I'm making.

mostafaberg commented 7 years ago

No need to record you can just send the steps to reproduce, and also if you can give us more details about the peripheral that you're trying to flash and the phone(s) you're testing with and I'll try to reproduce the issue

philips77 commented 7 years ago

Hi @michaello If you don't have the Service Changed characteristic in the Generic Attribute service (please verify that you don't have it on Android or PC as iOS/Mac does not expose this service at all) then you will not be able to update your device on iOS at all, unless your device has a "button to reset device in DFU mode" feature. If you have this feature, you have to make sure that the device running bootloader mode triggered with a button starts to advertise with an address different than it had in app mode. This is a default behaviour in our DFU bootloader (I'm not sure if since SDK 7 or 8). Lack of the Service Changed characteristic means, according to Bluetooth Core Spec, that your device will never change its services. Then iOS is free to cache them. The iOS cache is cleared when Bluetooth is reset. However, if you don't have this magic button that triggers DFU mode, and rely only on the buttonless jump, you have a problem. The iOS will have to connect to the device to send the jump command, but it will cache services when connected... Also, you can't connect, send jump command, quickly turn Bluetooth off and on to clear the cache and connect again, as the bootloader advertises with direct advertisement and iOS devices change their MAC address every time you reset Bluetooth so it will not scan the advertising device any more nor any other device will.

So, as a summary:

  1. You have to enable Service Changed characteristic in Generic Attribute service if you want to use DFU
  2. If you don't have it, at least have a way to force DFU mode. A Button that you need to hold while turning on the device, or any other way that does that. Make sure the DFU bootloader started that way advertises with a MAC address incremented by 1 (or at least different one that in app mode).
  3. If you don't have this magic button - you have to use Android device to upgrade your device. Make sure that the update adds the Service Changed characteristic.
michaello commented 7 years ago

Thank you very much for your answer. The problem is that when I'm sending command to device to make it into DFU mode, then I'm losing connection. I guess I can't listen for service changed notification even though device could implement it.

philips77 commented 7 years ago

If your device is not bonded then Service Changed indications are ignored, I think (or at least should be?). It's just the presence of this characteristic that says that services should never be cached and iOS will not cache them. Every time you do service discovery after connection it performs the discovery from scratch.

Yes, sending "jump to bootloader" command will cause reset of the device and connection will terminate. The bootloader will then start direct advertising to the address from which this command was received. It's services will be different than in app mode of course.