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.
Related
So sum up, I have a device communicating via ble to my application. The application uses Corebluetooth to handle the connection and communication.
The problem:
When a device goes out of range it will disconnect -> it calls the diddisconnect() function where I check if its timed out and call the central.connect again.
it will then start the connection and immediately on some devices(only iOS14) at the moment, it will go to didfailtoconnect with an error saying:
"Error Domain=CBErrorDomain Code=15 "Failed to encrypt the connection, the connection has timed out unexpectedly." UserInfo={NSLocalizedDescription=Failed to encrypt the connection, the connection has timed out unexpectedly."
Only seem to exist on iOS14 devices as far as I have seen.
I also tested blocking signals, if i block signals from device the issue is not apparent. if I block signals from phone, the issue is apparent. Not sure how this is, feels like they both trigger timeout.
May be problem elsewhere aswell but it seem to handle fine on my iOS13 device.(mainly tested on 4 phones, 2 on each version)
Also to clarify, the application is acting as the central, and the device as the peripheral.
If I interpret the error message correctly from CBErrorEncryptionTimedOut (15), it means that the devices initiated a connection, then, suddenly after the iOS device initiated the encryption procedure, the connection unexpectedly died.
So it should be treated more or less the same way as CBErrorConnectionTimeout (6).
If this happens all the time, you should use a BLE sniffer to see what's going on; maybe it's some bug in iOS.
I am using CoreBluetooth for IOS app, after upgrading IOS version the central manager can´t connect to peripheral and not showing any error.
Before starting te process to connect I receive this values from the device:
CBPeripheral: 0x1c0106e40, identifier = BE2B06BF-F385-82AC-95E6-65EA1CF8B11F, name = icomon, state = disconnected
and after I try to connect to the device the state changes:
CBPeripheral: 0x1c411bea0, identifier = BE2B06BF-F385-82AC-95E6-65EA1CF8B11F, name = icomon, state = connecting
and nothing more, nothing happen next, I do not receive any data from the central manager and the device state never change.
I was struggling with a problem that has the exact same symptoms. Hopefully it is the same problem, and this response is helpful.
In my case, the problem was caused by a link layer control PDU that my device was sending immediately after the connection was established. Specifically, I am running Apache Mynewt (http://mynewt.apache.org/), and its NimBLE controller initiates the Feature Exchange Procedure immediately after a connection is made. Since my device is the peripheral, while the iOS 11 device is the central, my device sends the LL_SLAVE_FEATURE_REQ PDU.
I don't know if it is the timing involved or the PDU itself, but whatever the case, the iOS device never sends any application-layer data after the feature exchange. When I change NimBLE so that it does not initiate the feature exchange, device interrogation happens normally, and the CoreBluetooth connected callback gets called.
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.
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.
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.