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

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 ?

Related

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).

HID service not visible

I am working on a Swift app (iOS) that connects to a device via BLE.
I have written some code, managed to connect to my device, managed to get the advertised data but when I scan for the services using the didDiscoverServices method, I only get the Battery and Device information service, not the HID service.
I can see the Device information, Battery, and HID service in the advertise data but not in the didDiscoverServices method.
I can access characteristics of the Battery and Device information in the didDiscoverServices method.
I have read in multiple places that HID is managed by iOS and not by the app.
Is there any way to access the HID service?
Unfortunately, there is no way to access this service. Apple actively filters certain BLE services so you cannot access them via the CoreBluetooth API. In particular, the HID over Gatt service is maintained by the system so you can use your device to enter text etc. but you will be unable to intercept any events in your app.
The aforementioned filtering also applies to the peripheral role. If you try to add the HID over Gatt UUID as a new service to a CBPeripheralManager you will get an error.

What is the nature of BLE characteristic notify property?

I need to provide communication via Bluetooth Low Energy 4.0 between iOS-device from one side and certain peripheral device, which has bluetooth-module onboard.
Peripheral device was assembled with DORJI DBM01 bluetooth LE 4.0 module which allow to communicate with it using one characteristic with READ property (UUID = 0xFFF4) and one characteristic with WRITE property (UUID = 0xFFF1). Technical manual of DORJI DBM01 is here: http://dorji.com/docs/data/DBM01.pdf
I succeeded in establishing bluetooth LE connection between iOS-device (using Core Bluetooth Framework) and described above peripheral device, but characteristic with READ property doesn't have a property of notify (CBCharacteristicPropertyNotify in Core Bluetooth Framework). So I have to use some kind of infinite loop to scan and read new information from characteristic with READ property (by calling method of CBPeripheral instance - readValueForCharacteristic:) instead of subscribing to a characteristing value (by calling method setNotifyValue:forCharacteristic:).
Is it possible to add notify property to read characteristic of peripheral device (for instance, by engineer who built peripheral device or adding some low-level code which run on peripheral device) or notify property is an integral part of BLE-module (DORJI DBM01) and it can't be added without using another BLE-module?
The DBM01 is based on a Texas Instruments CC2540, like many other BLE-to-serial modules, which do implement notifications, so technically, it is definitely possible to do it.
However, this requires the firmware of the DBM01 to support it, and you'll of course have the issue of upgrading said firmware in existing modules. Unless there are undocumented features on the module, I doubt there would be a way to change the behaviour of the module without touching the firmware.
Another option could be (but that really depends on your scenario) to switch the central and peripheral roles, so that the iOS app would be notified when the BLE module writes data. As I understand it, this needs to be done in hardware (the level on a specific pin needs to be changed).

Purpose of CBCentralManagerScanOptionSolicitedServiceUUIDsKey

The CBCentralManagerScanOptionSolicitedServiceUUIDsKey option is available when scanning for devices in CoreBluetooth, the documentation states
Specifying this scan option causes the central manager to also scan
for peripherals soliciting any of the services contained in the array.
What is the purpose of this option? What is a 'solicited service'? How is it different to specifying the service UUID in the services array?
As I understand, service solicitation means that the peripheral can indicate what services it will want to use on the central when the connection happens. For example, you have an ANCS peripheral (a smart watch or whatever) that advertises itself and adds the ANCS service as solicited service to the advertisement. This will indicate to the central that this peripheral will look for the ANCS service once connected. In case of iOS this results that the user can connect to the peripheral from the Bluetooth devices list without running any apps.
In the current Core Bluetooth implementation this is not really feasible between two iOS devices. But if you initialize a CBPeripheralManager with the solicited services (no need to advertise those) and use a CBCentralManager to scan for peripherals that want to use those solicited services, then in the end the scanned peripheral will probably be able to use the services provided by your CBPeripheralManager once the connection is established. Two iOS devices cannot do this at the moment, as there is no API for it to add the solicited services to the advertisement. (Connecting back from the peripheral side may be possible if the retrieveConnectedPeripheralsWithServices method on the CBCentralManager returned the other end as CBPeripheral if it has its CBPeripheralManager initialized.)
Related SO question: Bluetooth LE and ANCS in iOS
Referenced blog post: http://blog.punchthrough.com/post/63658238857/the-apple-notification-center-service-or-wtf-is
Bluetooth spec V4.0 Vol 3. Part C. Section 11.1.9
11.1.9 Service Solicitation
One of the Service Solicitation AD types may be sent to invite other devices that expose one or more of the
services specified in the Service Solicitation data to connect. The
device should be in the undirected connectable mode and in one of the
discoverable modes. This enables a Central providing one or more of
these services to connect to this Peripheral, so that the Peripheral
can use the services on the Central.

iOS CoreBluetooth reconnecting device with UUID

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

Resources