CoreBluetooth data package lost when sending via withoutResponse - ios

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 ?

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.

Related

iOS CoreBluetooth How to differ from read response and notifying packet?

We have a battery characteristic on our peripheral device that is both readable and notifying.
In Android, it's easy, there's a callback function for read responses (onCharacteristicRead) and one for notifying packets (onCharacteristicChanged).
But in iOS, there's only one callback function for both reads and notifications (didUpdateValueForCharacteristic) and we can't seem to find a way to identify which peripheral action is happening (read or notify).
Is there a way to know if we're getting a read response or a notification?
Note that for characteritics that are only readable or notifying, we don't have any problem and the code works like a charm.
There is no way to identify the read response and the notification because they comes from the same callback without identifier.
If you want to differentiate them for special case, you may need to use two protocol (one for read, another for notify) in the firmware side. So you can differentiate them in the App with your protocol. Generally, we don't differentiate them.

Concurrent delegate methods

I'm currently working with a Corebluetooth, with my phone acting as central, and a separate peripheral.
I'm successfully reading data from a peripheral device using the didUpdateValueFor delegate method. The problem I'm having is when I'm sending multiple packets of information at the same time.
For instance, I send "abc" first and "def" later. As the central updates the reading upon indication from the peripheral, I should be able to get "abcdef" at the end. This works fine if I am sending indications at a speed of 10 packets per second.
However, once my speed gets to the default indication speed, it's too fast for the central to keep up. I only get the first indication "abc", but I never receive the indication for "def".
Is there a way I can force the didUpdateValueFor method to run concurrently so it captures all incoming notifications regardless of speed?
The best-practices chapter of the Core Bluetooth Programming Guide recommends the use of subscription via setNotifyValue:forCharacteristic: rather than plain reads for characteristics that will change often.
It isn't clear from your question as to whether you are using subscription or polling via readValueForCharacteristic:
I think that you mean didUpdateValueForCharacteristic instead of didWriteValueForCharacteristic. Otherwise, the question doesn't seem correct as didWriteValueForCharacteristic can not be used to read data from the remote device (except if you are using error codes for communication - which you shouldn't ;) ).
There are two methods to push data from the peripheral to the central: indications and notifications.
Notifications may be discarded if sent too fast or for whatever reasons.
Indications can only be sent one at a time (you'll have to wait until the central replies with a confirmation that the indication has been processed, before sending another one!).
I see multiple ways how your current implementation may be incorrect:
You are not waiting for the Handle Value Confirmation packet and send the next indication too early.
You are sending indications / notifications before the Client Characteristic Configuration has been written by the central.
Your peripheral has flagged the characteristic to support both notifications and indications. In this case, Core Bluetooth only supports notifications and doesn't enable indications, in which case you are back at unreliable notifications ([CBPeripheral setNotifyValue:forCharacteristic:])
If the specified characteristic is configured to allow both notifications and indications, calling this method enables notifications only.

CoreBluetooth not caching discovered services and characteristics

iOS 6 supposedly caches the discovered services and characteristics of peripherals so that reconnecting to known peripherals is faster. However, it doesn't seem to work in my app.
I save a peripheral after connecting to it, and on the reconnect I call retrievePeripherals with the saved peripheral's UUID. My didRetrievePeripherals gets called and I connect to the peripheral. My didConnectPeripheral gets called and I call discoverServices. I was expecting the services to be returned to my app from the cache. However, the service tables are read from my peripheral.
Because my app is busy reading the service tables, I miss the first measurement sent from the peripheral.
Does anyone know why the cache is not being read? Is there some option I need to set somewhere?
You should try to encrypt the connection. Encrypted connections require pairing and after that, the caching is truly turned on. Plain connections not necessarily trigger the caching mechanism and there is no official statement on this. You should also try iOS beta to see if this has changed there. ;)
The other thing you should consider is optimizing the service discovery. If measurement is very important, then discover the characteristics for that first and once that is done, continue on with the others.

Max achievable polling frequency using Bluetooth LE GATT profile?

