I'm developing an application that will use iBeacons for indoor navigation, and I found that the rate the function locationManager:rangingBeaconsDidFailForRegion:withError: is being called is not high enough, so I'm going to add the RSSI data from CoreBluetooth's centralManager:didDiscoverPeripheral:advertisementData:RSSI:.
And I found a curious fact: when I listen to the iBeacon with CoreLocation and log the peripheral id:
- (void)centralManager:(CBCentralManager *)central
didDiscoverPeripheral:(CBPeripheral *)peripheral
advertisementData:(NSDictionary *)advertisementData
RSSI:(NSNumber *)RSSI
{
NSLog(#"%#, RSSI: %#", peripheral, RSSI);
each beacon is reported with different UUID on different devices:
A25804BD-D77A-5004-4C2C-301D996C7367 - my iPhone 5
843F4237-6059-9A5E-AA34-0BD92304BE1F - colleague's iPhone 5
77685805-C253-52BD-B787-8B95308834FB - other colleague's iPad mini
The idea was to bind UUID of each beacon to its location, so this behavior is quite functionality-breaking.
Why the UUID of physically the same beacon (not powered off/on) is different on different devices? Is it an expected behavior? If it is, how could I opt out of it?
You cannot read the identifiers of standard iBeacons using CoreBluetooth. As Chris Stratton said in his comment, the UUID that CoreBluetooth gives you is a device UUID, which is randomly generated on a per-session basis by iOS. It has nothing to do with the iBeacon ProximityUUID.
More details on why you can't read iBeacon identifiers with CoreBluetooth are here: http://developer.radiusnetworks.com/2013/10/21/corebluetooth-doesnt-let-you-see-ibeacons.html
It is true that you only get on RSSI measurement per locationManager:didRangeBeacons:inRegion: callback. This is a real obstacle to doing a custom distance estimate. Behind the scenes, iOS can gather 10x as many measurements for iBeacons transmitting at 10Hz. You are correct that you can get more measurements using CoreBluetooth, but the problem is that there is no reliable way of lining up which bluetooth devices you see with CoreBluetooth correspond to the iBeacons you can see with CoreLocation.
Related
I'm working on with BLE of iOS where in i could able to establish a peripheral advertisement and scanning at the same time.
I have 2 devices where in both would scan advertise at the same time, it works fine when the devices are in foreground.
Now the issue is: The device is in background meaning the advertisement is not working(not able to show the Local Name and UUID as it will mask as per Apple) because as per Apple Docs:
advertisement key are placed in a special “overflow” area; they can be discovered only by an iOS device that is explicitly scanning for them.
Now my question how can i scan for this overflow area?
Also note when a device is advertising in background i do not get a call back in below delegate too:
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *, id> *)advertisementData RSSI:(NSNumber *)RSSI;
Any possible way i can do this approach, which would really helpful.
The Solution to the above scenario is as per #Paulw11 said.
We just need to scan for specific Peripheral Id.
Thanks Paulw11
how is it possible to fetch the UUID from Peripheral side. Not the service UUIDs out of the advertising package.
Lets see it from the central side:
- (void)centralManager:(CBCentralManager *)central
didDiscoverPeripheral:(CBPeripheral *)peripheral
advertisementData:(NSDictionary *)advertisementData
RSSI:(NSNumber *)RSSI
What I need is the UUID out of peripheral.identifier, not the ones included in the advertisementData. I can fetch them from the central side, but I need to do this also from the peripheral side.
I don't think there is a way do get UUID from CBPeripheralManager side.
Since this UUID is hard coded in Bluetooth chip,and people barely change their iPhone's Bluetooth chip,this method would be used to track down user ,which is versus to Apple's intend.
Apple banned UDID in iOS 5,banned MAC address in iOS 6.
So there is no reason Apple gave you access to Bluetooth UUID when Apple add CBPeripheralManagerin iOS 6.
If I want to do "NFC based transactions" but in the iBeacon way, when 2 iOS devices are in the "immediate" range, how can I connect both device automatically without pairing/authentication, so that I can do the transaction communication?
The scenario:
User's iPhone detects iBeacon signal, prompt user to open the app
User opens the app, and wants to purchase a physical item
User grab his device to the retailer's iOS-based POS device
Both iOS devices detect each other are in the "Immediate" range and start the connection/pairing without manual authentication
Transaction communication starts here
From my understanding, scanForPeripheralsWithServices cannot return enough information for the above scenario to happen, does anyone know the correct way to implement such service?
Thank you
The scenario you describe can be carried out with very basic connections exactly the way you listed it, no BLE level pairing, authentication is required at all. More precisely:
The presence of iBeacon triggers the app to notify the user of an opportunity to buy something.
The client's iOS device scans for peripherals and the retailer's POS advertises itseld. The distance between the two is estimated using the RSSI value from the advertisement packages. The next step is activated if this value gets high enough (practically, higher than a given threshold, like -30 or so. You can select whatever fits your needs)
The client uses connectPeripheral: to connect to the POS. This alone will not trigger authentication or pairing.
You implement the the transaction by using secure encryption and authentication and whatever needed. This is completely proprietary to your solution and you need to create it or get some existing one to work. (I don't know about existing implementations, though.)
To see an example, the of the RSSI based distance estimation, the BTLE example is a good starting point. Here is the corresponding excerpt from BTLECentralViewController.m
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI
{
// Reject any where the value is above reasonable range
if (RSSI.integerValue > -15) {
return;
}
To implement the transaction, you need to understand the basics of Core Bluetooth: the services, characteristics, BLE transactions (read, write, notifications...)
Pairing and authentication are never required for detecting and ranging for iBeacons when using iOS CoreLocation APIs. iBeacons are connectionless and transmit only. You can tell that two devices are in immediate range from one another by making both transmit as iBeacons, and then using the CoreLocation ranging APIs to detect the proximity:
- (void)locationManager:(CLLocationManager *)manager didRangeBeacons:(NSArray *)iBeacons inRegion:(CLBeaconRegion *)region {
for (CLBeacon *iBeacon in iBeacons) {
if (iBeacon.proximity == CLProximityImmediate) {
// Add code to process transaction here
}
}
If you want to exchanges secure data wirelessly between the two devices after you have verified they are in immediate proximity, you can do so with Bluetooth as #allprog suggests in his answer. You can also do this through web services, which allow you to have an iBeacon-only solution without having to drop down to using CoreBluetooth APIs. See this answer for more details on how that might work.
Is it possible to calculate the distance between the device and the iPhone, or at least, have an idea of the strength so I can check if the distance is getting smaller or bigger as I move. I would like to build some sort of system which makes it easier to find "lost" things in a close perimeter.
Is such thing possible? And if so, what is the best way to do this.
I assume that there is no obstruction between the two devices.
Yes, absolutely!
Actually it's basic functionality provided by Core Bluetooth. To reference a link provided by a previous answer:
As soon as a Peripheral is discovered during the scanning, the Central
delegate receives the following callback:
(void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral
advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber
*)RSSI
This call notifies the Central Manager delegate (the view controller)
that a peripheral with advertisement data and RSSI was discovered.
RSSI stands for Received Signal Strength Indicator. This is a cool
parameter, because knowing the strength of the transmitting signal and
the RSSI, you can estimate the current distance between the Central
and the Peripheral. So, you can use the distance as a filter for a
given service: only if the Central is close enough to the Peripheral,
then your app does something.
The RSSI value allows you to estimate the distance, create virtual fences and can also be used to zero-in on a BLE device by comparing the new RSSI measurement to the previous one.
Can I measure the signal strength of Bluetooth devices within range of my iPhone?
Basically what I want to do is scan for a list of devices within range, and then see which one has the highest signal strength.
Is it possible in iOS and how would I do it if so?
Yes there is a way to measure the signal strength for Bluetooth Low Energy (4.0) it is the RSSI number. When you scan for peripherals you will set the flag CBCentralManagerScanOptionAllowDuplicatesKey to YES as shown below:
NSDictionary * dictionary = [NSDictionary dictionaryWithObjectsAndKeys:#YES, CBCentralManagerScanOptionAllowDuplicatesKey, nil];
// Start scanning for peripherals
[cmanager scanForPeripheralsWithServices:nil options:dictionary];
If you want to see the RSSI number work without writing any code you should check out the app LightBlue in iTunes. When you connect to a peripheral it will show you the updated RSSI number every second when it is connected.
Have a look to the CoreBluetooth documentation:
- (void)centralManager:(CBCentralManager *)central
didDiscoverPeripheral:(CBPeripheral *)peripheral
advertisementData:(NSDictionary *)advertisementData
RSSI:(NSNumber *)RSSI
{ ... }
RSSI is what you are looking for. Disclaimer: Core Bluetooth is made for Bluetooth 4 LE only.
If the exact range does not matter, but you are interested in scanning devices which are available in general, you may have a look to the github project BeeTee, which allows you to scan for all Bluetooth devices around you (not only Bluetooth LE). Again disclaimer: I am the author of BeeTee. ;-)
When you're coding your 'central' code using CBCentralManager, you'll eventually connect to the CBPeripheral you're looking for. Once you're connected to the peripheral, keep a reference to it, set your object as the peripheral's delegate and invoke 'readRSSI' on the peripheral. You'll get a delegate callback peripheral: didReadRSSI: error: If you write a method that invokes 'readRSSI', you can invoke it using performSelector: withObject: afterDelay:.
Another of the suggested answers to this question is to supply the 'allow duplicates' key when scanning. The docs for the dictionary key CBCentralManagerScanOptionAllowDuplicatesKey when passed to scanForPeripheralsWithServices:options: indicates that "Disabling this filtering can have an adverse effect on battery life and should be used only if necessary".
If you choose to write a delayed invocation, you can tune the frequency of the calls to help you manager the impact on your users' batteries.