I am attempting to connect two iPhones using Core Bluetooth. One is running a peripheral, the other a central. It seems the two devices are connected. And every method is getting called in the typical control flow up until peripheralManager:central:didSubscribeToCharacteristic: does not get called on the peripheral side and peripheral: didUpdateValueForCharacteristic: error: does not get called on the central side. However, If I call readValueForCharacteristic: it works and grabs the first value, but it does not seem to register for notifications and we get an "unknown error 2" in the updateValueForCharacteristic: method. I have been going through all the sample code I could find (heart monitor, business card, etc.) and my code is the exact same yet I cannot get it to work.
Anyone have any ideas on what the issue could be?
I resolved this a while ago but I thought I would post a solution since it may be useful to people going forward... The solution to this problem had to do with the bluetooth caching. Basically, the issue was that something had cached improperly... I restarted my phone, which clears the BLE cache, and did the same for the phone that I was testing it with and voila it worked. I recommend if people have these sorts of BLE issues (especially if you have been registering your phone as both a peripheral and a central for similar code) that you clear your cache by restarting!
Related
I'm currently working on a project, for which I need ti use BLE GAP, I have found a tutorial in the ios documentation and adapted the code for my needs.
The code does not throw any errors nor warnings, it runs smoothly but for some reason the discovery does not happen and it seems like the two devices would be miles apart
I tried everything but really cannot find the problem or even imagine what it could be since the code is almost identical to the original(which works.) It has to be something obvious I'm overlooking....
Peripheral log:
name[587:75475] Peripheral CBManager is powered on
Central log:
name[4803:2263279] Central CBManager is powered on
name[4803:2263279] Found connected Peripherals with transfer service: (
)
Peripheral Code:
https://pastebin.com/WuKymHAS
Central Code:
https://pastebin.com/ijjHk4cG
I've changed the UUID, but I've also tried it with the ones from the tutorial. The classes are initiated with the loadBLE methods.
I would be very thankful I someone could give me a hint, why they don't "see" each other
greets!
I have implemented restoration functionality for CoreBluetooth and it works fine, except one thing - after a week (+/- - not sure for 100%) in the background, without opening, app terminated and not restored anymore on any BLE-based events (tested few times).
I also add logging to all BLE related task, add analytic for capturing crashes, track restoring process/events in additional - and after checking this logs/info/reports - not found any exceptions or something that can terminate my app.
The question is - can someone explain me the reason why I got such behavior?
Bluetooth related issues can be really hard to identify. We came across several unexplainable bugs in the past. The only way to determine why BLE was no longer working or misbehaving was to look at the CoreBluetooth logs on the device.
In case you have not tried that already you can enable these logs via a configuration profile that you can download from the Apple Developer Pages (look for Bluetooth and follow the instructions).
As soon as you enable the logging you can download a complete log from the underlying BLE stack that will tell you what went wrong. They even tell you if you have misused the API in some way. Hope that helps.
From my experience with CoreBluetooth State Preservation and Restoration I have come to the conclusion that it does not work reliably. You can get it working sort of "ok", but you will never get it to reconnect reliably while using long running pending connections in the background.
I have reported several bugs regarding this, but the most serious one in my opinion is that If the bluetooth manager switches state while the app is terminated then all pending connections will be lost (since they are not remembered by the module or the iOS stack). Since bluetooth state switches will not cause your app to be relaunched then it simply means that at any point in time your pending connection can be lost and you will not know about it. This on its own effectively means that State Restoration is rather useless. You could say that this is not a bug, but rater a poor design choice…
Another one is that sometimes the framework gets ”stuck” in a bad state (possibly by an internal race-condition or similar). This can happen randomly, but you can pretty easily reproduce this by calling connectPeripheral immediately in the didFailToConnect or didDisconnect callback. When this happens the connectionState will be set to connecting while in fact it is not.
And so on…
/Anton
Sorry for the long title, but we are having a pretty interesting issue with using corebluetooth for ios. We are issuing a call to retrievePeripherals in CBCentralManager and are able to find the previously paired device.
This happens though regardless if the device is on or off though. I can't find anything in apple's documentation as to why it's able to find the device when it is off though and it isn't showing up in Settings -> Bluetooth -> Devices. I'm suspecting that Apple is caching this information but can't find any documentation to confirm this. Also, when the device is off and we issue the connect call, the program continues to execute as normal but the delegate for didFailToConnect never gets called. When the device is turned on, it will connect immediately.
Is there a way to pass a timeout parameter when trying to connect to a device? If not, what would the best solution be to handling reconnecting to a previously used device for an application (we're storing the last connected device within the app).
Two points you need to know about retrievePeripherals: and connectPeripheral:
1.) retrievePeripherals: attempts to retrieve the CBPeripheral object associated with the uuid you supply. Even if the ble device is off (or on the other side of the country) retrievePeripherals: will still return an instance of CBPeripheral that you can call connectPeripheral: on. This is done intentionally so that you can issue a call to a peripheral that is not even around and still automatically connect to it when it comes back into range. It basically creates a marker inside the system bluetooth so that when the device is actually seen, it will know it should connect to it.
2.)connectPeripheral: will not time out unless the communication channel is broken with the actual device. If the iOS device has not seen the device, it will not fail and should not time out (unless some error occurs inside the system bluetooth).
And as for the timeout parameter, there is no documented way inside the CoreBluetooth framework. You can create your own implementation for it, however I believe you'd be better off keeping a list of which peripheral uuid's you've actually called connectPeripheral: on and then just pop them from the list when they connect. If you no longer want to connect to a peripheral in the list call cancelPeripheral: on that UUID, call connectPeripheral: on the other, and swap entries. Good to go.
I've looked everywhere and tried everything, but nothing seems to work :(
On iOS, I'm making an app (for iOS 6 and above) in which iOS devices need to exchange data. Therefore, both devices need to be peripheral and central at the same time. I've done exactly as specified in the WWDC video, but the devices can't connect successfully with each other.
When I make one device only central and the other only peripheral, the central connects seamlessly to the peripheral.
However, when both devices are peripheral and central at the same time, I get random errors: at any stage (discovering services/characteristics or setting notify value to YES) errors sometimes happen, and sometimes discoverServices doesn't even call didDiscoverServices
Is there something different I should be doing? I simply merged the peripheral and central code into one view controller. I've noticed that if device "a" connects to device "b", and then device "b" connects to device "a", it works more often than not. I manage this by using NSThread sleepForTimeInterval: manually for different amounts of time on each device, but how could I get one device to connect first (and then the other) in a reliable (and not manually pre-defined) way?
If I do get errors, usually they're simply Unknown error
Please let me know if you need any code or any other information :)
Yes, it can be in both roles at the same time. You just have to initialize a CBPeripheralManager and a CBCentralManager. As soon as the peripheral manager is initialized and you receive the POWER ON state the device starts acting as a peripheral. You can add your services at this point and receive connections from other devices. At the same time you can use the central manager to scan and initiate connections to other peripherals.
Note that you cannot connect to your own device even if it acts as a peripheral.
For your errors, I suggest:
Turn off scanning before initiating a connection. That is, scan, find peripheral, stop scan, connect. Connection and scanning do not like each other.
Use a dedicated queue for handling bluetooth events, not the main queue. [[CBCentralManager alloc] initWithDelegate:self queue:my_dedicated_bluetooth_q]
Unfortunately, the stack sometimes become unstable. Even restarts are possible. But this usually happens only under heavy loads or several simultaneous connections. Hopefully, this will be improved in iOS7.
The unfamous Unknown error started to appear for several developers recently. Judging from your description there are probably a number of reasons why your setup may fail and it would require much more info that what fits well into a SO question.
For more info I suggest you search the bluetooth-dev mailing list archives https://lists.apple.com/archives/Bluetooth-dev or send a mail Bluetooth-dev#lists.apple.com. The community provides great help if you approach with reasonable questions like this.
As per my understanding one device can work with one mode at a time . That is if the device is working in the peripheral mode then it you cant work it as a central mode .If you see some standard examples like BTLE transfer or lilke Light Blue those are working in one mode at a time .
Firstly, what do you mean "the same time"?
If you mean the device advertising to other devices while it scanning for other devices, it can not.
But you can create two threads which share same lock to advertising and scanning.
Before scanning, stop advertising, before advertising, stop scanning.
I tested on my iPhone 4s and iPad air, worked well.
I'm implementing CBPeripheralManager on my iPhone running iOS6 and I have created and added a custom service and characteristic. An external Bluetooth Device (the BLE112, if it makes a difference) acts as the Central and starts writing data to the characteristic I have implemented. I know it's being written because a test application on the App Store (light blue) sees it as being written, but I'm just not sure how to see it from the CBPeripheralManager. didReceiveWriteRequests is never called, and the "value" property on my characteristic variable is never called either. So, to sum up, my question is: From the CBPeripheralManager, how can I tell when updates to a characteristic have been written from a central?
Through much trial and error, I found that under the Service property for the iOS side, you need to create it with "CBCharacteristicPropertyWrite" instead of "CBCharacteristicPropertyWriteWithoutResponse" in order to get didReceiveWriteRequests. Seems obvious, but kept me scratching my head for a while.