Minimise battery consumption on Beacon Ranging in iOS while using iBeacon - ios

I'm working on a use case that needs continuous callbacks while scanning the beacons.
I've thought of 2 approaches but they both have issues.
Monitoring: Monitoring only gives entry and exit callbacks. There's a limitation of listening to only 20 beacons. Also is there a range that i can define to get entry and exit callbacks? Like say if a an advertising beacon comes in range of 2 meters i get an entry callback and if the device moves out of that range i get an exit callback.
Ranging: Ranging gives continuous callbacks along with a set of other parameters like rssi to calculate distance. The big issue when it comes to ranging though is that it consumes insane amount of battery compared to monitoring. What should be an approach for getting continuous callbacks while optimising battery consumption?
I've tried both the approaches and reached to a dead-end. Hence it may seem like a theoretical question yet any insights to solve the use-case in some manner.

It is not possible to set any kind of rssi or distance filter when using iOS CoreLocation beacon monitoring APIs. Ranging is the only alternative when working with iBeacon.
While battery drain of constant BLE scanning is an issue, you an mitigate this by scanning on a lower duty cycle as needed to meet your requirements. For example, you can scan on a 20% duty cycle (and use 20% as much battery as constant ranging) by ranging for 12 seconds every minute. You can adjust this duty cycle as needed to balance your goals between battery savings and responsiveness. I have worked on projects where I change this duty cycle depending on app state, so the app can be more responsive to beacons when it is important and save battery when responsiveness is less important.
In order to be able to do this at all, you must unlock the ability to have iOS let your app run in the background for an unlimited time period as described in my blog post here:
Add the location background mode to Info.plist
Obtain "always" location permission from the user. It is not sufficient to get "when in use" permission.
Start a background task as described here: http://www.davidgyoungtech.com/2014/11/13/extending-background-ranging-on-ios
Request 3km location updates from CoreLocation. This will keep the app running in the background without extra battery drain from GPS, as 3km accuracy only uses the cell radio You don't need to do anything with these results. You just need to request them to keep the app alive.
Once you do the above, you can call locationManager.startRangingBeacons(...) and locationManager.stopRangingBeacons(...) on a timer to implement whatever duty cycle you want.

Related

Caveats for background iBeacon Region Ranging

