iBeacon for auto peripheral pairing + authentication? - ios

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.

Related

Unique identifier for peripheral BLE device

So I have peripheral BLE device and I need some identifier for it to later share with another iPhone. Example I connect with iPhone 'A' to peripheral. iPhone 'A' saves peripheral's identifier to database and later I easily can take iPhone 'B' and connect to the peripheral found by this identifier.
Now there is UUID what changes for every iPhone-peripheral connection, but MAC
Address is not available. What could you suggest?
If you do not manufacture the peripheral yourself then you can not uniquely identify peripherals across different iOS devices. This is exactly how Apple wants it to be since it means that developers can't track user's locations or movement patterns based on device addresses. It would be a privacy concern.
If you manufacture the peripheral yourself then you can do pretty much whatever you want. For example: Use a custom Service uuid, add uuid to manufacturer data in advertisement, set unique advertisement names, etc etc.. But if the core purpose of all this is to track your user then I would guess that Apple might deny your submission.
I have a similar problem and many thread like this say you cannot obtain MAC address by CoreBluetooth. I want to connect to a BLE peripheral (I mean open and connectable peripheral) and store some information on a database so that a different iPhone can read the same database and use the informations when it connects to the same kind of BLE peripheral. For now I rely on the device name, obtained like this:
-(void)addDiscoveredDevice:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI
{
NSString * name = [peripheral name];
}
because NSString * uuid = [[peripheral identifier] UUIDString]; gives me different results on different iOS devices.
Therefore, let's say my first problem is to know how many Apple Watch I have close to my iPhone, the second problem is to know if a given Apple Watch (e.g. John's Apple Watch) is close to my iPhone. Unfortunately this method doesn't work because users (e.g. John) or companies can change the name of their peripherals.
Yes I can rely on a combination of name + service uuid + characteristic uuid but for the same reasons, if the name changes it is useless.
Does anybody found a solution?

How to detect with CoreBluetooth when a peripheral disappears?

I would like to have a list of ble devices to which my iOS can connect, which refreshes when ble devices appear and disappear.
In order to do that, I created an NSMutableDictionnary* peripheralsAvailable, and everytime - (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI; is called by the CBCentralManager, I add the peripheral to the peripheralsAvailable dictionnary (and then update a UITableView). All is OK here.
However I can't find how I can update the dictionnary if a Peripheral "disappear". It seems that I can only add peripherals in my dictionnary when they are detected, but I can't remove one when I shut it down for example.
Could you tell me if I'm missing something?
The OS will cache discovery of devices. I.e. you will only get one "discovery" event per device.
To continuously get discovery events while the peripheral is advertising, you must use the following option:
CBCentralManagerScanOptionAllowDuplicatesKey
A Boolean value that specifies whether the scan should run without
duplicate filtering.
The value for this key is an NSNumber object. If YES, filtering is
disabled and a discovery event is generated each time the central
receives an advertising packet from the peripheral. Disabling this
filtering can have an adverse effect on battery life and should be
used only if necessary. If NO, multiple discoveries of the same
peripheral are coalesced into a single discovery event. If the key is
not specified, the default value is NO.
Setting the above option to YES, you could keep track of all of the peripherals that are advertising and when it stops advertising, you could remove it from the list.
For a device that you've connected to, there is the didDisconnectPeripheral delegate event.
Bluetooth devices don't advertise that they're about to go away, nor do they advertise that you're about to go out of range. You get an advertisement while they're advertising and you're within range, and you get nothing when you're out of range or they stop advertising. There's no event to trigger on when they're gone. You have to remember the devices that are advertising, and when they stop advertising (you haven't received an advertisement in awhile), you can remove it from the list.
Great answer by Marcus. One additional note to add is that the scan option CBCentralManagerScanOptionAllowDuplicatesKey as mentioned above does not work in the background.
Apps that have specified the bluetooth-central background mode are allowed to scan while in the background. That said, they must explicitly scan for one or more services by specifying them in the serviceUUIDs parameter. The CBCentralManager scan option is ignored while scanning in the background.

UUIDs of iBeacons are different on different devices

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.

Core Bluetooth advertise and scan in the background

I have been trying to setup an app to make the device both scan for peripherals and advertise as a peripheral. The goal is for two devices to be woken up in the background when they become near each other via bluetooth discovery. From the Apple Documentation, it seems that you should be able to run BLE in the background (with bluetooth-central and bluetooth-peripheral background modes enabled), and my application works when one device is in the foreground.
First, I advertise data like so:
NSDictionary *advertisingData = #{CBAdvertisementDataLocalNameKey:#"my-peripheral",
CBAdvertisementDataServiceUUIDsKey:#[[CBUUID UUIDWithString:identifier]]};
// Start advertising over BLE
[peripheralManager startAdvertising:advertisingData];
I then set the device to scan for data:
NSArray *services = #[[CBUUID UUIDWithString:identifier]];
[centralManager scanForPeripheralsWithServices:services options:nil];
However, when both go into the background (device has to be locked), the bluetooth cannot discover and
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI
never gets called on either device. How can I fix this?
Thanks
I'm afraid what you are trying to do will not work.
I have tried to achieve the same thing.
The problem is the difference in scanning in foreground and background.
When you are scanning for devices in the foreground you can scan for anything. In the background you must specify the actual service UUID you are scanning for. Ok, this isn't actually a problem as you know the UUID you are looking for.
Peripheral:
Broadcasting as a peripheral again works differently in foreground and background.
In foreground it works like any normal BT peripheral. In the background it has a very limited amount of space to work with, so your peripherals UUID is hidden away and not broadcast. Only when a central device (an iPhone in foreground) requests the information from it will it wake your app and show it's UUID.
So the 2 cancel each other out. As your background scan can only scan for devices with a specific UUID and your background peripheral cannot advertise its UUID, they cannot see each other.
1 of your devices (either peripheral or central) must be in the foreground to work.
This has been discussed several times on the Apple Bluetooth mail list.
You should elaborate on how you're testing this, because theoretically it looks like it should work. There's two primary issues you may be facing:
1.) Scanning is throttled down when iOS devices are in the background.
While scanning in the foreground will likely immediately discover a device advertising next to it, discovery in the background can take up to ~60 times longer. The iOS system makes no assumptions that the user would prefer one app to have better Bluetooth functionality than another (or that only one app wants to use it). And since it is shared functionality, they want users to have a uniform experience across apps. You should check out the technical specifications regarding Advertising and Scanning intervals to get a better idea of what Apple has to do under the covers.
2.) Your devices may have already discovered each other before entering the background.
We must remember that Apple disables the CBCentralManagerScanOptionAllowDuplicatesKey scanning flag when we enter the background. Since you're not even specifying this flag, it defaults to NO anyways. So if they've even seen each other once, you will not get another callback when they are in the background.
I personally needed such a feature and I developed an open source component: https://github.com/omergul123/Discovery
It might be very helpful if you want to exchange an ID even if the peer apps are running at background.

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