With the onset of the Internet of Things, Bluetooth Low Energy (BLE) has become a popular choice for connecting interesting new devices to the smart phones we all carry in our pockets.
In dealing with BLE on iOS devices, I’ve had great success with iBeacons and single devices. However, when dealing with multiple BLE devices in iOS, things get tricky.
The Problem with Managing Multiple BLE Devices in iOS
Why many connections? Throughput. I need to read kilobytes of data from each device as quickly as possible. Given the nature of BLE’s sparse use of the radio, running multiple connections greatly increases our throughput.
Apple has also added extra limitations to its implementation of the BLE spec. Connection intervals (the time between devices connecting and exchanging data) cannot be faster than 20ms on iOS. The BLE spec allows for 7.5ms. That’s more than twice as slow; we need to use multiple connections.
Unfortunately, the current version of iOS has some major Bluetooth bugs when dealing with many connections. On iOS, Bluetooth is managed by a process called BTServer. When loaded up with devices, it likes to crash. It crashes worse than my six-year-old after eating an entire bag of Halloween candy.
... Hardware Model: iPhone7,2 Process: BTServer  Path: /usr/sbin/BTServer Identifier: BTServer ... Exception Type: EXC_CRASH (SIGABRT) Exception Codes: 0x0000000000000000, 0x0000000000000000 Exception Note: EXC_CORPSE_NOTIFY Triggered by Thread: 3 Filtered syslog: None found Thread 0 name: Dispatch queue: com.apple.main-thread Thread 0: 0 libsystem_kernel.dylib 0x00000001995d8a40 mach_msg_trap + 8 1 libsystem_kernel.dylib 0x00000001995d88bc mach_msg + 72 2 CoreFoundation 0x000000018408c108 __CFRunLoopServiceMachPort + 196 3 CoreFoundation 0x0000000184089e0c __CFRunLoopRun + 1032 4 CoreFoundation 0x0000000183fb8ca0 CFRunLoopRunSpecific + 384 5 CoreFoundation 0x000000018400644c CFRunLoopRun + 112 6 BTServer 0x00000001000a0fc4 0x100098000 + 36804 7 libdyld.dylib 0x00000001994d68b8 start + 4 Thread 1 name: Dispatch queue: com.apple.libdispatch-manager Thread 1: 0 libsystem_kernel.dylib 0x00000001995f44fc kevent_qos + 8 1 libdispatch.dylib 0x00000001994b8a04 _dispatch_mgr_invoke + 232 2 libdispatch.dylib 0x00000001994a7874 _dispatch_source_invoke + 0 Thread 2 name: StackLoop Thread 2: 0 libsystem_kernel.dylib 0x00000001995f2f48 __psynch_cvwait + 8 1 libsystem_pthread.dylib 0x00000001996bad20 _pthread_cond_wait + 704 2 BTServer 0x00000001000bd044 0x100098000 + 151620 3 libsystem_pthread.dylib 0x00000001996bbb28 _pthread_body + 156 4 libsystem_pthread.dylib 0x00000001996bba8c _pthread_body + 0 5 libsystem_pthread.dylib 0x00000001996b9028 thread_start + 4 Thread 3 name: RxLoop Thread 3 Crashed: 0 libsystem_kernel.dylib 0x00000001995f3140 __pthread_kill + 8 1 libsystem_pthread.dylib 0x00000001996bcef8 pthread_kill + 112 2 libsystem_c.dylib 0x0000000199566b78 abort + 140 3 BTServer 0x00000001000b0ea8 0x100098000 + 102056 4 BTServer 0x00000001000ff944 0x100098000 + 424260 5 BTServer 0x00000001000ff5dc 0x100098000 + 423388 6 BTServer 0x00000001000b4560 0x100098000 + 116064 7 BTServer 0x00000001000b3c58 0x100098000 + 113752 8 BTServer 0x00000001000b0b7c 0x100098000 + 101244 9 libsystem_pthread.dylib 0x00000001996bbb28 _pthread_body + 156 10 libsystem_pthread.dylib 0x00000001996bba8c _pthread_body + 0 11 libsystem_pthread.dylib 0x00000001996b9028 thread_start + 4 ... Thread 3 crashed with ARM Thread State (64-bit): x0: 0x0000000000000000 x1: 0x0000000000000000 x2: 0x0000000000000000 x3: 0x000000014c6402d0 x4: 0x000000014c640300 x5: 0x00000001a269f040 x6: 0x0000000120048348 x7: 0x0000000000000000 x8: 0x0000000008000000 x9: 0x0000000004000000 x10: 0x0000000000003c96 x11: 0x00000001a2cadbaa x12: 0x00000001a2cadbaa x13: 0x0000000000000018 x14: 0x000000008000001f x15: 0x0000000080000023 x16: 0x0000000000000148 x17: 0x00000001002b4550 x18: 0x0000000000000000 x19: 0x0000000000000006 x20: 0x000000016e733000 x21: 0x000000000000003e x22: 0x09005a330fcd5075 x23: 0x00000001002d9958 x24: 0x09005a330fcd5075 x25: 0x0000000000000002 x26: 0x0000000000000000 x27: 0x00000001002d3000 x28: 0x000000000000ffff fp: 0x000000016e732a70 lr: 0x00000001996bcef8 sp: 0x000000016e732a50 pc: 0x00000001995f3140 cpsr: 0x00000000 ...
In my testing, I’ve found that connecting 15 devices crashes all Bluetooth on the phone. Just don’t connect 15 devices at once, you say? No problem. Except, iOS keeps connections open for a short period of time after calling:
This allows connections to stack up while waiting for disconnect. Now, I’m not sure of the exact cause of the crash but I see it often when working with more than 10 devices.
How to Handle Multiple Connections
My advice: if you control the firmware, implement a disconnect command to terminate the connection from the peripheral. This prevents connections from hanging on longer than needed. By allowing the peripheral to hang-up, I was able to stair step many connections. Only when trying to keep 7 or more active connections did I see BTServer crashes rear their ugly head. Also, be prepared for the BLE state of your
to come back as:
You will need to scan for your devices, services, and characteristics all over again but you should be able to recover gracefully. Using multiple connections can definitely help with throughput on iOS.
Apple has some work to do to clean up their handling of many BLE devices in their Bluetooth stack. If you are encountering these same issues, please send the bug report to Apple to expedite work on these features.
Is this worse on the iPhone 7? I have 8 BLE Tiles and saw Bluetooth enter a crash loop every time I opened the Tile app. Worked fine on iPhone 6. Tile have had to update the app so it only connects to 2 Tiles at once on the iPhone 7, to avoid Bluetooth crashing.
I am experiencing same issue with iPhone7 and 7 or 8 BLE devices. Was fine with iPhone6 …
Any idea on how to fix it ?
Our team are experiencing the same thing with 8 or more BLE connections – and it appears to be iPhone 7 only. Have you reported this to Apple?
James, Ross, and Oliver,
I have not had a chance to go back and revisit this work for iPhone 7 and have not written up a bug to Apple yet. Do you have any stack traces, gists, or other info that could help others track this down?
Have any of you found work-arounds yet?
No workaround found yet but we did log a bug with Apple. Will try and share if anything comes of it.
PS, the automatic notification of comments on this page doesn’t seem to work, I only saw your comment because I checked back manually…
My lighting business depends on an app and my customers expect to be able to control 10 or 20 lights, at a big event, there might be a few hundred lights. Even a small wedding will need more than 7 lighting fixtures.
I have gone back to using my old iPhone 6 to control the lights in my lab.
I sincerely hope this issue rises to Apples attention.
Comments are closed.