Use Core Bluetooth instead of iBeacon - Any Downsides? - ios

I am working on a project where I first wanted to advertise a device as an iBeacon and make it possible to connect to that device via Core Bluetooth at the same time. Besides the fact that this is not easily doable (a device cannot advertise as an iBeacon and CB device at the same time), I noticed that the iBeacon part seems unnecessary - discovering peripherals with Core Bluetooth seems to be basically the same as discovering iBeacons.
My first Question: Am I right in assuming this? Or does iBeacon provide anything that central/peripherals in CB do not? Especially in regard to background advertisement/searching?
The only issue I can see right now is that the CLBeacon gives me both an rssi and an accuracy (and from this, the approximated proximity is calculated). With Core Bluetooth, centralManager:didDiscoverPeripheral:advertisementData:RSSI: gives me only an RSSI. Is there any way to retrieve the accuracy here so I can calculate a proximity? This is important for me and I guess relying on RSSI only for the proximity will give me less accurate results?
My second Question: Can I get the accuracy that I get with iBeacon in Core Bluetooth or a similar measure to calculate the proximity?

You can calculate your own distance estimate with RSSI using an algorithm like the one I posted here:
https://stackoverflow.com/a/20434019/1461050
The trick is that you will need as many RSSI measurements as possible averaged over a time window of 20 seconds or so to reduce the noise on the estimate.
The main advantages of using CoreLocation APIs to detect standard iBeacons vs. using CoreBluetooth to detect custom beacons are:
A variety of cheap off-the shelf hardware is available for the iBeacon standard.
CoreLocation can scan for iBeacons in the background (likely using hardware assist on iPhone 5+) in a way that can automatically launch your app relatively quickly, even if the user did not manually launch it since boot. As of iOS 7.1, even if the user kills the app from the task switcher, CoreLocation can re-launch it into the background if an iBeacon is detected. I do not believe all this is possible with CoreBluetooth.
The iBeacon transmission allows you to easily read the UUID/major/minor identifier combination in iOS without pairing. This 20 bytes of data (with the major and minor fields able to be set to arbitrary values) is more than you can get from a 16 byte Bluetooth Service UUID.
You don't have to roll your own software for distance estimation.

Related

Detecting beacons via iBeacon Monitoring & Ranging vs CoreBluetooth scanForPeripheralsWithServices

