With iOS CoreBluetooth, when sending a relatively large amount data, it's important to break it up into 20 byte chunks and then write them one at a time into the peripheral object. This is pretty easy to do when using a WriteWithResponse characteristic: write 20 bytes, wait for the callback, write the next 20 bytes, and so on.
But what about with a WriteWithoutResponse characteristic? I need to send of 1-2kB of data as quickly as I can over BLE. WriteWithResponse is very inefficient at doing this, because it acks every 20 byte packet. Error correction and reliability are taken care of at my application layer, so I have no need for BLE to ack the data.
The issue is that WriteWithoutResponse does not give you a callback, because there is no way for CoreBluetooth to know when the data was actually written. So the question is: how do we properly space out sending a large amount of data using WriteWithoutResponse?
The only solution I've thought of is to do the following:
- Get the connection interval and the number of packets that the link is capable of per connection interval.
- Immediately write X packets of 20 bytes each, wait Y time, and repeat until there is no data left. (X = Number of packets per connection interval, Y = The connection interval)
There are two glaring problems with this approach:
- CoreBluetooth does not expose the Connection Interval to us (why??). So there are two options. The first being: guess. Probably either a worse-case or average-case depending on your preferred connection parameters, I think iOS likes to pick 30ms. But this is a bad idea because a central has the right to completely ignore the suggested parameters. The second is that you could have the peripheral store and transmit the agreed upon CI to the iOS device. The issue with this is that you can't send the CI until iOS device has finished discovering the services and characteristics and subscribed to the appropriate notification. So you'd have to either put in a somewhat arbitrary fixed delay after connection before sending the CI, or send a small amount of data from the iOS device notifying the peripheral that it is ready. Both create latencies and are pretty poor solutions.
- We don't know how many packets per connection interval can be supported. There is a theoretical maximum of 6. But the average case is probably 4 or less. It is also dependent on the peripheral.
Of course a great option for sending large amounts of data is to increase the MTU size to larger than 20 bytes to accommodate our large amount of data. But it seems few peripherals support this; ours does not.
Anyone have any insights on how to solve this?