According to the ibeacon protocol "measuredPower" is supposed to be sent by every ibeacon in its advertising packet as the last value after uuid, major & minor values. However, the iOS corelocation service does not have any method which returns this "measuredPower". Also, if we reconfigure an ibeacon and change its txPower then the beacon should ideally advertise a different "measuredPower". Do all beacons do this? And how do we fetch the measuredPower of a beacon in iOS/corelocation.
Directly, it's not possible on iOS. The closest you have is the RSSI on CLBeacon. Given a known distance, you should be able to calculate the signal power.
Related
I was trying to understand the difference between Beacon Ranging and BLE Scanning. From what I understand, beacon ranging uses BLE scanning to find the beacons. On top of that it calculates proximity of beacons using the signal strength(which I do not need). I just need to detect the beacons(similar to beacon monitoring). I am not using beacon monitoring because of the Always permission requirement in IOS. I know that beacon monitoring is highly optimised as compared to ranging, but I wanted to know how ranging compares to BLE scanning.
Can i use BLEModule.scanForDevices(UUID) in place of CLLocationManager.startRangingBeacons(region) to detect beacons? If yes, can I get callbacks in similar fashion when beacon is detected?
Are there any downsides of doing this in terms of battery performance or detection time, etc.?
Note that I am willing to make do without proximity information(distance from beacon).
Also, a related question:
Can beacon monitoring be used in foreground with WhenInUse permission in iOS?(I found mixed views on this in my initial investigation)
You cannot use Core Bluetooth scanning to detect an iBeacon. You must use Core Location and monitor a CLBeaconRegion to discover iBeacons.
Having discovered a beacon you do not need to range it.
As stated in Apple's documentation
Important
Apps must have always authorization to use region monitoring, and they must be configured with the Location updates background mode to be launched.
Even if you only want beacon notification when your app is in the foreground you must request always permission.
I have played around with Airlocate and two iPhones. Also between a Linux /hcitool beacon transmitter and Airlocate ranging. I see that the set of proximity UUIDs to be tracked are hardcoded in the Airlocate source code.
Is it possible for an iOS app to get a more generic call back , when it is in the vicinity of any iBeacon (any proximity UUID) and let that app decide how to go about behaving the way it wants.
I understand that this should definitely be possible from the OS perspective, as I see a 9 byte iBeacon prefix in the start of the PDU, which is independent of the proximity UUID.
Another query - I view hardcoding the proximity UUID in the APP as some sort of pairing . How is general bluetooth pairing different from this.
My aim in asking these questions is to understand the feasibility of using beacons to beam (broadcast) data, so that all recipients in the vicinity can receive it. If I choose to go with a specific 16 byte proximity UUID, then I am left with only 5 bytes (Major/minor/power) to beam data. Otherwise I get 21 bytes
By "more generic call back", it seems like you are trying to listen to iBeacons that are not deployed by you. The answer to this is NO, you can't do that.
According to this post:http://beekn.net/2013/10/ibeacons-can-my-ios-app-find-beacons-that-arent-mine/ , in order to listen to an iBeacon, you must know its proximityUUID first.
Unfortunately Apple decides to limited your ability to do that. CoreBluetooth framework can detect iBeacon nearby but since it only returns the device UUID, not the proximityUUID, you cannot add callback to them.
And Android can read the proximityUUID of any iBeacon without any problem.
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.
I order to test an iBeacon app for iOS, I have configured a RaspberryPi with four BLE usb dongles to simulate real iBeacons.
It happens that all iBeacons are detected but not simultaneously. The beacons array passed to locationManager:didRangeBeacons:inRegion: method contains no more than 2 iBeacons and it changes content almost at every invocation.
How should I interpret the documentation for locationManager:didRangeBeacons:inRegion: method?
beacons:
An array of CLBeacon objects representing the beacons currently in range. You can use the information in these objects to determine the range of each beacon and its identifying information.
Discussion:
The location manager calls this method whenever a beacon comes within range or goes out of range. The location manager also calls this method when the range of the beacon changes; for example, when the beacon gets closer.*
Seems to me that all beacons in range should be in the beacons array. If not, how the beacons that become out of range can be determined? Or may be this is a problem due to the fake beacons?
UPDATE
I added another BLE usb dongle and now the device detects three beacons every time. Seems to me that the device has no memory of the beacons detected in previous scanning windows. My understanding of this behaviour is illustrated in this picture.
At end the device detects all beacons probably because there is some random delay in the scanning period (as illustrated in the picture) and/or some other delay in beacon transmission.
Understand that that method is invoked once per second for each region you are ranging. Each invocation will only include the beacons visible in that one second period in that same region. Note that the region object is passed in the method as well.
EDIT: Further investigation revealed that the questioner was using a Raspberry Pi iBeacon transmitter that is probably only sending out an advertisement every 1280ms, which will definitely cause the problem reported. The solution is to increase the advertising frequency so that at least one packet goes out every second (ideally at a rate of 10Hz or higher for the best distance estimates.)
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.