11

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:

  1. Get the connection interval and the number of packets that the link is capable of per connection interval.
  2. 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:

  1. 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.
  2. 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?

jcady
  • 3,850
  • 2
  • 21
  • 21
  • What peripheral do you use? Can you modify its firmware, or is it a 3rd-party one? And what is the purpose of writing this amount of data? – Michał Ciuba Nov 02 '15 at 10:33
  • @MichałCiuba I'm using the nRF51422 (nRF51-Dongle for development) and I can alter the firmware. The purpose is to transfer an encrypted block of data to a physical device the user is interacting with in order to verify their identity. – jcady Nov 03 '15 at 01:45
  • Did you find a solution to this problem? I am facing the same. I have tried to write 4x20b packets per connection, by writing 3 of them without response, and the last one with response so I stay in sync with the connection interval. However, this does not seem to be very reliable. – Reneli Jun 24 '16 at 21:17
  • I didn't find any solution, unfortunately. I actually entirely switched roles and used the iOS device as the peripheral to update a subscriber using notifications. The peripheral mode has proper callbacks to notify you when you can send more data (even if it's not acked). – jcady Jun 24 '16 at 22:07

1 Answers1

3

If You are supporting iOS 11: iOS Website

@property(readonly) BOOL canSendWriteWithoutResponse;

This property lets you know, if Buffer is filled up or not and can transmit more without response. After each transmission keep an eye on this variable and a call back: Peripheral Delegate

peripheralIsReadyToSendWriteWithoutResponse:

Which should be sufficient to let you know, when to send more data.

soan saini
  • 230
  • 3
  • 9