There is a lot of confusion regarding the restrictions that are applied by the iOS on apps that want to scan BLE beacons\peripherals.
After reading several blogs and Stack Overflow answers, I want to see if I understand all the issues correctly. Please correct me if there is anything I misunderstood or missed. I refer only to iOS 7 and above, and focus on detection and not connection (Can you connect to a CLBeacon using the iBeacon Monitoring & Ranging API?).
The options for the beacons are clear - Use a general purpose BLE peripheral or use a BLE peripheral that advertises in the iBeacon format (Also, a non-standard peripheral can advertise in the iBeacon format in the adv-packet and a different format in the scan-response packet).
General Restrictions
iBeacon Ranging will let you know which beacons are around you. You must specify the ProximityUUID that the beacons advertise beforehand (no "general" scanning). didRangeBeacons will be called every second with an array of CLBeacon objects that were found recently. The distance from the beacon and its accuracy are calculated by the iOS using some confidential algorithm that only Apple's developers really know (The algorithm is based on the rssi values and the rssi-at-1-meter calibration byte that the beacon advertises). You can also use iBeacon Monitoring to call a delegate every time you enter or exit a region - again you must specify the ProximityUUID that you are looking for (you can also specify a major & minor). "Exiting a region" is defined by some time of not receiving any advertisement, and therefore cannot be immediate. The number of regions that can be ranged\monitored simultaneously per device is limited to 20 - This means that if other apps do monitoring\ranging at the same time, your app may not be able to monitor\range (right?).
CoreBluetooth - You can also detect other ad-structures in the beacon's advertisement. If the beacon advertises in iBeacon format too, you cannot see the iBeacon fields (ProximityUUID, major, minor...), despite the fact that they are sent under a standard "Manufacturer Specific" ad-structure that you can see in other cases.
Running in the Foreground - The less restricted use-case:
iBeacon Ranging and Monitoring - no further restrictions.
CoreBluetooth - Passing nil in the serviceUUIDs of scanForPeripheralsWithServices will scan for all peripherals. Passing CBCentralManagerScanOptionAllowDuplicatesKey as YES in the options will make the didDiscoverPeripheral to be called multiple times for the same peripheral\beacon (I assume that using a timer you detect the advertisement was not received for some time and assume that the user exited the "region").
Running in the Background - The more restricted use-case:
iBeacon Ranging will not work directly. iBeacon Monitoring will call didEnterRegion and give the app runtime of 6 seconds - in which you can start Ranging (for example, to detect major & minor). The detection may not be immediate since iOS turns scanning on and off to preserve the battery power. If you enter a region of multiple beacons with the same ProximityUUID, and you monitor this UUID without a specific major and\or minor, didEnterRegion will be called when you start receiving the signal from the first beacon - however, if you did not exit the region of the first beacon and you also entered the region of a second beacon the app will not be woken up again (didEnterRegion will not be called again) so you cannot start ranging to detect the second beacon's major & minor. The app cannot simply pop up to the foreground, but can create local notifications and other background operations.
CoreBluetooth - According to Core Bluetooth Background Processing scanForPeripheralsWithServices can run in the background using, but you must specify at least one serviceUUID. didDiscoverPeripheral will be given a runtime of 10 seconds. Using CBCentralManagerScanOptionAllowDuplicatesKey will not work - didDiscoverPeripheral will be called once for every peripheral. Therefore, you cannot detect "exit" from the region and "re-entry". I suppose you can use a non-standard BLE peripheral that changes its MAC address to overcome this issue. The app cannot simply pop up to the foreground, but can create local notifications and other background operations. The detection may not be immediate since iOS turns scanning on and off to preserve the battery power.
Running after the app is killed
iBeacon Monitoring - Works! Even if the user killed the app or the device was restarted.
CoreBluetooth - The app will be woken up if it was killed by the iOS (due to inactivity or memory constraints). However, if the user explicitly killed the app it won't be woken up (which makes the first case hard to test). I don't know what happens after a device restart...
Does anyone have more experience with these restrictions? Can scanForPeripheralsWithServices be used as a better alternative to iBeacon Monitoring in some use-cases?
Thanks!
You are mostly right with your description. Just two clarifications:
The 20 region limit is not per device, it is app-specific. No matter what other apps are doing on the mobile device, your app is still allowed to monitor up to 20 regions by iOS. That said, there are likely hardware limits that are device-specific on how many regions can be monitored in the background with hardware assistance. These limits are undocumented. If you surpass these undocumented limits, it will probably take a lot longer to detected beacons in the background. (Although that said, there is no OS guarantee of when the detections come, anyway.)
You cannot connect to a CLBeacon using Monitoring and Ranging APIs. These APIs only work with BLE advertising packets, which are connectionless.
Yes, it is possible to use scanForPeripheralsWithServices as an alternative. This is what Gimbal beacons do in order to implement a proprietary system. There are real disadvantages, however, in terms of background detection time and reliability.

How to reduce the time interval for didRangeBeacons method

I am working on a project involving iBeacons. I am observing that the didRangeBeacons method is called in 1 second interval. Is there any way to reduce this time interval? My project requires low latency proximity detection and I was wondering how this could be achieved.
No, you cannot reduce this interval on iOS because it is baked in to the CoreLocation APIs. You can do so on Android, or you could do something similar with lower-level CoreBluetooth APIs on iOS, but using CoreBluetooth is a bigger roll-your-own project than can be answered here.
While I am unsure of your use case, you should consider that reducing the interval might not help. Beacons only send out advertisements at a certain frequency (typically 1-10Hz). If you are using a beacon transmitting at 10Hz, you could potentially get a detection in 1/10 of a second. But if you need a distance estimate, this fluctuates greatly with each packet, so you really need to average the measurements over many samples anyway.
Also, consider that a CoreBluetooth-based solution would only be able to do faster ranging when the app is in the foreground. Background processes on iOS have a whole different set of delay challenges.
Finally, any CoreBluetooth solution could not work with standard iBeacons because iOS sandboxes the ability to read iBeacon identifiers with anything other than CoreLocation. So you would need to build a custom beacon.

