Core Bluetooth throughput issues

Hello everyone, we are having some trouble with BLE on an iPhone. We are using iOS 18.2, and an iPhone 16 Pro Max as our testing device. We wrote BLE scanning and connection. After connection, we are able to the physical device to collect data and send it us in various configurations.

Data collected by the device is sent to an iPhone using characteristic notifications, and not manual reads. We've coded up throughput tester app, that measures how many bytes can be reliably received from the bluetooth device, without packets dropping. We found that the maximum achievable throughput is around 300-400 kbps, which is significantly less than what we achieve on Android with the same bluetooth device, where we commonly have values well over 1000 kbps.

Our device has a separate characteristic for BLE parameters, and we've validated that in all cases, parameters of the connection are same as they are on Android, and they are: PHY - 2 MTU - 244 Connection Interval - 15ms

We have also tried testing it with an airplane mode turned on, and only bluetooth active - it didn't make any significant changes to the throughput.

Do you have some recommendations on what we could try to maximize the throughput? All answers are welcomed, thanks!

One thing to be careful about when connection to an iOS device is, the connection parameters the peripheral requests are not guaranteed to be accepted as-is. Depending on the state of the device and resource load, iOS can instead come back with a different set of connection parameters. Which may not be ideal for your expected throughput.

That said, things that you can do for maximizing the throughput would be:

  • PHY2 (which you are asking for)
  • write without response (not sure if you're doing that)
  • larger MTU (which you are setting)
  • Extended Data Length
  • L2CAP
  • 15ms interval (which you are asking, but do check that you are getting it)

These are things you can ask via your peripheral(connection params), and implement in your app (L2CAP and write w/o response)

Unfortunately for the params, you are stuck with whatever iOS will accept. You would have no opportunity to influence this via your app, or user action.


Argun Tekant /  DTS Engineer / Core Technologies

When connecting to our peripheral (I will use peripheral for the physical device we are connecting to and it's firmware, sorry for the previously confusing language), during negotiations the only parameters that the firmware will accept are MTU=244 and ConnectionInterval=15ms. Only thing that is negotiable is PHY, either 1 or 2, but 2 is requested by default.

On the peripheral, due to vast differences in bluetooth APIs on various platforms, we have added characteristic that notifies us whenever connection parameters change. As of now, iOS hasn't succeeded (or tried) to change them. We had a similar issue on Android, where OS would change the PHY from 2 to 1, thus drastically reducing our throughput. Whenever this had happened, we were immediately notified by our peripheral. As I've said, we have not (yet) observed this on iOS.

When our peripheral send data to the phone, it is always writing without response, and we rely on notifications to get and parse the bytes. When sending data from the phone to the peripheral, we are also writing without response, though these are usually a couple of bytes to configure the device and get it to start the data stream.

Is it possible that the iOS changes connection parameters in a way that is one sided? From my understanding, for all changes to the connection parameters, devices need to handshake and we would be notified like we are on the other platforms?

Best throughput we've observed is around 400-600 kbps, but it's quite unstable and a bit opinionated. Is it possible that the iOS is throttling the throughput without affecting the connection parameters?

Also one important piece of information is that we are sending larger amounts of data than it's common for BLE devices. Is it possible that we are throttled by a number of packages it is possible to send to the iOS device during one connection interval? When sampling every 4ms, everything works great, as there are only 3-4 packages sent every connection interval. When sampling every 2 or 1ms, that's when we start to observe the problems, as it can be required to send up to 15 packages during one connection interval? I read somewhere (though on a quite old forum post) that the iOS limits the number of packages it will accept during one connection interval, and I don't know if this is still a case.

Thanks a lot for the fast response, and sorry for the information-bomb!

If the only parameters you are able to set are those three, without Extended Data Length and L2CAP, I would say 600 kbps is not a bad number.

The 1000-2000 kbps "theoretical" limits you talk about is the theoretical on the wire bandwidth. There will definitely be some loss of throughput of actual data bytes is expected due to protocol overhead. Especially GATT will have a lot of overhead which you can avoid by using L2CAP protocol instead.

Protocol overhead in LE can be significant. The Bluetooth Specification defines the LE maximum application datalink to be 27 Bytes, but we lose 7 of that because your data has to traverse from the application through GATT, through ATT and through L2CAP, so you lose about 25% of the packet, and in the end the useable data length is only 20 Bytes. That is why using Extended Data Length will improve your overhead cost.

Extended data length is a 4.2 feature that increases the maximum application data length from 27 to 251. And in fact, we can send an entire maximum GATTs write of 512 Bytes in a single interval using Extended Data Length, and this should be about 3 times the throughput of your normal performance

Also, why is your connection interval 15ms? It may look the fastest, but as you are writing without response, you might be losing the opportunity to write maximum number of writes in a single interval if both the peripheral and the phone is not fast enough to do so. iOS does not "limit" the number of writes. It is just a factor of ability. iOS is not a dedicated Bluetooth device, and your app may not even be the only app using Bluetooth. As for your peripheral, I don't know what chipset you are using but I can say that a lot of chipsets that advertise that they are capable of 6 writes per interval, ain't.

And, if you are not already using it, you should look into using L2CAP Connection Oriented Channels. This will eliminate all of the overhead previously in GATT and ATTs, but more importantly it eliminates limitations and restriction in GATT like the maximum attribute size of 512, so we can now write much larger values and use much larger MTU. You might be increasing your MTU, but you are possibly being burdened by all these other things.

Information-bomb right back at you.


Argun Tekant /  DTS Engineer / Core Technologies

Core Bluetooth throughput issues
 
 
Q