DEVELOPERS BLOG

A Simple Bluetooth Smart (Low Energy) iBeacon Cordova Plugin

main pic

Image “Beachy Head Lighthouse” by Adam Hickmott / FreeDigitalPhotos.net

Written by:  John Murray

Introduction

In the time that I have been writing about Beacons they seem to have become all-pervasive with new use-cases appearing almost every day. I’ve already discussed iBeacon technology in previous posts such as this one: Where’s My Beacon, so if you need to refresh your understanding of the technology you might like to take a look at that article or the others in the series which I’ve linked in the Resources section of this post.

These earlier articles and associated GitHub samples generally took the Native code approach, so, I thought that it would be a good idea to meld this iBeacons work with the Cordova Plugin approach that I’ve been writing about recently. If you want to refresh your memory about how I’ve been integrating Bluetooth, both “legacy” and “Smart” variants, with Cordova on BlackBerry 10 you might like to take a look at this article: A Simple SPP Plugin, or one of the others I’ve linked in the Resources section below.

The Bluetooth iBeacon JavaScript API

iBeacon is really a de-facto standard that was defined by Apple Inc. using the Manufacturer specific field in the Bluetooth Low Energy Advertising frame. As such there isn’t an official Bluetooth SIG specification covering iBeacon and APIs generally have evolved to cover the main aspects of Beacons:

  • Discovery of Beacons
  • Exposure of the key iBeacon attributed of: UUID, Major Number, Minor Number, Received Signal Strength Indication (RSSI) and a reference transmitted power

iBeacon vendors generally supplement these with their own Bluetooth Smart management APIs that are generally proprietary and different from one vendor to the next. In this plugin I’ll implement only the common aspects of iBeacon discovery and attribute determination – iBeacon management is out of scope for this simple example.

Both the iBeacon Cordova plugin that I’ve written along a simple application that uses it are available as a project on GitHub here:

https://github.com/blackberry/WebWorks-Community-APIs/tree/master/BB10-Cordova/simplebeaconplugin

You’ll find instructions on how to build and use the project here:

https://github.com/blackberry/WebWorks-Community-APIs/blob/master/BB10-Cordova/simplebeaconplugin/README.md

And, lastly, but by no means least, I’ve documented the iBeacon JavaScript API here:

https://github.com/blackberry/WebWorks-Community-APIs/blob/master/BB10-Cordova/simplebeaconplugin/documents/API-Reference.md

I’m not going to go through the details of each element of the JavaScript API since it’s been documented in the link above and you can read all the details there. Rather, I just want to point out one or two features of how it works and was designed.

There are two types of elements in the API:

  • Synchronous functions
  • Asynchronous functions

Synchronous Functions

I use this form when some operation, such as initialising Bluetooth Smart, needs to be performed and I’ll know whether this has been successful or not by the returned value from the call.

code 1

So, the returned value from “initialiseBluetooth” would be one of these:

code 2

Note that, in line with my previous plugins, all return values are of this general pattern: JSON format with common interpretation of attributes such as “status” and “desc”! I’ve chosen to use JSON preferentially since it’s easy to parse both in WebWorks and the plugin itself using the “Json::FastWriter” class and its relatives.

Asynchronous Functions

I use this type of function when some operation needs to be initiated. I’ll be able to tell whether the operation has been successfully started by the return value from the call to the function; and, as the operation itself is completed I’ll receive information back from the plugin using a call-back. A good example of this pattern would be the call, startMonitoring() that starts the plugin listening for inbound heart rate notification events that are delivered via the beaconCallback call-back.

code 3

I’d use it something like this:

code 4

I can test the variable “foo” to check that the request to start monitoring for iBeacons has been initiated correctly; and then, when an inbound iBeacon advertisement is detected by the plugin, the call-back “beaconCallback()” is called by the plugin. The data that is passed back as an argument to beaconCallback() might look something like this:

code 5

