DEVELOPERS BLOG

A Simple Bluetooth Smart (Low Energy) WebWorks Plugin

heart

Image by Stuart Miles /  FreeDigitalPhotos.net

Written by:  John Murray

Introduction

I recently wrote a blog post describing a WebWorks/Cordova Plugin I (@jcmrim) had written that allowed a BlackBerry 10 WebWorks/Cordova application to interact with Bluetooth devices that used the Serial Port Profile (SPP). SPP is a very common mechanism that allows BlackBerry 10 to interact with devices as diverse as medical devices, portable fingerprint readers, barcode scanners and printers.

The pattern of using Cordova and Bluetooth in this context has been very successful and I wanted to share some additional Cordova and Bluetooth patterns with the developer community so I wrote another Cordova Plugin, which this time used Bluetooth Smart, or Low Energy, as the underlying technology. Bluetooth Smart is very well designed and the basic pattern that a developer needs to use is pretty much identical across many devices, the only real difference being the type of data, or Characteristics, that are delivered to the application.

I settled on the Bluetooth Low Energy Heart Rate Monitor profile as a typical medical example since it’s both in common use and the details of the profile have been discussed in earlier blog posts so there is no need to re-iterate the details of the profile itself and thus allow concentration on the Cordova specifics.

The Bluetooth Smart Heart Rate Profile JavaScript API

Both the Heart Rate Monitor Cordova plugin and 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/SimpleBtLeHrPlugin

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

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

And, lastly, but by no means least, I’ve documented the Bluetooth Smart Heart Rate Monitor JavaScript API here:

https://github.com/blackberry/WebWorks-Community-APIs/blob/master/BB10-Cordova/SimpleBtLeHrPlugin/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 three types of elements in the API:

  • Properties
  • Synchronous functions
  • Asynchronous functions

Properties

I use these to set and get values that should be persisted in the plugin. There is only one example of this in the API and this is the Bluetooth Address of the Heart Rate Monitor device to which to connect. You’d use it like this:

jm1

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.

jm2

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

jm3

Note that, in line with my previous plugin, 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 callback. A good example of this pattern would be the call, startMonitoringHr() that starts the plugin listening for inbound heart rate notification events that are delivered via the gattCallback callback.

jm4

I’d use it something like this:

jm5

I can test the variable “foo” to check that the request to start monitoring has been initiated correctly and then when an inbound heart rate notification is detected by the plugin the callback “gattCallback()” is called by the plugin. The data that is passed back as an argument to gattCallback() might look something like this:

jm6

This represents a typical successful Heart Rate Measurement notification event. The “data” attribute matches the data expected from the Bluetooth HRM Profile. This represents a GATT Notify event from the GATT Heart Rate Service (UUID “0x180D”) for the Heart Rate Measurement Characteristic (UUID “0x2A37”). These fields are described here: Heart Rate Measurement Characteristic. As you can see it contains more information than just a heart rate measurement.  Also notice that I supply a description field “desc” in all status messages that you can use in any error messages you need to provide.

As I said before, check out the API reference for details: https://github.com/blackberry/WebWorks-Community-APIs/blob/master/BB10-Cordova/SimpleBtLeHrPlugin/documents/API-Reference.md .

The Plugin Itself

In a sense I’ve cheated – 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 APIs.

It’s probably sufficient to look at how one single operation is implemented since the others follow the same pattern. I’ll look at startDeviceScan(), the operation of which looks like this:

jm7

I’ll get some immediate status as to the initialisation of the scan operation in the variable “foo” and subsequently the “data” argument of the callback function will contain the result of the scan.

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

jm8

Much of this is boilerplate code, setting up the “success” and “fail” callbacks and the 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.

jm9

The actual call into native code is made in the fragment above – 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 simpleBtLeHrPlugin.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, which calls the appropriate method on an instance of a class the template has provided for us.

jm10

In essence this method is quite simple – all it does is start a pthread() that will perform the actual scan, since it can be a lengthy operation, 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.

jm11

Once the pthread() has started it scans for Bluetooth devices in the same way that I’ve done in many other examples.

jm12

The key difference is this call made by the thread when it’s just about to exit:

m_pParent->NotifyEvent(m_scan_callback_id + ” ” + writer.write(root));

This is where the JSON response containing all the devices that have been scanned is passed back up through the JavaScript layers as an asynchronous event to the correct callback that the user of the API has specified.

If everything has worked correctly the user of the plugin’s API will see a response on the callback they have provided with data similar to this:

jm13

You can see that as well as a couple of BlackBerry 10 devices there are also a couple of Estimote beacons in the vicinity as well. If no devices are found the response would be like this:

jm14

It’s not an error to find no devices so the “status” field is “OK” but the “devices” array attribute is an empty array.

That’s about it!

If you follow the path of one operation all the way from the JavaScript API layer in the WebWorks application you can discern the basic pattern and all the other operations work in basically the same way.

Summary

I’ve released v1.0.1 of the SimpleBtHrPlugin 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 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/SimpleBtLeHrPlugin

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

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

Contacts

John Murray – @jcmrim

BlackBerry

About BlackBerry

BlackBerry is an enterprise software and services company focused on securing and managing IoT endpoints.