Image by Stuart Miles / FreeDigitalPhotos.net
Written by: John Murray
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.
Both the Heart Rate Monitor Cordova plugin and a simple application that uses it are available as a project on GitHub here:
You’ll find instructions on how to build and use the project here:
There are three types of elements in the API:
- Synchronous functions
- Asynchronous functions
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:
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.
So, the returned value from “initialiseBluetooth” would be one of these:
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.
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.
I’d use it something like this:
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:
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:
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:
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.
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.
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.
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.
Once the pthread() has started it scans for Bluetooth devices in the same way that I’ve done in many other examples.
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));
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:
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:
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!
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.
John Murray – @jcmrim