OUR GOAL WITH ESTIMOTE BEACONS : We are planning to setup Estimote beacons in golf course. our scenario is whenever player reaches the hole and what is the pace of play, It should be detected by the beacon & respond to the server through our IPhone application.
In this case user doesn't need to open the app, because mobile is in his pocket & he is playing.
WHAT WE HAVE DONE SO FAR:
Ranging only works when the app is in only active state. (GOAL NOT ACHIEVED).
To achieve this goal we are using Monitoring, but the problem is monitoring delegate (didDetermineState state: for region:) some times called some times not (called: instantly, someDelay & never). In other words beacons don't always detect by iOS by monitoring. (GOAL NOT ACHIEVED).
If we are testing on two or more iPhones 6s with same iOS versions 10, each phone have different results some detects & others not. For testing we used flip to sleep & setup beacons in different locations to be enter/exit events fired & lowest advertising interval.
Implemented edystone on estimote beacons, they are not working when application is in background. (GOAL NOT ACHIEVED).
We tried following solutions found on internet or forums of estimote beacons.
Its Nov 2013 article Ibeacon monitoring but in Dec 2016 it seems no difference.
We also tried for background ranging through CoreLocationManager.startUpdatingLocation:
Here and Here
We tried to speak Estimote but they answer was ambigious, "We're painfully aware that iBeacon monitoring can be a bit finicky at times. We hate it as much as developers trying to build beacon-powered apps do, but when it comes to iBeacon, we're quite helpless, because Apple has the API on lockdown—there's no way to detect iBeacon packets on iOS other than through the built-in API, which exhibits these problems."
It seems like some how it is possible.
This is how i am registering array of beacons currently we have 3 to 6 beacons.
func loadBeacons() { // Load beacons
self.beacons = getAllbeacons()
self.beaconManager = ESTBeaconManager()
self.beaconManager.delegate = self
self.beaconManager.requestAlwaysAuthorization()
if self.beaconManager.isAuthorizedForMonitoring() == true {
self.rangingBeaconsSetup()
} else {
self.beaconManager.requestAlwaysAuthorization()
}
}
func rangingBeaconsSetup() { // SET UP Ranging beacons
for beacon in self.beacons {
if let beaconRegion = self.beaconRegionFromItem(beacon) {
beaconRegion.notifyEntryStateOnDisplay = true
self.beaconManager.startMonitoring(for: beaconRegion)
self.beaconManager.startRangingBeacons(in: beaconRegion)
}
}
}
func beaconRegionFrom(_ beacon: Beacon) -> CLBeaconRegion? { // GET VALID REGION
let val = 1 << 16
if let uuid = NSUUID(uuidString: beacon.uuid), beacon.major < val && beacon.minor < val {
return CLBeaconRegion(proximityUUID: uuid as UUID, major: CLBeaconMajorValue(beacon.major), minor: CLBeaconMinorValue(beacon.minor), identifier: beacon.deviceName)
}
return nil
}
func beaconManager(_ manager: Any, didDetermineState state: CLRegionState, for region: CLBeaconRegion) { // Monitoring delegate.
if state == .inside {
let notification = UILocalNotification()
notification.alertBody = "By tapping you will be able to check-in"
notification.alertAction = "OK"
notification.fireDate = Date()
application.scheduleLocalNotification(notification)
}
}
These are following responses we receive when we have same IOS devices (OS 10) both have inconsistant behaviour.
RELATED QUESTIONS :
Is our expectation for beacon accuracy is quite high?
If a beacon is broadcasting the data to all the phones then every device must behave same because we have same IOS version, same Iphone and same code.
How can we get consistant results
Our experience is, Detection varies "from few seconds to 15 minutes and some time it does not detect at all"
What can we do to get consistant and reliable results?
From your problem description, the core problem you have is inconsistent behavior in background monitoring callbacks.
In theory, you should be able to to get a monitoring callback that wakes up your app in the background and lets it range in the background for 10 seconds (extendable to 3 minutes if you use this technique) each time a beacon is first detected in a monitored region or each time all beacons stop being detected in a monitored region. This will fire a didEnterRegion or didExitRegion callback.
As you have seen, sometimes these callbacks do not come when expected. There are two primary reasons for this:
You don't get a entry or exit callback because the region state has not changed. This often happens when iOS believes it was always inside a CLBeacon region, whereas an app tester briefly removed the iOS device from the vicinity of the beacon (either by moving the phone or turning the beacon off) and then returning it to the region. The failure to get an exit/entry sequence in this case is often caused by not giving iOS enough time to detect it has exited the region. In the background, this can take up to 15 minutes. Most of the time this is purely a testing problem and not a problem your actual users will face -- testers are under time pressure to get their tests done, so they often just don't wait long enough in thse test cases. Adding logging, notifications or other insight for when region exits occur can help your testers make sure they wait long enough.
You don't get an entry callback quickly because all hardware acceleration slots are filled. In order to detect becacons and send didEnterRegion callbacks within a second or two, iOS relies on Bluetooth hardware filters to wake up the OS when a beacon of interest is detected. The problem is that these hardware filters are a scarce resource, and if they are exhausted by other apps that were installed on the phone first, then your app won't get access to them, meaning that detection times will fall back to software scans that can take up to 15 minutes. There is no way to know wheter your app has been granted access to these hardware filters, and even uninstalling and re-installing an app on your phone can change whether this is true, leading to inconsistent resuls. The number of available filters is undocumented, but some evidence suggests the number is 30, meaning only the first 30 CLBeaconRegions monitored on a phone get priority access.
In order to solve problem #2 in your tests, uninstall any other apps that you think may be monitoring for beacons, then reinstall your app. You should then get more consistent results.
Of course, you can't make real users uninstall other beacon apps, so they may still face these problems. But the good news is that most average users won't have a lot of beacon apps on their phones, so this is far less likely to happen to real users than it is to deveolopers or testers who often have lots of beacon apps on their phones.
Related
My goal is to find all iBeacons nearby an I plan to send local notification when new ibeacon is found. I am assuming all of them will have the same proximity UUID. And I will use combo of major and minor ids to identify them (each user will have unique combination of major & minor id).
I am assuming I need to monitor for this specific UUID and that locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) will be triggered only for the first UUID right? So when it happens I need to startRanging to find every beacon nearby except the one Ive found. What will be achieved by callback from locationManager(_ manager: CLLocationManager, didRangeBeacons beacons: [CLBeacon], in region: CLBeaconRegion). But the question is what will happen if new beacons will appear nearby? I need to fire startRangin also on didExitRegion? I am only afraid that if the one already found beacon will stay in range for like 1 hour, I won't be able to detect any other beacons which will appear in this region/range.
Any advice for most efficent search?
Using a backgrounded iOS app to track multiple BLE beacons with the same UUID and differing major and minor values is challenging. (Ranging normally times out after 10 seconds in the background.) Two basic approaches:
Solution #1: Constant Ranging While in Region
For this approach, you need to set up your app to unlock unlimited ranging in the background while you are inside the beacon region. This is a bit tricky, and will use significant battery if you are around beacons for long periods of time. But is legal to do for App Store distribution provided that your app makes it clear that it uses location in the background for an approved purpose.
I documented this in a blog post here: http://www.davidgyoungtech.com/2023/02/10/forever-ranging
The basic steps are:
Setup:
Put the following declaration in your Info.plist
<key>UIBackgroundModes</key>
<array>
<string>location</string>
</array>
Obtain NSLocationAlways permission from the user.
Start monitoring for a CLBeaconRegion with the ProximityUUID and a nil major and nil minor.
Start ranging for the same region. There is no real reason to turn ranging off as logic below will handle throttling whether the OS allows it to do work in the background.
In your didRange callback, add logic to process all the detected beacons.
On Region Entry:
Start location updates at the lowest power setting (basically just using cell radio) with:
locationManager.pausesLocationUpdatesAutomatically = false
locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers
locationManager.distanceFilter = 3000.0
if #available(iOS 9.0, *) {
locationManager.allowsBackgroundLocationUpdates = true
} else {
// not needed on earlier versions
}
// start updating location at beginning just to give us unlimited background running time
self.locationManager.startUpdatingLocation()
Start a background task as described in my blog post here: https://developer.radiusnetworks.com/2014/11/13/extending-background-ranging-on-ios
On Region Exit:
Stop the background task from the previous step
Stop the location updates with self.locationManager.stopUpdatingLocation(
The two changes above will effectively make it so you no longer get ranging updates in the background when no beacons are visible, which will save battery.
Solution #2: Use One Extra Region Per Beacon
This solution is not foolproof, but has the advantage of not requiring a constant background task and background updates. Depending on the placement of your beacons and how users move between them, you will not guarantee detecting each one. But it works for many cases where beacons are not typically placed close together.
Obtain NSLocationAlways permission from the user.
Start monitoring for a CLBeaconRegion with the ProximityUUID and a nil major and nil minor.
Start ranging for the same region. There is no real reason to turn ranging off as logic below will handle throttling whether the OS allows it to do work in the background.
In your didRange callback, add logic to process all the detected beacons.
Each time you detect a new beacon (with a different major and minor) do the following:
Start monitoring a new CLBeaconRegion with the UUID, major and minor of the known beacon. This will allow you to get a didExit callback when it disappears. Because iOS only allows you to monitor 20 regions, you can only do this for 19 additional regions at the same time.
On Region Exit for a CLBeaconRegion for a specific major and minor:
Stop monitoring that region.
The advantage of this approach is that if you have a large number of beacons in overlapping transmitter range, you will get an additional didExit callback as you go out of range of each one. Each time there is a region transition (entry or exit) you get another 10 seconds of background ranging time. This allows you an opportunity to look for new beacons in the area periodically.
This second solution is not perfect. If you encounter beacon A, then it stays in range for 20 seconds before you encounter beacon B, you won't get a ranging callback for beacon B because ranging times out after 10 seconds. In this scenario, you might detect beacon B later on if beacon A goes out of range before beacon B does (the region exit from beacon A gives you more ranging time), but if beacon A does not go out of range before beacon B, then you will never detect beacon B.
Which Solution Should You Use?
Use solution #1 if you must have perfect detections, you app obviously provides a location-specific benefit to the user, and you are OK with the battery drain of constant ranging while beacons are around.
Use solution #2 if you don't need perfect detections, if beacon placement is tolerant of the technique's shortcomings, or if you can't live with the battery drain or constant background ranging for other reasons.
You will initially be monitoring for a CLBeaconRegion that specifies only the beacon uuid you are interested in.
When any beacon advertising this uuid is detected you will get a call to didEnterRegion.
At this point you should call startRangingBeacons(satisfying constraint:) where your CLBeaconIdentityConstraint specifies only the uuid.
You will then get calls to the locationManager(_:didRange:satisfying:) delegate method.
You can examine the minor and major values of the CLBeacon objects passed to the delegate function to determine the beacons that are in range and their range. As beacons satisfying the constraint come into range they will be delivered to the delegate, even if they weren't originally in range when the region was entered.
At some point there will be no beacons with the specified uuid and you will get a call to didExitRegion. At this point you should stop beacon ranging.
When I tried to create iBeacon and then detect the beacon for some region range notifier does't notify the bacon for very small interval of time after again it starts appearing but there is drop for second and beacon gets disappear and again is start to appear.
I expect when an beacon is emitting then Range notifier should always return the beacon but sometimes it goes to 0 count and then again it returns the beacon.
**val beacon = Beacon.Builder()
.setId1("f7826da6-4fa2-4e98-8024-bc5b71e0893e")
.setId2("1")
.setId3("2")
.setManufacturer(0x004c)
.setTxPower("-59")
.build()**
**beaconTransmitter = BeaconTransmitter (this, BeaconParser()
.setBeaconLayout ("m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24");
beaconTransmitter?.startAdvertising(beacon)
mBeaconManager = BeaconManager.getInstanceForApplication(applicationContext)
mBeaconManager?.beaconParsers?.add(BeaconParser().setBeaconLayout("m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24"))**
**private val mRangeNotifier = RangeNotifier { beacons, region ->
//Here I receive zero beacon some time, mostly it works but form very small interval of time beacon count is zero
}**
There should be proper detection of beacon so no inconsistency occurs, at least I could always track the beacons.
I have tested it on -
One plus 5 with Android version 9
One plus 6 Plus with Android version 9
Poco F1 with Android version 8
Library version - 2.16.4
There are lots of variables here where there could be a problem:
The code shown (or supporting code not shown in the question.)
Hardware issues with the transmitting phone.
Hardware issues with the receiving phone.
Because you are working with three different phones, the combinations of possible sources of trouble grow with the combinations of phones.
In order to track this down, you must simplify your test cases:
Use a reference beacon transmitter for your tests that is most reliable. Choose a hardware beacon (best) or laptop-based beacon transmitter program (better) if you have one available. If you have no other choice, pick one of the phones to be the transmitter, and choose the one you suspect is most reliable.
Use an off-the-shelf beacon detector program based on this same library such as Beacon Scope, and test detections of the beacon. Using an off the shelf program will eliminate any coding errors in your detection software.
Test the off the shelf detection on each of your phones. This will tell you which, if any, have hardware trouble if they fail to detect reliably.
Once you have a reliable transmitter and know which phones are reliable detectors, go back to your program and work to make it as reliable as what you see with Beacon Scope using the most reliable transmitter.
I have an app where I'm scanning for beacons ( I know the UIIDs) and displaying them. The first few times I ran it I found the beacons almost immediately. But now each time I run it, it seems to take longer and longer to detect them. Even though I have the beacons sitting on the desk right next to the device.
I've tried replacing the batteries, changing from a iPhone to an iPad, checking a beacon scanning app on the Mac, rebuilding the app, etc. I also have a range of different beacons from different manufacturers and they all seem to have the same issue. Nothing seems to fix this.
My code basically looks like this:
let locationManager = CLLocationManager()
locationManager.delegate = self
let region = CLBeaconRegion(proximityUUID: UUID(uuidString:"61687109-905F-4436-91F8-E602F514C96D")!, identifier: "BlueCat beacon")
locationManager.startMonitoring(for: region)
And then I can wait for anything up to 5 or more minutes before any of the delegate methods get call.
This is the first time I've attempted to use beacons. Is this normal? Can it take a long time for a beacon to be seen by a device?
Under ideal conditions, didEnterRegion callbacks on iOS come within a few seconds of a beacon coming into range. Under less ideal conditions, these callbacks can take 15 minutes or more.
There are lots of things that can cause delays in detecting beacons when monitoring, and it's hard to say for sure without knowing exactly how you are testing. Two tips:
Hardware filter slots on iOS devices are limited and are on a first-come, first-served basis. If you have a bunch of beacon apps installed that have taken up these slots (or if your app has registered a bunch of regions), all these slots may have been taken up. Once hardware filter slots are gone, detections fall back to software scans that happen about every 15 minutes. Uninstall all apps (including yours) that may be holding on to these slots, then reinstall just your app.
Make sure you aren't "in region" when you put the app to the background. If iOS believes it is in the region, you won't get a new didEnterRegion callback until after it realizes it left the region and then entered again. The time for iOS to realize it exited the region is much longer than the time it takes to realize it entered. To ensure it realizes it exited the region, you should always range for beacons when the app is in the foreground, and leave the app ranging in the foreground for about a minute to ensure iOS realizes it is no longer in region (it usually takes 30 seconds of ranging to trigger an exit.) Only then should you put it in the background and turn on a beacon to measure detection times.
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.
I have written a small iBeacon demo app that ranges for beacons and then will monitor for beacons in range after they have been found for the first time. After I run this app (if it's still in the background), I find that the device has trouble making new Bluetooth connections. When I enter my car, for instance, my car infinitely tries to connect to the phone with no success until I turn Bluetooth off and on again. This seems like an issue of keeping Bluetooth active after leaving the area of the iBeacons, but I have stopped ranging for beacons on didExitRegion. I have even attempted stopping ranging for beacons when the view disappears or when the app is closed. Is there some other step to take in order to properly close connections when leaving a range?
Edit: I only say ranging in the OP, but I have since also made sure to stop monitoring for beacon regions when the app goes inactive and when exiting regions. The problem persists
Edit 2: If I must say specific behavior expected, I expect to be able to range and monitor for beacons while the app is running and while it is in the background without stopping up other Bluetooth connections. The actual behavior is that it does keep track of beacons in these cases, but it doesn't allow other bluetooth connections to be formed.
Code to initialize and start monitoring for beacons (in viewDidLoad):
if (self.beaconManager == nil) {
self.beaconManager = [[ESTBeaconManager alloc] init];
self.beaconManager.delegate = self;
}
if (self.icyMarshmallowRegion == nil) {
self.icyMarshmallowRegion = [[ESTBeaconRegion alloc] initWithProximityUUID:ESTIMOTE_PROXIMITY_UUID major:[kIcyMarshmallowMajor integerValue] minor:[kIcyMarshmallowMinor integerValue] identifier:kIcyMarshmallowRegionID];
self.icyMarshmallowRegion.notifyOnEntry = YES;
self.icyMarshmallowRegion.notifyEntryStateOnDisplay = YES;
}
[self.beaconManager startMonitoringForRegion:self.icyMarshmallowRegion];
Code to stop monitoring (in didExitRegion):
[self.beaconManager stopMonitoringForRegion:self.icyMarshmallowRegion];
There is no more step to "close the Bluetooth activity", and anyway your device should be able to take other connections while monitoring for iBeacon regions (I'm not sure for ranging cause it's more an 'active' task, but I bet it's the same thing)
I've seen a lot of strange behaviour related to iBeacon in iOS7, part of them are fixed in iOS8, hopefully everything will be soon ok. I think you shouldn't worry about your code for the problem you describe, and wait for iOS8