2

Here is my challenge. I have a gimbal series 20 configured as an iBeacon and my goal is to allow my users to hold their phone in near physical contact with the beacon with the app backgrounded. Think apple pay.

I have the transmit power set all the way down to -23 but it's still too strong and the beacon is triggering the DidEnterRegion delegate meathod up to a foot away. Using ranging I can further threshold by measuring the rssi directly. This solution is working perfectly when the app is running. My issue is that this solution is not reliable running in the background as it only ranges for a few seconds after an enterRegion event and then stops.

Is there a way to further dampen the signal strength of the beacon or other way to cause the locationManger:DidEnterRegion: to trigger against a lower rssi reading?

nwales
  • 3,521
  • 2
  • 25
  • 47

3 Answers3

3

Once you have enabled beacon ranging in the background (see below), you check for beacons with proximity Immediate in your implementation of the delegate function locationManager(_:didRangeBeacons:inRegion).

func locationManager(manager: CLLocationManager, didRangeBeacons beacons: [CLBeacon], inRegion region: CLBeaconRegion) {
    let immediateBeacons = beacons.filter() { $0.proximity == .Immediate }
    if !immediateBeacons.isEmpty {
        let tappedBeacon = immediateBeacons.first!
        // Your code for processing tappedBeacon ...
    }
}

Beacons have Immediate proximity if the phone is less than 30cm away (at default transmission power). If you reduce the transmission power even more, you can get it down to 5-10cm. tappedBeacon in the code above is the beacon that you tapped with your phone or that you are nearly touching with your phone.

That was the easy part. Keeping beacon ranging alive in the background is a bit trickier.

First, you must enable "Location updates" under "Background Modes" in the "Capabilities" section of your project settings. This results in the following entry in the Info.plist file of your project:

<key>UIBackgroundModes</key>
<array>
    <string>location</string>
    <string>audio</string>
</array>

Second, you need to initialise an CLLocationManager object in the right way:

func initLocating() {
    if CLLocationManager.isRangingAvailable() {
        self.locationManager.requestWhenInUseAuthorization()
        self.locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers
        self.locationManager.allowsBackgroundLocationUpdates = true
        self.locationManager.delegate = self
    }
}

This function is typically called from the init() function of your class deriving from CLLocationManagerDelegate. If your device supports ranging at all, this function requests authorisation for location updates. Then, it sets the desiredAccuracy to kCLLocationAccuracyThreeKilometers, which switches off any location methods other than cell tower triangulation. This is not required for background updates but it makes the battery of your phone live longer.

For background ranging, it is essential to allow background updates by allowsBackgroundLocationUpdates = true. The final line of the function simply makes this class the CLLocationManagerDelegate, which triggers the function locationManager(_:didRangeBeacons:inRegion) whenever there is a change in the visible beacons or their attributes.

Third, you must start location updates whenever you need them. This could be for the whole lifetime of your app or triggered by a certain condition.

func startLocating() {
    self.locationManager.startUpdatingLocation()
    self.locationManager.startRangingBeaconsInRegion(CLBeaconRegion(proximityUUID: self.proximityID, identifier: "AllBeacons"))
}

The first call starts location updates in general. Without this call, beacon ranging in the background will stop eventually. The second call starts beacon ranging specifically for the beacon region you are interested in. It triggers the execution of locationManager(_:didRangeBeacons:inRegion).

If you can stop ranging beacons for some time, you can use the following function. It is just the inverse of startLocating().

func stopLocating() {
    self.locationManager.stopRangingBeaconsInRegion(CLBeaconRegion(proximityUUID: self.proximityID, identifier: "AllBeacons"))
    self.locationManager.stopUpdatingLocation()
}

Stopping and starting beacon ranging only when needed is a measure to increase the battery life of your phone.

I have described beacon ranging in the background in detail in a recent post. The above is a shortened version of my post.

Burkhard
  • 169
  • 1
  • 3
  • Great answer! I would probably add a mention though that using Background Modes this way will most likely not fly well with the App Store reviewers. "Audio" background mode is not necessary here (and is reserved for audio-playback apps), and "Location" background mode reviewers say is restricted to turn-by-turn navigation apps. – heypiotr Jan 15 '16 at 14:16
1

A couple of tips:

  1. Instead of reducing the transmit power on your beacon, turn it to the maximum you can for acceptable battery life. The reason to do this is because you'll get more consistent RSSI readings using your technique of reading the RSSI.

  2. You can extend background ranging up to 3 minutes using the technique in my blog post here. Then, after region entry, you can collect RSSI samples via ranging for 10 seconds or so, and if the average is strong enough, trigger your logic.

Be careful to test with a number of devices (iPhone 4S/5/6, iPod, iPad variants) because each has a slightly different Bluetooth antenna gain and you'll need to tune your threshold to make it work on all devices where your app is compatible.

davidgyoung
  • 63,876
  • 14
  • 121
  • 204
1

This might not be the answer to your question, but take it as a suggestion. Apple recommends to user Ranging only when app is in Foreground. Use Monitoring while your app is in the Background.

Apple: Determining the Proximity of a Beacon Using 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.

I am working with iBeacon since few months. Estimote is a leading manufacturer of beacons. You might like to refer to Estimote's blog Is it possible to use beacon ranging in the background?

Just trying to help. Sorry if i did not answer your question.

Om Patel
  • 151
  • 1
  • 7