Disconnection request from iOS device not forcing disconnection from nRF52

This is my environment:

IDE: VSC

SDK: NCS v2.2.0

iPhone 14 running iOS 16.6

nRF52-DK

This is how I have things set up:

My nRF52-DK is a sensor that is recording various events.  I have either a custom App on the iOS device, or I am using the nRF Connect App - behaviour is the same with both.

  • nRF52-DK starts advertising, so is acting as the Peripheral.
  • iOS device attempts a connection, so is acting as the Central.
  • once connection is made, it can read any of the sensor data.  All of the services/characteristics have L2 security, which forces bonding/pairing to occur.  The data then gets successfully read.
  • Once bonding/pairing has been established, the CTS functionality is enabled, which means the nRF52-DK then becomes the Client and the iOS device becomes the Server.  CTS info is successfully transferred.  All good so far
  • However, at this point, a disconnect request is initiated by the iOS device.  I can see this in the logs when using the nRF Connect App, and our App Developer can see the same thing when using his App.  However, we do not see a successful disconnection initiated on the nRF52-DK, which means it remains connected.  This presents a bunch of issues.
  • Up until this point in time, we've been implementing a "brute force" solution, whereby whenever the iOS device reads one of the Services/Characteristics, it also writes to a "Disconnect Service" on the nRF52-DK, which then forces a disconnection.  That works, but it doesn't seem like the correct approach to me.  A disconnection request from either Client or Server should result in a disconnect of both devices, but this isn't happening.
  • If I disable the CTS functionality, I can get the iOS disconnection request to work successfully.  However, the nRF52-DK will only show a successful disconnection ~ 30secs after the connection was first made.  So, if I connect, then issue a disconnect request, I have to wait 30sec for this to occur.  If I connect, then wait 15sec before issuing the disconnection, the disconnection will occur 15sec later.  And if I connect, then issue the disconnection request anytime from 30sec or more after, the disconnection occurs immediately.

So, there are two issues here:

1. CTS seems to prevent a disconnection from the iOS device actually resulting in a disconnection of the connections

2. Without CTS, there is a minimum 30sec connection time before a disconnection will occur

Is anyone able to help me resolve both these issues?

Best regards,

Mike

