This article arose out of a real-life project I (@jcmrim) had been working on which involved connecting medical devices to BlackBerry 10 handsets using Bluetooth Serial Port Profile (SPP). What made it particularly interesting is that the BlackBerry 10 application was a WebWorks / Cordova application.
This model was very successful, so obviously I wanted to share this with the developer community. However, the plugin itself was matched closely to the medical devices it needed to communicate with, and it was unlikely that the ordinary developer would have access to a blood pressure and oximeter monitoring device. So I crafted a simpler version of the plugin that was device-agnostic, that would simply send and receive data over an SPP connection.
Using this as a base, and inspired by an existing Cascades application on GitHub, I wrote a simple WebWorks “chat” application that used the plugin to communicate, using Bluetooth SPP, between two BlackBerry 10 devices to demonstrate the basic architecture and model of the application and plugin. If you have another Bluetooth use-case, it should be easy to follow this pattern to develop your application.
Before delving into the plugin itself, let’s just look at how the “chat” application behaves.
The “Chat” Application and How It Behaves
I recorded a short video that demonstrates the main elements of this simple application:
- Listening for an inbound Bluetooth connection
- Scanning for nearby devices
- Connecting to a selected Bluetooth device
- Bi-directional exchange of simple messages
- Disconnection of the two devices
OK, I’m not by any means an expert at designing user interfaces, particularly HTML5 ones, so forgive me if it seems a little Spartan. It is, however, completely functional.
Let me just summarize what you’ve just seen: Two BlackBerry 10 devices running a simple WebWorks application are able to establish a Bluetooth SPP connection between themselves and exchange simple messages over this connection. It’s Bluetooth “chat.”
Both the sample “chat” application and the plugin 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. The best example of this is the Bluetooth Address of the device to which to connect. You’d use it like this:
I use this form when some operation, such as sending a message, needs to be performed. I’ll know whether this has been successful by the returned value from the call.
So, the returned value from “sendMessage” would be one of these:
The first thing I want to note is that 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 “listenForConnection()” that starts the plugin listening for inbound connection events. I’d use it something like this:
I can test the variable “foo” to check that the listen request has been initiated correctly and then when an inbound connection is detected by the plugin, the callback “inboundConnectionCallback()” is called by the plugin. The data that is passed back as an argument to the callback function might look something like this:
I can check this and figure out that I’m now connected to a device at the address: “34:BB:1F:3E:77:BC”. 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/SimpleBtSppPlugin/documents/API-Reference.md .
There’s just one other point I’d like to note before moving on, and that’s got to do with what actual data the “chat” application will pass to the plugin when it issues a “sendMessage()” request to send data to the other device.
I took the view that I wanted to be able to send arbitrary byte streams across the underlying SPP connection to the peer device and conversely receive arbitrary byte streams. The reason might be that I’d want to use this API to communicate with a device that “speaks” a protocol that is byte-oriented rather than character-oriented. Most medical devices that use SPP communicate like this.
So, the plugin will accept messages of the following format:
And this will be sent over the SPP connection as the byte stream: 02,74,02,05,05,03. This isn’t a random stream of bytes, by the way, but is in fact a request to a particular type of medical device to report its serial number. The 0x02 is “STX” and the 0x03 is “ETX”, which, if any of you remember your basic serial communications, are mnemonics for framing symbols around a message. The reason for this is that many medical devices were designed to work with real serial cable connections to monitoring equipment, and with the advent of Bluetooth the same protocol is simple transported over SPP. This is the reason for the word “Serial” in Serial Port Protocol.
Conversely, data returned from the remote device might be encoded the same way, something like:
So, I can use this to send, unambiguously, the text of messages entered into the input field on the “chat” application. The text of the message entered in this way will be in UTF-16 format; so, for example, string “hello” in the images at the start of this article would actually be passed to the plugin as:
If you’re interested, here’s how I did this in the code of the “chat” application itself:
Using an “ArrayBuffer()” mapped with two views “Uint16Array()” and “Uint8Array()”, I could encode an arbitrary string encoded as UTF-16 unambiguously as a byte array before sending it to the plugin as a JSON string.
Now let’s look in more detail at the plugin itself.
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 customize 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 organized.
I’m going to assume that you’ve 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 SPP 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()” operation, which looks like this:
I’ll get some immediate status as to the initialization 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, which 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 is saved for later when the asynchronous operation returns. The actual call to native code is made using the object returned from “simpleBtSppPlugin.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 what is shown 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 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, along with a couple of BlackBerry 10 devices, there are also a couple of Estimote beacons in the vicinity . 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.0 of the SimpleBtSppPlugin plugin containing the features described in this article, 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 to developing real-world Bluetooth applications for BlackBerry 10.
Please feel free to contact me on Twitter (@jcmrim) if you need help on anything Bluetooth SPP or Smart-related.
The WebWorks version of this “chat” application sample was inspired by the Cascades version here:
John Murray – @jcmrim