The Museum Guide Application
I find the best way to learn about something is to roll up your sleeves and dive right in. Rather than just read about the AltBeacon format and the AltBeacon Android API I decided to write an application that uses it. The application is a simple “museum guide” which informs the user about the nearest exhibit to them as they explore. Beacons are distributed around a fictitious museum and positioned by the most important exhibits so that armed with the application, the museum visitor won’t miss any of the best items on display.
I wanted the application to detect beacons whilst running silently in the background and provide a screen with some information about the exhibit associated with the nearest beacon. This should only happen when the user was sufficiently close to the beacon—2 metres or closer. I would also expect the beacons to be in range and therefore detected from much further away. I also wanted to make sure I didn’t annoy the user by repeatedly notifying them about the same exhibit. Finally, to satisfy my own curiosity, I wanted to be able to see lower level beacon-related events in a system log.
Before we proceed and look at how the application was developed and how the AltBeacon API helped me, here are some screen shots of the finished application.
The first two screen shots are of the main exhibit information screen. The screens show information about an exhibit together with a photograph. The third shot shows the log screen which is accessible via the Android overflow menu. If the application is in foreground when the user needs to be informed about an exhibit they are nearest to, the exhibit information screen is updated. If it’s not running in foreground, a standard Android notification is generated and the user can choose to tap it and launch the application or ignore it. If they opt to launch the application, it opens with the exhibit information screen visible and populated with details of the exhibit associated with the nearest beacon.
Monitoring vs Ranging
The AltBeacon API supports both “Monitoring” and “Ranging”. Monitoring generates events whenever the application finds itself within range of a defined set of beacons known as a “region”. It’s a fairly binary concept—you’re within the region or you’re not. “Ranging” allows you to keep track of your distance from a defined set of beacons. For my purposes, I chose to use Ranging so I could trigger actions when at a certain estimated distance from a beacon.
My application consists of a custom Application class and Activity classes for each of the two screens—the main exhibit information screen and the beacon event logging screen, plus a number of other supporting Java classes. The AltBeacon API itself is a library which takes care of background launching and provides a number of interfaces to support call backs relating to beacon detection. These are implemented by my custom Application class “GyboApplication.java”.
There are a number of things to be done when the Application class is first created. We use the AltBeacon API BeaconManager class to bind our application to the AltBeacon library’s Service which runs in the background.
We also specify one or more “regions” that we’re interested in. A region is a set of one of more beacons, specified using values from the AltBeacon Beacon ID field (see Part 1 of the series) which splits the 20 octet identifier field into a 16 byte primary ID, a two byte secondary ID and a two byte tertiary ID. In this case I’m interested in all beacons with my primary ID and so I was able to leave the secondary and tertiary values as null.
The RangeNotifier interface which the Application class implements has a single method we must code. The method is “didRangeBeaconsInRegion(Collection<Beacon> beacons, Region region).”It gets called once per second and it provides a list of beacons “seen” by the AltBeacon library’s background Bluetooth scanning process in that period. Most of the action happens in this method.
The Beacon class, as you’d expect, represents a physical beacon. It has a series of attributes including an indication of the beacon type, its Bluetooth® MAC address and name, the RSSI (Received Signal Strength Indicator), the calibrated transmitted TX power of the beacon (which was set during the manufacturing process) and an estimate of the distance to the beacon in metres, calculated using the latter two values.
When the application receives a range update, it needs to decide whether or not to take any action. This involves figuring out which beacon is nearest to the user and then deciding whether or not to notify the user about the exhibit with which the beacon is associated. The user should be notified if there’s been a change in the nearest beacon, i.e., the user has moved and the nearest beacon is not the same that was nearest the last time we received a range update. The nearest beacon also needs to be “near enough” to warrant generating a notification. In my application, I set this to 2 metres and I hard coded this value. You may prefer to make such configuration values user definable via a settings screen of course.
The Nearest Beacon
Given a list of Beacon objects, each with an estimate of the user’s distance to the beacon, figuring out which one is the closest should be a very simple task. However, in testing I noticed an issue. Sometimes a beacon I knew was in range was not included in the list of Beacon objects passed to the didRangeBeaconsInRegion method and so was not included in the comparisons I was running to determine which beacon was currently the nearest one. I concluded this was probably due to a mismatch between the frequency with which my test beacons were advertising and the frequency and duration with which my Android application was scanning so there would be occasions where the scanning process would not receive an advertising packet from all beacons in physical range. This raised the question as to how I could control these parameters in my Android application.
The BeaconManager class allows configuration of the length of time the application will spend scanning for advertising packets and how long it should spend resting in between these periods of activity. I added the code shown below to my initialisation code.
Arriving at a scanning configuration that makes sense for a particular application requires thought. Scanning is a relatively expensive operation with respect to battery use so scanning very frequently and for extended periods of time will potentially use more battery power than you’d like. Conversely, if you do not scan often enough then the user may perceive a significant delay in beacons being detected and the user experience may be less than optimal. So there’s a compromise you may need to make and the precise nature of your application will have significant influence on the conclusion you reach. For example, an application which needs to detect beacons in a shop as the user walks briskly past the window will probably need to detect and react to beacon advertising much more quickly than my museum guide application.
You should also consider the beacon’s advertising frequency. If you scan more frequently than your beacons advertise, you may occasionally find that a beacon which is in physical range is not reported in API call backs since no advertising packet was received from it during that most recent scanning activity.
Note that the AltBeacon API documentation has more to say on conserving battery power: http://altbeacon.github.io/android-beacon-library/battery_manager.html
Detecting Beacons in the Real World
It’s worth remembering that we don’t live in a perfect world and that sometimes we need to accommodate real-world issues in our code so that our application behaves in the best possible way for the user. During testing I noticed that despite having set my scanning configuration carefully, I still received occasional calls to didRangeBeaconsInRegion which did not include all beacons I considered to be in physical range. In the real world, this could be caused by physical barriers between the user and the beacon or due to the beacon configuration not being what you think it is. Barriers may cause anomalies in the data reported to the application to exist only very briefly and temporarily, as the user moves around a room. Because of this, reacting instantly to the latest data reported might not the best strategy for a good beacon application. Realising this taught me a valuable lesson—a degree of “fuzziness” in algorithms could perhaps improve beacon application behaviour as opposed to rigidly accepting to the data reported in each call back from the AltBeacon library. To improve my algorithm, I decided to take a slightly more sophisticated approach to tracking beacons in range.
Fuzzy Beacon Tracking
My approach was simple but effective. I decided to maintain a cache of data relating to beacons which had been reported as in range in the last 15 seconds together with the precise time at which each beacon had last been encountered. Using a Timer Task, beacons which had not been encountered during the 15-second period were then removed from the cache as “expired”.
In the all-important didRangeBeaconsInRegion method, I updated the cache of beacon data with the list of Beacon objects received in this call back but then assessed the data in the entire cache to determine the beacon which was currently nearest. This provided better results during testing and can be likened to a data smoothing process. Relevant code fragments appear below.
Notifying the User
Once you implemented code to receive reports of beacons in range, implemented an approach to tracking beacons in the real world, and subsequently used that data to decide which beacon was nearest to the user, all that is left is to notify the user about the museum exhibit associated with the nearest beacon if appropriate. The question of whether or not it is appropriate to notify the user depends on whether or not the last notification we generated related to the same beacon or not. We don’t want to notify the user about the same exhibit repeatedly and in sequence. We also need to check whether or not the nearest beacon is close enough by making a simple comparison between the beacon’s estimated distance from the user and the minimum required distance we’ve set.
If the application is running in foreground, which is easy to establish, then notifying the user involves updating the main exhibit information screen with details of the associated exhibit. I used an in-memory data structure which associated beacons with exhibits using each beacon’s ID as a key and allowed for the possibility of multiple “items of interest” associated with each beacon. I hard coded this data, but as mentioned in Part 1, for anything other than a prototype application it would probably be better to source data mapping beacon’s to locations or items of interest from a remote server.
With the application running in the background, I used Android’s standard notification system to display a notification in the pull down notification list and linked it to the Activity defining the main exhibit information screen.
Here’s the code for the Android notification.
In Part 3 of the series, I’ll explain how I created test beacons for two different developer boards and used them to help test my beacon application.