iBeacons: how to get broadcasted beacon power (txPower)

iBeacons seem to broadcast their txPower parameter (report RSSI power at 1 meter distance) which is used in calculating beacon.accuracy and beacon.proximity properties (details on iBeacon advertisements packet can be found here).
However, CLBeacon class does not seem to have a property for txPower. Is there a way I can get txPower using Core Location framework, or need I to go down to Core Bluetooth? The reason I need this, is I want to experiment with custom beacon accuracy/proximity calculation for very quick beacon immediate range discovery. In this circumstances reverse calculating txPower from accuracy is no helper.
Unfortunately you can not get this value programmatically with either CoreLocation or CoreBluetooth. Apple blocks access to all iBeacon data with CoreBluetooth (See my breakdown of this here.) Similarly with CoreLocation it is simply not exposed in the CLBeacon class as you have seen.
Since you say you can't to do a reverse-calculation, then the only other ways I can think of to do this are:
Use an Android, OSX Mavericks, or Linux device, which offer no such restrictions on reading this field.
Make a lookup table in your iOS app of all your iBeacons (with unique UUID/major/minor values vs. their txPower value)
Option 2 above obviously requires that you assign unique identifiers to all your iBeacons under test.

How to limit the advertising range of a beacon?

Is it possible to limit the ranging of the beacon, so that only devices within a certain close range(or proximity) can identify and connect to the beacon? Lets say for example the devices outside 0.5 meter zone shouldn't be able to see or connect to the beacon. I am using a iOS device as a beacon. In the Apple's CoreLocation API, there is a method called peripheralDataWithMeasuredPower in the CLBeaconRegion class which says:
peripheralDataWithMeasuredPower:
Retrieves data that can be used to advertise the current device as a beacon.
(NSMutableDictionary *)peripheralDataWithMeasuredPower:(NSNumber *)measuredPower
Parameters:
measuredPower:
The received signal strength indicator (RSSI) value (measured in decibels) for the device. This value represents the measured strength of the beacon from one meter away and is used during ranging. Specify nil to use the default value for the device.
Can this be used to limit the range of beacon? If yes, I am unable to understand how to decide the value to set for measurePower parameter? What are they trying to say by ...value represents the measured strength of the beacon from one meter away..?
Please forgive if this is a very basic question. I've recently started iOS development and will appreciate your help. Thanks.
Unfortunately, there is no easy way to adjust the range of an iBeacon without special hardware.
The power field that you mention is simply a calibration value transmitted by an iBeacon. It doesn't affect the actual physical radio range of the iBeacon. If the transmitter can be seen by an iPhone 50 meters away, altering the power field value will not change this at all. The only thing it does is change is the calibration constant which is an input to the distance estimation algorithm (used for the accuracy and proximity fields) inside the iOS software. Altering the power field will affect the estimated distance returned by the API, but it won't change the actual distance at which the iBeacon is first detected.
Altering the transmit power of a standard bluetooth iBeacon is practically impossible. In theory you can use metal shielding to construct a "faraday cage" around the transmitter to mute its power, but my experience is that it isn't very effective and it is highly susceptible to tiny imperfections in the shielding. If you want to change the transmit power you have to have somebody build you custom hardware.
The software alternative is to use the ranging API to track an iBeacon while it is visible, and only perform an action when the estimated distance is close enough, say 0.5 meters as you suggest. This works great -- only in the foreground.
If you require actually waking up your app in the background at a close range, this won't work. The best you can do is have the monitoring API wake up your app when the iBeacon is first detected, and then send a notification to the user and start ranging. If the user elects to bring the app to the foreground (at 50 meters) you can keep monitoring and then perform your desired action at 0.5 meters. If the user does not elect to bring the app to the foreground, iOS will only give you about 5 seconds of time to continue ranging before it suspends your app. It is very unlikely that the distance will change from 50 meters to 0.5 meters in this time.
With most BLE chips I've investigated, there are usually at least four settings for transmission power level that can be used to limit the advertising range.
The Texas Instruments CC2541 (as used in their SensorTag development device) and CC2540 have +4, 0, -6, and -23 as their power level options. However, changing that in the SensorTag does require a recompile of the firmware. As-is, the provided firmware mentions the power level in only one place, but that is just a value that is broadcasted to inform any central listener how loud the beacon is—so that the central device can better calculate an estimated range based on received signal strength (RSSI). An additional line must be added to the firmware to actually change the transmission power. For example:
HCI_EXT_SetTxPowerCmd( HCI_EXT_TX_POWER_0_DBM );
Based on this, there should be two places on an iOS device where you can set the power level: one that just informs the listeners what the level is, and one where the BLE chip's true transmission power is actually changed. However, expect these values to be restricted to only a few enumerated choices which may or may not meet your real-world range needs.
(The SensorTag's -23 setting would probably do well for a 0.5 meter detection range. But if you want the SensorTag to always be advertising, it will require an additional firmware change.)
Have you looked to see if the proximity property was helpful? From the apple docs:
CLProximity
Constants that reflect the relative distance to a beacon.
typedef {
CLProximityUnknown,
CLProximityImmediate,
CLProximityNear,
CLProximityFar
} CLProximity;
I would also experiment trying to combine the the proximity with accuracy and rssi.
It's gonna vary from beacon to beacon. If you use beacons from Radius Networks, they have a transmit power setting that lets you essentially limit the ability of the beacon radio to broadcast to long ranges. I don't know if other brands have it, but most do not from what I've seen.

Is it possible to get Bluetooth MAC and/or signal strength in iOS 6?

I am developing a system to locate an iPhone indoors. I am considering using Bluetooth triangulation based on signal strength.
Is it possible to get the signal strength of nearby bluetooth devices on iOS 6?
Is it possible to get the phone's bluetooth MAC (so I can identify the phone and do signal strength measurement from the other side)
Any other ideas to calculate position of iPhone based on bluetooth triangulation?
It's for a restaurant type app. The restaurant wants to know at which table the phone/app is sitting.
If your iOS device supports Bluetooth LE, you can easily obtain the RSSI between it and sensor stations, as well as a UUID for identifying the device. You can also read the MAC address if you're using the right software on your sensor side, but I don't believe that this information is exposed by Core Bluetooth in the same way that RSSI and UUIDs are.
There are two ways to approach triangulation if using Bluetooth LE. One is to do the triangulation by monitoring the iPhone from a series of placed sensor locations, and the other is to do triangulation on the iPhone itself by reading the signal strength between it and devices that are advertising from known points.
The latter can be done by placing a series of LE peripherals in known locations and having them advertise at regular intervals. Within the advertisement information, you could stamp the location of that peripheral in the room. The iPhone could read these advertisements, get the RSSI information from them using the -centralManager:didDiscoverPeripheral:advertisementData:RSSI: delegate method, and triangulate its location.
The former approach would be a little more involved. It would require that the iPhone itself be placed into an advertising mode using the new iOS 6.0 support for making the iPhone a Bluetooth LE peripheral. The sensor locations could then pick up the RSSI from them to the iPhone via these advertisement packets, as well as a UUID you generate for the phone. You can also pick up the MAC address of the phone is running the right software on the sensor nodes. A central server would then need to combine these readings to triangulate the iPhone's location.
You might need to experiment with the transmission strength to determine what kind of resolution this would provide. The RSSI information is noisy, and in my experience it only seems to resolve to +-30 feet at the normal transmission levels for my peripherals. You may be able to improve upon this with multiple sensor stations and a lower transmission strength.

Resources