Search code examples
iosswiftcore-bluetooth

CoreBluetooth data package lost when sending via withoutResponse


On iOS some data packets are lost via BLE.

On a characteristic we have the type .withoutResponse we check of course for canSendWriteWithoutResponse and func peripheralIsReady(toSendWriteWithoutResponse peripheral: CBPeripheral). This is also stated in the documentation.

On the other hand, if you specify the write type as CBCharacteristicWriteType.withoutResponse, Core Bluetooth attempts to write the value but doesn't guarantee success. If the write doesn't succeed in this case, you aren't notified and you don't receive an error indicating the cause of the failure.

Anyone have an idea how I can fix this without switching to withResponse ?


Solution

  • BLE is a distributed protocol. It is always possible to lose data. The only way you can ensure that you haven't lost data is to receive some notification back that the data was received correctly (often called an ACK for Acknowledge). The standard BLE way to achieve this is with responses. But you're free to implement your own ACK scheme if you don't want to use the standard one.

    As an example, you can create a "confirmation" characteristic in your peripheral that your central subscribes to. When you send a write, you would wait until you receive a notification from that characteristic. If you don't see a notification after some timeout, the write may have been lost. This re-implements the standard write-with-response system at a higher layer.

    Several of the BLE protocols I work on are actually serial protocols (we write to a characteristic, and read responses from the same characteristic). We often implement the ACK directly into the serial protocol (for example, returning <msgid>OK) without needing a BLE write-response.

    Another solution is to write to the characteristic, and then read from the characteristic to see if it's updated, provided the characteristic is symmetric in that way (many aren't). I am fairly certain that there's no write-caching that would bite you here, since BLE reads and writes often aren't symmetric, but you should double-check that before relying on it. (See the Core Bluetooth WWDC videos from 2019 and 2017.)

    As with all distributed systems, it's also never possible to know that data was lost. It's possible that the data arrived, and that the response was lost. This is the nature of distributed protocols, and just has to be figured into the system. This is why it's possible to build a system that will deliver a message at least once (provided that delivery is ever possible), or a system that will deliver a message at most once, but it's impossible to build a system that will deliver a message exactly once.

    If you have no control over your peripheral, and it doesn't provide any error detection mechanisms, then there's nothing you can do on the central (phone) side. You have to have some cooperation from both sides to build a fault-tolerant distributed protocol.