Parents
  • Have got to the bottom (I think) of problem 1. Turns out I’d subscribed to notifications for the CTS, so that was probably preventing the connection from properly disconnecting. I don’t need notifications in the CTS (it was a legacy from copying code from the peripheral_cts example), so have just removed that code. Seems to disconnect as expected now.

    However, I’m still seeing problem #2, but ONLY on iOS devices. On an Android device, the connection is disconnected the moment I trigger it from my Android device. Wwith an iOS device, there is still this unusual need to have a connection active for at least 30 sec before the disconnection request is honoured.

    This ticket seems to hint at a similar issue, but it was never resolved:

    https://devzone.nordicsemi.com/f/nordic-q-a/72834/long-time-to-disconnect/308335

    Regards,

    Mike

  • Hello Mike,

    I am glad to read that you already have resolved part of your issue.

    It sounds very strange to me that you are seeing the disconnection issues in the nRF Connect application logs, but not seeing this registered on the nRF52 DK. To properly investigate this it would be great if you could capture a sniffer trace of the on-air traffic between the devices when this occurs.
    Are you already familiar with the nRF Sniffer tool
    If not, please see the documentation here for a guide to how to get started using it, and how to capture specific scenarios such as this one where the devices enter into a connection.
    It would also be great if you could share the logs from the peripheral device and the central device for the scenarios as well, so that I may take a look and see if I can spot the reason for this unexpected behavior.

    Please do not hesitate to ask if you should have any questions to this! :) 

    Best regards,
    Karl

  • Hi Karl,

    I'll need to spend a bit of time getting the sniffer stuff set up, and might well use the Nordic peripheral_cts example to do this, as my custom firmware is pushing memory limits, and whenever I enable logging, I run out of room so can't build :-(

    I did a bit of homework on Google, and asked a few other BLE experts I know, and general consensus is that this is an iOS "feature".  Apparently iOS multiplexes the BLE connection management over any Apps that have BLE connectivity, and so if one decides to drop the connection, it first has to satisfy itself that any other active Apps don't want to have that connection remain in place before it actually makes the disconnect call via the radio layer.

    So, even though the nRF Connect App on iOS has requested a disconnection, the iOS itself has to satisfy itself that there are no other Apps with an active connection before it actually sends the disconnect request to the Peripheral.

    Not sure why, after 30secs of connection (but no activity) it decides to disconnect immediately.  Maybe that's an iOS setting?

    Cheers,

    Mike

Reply
  • Hi Karl,

    I'll need to spend a bit of time getting the sniffer stuff set up, and might well use the Nordic peripheral_cts example to do this, as my custom firmware is pushing memory limits, and whenever I enable logging, I run out of room so can't build :-(

    I did a bit of homework on Google, and asked a few other BLE experts I know, and general consensus is that this is an iOS "feature".  Apparently iOS multiplexes the BLE connection management over any Apps that have BLE connectivity, and so if one decides to drop the connection, it first has to satisfy itself that any other active Apps don't want to have that connection remain in place before it actually makes the disconnect call via the radio layer.

    So, even though the nRF Connect App on iOS has requested a disconnection, the iOS itself has to satisfy itself that there are no other Apps with an active connection before it actually sends the disconnect request to the Peripheral.

    Not sure why, after 30secs of connection (but no activity) it decides to disconnect immediately.  Maybe that's an iOS setting?

    Cheers,

    Mike

Children
  • Hello Mike,

    Mike Austin (LPI) said:
    I'll need to spend a bit of time getting the sniffer stuff set up, and might well use the Nordic peripheral_cts example to do this, as my custom firmware is pushing memory limits, and whenever I enable logging, I run out of room so can't build :-(

    I know that it might look daunting to familiarize with the nRF Sniffer tool the first time around, but it is a very powerful tool to wield when developing with BLE, and we are ready to help you if you should get stuck or have any questions so I highly recommend that you give it a shot! :) 

    Mike Austin (LPI) said:

    So, even though the nRF Connect App on iOS has requested a disconnection, the iOS itself has to satisfy itself that there are no other Apps with an active connection before it actually sends the disconnect request to the Peripheral.

    Not sure why, after 30secs of connection (but no activity) it decides to disconnect immediately.  Maybe that's an iOS setting?

    The explanation seems plausible to me - there are many things that iOS applies their own twists to in order to provide smoother customer experiences - but I still think there is something else going wrong here since the operation takes 30 seconds to disconnect, which is an ocean of time for any app to confirm that it can be closed.
    This is also a reason why it would be great to see a sniffer trace still, because then we can see if there's anything being sent between the devices leading up to this (or if it is just empty packets), so maybe we can find the reason for the delay there.

    Do you know if there exists any option in iOS to view its 'system log' or similar? I am unfortunately not personally familiar with iOS development, and so I dont know much about the chances of this being possible.

    Lastly, could you test your same application against another non-iOS device, so that we can isolate the behavior to occur with a iOS central?

    Best regards,
    Karl

  • Hi Karl,

    it definitely seems to be an iOS issue.

    our testing on Android shows no delay between Centrsl issuing a disconnect request and Peripheral honouring the disconnect.

    With iOS, the delay seems to be (30-n) secs, where n is the time after the connection is made that the disconnect request is issued. So, if I issue a disconnect request 32 sec after the connection, it is honoured immediately. If I issue the disconnect request 25 secs after the connection is made, it takes 5 sec for the disconnect request to be honoured. If I issue the disconnect 1 sec after the connection is made, it takes 29 secs for the disconnection to be honoured, etc

    Regards,

    Mike

  • Hello Mike,

    Thank you for the update and for confirming that this is not an issue when testing against non-iOS devices centrals.
    Perhaps there exists some sort of 'minimum connection time' within iOS? I would be surprised to learn this, but again I do unfortunately not have any personal experience with developing iOS applications.

    In this case I would have to recommend that you reach out to Apple through the iOS app developer forums to see if they can confirm that this is the correct behavior. Perhaps they can also explain if there is any way to work around this potential 'minimum connection time'.

    Best regards,
    Karl

  • Hi Karl,

    Set up the sniffer.  Watching the traffic being sent/received by my DK, I can see that it is in fact taking some time for iOS to issue the disconnect request.  So, for some reason, iOS requires a minimum ~ 30sec connection time before it will allow the connection to be removed.  Android doesn't have this limitation.

    Our workaround is to have a "Disconnect Service" on the Peripheral.  To terminate the connection, the Client writes to the characteristic for this Service.  The Peripheral then initiates the disconnection in response to this.  This all happens instantaneously, regardless of the length of the connection period.

    Still not clear why iOS behaves like this, but its not something I can change anyway, so our workaround is what we have settled on as a solution

    Cheers,

    Mike

  • Hello Mike,

    Mike Austin (LPI) said:
    Set up the sniffer. 

    Great, I am glad to read that you've got the sniffer up and running - it is a very powerful tool to have when developing BLE application.

    Thank you for the update about the iOS findings - your workaround sounds like a good approach for now, but I would still recommend that you reach out to Apple or an iOS forum to check if it is possible to disable or work around this behavior, or if there is anything else/underlying reason for this behavior, just in case. In either case I am glad that you've got a solution in place that works for your application.

    Please do not hesitate to open another ticket if you should encounter any other issues or questions in the future.

    Good luck with your development!

    Best regards,
    Karl

Related