iOS CoreBluetooth reconnecting device with UUID - ios

I have a Bluetooth 4.0 (BLE) device using the CC2541 chipset which I am interfacing with via the iOS Core Bluetooth Framework.
I can successfully make a connection to the device using Core Bluetooth when the device is in a discoverable/advertising mode and transfer data to and from the device without any problem.
I maintain a collection of device UUIDs that I have connected with and I am now attempting to connect to one of these devices again using:
CBCentralManager
- (void)retrievePeripherals:(NSArray *)peripheralUUIDs
Calling this function appears to work and I receive a callback to my implementation of the the following function:
CBCentralManagerDelegate
- (void)centralManager:(CBCentralManager *)central didRetrievePeripherals:(NSArray *)peripherals
The device I'm attempting to connect with is the one and only peripheral listed in the peripherals array that is passed to this function.
When I then attempt to connect to this device using my connect function (shown below) the connection will not initiate and I get no callbacks at all on either the CBCentralManagerDelegate or the CBPeripheralDelegate.
- (void) connectPeripheral:(CBPeripheral *)peripheral {
NSLog(#"Connecting to peripheral with UUID : %s\r\n",[self UUIDToString:peripheral.UUID]);
activePeripheral = peripheral;
activePeripheral.delegate = self;
[CM connectPeripheral:activePeripheral options:nil];
}
I can make the connection go through successfully, as described above, if I first place the device into discoverable/advertising mode, but this is not a workable solution. The device must allow reconnection without it being placed into discoverable mode.
I did note the answer given to this question CoreBluetooth: What is the lifetime of unique UUIDs suggests I need to pair/bond the BLE device with the iOS device but that this may be dependent on the BLE chipset, the device I'm using is the CC2541. Any advice on how to go about pairing with the device would be most useful, or indeed whether this is a necessary step. I have attempted to watch WWDC 2012: Advanced Core Bluetooth which might give me some assistance, but since Apple were hacked on Thursday I'm not able to access my acccount to watch the video.
Please let me know if any more details are required. The Bluetooth device is stable, but it is being developed internally. If changes may be required to the firmware to assist with the pairing process, I can pass this information along to the electronics team.

Why dont you try this?
It specifies first you need to send a request to a GATT characteristic that requires GATT_AUTHEN_READ permission. Then your CC2541 will respond with INVALID AUTHENTICATION. This will trigger Apple's internal Bonding mechanism and ask for a key. You can enter the passkey and then if the device and CC2541 are successfully paired, it will read the characteristic value and enter your callback.
NOTE: I'm not an iOS developer, but I have worked with CC2541. If you are using the default simplePEripheral example, then a characteristic in simpleProfile characteristic5 requires authentication to read. Take a look at profiles/SimpleProfile/simpleGATTprofile.c

Related

No connectionEventDidOccur for GATT over BR/EDR (aka Classic) using CoreBluetooth

I'm trying to use the iOS 13 introduced capability in CoreBluetooth to do GATT over BR/EDR.
The accessory I'm using obviously implements GATT over BR/EDR (it even publishes its services using SDP).
According to the session WWDC 2019 What's New in Core Bluetooth session, we have to use the registerForConnectionEvents API on a CBCentralManager instance, providing services UUIDs (CBUUIDs) that will be available to use on the BR/EDR accessory.
In the session, they explicitly say:
Your app will have instantiated a CBCentralManager, passed us a known
service UID, and in the case of a BR/EDR or classic device, your user
will go to the Bluetooth settings and search for the device, in this
case let's say it's a headset running heart rate.
They'll discover the device, find it, and attempt to connect.
Pairing will be triggered, and then afterwards when we're connected,
we'll run a service discovery of the GATT services.
If we find a service that you want, then you'll get the delegate
callback.
So when doing a manual connection in the Settings app to the accessory, iOS does a GATT service discovery and if it matches, we receive an associated connectionEventDidOccur delegate callback with the event (peerConnected) and the associated CBPeripheral.
However, this delegate callback never happens.
I'm using the UsingCoreBluetoothClassic sample from Apple, with custom service CBUUIDs implemented by my accessory.
I did multiple attempts: providing only one service CBUUID, severals CBUUIDs, none, generic ones, very specific ones... It never fires.
Note that on Android, it seems to work: connection is possible using the MAC address directly, and a service discovery using SDP lists the accessory services.
Also note that the event fires when I'm scanning using scanForPeripherals and connect to my accessory (because my accessory also does GATT over BLE, but for specific reasons we explicitly want to perform GATT over BR/EDR).
Is there something I have to do on the accessory that I probably missed ?

Bluetooth peripheral does not pair

I have written an apple watch app to try to communicate with a bluetooth peripheral which has implemented some custom encryption and pairing process I already managed to implement correctly.
The device seems not to conform to standards and as such does not indicate that it needs encryption through its chatacteristic properties. After going through the pairing I can write values to it and recieve notifications.
The problem is that when I disconnect and connect again I have to go through the pairing process again. Also I have noticed that after pairing via other apps the device does a long beep sound which seems missing when I pair it through my app.
Is there any way to force pairing/binding in CoreBluetooth?

CoreBluetooth peripheral service becomes empty while reconnecting/restoring

In my iOS app I am able to successfully connect to BLE peripherals, discover service and subscribe to characteristics so that app gets notified whenever there is a change in characteristic value. All these happens with no issues as long as app runs in background.
But issue occurs when state restoration happens. In centralManager:willRestoreState: method, I am able to retrieve previously connected peripherals using the method retrieveConnectedPeripheralsWithServices:. But nothing happens when I call discoverServices: method on the retrieved peripheral. peripheral:didDiscoverServices: method never gets called. The value of retrieved peripheral's services property is also null. Does iOS not cache the services and characteristics ?
Note: Our BLE peripheral advertises service initially. When the app launches for first time, it reads value from peripheral and writes user specific data to a characteristic. Post writing, peripheral stops advertising services. But firmware engineer claims that even though service is stopped by peripheral, peripheral retrieved from state restoration should have the cached service. Is it true ?
Have you checked the connection state of the peripherals that you have retrieved via retrieveConnectedPeripheralsWithServices:? The CoreBluetooth framework has some quirks that you need to be aware of, one being the behaviour of the retrieveConnectedPeripheralsWithServices: method:
When you obtain the peripherals via the aforementioned method they might only be connected on the system level (iOS) but not within your app (see the Discussion section in the API documentation). Therefore, you still have to call connect on the peripherals before you can use them properly. Here is the corresponding part from the API documentation:
The list of connected peripherals can include those that are connected by other apps and that will need to be connected locally using the connectPeripheral:options: method before they can be used.
Regarding the caching of services: iOS caches any discovered service and corresponding characteristic. There are only to ways to force iOS to update the cache:
a BLE power cycle, e.g., turning off and on BLE in the Settings App or restarting your iOS device
send a Services Changed notification via the GAP service from the peripheral side (requires an active connection)
Note: You can also activate log messages from the Bluetooth Stack via the Bluetooth Configuration Profile. They can be quite helpful when debugging BLE related issues especially with custom hardware involved (even though the logs are a bit cumbersome to use).

CoreBluetooth:Disconnect peripheral Connection from application

My application related to bluetooth communication with the peripheral device.Every functionality is working fine right from discovering to connecting .While coming to disconnecting the peripheral from the application i have written code like this
-(void) disconnect
{
if (_selectedPeripheral != nil &&
_selectedPeripheral.state != CBPeripheralStateDisconnected)
{
NSLog(#"Peripheral disconnecting");
[_centralManager cancelPeripheralConnection:_selectedPeripheral];
_selectedPeripheral = nil;
}
}
When i click button this above method is calling and app showing that peripheral is disconnected and when i came out of the application and look into settings /bluetooth/ .Peripheral is showing connected.How to stop connection the peripheral in the device level i.e in the settings .Please help me with the proper solution.
You are unable to guarantee a system level disconnect from the peripheral.
This is a link directly from the CBCentralManager documentation:
cancelPeripheralConnection:
Discussion
This method is nonblocking, and any CBPeripheral class
commands that are still pending to peripheral may or may not complete.
Because other apps may still have a connection to the peripheral,
canceling a local connection does not guarantee that the underlying
physical link is immediately disconnected. From the app’s perspective,
however, the peripheral is considered disconnected, and the central
manager object calls the centralManager:didDisconnectPeripheral:error:
method of its delegate object.
In my experience the physical link is disconnected quickly if you are the only application using the peripheral, but if you potentially are not as Apple clearly states there is a potential for other applications to be maintaining a persisted connection which would cause the physical link to not disconnect even though it is stating to you it has.
We were facing the same issue but we managed to workaround this nasty bug (or API design flaw) by using
Objective C
[peripheral writeValue:x forCharacteristic:y type:CBCharacteristicWriteWithResponse];
Swift
peripheral.writeValue(x, for: y, type: .withResponse)
It's strange that iOS doesn't cancel the physical connection to the peripheral we implemented in the peripheral to perform the disconnect, so we send a string indicating a disconnection should happen.
We used
Objective C
[peripheral writeValue:x forCharacteristic:y type:CBCharacteristicWriteWithoutResponse];
Swift
peripheral.writeValue(x, for: y, type: .withResponse)
and the peripheral disconnects as expected.
Hope this helps anyone facing the same issue by this CoreBluetooth API flaw.
x is the specific cmd supported in your peripheral device (i.e Firmware)
y is the specific characteristic you want to send the value
I know this is an old thread, but I figured I'd add a potential solution here for others.
What you could do is issue a command that causes the peripheral to reboot, resetting the Bluetooth connection. If your device has such a command that you can issue through Bluetooth, you're in luck, otherwise, you'll need access to the firmware for the peripheral in order to add a new command that does this. I'm not a firmware guy, so I can't tell you what exactly you need to do; all I know is the device I'm working with has such a command (it is a proprietary command specifically for our device, not part of the Bluetooth protocol) and that allowed the disconnect to be guaranteed as long as I issue it before calling cancelPeripheralConnection.

How to find the specific BLE 4.0 peripheral from two devices with same service UUID

For BLE 4.0, it provides API to discover peripherals with array of service UUID.
I just want to find the specific one. How to achieve this ?
If need assign the identifier to the specific device, how to do it ?
(I think my question need some context of core bluetooth of iOS. )
The process for reconnecting to known peripherals is described in the Core Bluetooth Programming Guide.
Essentially if you know the UUID of the device you want to reconnect to (which is the identifier property of the CBPeripheral object) then you can use the retrievePeripheralsWithIdentifiers: method of the CBCentralManager to obtain the CBPeripheral object and then attempt a connection -
NSUUID *pid=[[NSUUID alloc]initWithUUIDString:#"45D956BB-75E4-6CEB-C06C-D531B00174E4" ];
NSArray *peripherals=[self.centralManager retrievePeripheralsWithIdentifiers:#[pid]];
if ([peripherals count]>0)
{
CBPeripheral *peripheral=[peripherals objectAtIndex:0];
peripheral.delegate=self;
[self.connectingPeripherals addObject:peripheral];
NSLog(#"Found peripheral %# with name %#",peripheral.identifier,peripheral.name);
[self.centralManager connectPeripheral:peripheral options:nil];
}
This may not work, and you may have to scan for your peripheral as the identifier of some peripherals (iOS devices in particular) periodically change their UUID.
The programming guide notes -
Note: A peripheral device may not be available to be connected to for
a few reasons. For instance, the device may not be in the vicinity of
the central. In addition, some Bluetooth low energy devices use a
random device address that changes periodically. Therefore, even if
the device is nearby, the address of the device may have changed since
the last time it was discovered by the system, in which case the
CBPeripheral object you are trying to connect to doesn’t correspond to
the actual peripheral device. If you cannot reconnect to the
peripheral because its address has changed, you must rediscover it
using the scanForPeripheralsWithServices:options: method.
You will also have to scan the first time you encounter a peripheral.
Tell me if I'm wrong. You have 2 devices working as central role who wants to connect to one BLE peripheral.
What it is important is the UUID of the services in the BLE peripheral. From your devices in central role you should search peripherals with a wanted UUID Service. Thats all.
To develop with iOS please follow Core Bluetooth Programming Guide. You have a good tutorial there.
As an example of the Apple documentation:
[myCentralManager scanForPeripheralsWithServices:nil options:nil];
In this line of code you can add an Array of UUID objects (CBUUID) instead of nil for scanForPeripheralsWithServices:

Resources