This represents a typical iBeacon advertisement event. The “data” attribute matches the data expected from the iBeacon attributes embedded within the Bluetooth Smart Advertisement Frame. This represents an advertisement from an iBeacon with a UUID of “CCBF9400E2C011E48A001681E6B88EC1”,  major and minor numbers of 1 and 1 respectively as 16-bit unsigned integers, an RSSI of -84 dBm and a reference transmitted power (txpower) of -64 dBm.

iBeacons typically advertise every 100 ms but I’ve chosen to throttle how often Bluetooth actually scans for new devices by setting the scan interval to 1000 ms using bt_le_set_scan_params() since it’s easier to see what’s happening in this example by throttling the rate at which events are delivered to the application.

Check out the API reference for details: https://github.com/blackberry/WebWorks-Community-APIs/blob/master/BB10-Cordova/simplebeaconplugin/documents/API-Reference.md .

The Plugin Itself

As with all of the other plugins I’ve written I’ve cheated, in a sense – I have to admit that I didn’t write this plugin from scratch myself. I had help. What I did was to use the excellent Cordova Plugin Template for BlackBerry 10 that you can find on GitHub here:

https://github.com/blackberry/WebWorks-Community-APIs/tree/master/BB10-Cordova/Template

It comes with an Ant script that you can use to customise various aspects of your plugin including its name. What you get, after you’ve done this, is a project that you can import into Momentics and a matching sample WebWorks/Cordova application that you can build. You should take a look at this in GitHub since it comes with a great explanation of how a plugin is architected and organised.

I’m going to assume that you have done this and now have a grasp of how a plugin works. What I’m going to explain now is how I’ve overlaid this model on top of the BlackBerry 10 Bluetooth iBeacon event model.

It’s probably sufficient to look at how one single operation is implemented since the others follow the same pattern. I’ll look at startMonitoring(), which I described in the previous section.

If you take a look at the usage example again you’ll see that I get some immediate status as to the initialisation of the scan for advertisements in the variable “foo” and subsequently the “data” argument of the beaconCallback() function will contain the result of the scan.

You have to provide some JavaScript “glue” first for any function or property that you implement.

code 6

Much of this is boilerplate code (see here for this code in GitHub), setting up the “success” and “fail” call-backs and thee plumbing to handle the synchronous and asynchronous aspects of the function call. The exec() call makes a call to the next level of the “glue” layer that is shown in the image below.

code 7

The actual call into native code is made in the fragment above (see here for this code in GitHub) – again pretty much boilerplate code where a PluginResult() object is obtained and a reference to it saved for later when the asynchronous operation returns. The actual call to native code is made using the object returned from simpleBeaconPlugin.getInstance().

Once you drop into native code you have to call the appropriate method based on the name of the function you’re calling represented as a character string – something like this in the image below (see here for this code in GitHub), which calls the appropriate method on an instance of a class the template has provided for us.

code 8

In essence this method is quite simple – all it does is call bt_le_add_scan_device()which starts the process of scanning for Bluetooth advertisements from any Bluetooth device address (BT_LE_BDADDR_ANY) and return the appropriate JSON to the caller indicating whether the operation was initiated correctly or not. In the snippet below “writer” is an instance of Json::FastWriter — see here for this code in GitHub.

code 9

Thereafter, any matching device advertisement is presented to the plugin through a call-back that had been established earlier and the advertising frame parsed in parseBeaconData()—click here to see the actual code on GitHub.

Parsing the actual iBeacon data in the advertising frame involves some bit-stuffing but in principle it’s quite straightforward: locate the Manufacturer’s field in the frame, ensure that the Manufacturer’s field contains a flag identifying its contents as a iBeacon data, and then extract the iBeacons attributes from the rest of the field.

Once that’s done return the appropriate JSON to the caller with the iBeacon attributes using Json::FastWriter.

code 10

The Sample Application

Finally let’s take a quick look at the sample application that I’ve packaged with the Plugin in GitHub to see how it uses the plugin — click here to see it on GitHub.

The user interface is fairly basic with a couple of buttons to start and stop monitoring form iBeacons, a static area to show the most recent iBeacon event received and a log area to show any activity related to newly discovered or lost iBeacons.

code 11