I am trying to understand BLE and GATT in more depth. My interest is in the max achievable number of reads you would able to make per second over the GATT profile.
I am aware of some of the post made on this topic before, for instance:
Bluetooth Low Energy - updating a characteristic value repeatedly
However, I am trying to explain these results looking at the BLE specification.
What is the relationship between connection events and GATT? Does each ATT read/write require a new connection event? If not, is it possible to say anything about how many ATT read/writes can be made per connection event?
Say I want to poll a BLE connected light sensor for a single byte value, what would be the max Hz I could achieve? Would it always be best to set the mininum connection interval as low a possible?
Would I be able to achieve better results using "GATT server notifications? In the BLE spec (Core_v4.0) it says that "The master initiates the beginning of each connection event". Then how are GATT server notifications implemented? I would think that would require the server to initiate a connection event.
Finally, if anybody knows about any specific iOS imposed limitations on the throughput I would be able to achieve when polling a sensor intensively, I would love to hear about it.
I can answer a portion of of those questions...
What is the relationship between connection events and GATT?
They're different levels of the protocol. You handle connections and connection events via HCI. GATT is something you use after you've connected.
Does each ATT read/write require a new connection event?
No. Once you're connected you can do multiple read/write or other GATT commands.
If not, is it possible to say anything about how many ATT read/writes can be made per connection event?
I think the best method is to actually benchmark the speed yourself. However, the whole point of BLE is a reduction in power usage at the expense of speed. If you're concerned about speed that you probably shouldn't be doing it with BLE. The whole point of notifications/indications is so you don't have to poll an attribute but only get a message when a certain event has occurred.
Say I want to poll a BLE connected light sensor for a single byte value, what would be the max Hz I could achieve? Would it always be best to set the mininum connection interval as low a possible?
See above 2 answers.
Then how are GATT server notifications implemented?
Once you've implemented a GATT connection there's 2 way communication going on between the master and slave-device. Either device can send events to the other. In order to use notifications, you set a bit on a particular attribute to say you want notifications on that information. Then, depending on how that notification works, you'll get events sent back to you whenever there's something to report. I have a feeling that a lightbulb wouldn't have any sort of notification unless there's some interface on it besides the BLE connection. I typical application would be something like a thermometer where it would send a notification every time the temperature changed by 1 degree.
Conclusion:
If you're polling attributes you're doing it wrong. But it's possible that you have to do it wrong because the device didn't properly implement notifications in the way you need and you can't modify the device. However, polling will ramp up the battery usage significantly and you'll have loss the benefit of using BLE.

Transmitting data with CoreBluetooth

I'm developing an iOS app with an accompanying Bluetooth LE peripheral. The one step I don't seem to be able to solve is how to actually transmit the data from my app to the peripheral or vice versa.
What I've built so far is a test app that can connect to my sample Bluetooth peripheral, and read all of its services/characteristics/descriptors. It can toggle notifications for a given characteristic, and write to given characteristics. It is just this last step of "transmit n bytes to the peripheral, and receive m bytes from the peripheral" that I can't seem to figure out.
Looking at the External Accessory Framework (what I would use if Apple would actually give me MFi approval for this project), they give you input and output streams on a given session to communicate with the accessory, but no such object exists for CoreBluetooth.
Is this simply an oversight on Apple's part on the functionality of CoreBluetooth? Or do I simply need to develop my own Bluetooth service profile to handle the inflow/outflow of data to and from the peripheral?
LE is fundamentally designed to work with these GATT based profiles, which are suited for monitoring sensors, not for data streams. While LE does allow for additional L2CAP streams to be opened for custom protocols, Apple's CoreBluetooth doesn't provide access to do so.
You can build a custom profile with private services and characteristics and have it work kind of like SSP; that's the way I'm using my BLE module to get data from some sensors to my app. The module I bought (Microchip's RN-4020) already has a custom profile made specifically for this known as MLDP (Microchip Low-energy Data Profile).
The way I get the data in my iOS app is by subscribing to the private characteristic, thus being notified when the values are updated. So far it has been working great, and the data rate can go up to 20 kbps according to Microchip (I haven't tested its limits, since I don't need much speed). Here's a link to Microchip's product page: http://www.microchip.com/wwwproducts/Devices.aspx?product=RN4020
Good luck!
You can use the bluetooth.org 'Immediate Alert Service' uuid=1802 with characteristic uuid=2A06 with property=write_no_response to send one byte values to your peripheral device from your iPhone. The peripheral device must be programmed to act on the data that is sent. For example, you might use a button on an iPhone app to send a hex address that causes one or more port pins to turn on or off on the peripheral. While this is not using the Alert Service as it was intended, it does provide an easy way to test out data transfer to a peripheral device. The same process could be used to send sequential data bytes similar to a serial data stream. I have not yet tried sending more complex data streams. The write_no_response does not provide any feedback to the app as to whether the data was received by the peripheral.
The IOS TemperatureSensor.xproj is an example of code for reading temperature data from a peripheral. The OSX HealthThermometerClient.xproj has the code needed to decode the somewhat complex thermometer data structure. The IOS TI-BLE-Demo.xproj TIBLECBKeyfob.m has code for reading and writing characteristic values, such as, reading temperature or battery levels from a peripheral device.

Resources