Central is getting error reponse for write request if Peripheral respond back a little late - ios

I have been using CoreBlueTooth framework to communicate between BTLE iOS devices and I see a strange behavior. Here is what I am doing:
iOS device 1 (Peripheral): Expose a writable characteristics.
iOS device 2 (Central): Scan for the writable characteristics and write data into it.
iOS device 1 (Peripheral): Receives write request. Wait for some time to acknowledge the receipt of data.
iOS device 2 (Central): Get a callback on the below delegate and received the mentioned error.
Issue: Here if I respond back to the write request in few seconds by calling the API [iPeripheral respondToRequest:iRequest withResult:iStatus] then it all works fine and I get a success on my Central. But if I take some time, even if my Peripheral has not responded to the write request, I get error response back.
Is this some kind of connection loss in few seconds or the known CB framework behavior, any idea?
- (void)peripheral:(CBPeripheral *)iPeripheral didWriteValueForCharacteristic:(CBCharacteristic *)iCharacteristic error:(NSError *)iError
Error Domain=CBErrorDomain Code=0 "Unknown error." UserInfo=0x183a6d70 {NSLocalizedDescription=Unknown error.}
Both my Central and Peripheral are running on iOS 7.0.

I also observed this problem when I had deadlocks in my code and couldn't respond in time ;-) The way I observed it, iOS responds with an automatic error request with an arbitrary error code if a request is not answered within 10 seconds. I have not found a way to change this, but it makes sense from a protocol perspective.
In Bluetooth Low Energy, a central can only send a single Characteristic Value Write Request at a time. After it has sent this request, it cannot send a different Write Request unless the first one is responded to. Therefore, it is crucial to always respond to requests as fast as possible.
In the comments, you mentioned that you are waiting for user input to affect the result code you want to send to the central. I guess "Success" if the user confirms in the UI that an operation should be started, and an error code if the user denies that. This is not the way an LE based protocol should be designed. It's like blocking the UI thread until an operation is finished, just from the other side. You are effectively blocking the BT communications until a blocking operation (waiting for user input) completes.
A different design would be to send a write request to the other phone, responding immediately with a "Success" error code to indicate that the request was received and the popup is displayed. Then, send a Characteristic Value Indication with the user's choice from the peripheral to the central.
There's one small caveat if you target iOS 6: indications don't work nicely in many cases (reentrancy bugs etc, best not touch them). There, you should send a Read Request from your central and return the user's choice in this read request if it's already available. Again, don't block while giving the answer, sending back a "user is still choosing" value back if the answer is not yet ready.
Single rule: Answer requests as fast as possible. It's the way, Bluetooth LE is designed to work.

You may be exceeding the maximum time allowed for a write to be acknowledged. Try testing several different ack times and see if it reliably fails beyond a certain threshold.

If you use iPhone 4 devices, this device no suports BLE. BLS are supported in iPhone 4 and later.

Related

Is it possible to implement flow control for CBCharacteristicWriteWithoutResponse?

Is it possible to use writeValue with type CBCharacteristicWriteWithoutResponse and still have some flow control to avoid sending data faster than BLE stack manages to actually send it out? Currently it works on Android but not on iOS.
The long story.
I have previously implemented duplex communication channel over BLE on Android. It is basically using two ATT characteristics - one is write/writeWithoutResponse and the other is notifiable.
On Android, even when I use writeWithoutResponse, Android sends me onCharacteristicWrite callback to signal that the data packet has at least reached BLE stack, and in this callback I send out the next data packet with size of current ATT_MTU-3 bytes.
This works fine, the data reaches the target intact and I can achieve transfer speeds about 10 KB/s.
But on iOS there is a problem. When using writeValue with type CBCharacteristicWriteWithoutResponse, iOS (at least iOS 8) does not call didWriteValueForCharacteristic and this is intended and documented behavior. Thus I have no way of knowing if the data packet has reached BLE stack. The best I can is to call writeValue in a loop. Also, writeValue seems to be non blocking (async). As the result, not all of my data reaches the peripheral device. In the logs I see that the incoming data stream is stopped too soon. My guess is that if I call writeValue too fast, iOS is just carelessly overwriting previous cached writable characteristic value and thus misses some data bytes in between.
If I use writeValue with CBCharacteristicWriteWithResponse, it works fine, and what's strange - it works fine even if I ignore didWriteValueForCharacteristic and just call writeValue in a loop. It seems, with CBCharacteristicWriteWithResponse iOS is doing some internal housekeeping and uses BLE acknowledgments to avoid overwriting current value of the characteristic, therefore the data is being sent in order and without any losses.
Of course, I don't expect to get reliable writes using CBCharacteristicWriteWithResponse, but at least make it work for most cases. If it works on Android, then why shouldn't it work on iOS?
Apple's implementation kind of sucks. All other implementations I've seen have proper flow control. What you could do if you don't want to implement some advanced TCP-like layer on top of BLE is to simply stick with Write Without Response packets but send each 10th packet or so as a Write With Response. Then you won't (with high probability) not get any packet drops. This will probably only be a small performance decrease. You should also increase the MTU to increase the throughput even further.