The first challenge in an application like this is that it’s effectively servicing a time-sequenced stream of events (iBeacon advertisements). You could handle these yourself in a fairly brute-force way but I like to use a small functional reactive programming (FRP) library for JavaScript called Bacon.js – have a look at the API reference and tutorial here: https://baconjs.github.io/ .

I won’t go through Bacon.js in detail since you can put it on your reading list if you don’t already know anything about it; rather I’ll just talk through a couple of the simple code snippets that use it and are in the sample application.

In essence Bacon.js allows you to manipulate time-sequenced event streams as first-class objects. That is you can manipulate then in just the same way as, say, integers and strings. Let’s take a look – the code below can be found in GitHub here:

code 12

This is the JavaScript code that processes iBeacon events from the plugin. Notice though that I do nothing with the event other than push it onto a Bacon.js object called beaconStream; this represents an event stream and I’ll process this elsewhere in the application. In fact the code fragment below shows the definition of beaconStream (an instance of Bacon.Bus()) and how it’s used – have a look on GitHub here.

The first thing I want to do is create another event stream that contains only those iBeacon events that represent newly discovered iBeacons. This means that I have to remember what ones I have seen before and when I last saw it. This information is maintained in the associative array beaconList.

The new event stream is called discoveredBeacons and the code fragment below shows how this is defined. In essence if I’ve seen the iBeacon before I remove the iBeacon event from the discoveredBeacons event stream and if it’s new I add it to beaconList and add it to the discoveredBeacons event stream.

That’s all there is to it!  Now I have two event streams: one representing all iBeacon events and one representing newly discovered iBeacons.

code 13

The next thing I want to do is determine when an iBeacon has gone out of range. For the sake of this example I’ve arbitrarily used an interval to 20 seconds of “silence” to represent this – it would probably be a different value in the real world but this sample is for educational purposes.

code 14

In Bacon.js you model almost everything as an event stream, so I create another one called lostBeacons – it’s here on GitHub. See if you can understand how it works.

Every 10 seconds (I really ought to make this more event driven but that’s a task for later) I check to see if any iBeacons I know about haven’t been seen for more than 20 seconds. If so I delete it from beaconList and emit an event representing the lost beacon.

It’s easy to use these event streams to update the user interface of the application. The code fragment below shows how I do this – see here on GitHub. It should be fairly self-explanatory.

As an exercise, take a look at the additional event stream I’ve created, but not described – the one called movedBeacons. I’ll leave it to you to figure out how I created a Bacon.js event stream representing a proximity measure for iBeacons that would trigger as I walked towards and away from an iBeacon.

code 15

Well, that’s about it! A Bluetooth iBeacon Cordova Plugin using Bacon.js as a way of handling streams of events which is one of the patterns that needs to be mastered when working in the world of the Internet of Things.

Summary

I’ve released v1.0.0 of the SimpleBeaconPlugin plugin containing the features described in this article and so you can download, review and reuse the full source code – you can find the URL in the “Resources” section below. I hope this has been interesting and will help set you on your way developing real-world event driven Bluetooth applications for Blackberry 10.

Please feel free to contact me, @jcmrim, via Twitter if you need any help on anything Bluetooth Smart.

Resources

BlackBerry Bluetooth LE Developer Resource Index Page

https://github.com/blackberry/WebWorks-Community-APIs/tree/master/BB10-Cordova/simplebeaconplugin

https://github.com/blackberry/WebWorks-Community-APIs/blob/master/BB10-Cordova/simplebeaconplugin/documents/API-Reference.md

http://devblog.blackberry.com/2014/05/wheres-my-beacon-using-beacon-technology-in-mobile-app-development/

http://devblog.blackberry.com/2014/07/wake-my-beacon/

http://devblog.blackberry.com/2014/05/beacon-testing-and-configuration-using-blackberry-10/

http://devblog.blackberry.com/2014/10/simple-bluetooth-profile/

http://devblog.blackberry.com/2015/04/a-simple-bluetooth-smart-low-energy-webworks-plugin/

Contacts

John Murray – @jcmrim

About jcmurray2012