I'm making an iOS app that uses Core Bluetooth and periodically checks to see if the peripherals are still around. My problem is that when I shut off the advertising device (even shut off the bluetooth entirely), my central device still returns the CBPeripheral object when I call retrievePeripheralsWithIdentifiers. I need it to accurately NOT return this the peripheral once it is no longer advertising. Thanks in advance!
retrievePeripheralsWithIdentifiers looks into the Core Bluetooth database to see if it can find an peripheral with the specified identifier, regardless of whether advertisements from that peripheral are currently visible. This allows you to issue a connect to a peripheral that has been seen before and may be seen again (The "lock" scenario described in the Core Bluetooth Programming Guide is one example where you could use this).
To see if a peripheral is advertising or not you need to call scanForPeripheralsWithServices, specifying YES for CBCentralManagerScanOptionAllowDuplicatesKey and keep your own table of visible peripherals, ageing them out when you haven't seen an advertisement for some period.
Related
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).
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).
I'm writing a suite of Bluetooth Low-Energy app for iOS 7.
When the app launches, there's a fair chance the peripheral I'm interested in is still connected to the iOS device, and to avoid scanning for the peripheral it would be great to connect immediately. retrieveConnectedPeripheralsWithServices: looks perfect for this, and indeed seems to work as advertised: I get a list of CBPeripherals which are connected to the device via some backgrounded app.
So far so good, but here's the issue:
There's information in the AdvertisementData which I want, but I don't know how to get the AdvertisementData when going this route.
As far as I can see, advertisementData is only available as a result of scanning.
So my question is this:
* Given a CBPeripheral returned by retrieveConnectedPeripheralsWithServices:, how can I get the associated advertisementData for that peripheral ?
One workaround would take advantage of the fact that the device was likely connected to another of the apps in my own suite, so I could persist the advertisement data myself in storage shared between the apps, but this is neither clean nor ideal, because there's a chance the device was connected to someone else's app, and so I'd have no insight into the advertisement data in that scenario, and would need to resort to a scan.
Unfortunately, the advertisement data is available only if you scan. It is not possible to retrieve it from Core Bluetooth or any other framework in any other way. You should revise your design if the advertisement is so important and rather go along the scanning route.
Is there a mechanism in iOS CoreBluetooth so that an event will be triggered when a discovered (but not connected) peripheral is "lost" i.e. a peripheral that was advertising is no longer advertising .
You could specify the CBCentralManagerScanOptionAllowDuplicatesKey:YES in the options to CBCentralManager's scanForPeripheralsWithServices: and then keep a table of observed devices, ageing the entries periodically, but this will impact battery life and you will not be able to operate once your application is no longer in the foreground. Connecting is a better option - is there some reason why you don't want to connect to the device?
You can look in AltBeacon, an open source project we released https://github.com/CharruaLabs/AltBeacon how we do this. Check the method reportDelegates of the class AltBeacon.m
A bit more in detail. What we do it to keep reporting in an preestablished interval and then age the signal (RSSI) of the device. After a while if it is too aged you can trigger the no longer advertising callback. What we do is just to change an enum status distance to unknown.
my app using scanForPeripheralsWithServices: to scan BLE device,the argument is specified servicesUUIDs array, sometimes could discover peripheral quickly,but sometimes need to wait for a moment.And my app is session backgrounding.Then,how can I discoverPeripherals as quickly as possible on background.
When the app is in the background iOS is enabling Bluetooth from time to time and it is not always listening. This is to save battery and because the WiFi and Bluetooth share the same antenna and cannot send at the same time. To make it discover your peripheral as fast as possible you should if you have access to peripheral implementation, make sure the peripheral advertises itself as often as possible. I believe Apple recommends at least once each 20th ms.