HTTP request times out. Weak wifi or slow server?

I have a client/server application where the client is an iOS app and the server is a RESTful api. I have received complaints of requests timing out and I know that some/most of the time, this is due to poor wifi connectivity. In these situations I would like to display an error message on the client which indicates whether the request was received by the server.
Is it possible in iOS to tell the difference between a HTTP request that never reaches the intended destination and one that makes it all the way to the intended destination but never receives a response?
API Call only after you check whether your internet connectivity is reachable or not .show error message if not connected to internet(this is due to poor wifi connectivity)
Example : Check for internet connection availability in Swift
When its checked make api call handle error code 404(never reaches the intended destination) and 504(timeout error code never receives a response) and let user make it success only when status code is 200.But its good to handle 401(authorization error) also.
I don't know which library you are using that why can't provide any code example right now
Try Alamofire Library in swift I suggest handled error codes very well.

BLE connects, reads value and disconnects

I am getting unexpected behavior. I am making a simple wrapper on Corebluetooth and made 2 apps: one acts as client and other as server.
On the server end, whenever I am updating characteristic value through [updateValue: forCharacteristic: onSubscribedCentral] I am getting response as 0. According to Apple documentation, It will return YES if the update could be sent, or NO if the underlying transmit queue is full.I googled for this error and found that this can be handled by sending the data again in peripheralManagerIsReadyToUpdateSubscribers: method. This is working fine.
Issue comes in client end.
After connecting to device, and reading values successfully, my peripheral gets disconnected with error : The specified device has disconnected from us. This happens in spite of the fact my server is continuously sending values. My problem is somewhat similar to
Peripheral transmit queue issue
but in my case sometime peripheral remain connected and value is updated on client side. But most of time peripheral disconnects.
I have googled a lot for the solution even tried few advices given at:
CoreBluetooth repeatedly disconnecting. But of no use.

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.

Peripheral transmit queue issue

I’m working on a simple wrapper around CoreBluetooth to send any data to any device.
During developing I encountered a lot of bugs in framework, they were very annoying and to make my wrapper stable I had to shorten some of functionality for reliability.
For now I’m working on sending data from peripheral.
Ok, so I have following case:
Client asks for value of dynamic characteristic
I get a callback on server-side - peripheral:didReceiveReadRequest:.
Note : I need to respond to this CBATTRequest in this method - I can’t store it elsewhere and respond to it asynchronously. (Im just putting some chunk #“PrepareToReceiveValue” that will be ignored on central side. All sending is done in queue.)
For providing data for various devices I constructed a queue with BTMessage's in it. (So for readRequest I create message and add it to sending queue. If chunk sending failed - I will get a callback from peripheral manager about readyToUpdateSubscribers and will ask queue to resend failed chunk)
So when I’m requesting immediately a lot of dynamic characteristic values and sending data from peripheral to central concurrently sometimes it just freezes sending progress and leads to disconnection.
After several testing I found out that it was all about transmit queue:
If transmit queue is full and you will receive read request - it just won’t respond to it.
So I have potential unstable system state:
Peripheral is sending data to some central.
In my sending method updateValue:forCharac… returns NO because transmit queue is full.
At this moment central requests dynamic value for characteristic and peripheral:didReceiveReadRequest: invocation will be added to current runloop.
After returning from sending method it will dequeue peripheral:didReceiveReadRequest: method and responding to this request will have no effect (transmit queue is full).
So in this case respondToRequest: is ignored like I didn’t invoked it at all.
CoreBluetooth will not be able to send/receive any data until I will respond to request. That was the reason for freezing any sending/receiving progress with concomitant disconnection.
As I mentioned before - I must respond to request in appropriate method - otherwise it will also have no effect. (Im saying it because I’ve tried to put those request in array if queue is full and respond to them when it will have some space but with no luck).
Im waiting for your proposals/suggestions how to resolve this problem, any help would be appreciated.

Resources