Apple explicitly discourages background iBeacon Ranging:
To promote consistent results in your app, use beacon ranging only while your app is in the foreground.
If your app is in the foreground, it is likely that the device is in the user’s hand and that the device’s view to the target beacon has fewer obstructions.
Running in the foreground also promotes better battery life by processing incoming beacon signals only while the user is actively using the device.
Should I choose to go naughty and do some ranging in the background (after entering a beacon range when monitoring), what consequences should I expect? (except for the famous 10-seconds running window before being put to sleep again?
In most cases, there is no reason to worry about battery drain from casual background ranging.
I've read that statement before, but I don't think it makes any sense, and suspect it was written before the CoreLocation iBeacon design was complete. (The statement has been there since the iOS 7 beta.)
Apple iOS generally enforces that you can only range in the background for 10 seconds at a time, typically triggered by a CLBeaconRegion monitoring entry/exit event. Unless you app is in an extreme situation where it is seeing beacon regions enter and exit all the time, 10 seconds of background bluetooth scanning just can't use that much battery.
Of course, there are techniques where you could range more often, such as requesting location background mode for constant ranging, or requesting an extra 3 minutes of ranging time as described in my blog post here. If you use one of these techniques, you should probably test the impact of your app on battery. But I certainly wouldn't describe doing these things as being "naughty" as long as you do so responsibly.

How can I get major and minor numbers in one UUID on iOS background monitoring status?

We are using one UUID, and major and minor combinations for different actions.
And we need to know major and minor numbers in iOS background monitoring.
The ranging can get major and minor numbers but this needs launching delay and battery consuming. So this is not the proper solution for us because we want immediate detecting and low battery consuming.
So we want to get major and minor numbers in same UUID on iOS background monitoring status.
This mechanism is necessary because we make an iOS application not for typical usage.
Is it possible?
You cannot read individual beacon identifiers using monitoring APIs. All you can do is access the CLBeaconRegion identifiers used to start monitoring. In your case, this is probably just the ProximityUUID with a nil major and minor.
The alternative is to combine ranging with background monitoring. Whenever you get a didEnterRegion event, you will also get ranging callbacks for about 10 seconds afterward, even if your app is in the background. You can use this callback to read all the identifiers.
While it is true that ranging in the foreground uses much more battery than monitoring, background ranging is actually pretty battery friendly. Consider that you will be only ranging for 10 seconds each time you enter or exit a region. (Even though ranging is still turned on, the OS automatically stops it after 10 secs in the background.) Unless you expect user to be constantly entering/exiting regions, then battery should not be a concern with such short periods of background ranging.

IOS: iBeacon scanning frequency when phone is locked?

If an iBeacon starts emitting (100ms), how long should it takes before a locked iPhone detects it? Let's say the iPhone is very close to the emitting iBeacon but my app is not even running in the background.
In other words, what is the LBE scanning frequency when an iPhone is locked?
We have done a lot of research on this subject at Radius Networks. The answers are complicated, but the executive summary is that their are two tiers of detection:
Hardware Accelerated: The first beacon apps to register the first 30 beacon regions on the phone get hardware accelerated detection. Thisey get background detection within 5 seconds about 90% of the time.
Software Scanned: Subsequently registered beacon regions get detections as software allows. If no other apps are ranging, this is on a fixed 15 minute interval. Statistics say that the average detection time would be 7.5 minutes for these regions. The maximum time is 15 minutes.
Unfortunately, there is no known way to tell which tier your app will get. Plan accordingly.
Read more here:
http://developer.radiusnetworks.com/2015/04/21/max-beacon-regions-ios.html
http://developer.radiusnetworks.com/2014/03/12/ios7-1-background-detection-times.html
The documentation doesn't seem to be really specific about the scanning times, and I feel like most of the information one can find is going to be anecdotal.
From my personal research and experience, beacon scanning when the app is backgrounded or inactive is variable on the iPhone's battery level and various other factors. For the most part, in most cases, I've determined that region detection should occur within a few seconds.
With that said, I've noticed that it happens a little faster when the phone is in use, taken out of the pocket, etc. This could be due to the fact that as the phone is moving, scanning is increased to bring the user up to speed and prepare apps for use.

iBeacon case - trigger didEnterRegion with distance constraints

For a specific business case, I would like to trigger proximity-based notification (push or UILocalNotification) by leveraging the iBeacon technology.
This is aimed to work while the app is in background/lockscreen.
I have some specific constraints :
The product has to scale at a certain level so it's not possible to range 20 regions. We will range on one UUID only (maybe 2 or 3 if we develop new set of features, but we will not register a region per physical beacon)
We will use the major and minor to call webservices
Regarding this, I know I will have to use the following approach : first didEnterRegion: with no prior information on the major/minor/distance, and then didRangeBeacon: to perform more advanced actions.
I already use local storage mechanisms to timeout a beacon after he has been used considering the fast beaming rate of beacons.
* The major constraints : we have to region events regarding to the distance of the beacon (exemple, only trigger the notification if CLProximityImmediate)
Now I see a major limitation. As my app will only range in background for a few seconds after it entered in a region, if I had constraints based on the distance/proximity, it is very possible that the app will:
return to background state before the user get close enough from the physical beacon for the event to be triggered
never be triggered again because it has to enter the region to range again, which will probably not happen
Do you have any ideas / work around for such a case ?
You are absolutely correct on the limitations CoreLocation applies in this use case. If you set it up as you describe, the typical behavior will be that the app will detect the beacon in the background at an unknown distance (often the max range of around 50 meters), range in the background for about 5 secs, then the app will be suspended by iOS. That five seconds of ranging time will typically not be enough for the user to get near enough to the beacon to trigger your use case.
Unfortunately, there are no easy workarounds with standard beacons and CoreLocation. CLBeaconRegion objects do not have a distance field like CLCircularRegion does for geofences.
Two more extreme approaches you might try:
Turn off monitoring of your one region as soon as you detect the device is too far away, then re-enable it right as your app suspends itself in the applicationWillResignActive: callback. You might get a new entry event and more background ranging time.
Use nonstandard beacons that periodically stop their transmissions to trigger forced exit/entry events.

iBeacon region monitoring AND proximity for >20 beacons?

I have been working on a prototype iOS app utilizing iBeacons to provide location-relevant information to office employees depending on where in the office they are. The ideal use case is that whenever an employee enters or exits their office, a callback is fired which provides them some information in the form of a notification (it might make a server query to get information first, etc - that sort of thing). We also want to be able to do this when the app is backgrounded or terminated; fortunately, we already know that beacon region boundary crossings trigger the appropriate CoreLocation callbacks even if the app is backgrounded or suspended.
From looking around, I understand that broadly, I have two options for how to approach the beacon region monitoring:
Give each iBeacon its own CLBeaconRegion, and monitor for each of these regions independently.
Monitor for CLBeaconRegions that correspond to multiple iBeacons - for example, each iBeacon has the same UUID and only monitor for a CLBeaconRegion corresponding to that UUID - then try to determine which beacon triggered the boundary crossing using ranging.
Thus far, I had chosen option #1. The advantage of this approach is that I get didEnterRegion: and didExitRegion: calls for each individual beacon and immediately know which beacon I have entered/exited. Also, I only get one enter call and one exit call, which is exactly what I want. Unfortunately, I just realized that this approach also limits me to 20 beacons (since each beacon gets its own region).
I'm not as familiar with the exact implementation details of #2, so correct me if I'm wrong. But it seems that this approach has more drawbacks:
Apple discourages ranging when the app is in the background because the results may not be as accurate.
The ranging calls fire once every second, while I only want to have "enter/exit" callbacks.
If the beacons have region overlap, the ranging calls might continually flip which one is "closest", which would further complicate things.
Basically, I'm wondering if there is a way to utilize option #2, but still have the benefits of option #1 - a quick and easy way to immediately determine which beacon triggered the region change with only one enter or exit callback?
I hope this question is clear enough. It's not all entirely clear in my own head, especially how ranging works.
Option #2 is absolutely more complicated, but you must accept these complications in order to get around the 20 region monitoring limit.
A few points:
In the background, you only have around 5 seconds of ranging time, which does not give you as much time to average RSSI (signal strength) from each beacon to get a good distance estimate. So, yes, the estimates will be less accurate. If you understand this limitation and can live with it for your use case, there is nothing wrong with ranging in the background.
Yes, you will get multiple ranging calls per beacon after region entry, and you won't get any callbacks on region exit. You have to write extra code to take care of this. I have done this by maintaining a NSMutableArray of all the unique beacons (same uuid/major/minor) seen and update it in the ranging callback. You can then access this array in the region exit callback, so you know which beacons disappeared. Of course, it is possible that additional beacons were seen after the 5 seconds of background ranging time expires, but your app will never know about them. With this option, you must accept this limitation.
While it is true that errors on the distance estimate in ranging may incorrectly tell you which beacon is closest, you have an even worse problem when doing monitoring, because you don't get a distance estimate at all. If multiple beacons come into monitoring range around the same time, there is no guarantee that the first entered region callback you get will be for the closest beacon. So if your use case requires taking action based on the closest beacon, then you must do ranging (knowing that there may be error on the distance estimate.)
The drawback of the second approach is detecting the entry of a particular beacon will be purely based on ranging, that will not work if the application is killed. The reason is we will get didEnterRegion only once, because we are monitoring only one region with a particular UID. The next beacon with same UID will not be detected again if the application is terminated or if the background ranging stopped.
I recommend a combination of the mentioned approaches ,
Use same UID for all the beacons.
A beacon is uniquely identified using major/minor value that is collected when ranging.
As mentioned in apple doc, always keep number of monitoring regions below 20 by removing and adding beacons when the user moves from beacon to beacon (better to keep a beacon neighbour relationship graph in the server.)
Start ranging when entering the region ... and identify major/minor and calculate proximity.
Stop ranging when exiting the region.
Find the closest beacon from ranging method (need to skip unknown range beacons).
Monitor only the neighbours of the closest beacon in a given time.
When implementing both options, We should consider one fact, An iBeacon will be detected in 200feet distance. There may be multiple beacons in 200feet range.
If you use the same UUID for every beacon, you can just set the major/minor numbers to differentiate between the different beacons. This way, you are only monitoring for 1 beacon instead of > 20. Then just sort out which one is which from the other identifiers. This is how it works currently with Starbucks and other retailer apps. 1 beacon no matter where you are in the world, and different identifiers to sort things out on the back end.

Resources