BlackBerry 10 - Bluetooth LE primer for developers

by Retired on ‎04-05-2013 10:37 AM (28,988 Views)

Introduction

 

This article is part of a series intended to help developers wishing to exploit Bluetooth® Low Energy® technology in their BlackBerry® 10 applications. No pre-requisite knowledge of Bluetooth Low Energy is necessary to appreciate this article as a summary of the technology is included. Readers of this article ought to be familiar with BlackBerry 10 native development, including Qt® and QML, and should be familiar with C++ in order to appreciate the sample application that is used to demonstrate the concepts.

 

The approach taken in this article is to describe the concepts and architecture that comprise Bluetooth Low Energy and then use the vehicle of a real world example of a Heart Rate Monitor application (HeartMonitor) to build on these concepts. This sample application has been published on GitHub® as Open Source.

 

The Authors

 

This article was co-authored by Martin Woolley and John Murray both of whom work in the BlackBerry Developer Relations team. Both Martin and John specialize in the application of proximity radio technology on BlackBerry devices including Bluetooth and NFC (amongst other things).

  

An Overview of Bluetooth Low Energy Technology

 

So, you’ve probably thought to yourself: “What can possibly be new in Bluetooth? What’s this “Low Energy” thing and what can I use it for? ” Bluetooth is a technology that’s been around for many years (it was originally conceived in 1994 as a wireless cable replacement technology) and we’ve become comfortable with its presence everywhere in devices such as Bluetooth Headsets, or other peripherals.

 

If I was to paraphrase how Bluetooth has evolved since its inception until about 2010, then it would be to say that it was following a path towards higher speed; bigger and faster.

 

Yet, recently, Bluetooth underwent a quiet revolution that allows it to address a completely different market and rather than following the “bigger and faster” trajectory it’s developed a capability that allows it to be “smarter”.

This “smarter” variant is called Bluetooth Low Energy and you’ll find it specified in the latest Bluetooth 4.0 specification. There’s some terminology that’s worth nailing from the start:

 

  • Bluetooth BR and Bluetooth BR/EDR: “BR” stands for “Basic Rate” whilst “EDR” stands for “Enhanced Data Rate”. In essence there terms capture the evolution of Bluetooth along the “bigger, faster” trajectory. These terms identify what has become known colloquially as “Classic” Bluetooth.
  • Bluetooth LE: is commonly used as a short name for “Low Energy”.
  • Bluetooth Smart: This is a marketing brand to identify ultra low energy devices that can connect to Bluetooth Smart Ready devices.
  • Bluetooth Smart Ready: This is a marketing brand used to identify products such as phones, TVs, tablets, PCs, etc., that can connect with the ultra low energy Bluetooth Smart Devices.

 

What is Low Energy?

 

Put simply: it’s a new technology:

  • The designers started with a blank sheet of paper;
  • It has been optimised for ultra low power;
  • It is different from classic Bluetooth technology.

 

Ok, that’s a pretty sweeping statement to say it’s a “New Technology” but how much is new and how much is based on classic Bluetooth. Well, it’s new in the sense that:

  • It uses very efficient discovery and connection procedures;
  • It exploits a new asymmetric design for peripherals;
  • It uses very small packets;
  • It uses client/server architecture.

 

It is not new in the sense that:

  • It re-uses the existing Bluetooth BR radio architecture;
  • It re-uses APIs that exist in Bluetooth BR at the HCI logical layer and physical transports;
  • It re-uses existing link layer (L2CAP) packets

 

The basic concept was to re-visit classic Bluetooth and to optimise everything for the lowest possible power consumption at all levels of the Bluetooth stack.

 

battery.png

The reason for this was the vision that Bluetooth LE devices would have a small footprint and the main power source for peripherals would be a small button cell like the one in this picture.

 
Everything that was factored into the Bluetooth LE design was geared to reduce the power consumption of these devices whether it be radio technology, memory technology or device footprint.

 

The model is asymmetric in the following sense. “Bluetooth Smart” devices, such as key-fobs, monitors, etc., are generally simple and resource constrained, whilst “Bluetooth Smart Ready” devices, such as phones, tablets, TVs, etc. are more complex, have more memory and battery power and are typically used as hosts for applications that acquire and process data from the “Bluetooth Smart” devices.

 

Some typical Low Energy Devices

 
Now that we know, in basic terms, what LE means, what sort of devices does this “enable”? Home automation is a good example where devices can be controlled using LE technology; Health and Fitness is another where wearable sensors can monitor things like heart rate. They might even be coupled with location awareness to allow correlations to be drawn between effort and heart rate. This list is endless and is probably only limited by imagination.

 
In fact LE is one of the key enablers of the vision of the “Internet of Things” where small devices can be instrumented, controlled and interrogated and exposed to the internet by other devices such as phones and tablets. This vision spans a variety of enabling technologies including NFC and Wi-Fi® Direct to name but two.

 

architecture.png

Figure 3 - Bluetooth functional layers

  

A closer look at the LE Architecture

 

One of the key points to understand is that in Bluetooth 4.0 the LE and classic BR/EDR pillars of the Bluetooth stack are partly separated, with some shared components. The architecture diagram here (Figure 2) shows this. The details are not relevant but the main key point is to note that the box outlined in green is the LE component whilst the box outlined in red is the classic (BR/EDR) component. 

 

This means that a Bluetooth 4.0 device can elect to implement only the classic (BR/EDR) elements; only the LE elements, or both. Devices like phones or tablets will typically implement both BR/EDR and LE allowing it to communicate with both classic peripherals, such as headsets, as well as new LE devices like heart rate monitors.

 

 

Figure 2 - Overall Bluetooth 4.0 Architecture

 

Low power peripheral devices will probably implement only the LE elements and hence not be visible or able to communicate with devices that implement only the classic (BR/EDR) stack.

 

In this article we’re more interested in the application level interaction between Bluetooth LE devices and as such, the diagram below (Figure 3) is the one we’ll use to focus on the appropriate elements of the Bluetooth LE stack. 

 

stack.png

Figure 3 - Bluetooth functional layers

 

To a large extent the details of the various layers below the application layers are of little concern to an application developer but it’s of value to have an understanding of how Bluetooth is an incredibly resilient technology and able to operate in environments that are electrically hostile.

 
Bluetooth operates in the 2.54 GHz band which it shares with Wi-Fi, digital cordless phones and microwave ovens! Bluetooth LE still retains its fundamental resilience by splitting its radio traffic across 40 channels as shown below (Figure 4).

 

hopping.png

Figure 4 - Bluetooth LE frequency channel usage

 

Bluetooth LE Radio traffic hops around these channels in a pseudo random manner so that the data with get through even though it’s in an areas shared by a number of Wi-Fi networks, or microwave ovens. One of the differences between Bluetooth LE and classic Bluetooth is the number and use of these channels.

 

Bluetooth LE from an Application Perspective

 
Right, we’ve now got a basic sense of what Bluetooth LE is and its overall architecture. From a topology perspective LE devices can take on a number of roles. The diagram below (Figure 5) shows a number of devices labelled variously as:

 

  • Scanner
  • Advertiser
  • Master
  • Slave

 

The roles of “Scanner” and “Advertiser” are adopted by devices before connections have been established.

 

roles.png

Figure 5 - Roles used during discovery and connection establishment 

 

Devices such as phones or tablets would generally (but not exclusively) adopt the role of “Scanner” and would “discover” other devices that have adopted the “Advertiser” role by a process called discovery which can be active (“are there any devices out there?”) or passive (“I’ll listen whilst devices advertise their presence”). Devices that adopt the “Advertiser” role are generally (but not exclusively) smaller footprint devices such as heart rate monitors or temperature sensors.

 
Once devices have discovered one another one will act as an “Initiator” (typically the phone or tablet type device) and attempt to connect to one of the devices that it has discovered. If successful it will adopt the role of “Master” and the other will adopt the role of “Slave”. “Master” devices will initiate commands and requests to “Slave” devices which will respond.

 
One of the first task of a Bluetooth LE application, such as on BlackBerry 10, is to discover other Bluetooth LE devices that it can connect to. A process called “bonding” can also be used whereby devices that have discovered one another in the past can cache important information about each other for use at a later time.

 

Services

 
Once a device has been discovered the next task is to figure out what services are offered by the device. So, what’s a service? Well, a service consists of:

 

  • A Service Specification, which consists of:
    • A collection of characteristics;
    • References to other service.

 

Hmm, ok, that was a bit opaque. I tend to think visually and so here’s a diagram (Figure 6) to help cement the concept. We need to introduce some more terminology when talking about services. Remember we encountered the roles of “Master” and “Slave” once two devices had connected to one another? Well, when talking about services we use the names:

 

  • Server
  • Client

 

This highlights the client/server model that is used at this level of the architecture.

 

client_server.png

Figure 6 - Services and Characteristics 

 

A server device would typically be something like, say, a heart rate monitor, or thermometer; whereas, a client device would typically be something like a phone or a tablet, such as a BlackBerry 10 device.
Services maintain attributes called “Characteristics”. Think of a Characteristic as something that you can measure and be read from the device or be written to the device. It could be something like a speed, measured in metres per second, a counter used to measure the occurrence of some event, or perhaps, a voltage used to measure the status of a battery measured in, say, millivolts.

 

In fact if we really want to be accurate, a Characteristic is really a data type, analogous to a class. A server could and often does, have more than one instance of a given characteristic available and each such instance has a unique, temporary identifier called a "Handle". Such instances are therefore analogous to simple objects and a Handle analogous to an object reference. We'll use the term "Characteristic" in a slightly more relaxed way in this article; the context should make our meaning clear.

 
It’s important to recognise that a Characteristic is not just a number or value. It has other attributes that allow us to interpret the value. For example a temperature may be represented in degrees Celsius, Kelvin or Fahrenheit; a speed may be represented in metres per second, miles per hour or centimetres per week.

 
So, the server might offer a service called “Temperature Service” and one of the characteristics of this service might be “Temperature measured in degrees Celsius”. Another service might be “Heart Monitor Service” and one of the characteristics of this service might be “Heart rate measured in beats per minute”.

 
The client/server model means that the client (BlackBerry 10 phone for example) would make some request of the server. For example it may request the current value of the temperature characteristic. The server responds with the appropriate values.

 

A client might also write information to a characteristic on the server which might be used to control the server. It might set a characteristic to a value that causes the service subsequently to advertise the value of particular characteristic whenever it changes.

 
This is useful to avoid having the client having to poll for data from the server. The server automatically sends data to the client through “Indications” or “Notifications” when the data is available. A good example would be a heart rate monitor where the server (the monitor) sends the latest heart rate data automatically every second.

 
The terms Notification and Indication are another set of terms of which you should have some understanding.

 

  • Notification – this is a procedure whereby a Server can asynchronously notify a client of a change in the value of a characteristic it’s measuring. For example, in the case of a heart rate monitor the current heart rate would be sent to the client whenever its value changes or once a fixed time has elapsed. No acknowledgement is required from the client.
  • Indication – this procedure is the same as an Indication except that an acknowledgement is required from the client.

 

Understanding the services offered by a device, how they are identified and accessed is key to the development of a Bluetooth LE application.

 

Services in More Detail

 
How are services and characteristics implemented? How are they named? How do we know that a “Temperature” service offered by one device is the same as one offered by another? This is the responsibility of GATT (Generic Attribute Profile). GATT rigorously defines the concepts around how services and their attributes are organised. It’s important to recognise that it does not define rules for their use.

 
Here’s a verbose description (Figure 7) of three services offered by a smart thermometer as an example. Two of these are defined by architecture (GAP and GATT) and allow the device to describe itself to the client.

 

gap_gatt.png

Figure 7 - How Services and Characteristics are organised

 

It is able to identify itself by name and appearance ("I’m a digital clinical thermometer" for example); it’s able to offer some more detailed information about itself and finally it’s able to describe its "Temperature" service.

 
In effect the server maintains a small "database" consisting of the services, their descriptions, characteristics and values. If we supply more detail (Table 1) to this verbose description we can see that each attribute in the server has been assigned a "handle".

 

characteristics.png

Table 1 - Detailed description of Services and Characteristics

 

The handle for the temperature characteristic’s value is 0x0022. This number is arbitrary and assigned by the server but is used by the client to uniquely identify the temperature value when it wants to access it.

 

Characteristics also have permissions so we can see that the Temperature value itself has a: “Read” permission – the client can read it!

 
All Bluetooth attributes are identified by UUIDs. There are two types:

 

  • A UUID which is 128 bits long like: 00000000-0000-1000-8000-00805F9B325A
  • A short UUID which is 16 bits long (xxxx) for convenience but which is defined to map to a standard 128 bit UUID as: 0000xxxx-0000-1000-8000-00805F9B34FB

 

UUIDs are managed and assigned by the Bluetooth Special Interest Group. So that you can look up what the UUID should be for, say, Temperature and find its unique UUID.

 
Now, you may ask yourself why we need handles if characteristics are uniquely identified by UUIDs? Well, here’s an example to help solidify this concept. Suppose the LE device we’re using is a Blood Pressure monitor. Blood pressure is measured using two pressures (Systolic and Diastolic). Both are measured in millimetres of Mercury (mmHg) and both are identified by the same UUID that identifies the characteristic as a pressure measurement.

 
So, the same UUID may appear in a service several times. How do you distinguish one from the other? You figure out the Systolic from the Diastolic one by the way the services are described and locate the different handles that have been assigned, by the server, to each one. Once you have done that you can access each one subsequently via its unique handle.

 
These are the main concepts that you will need to have to be able to write a BlackBerry 10 Bluetooth LE client application.

 
In fact that’s exactly what we’re going to do now! We’re going to walk through an application that will gather information on Heart Rate data from a heart rate monitor!

 

The Heart Rate Monitor Application

 

Introduction

 
With a bit of luck, our review of basic Bluetooth LE theory has got you asking the question “OK, so how can I use this wonderful stuff in my own BlackBerry 10 applications?!” If you are asking that question... or one rather like it, then read on.

 
Examples are a great way to learn and there’s nothing better in our opinion than “going hands on” so that knowledge has both a theoretical and a practical basis to it. So to accompany this article, we’ve written an application called HeartMonitor and we’ll review it now, illustrating as we do so, the way in which you can exploit the BlackBerry 10 Bluetooth LE APIs.

 
From Requirements to Design

 
There are various heart rate monitor devices on the market that support Bluetooth LE. We obtained a Wahoo® Blue HR device to see what we could do with it. See http://www.wahoofitness.com/Products/Wahoo-Fitness-Wahoo-Blue-HR-Heart-Rate-Strap.asp

 

The Wahoo Blue HR is a small, lightweight electronic device, encased in plastic. It has an elasticated strap to allow you to strap it around your chest and the strap has two conductive pads which need to be in contact with the skin. The pads work best when they’re been moistened. Working up a good sweat is one way to accomplish this!

 

wahoo_hr.png

 

 Figure 8 - Wahoo Blue HR heart rate monitor

 

We’d decided that we wanted to develop a BlackBerry 10 app which would somehow receive heart rate data from the Wahoo heart monitor and display that data in a useful and prominent way. We knew that the device supported Bluetooth LE. But this wasn’t enough to allow us to proceed, and before we get into the code, we’d like to share the steps we had to go through in preparation for coding.

 

Know your device. Get the specs.

 
In general, if you’re considering developing an app for a given Bluetooth device or device family, you need to know a little more about exactly what it is able to do with Bluetooth. Typically, a device will support a specific Bluetooth profile and it should be possible to determine this from the Bluetooth product’s documentation. Bluetooth profiles have a specification and the specification tells us things like the Bluetooth service or services that the device runs. The concept of a Bluetooth service was explained earlier in this article and you can think of a service as something which runs on a device which acts as a server, in the usual client/server sense of the term and which is able to deliver data (characteristics) to a client, either in response to requests or, asynchronously, whenever relevant data is available. Bluetooth services also have their own specifications and from the service specification we can find out precisely what data characteristics are available, under what circumstances and what their structure and values are.

 
So, the first thing we needed to do with our Wahoo heart rate monitor was to find out what Bluetooth LE profile it supports and then obtain the specifications for the profile and any services it referenced. It so happens that there’s a Bluetooth Heart Rate profile and that’s exactly what the Wahoo Blue HR device supports. A glance at the Bluetooth Heart Rate profile specification revealed that there’s also a Bluetooth Heart Rate service. Specifications for both profiles and services are available at the Bluetooth SIG web site here:

 
https://www.bluetooth.org/Technical/Specifications/adopted.htm

 
Now the specifications led us to understand that heart rate data would be delivered by the HR device asynchronously using Bluetooth notifications. These are the types of Bluetooth Attribute Protocol (ATT) message that are delivered by a server to a client and which do not require a response. We also learned from the specification that heart rate data would be delivered in a Heart Rate Measurement characteristic and that this is a structure comprising a number of fields. The first of these fields is called the Flags field and its bits give further information about the content of the heart rate measurement characteristic. Basically, some of the data defined for the heart rate measurement characteristic is optional and so not all devices will support it. The Flags field tells us whether each such optional data item is supported by the device or not. A supported data item may or may not be available at a particular moment however, so the Flags field can also indicate this.

 
You can examine the full structure definition for the Heart Rate Measurement characteristic at the Bluetooth SIG web site here:

 
http://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth....

 
One point to note is that the Heart Rate Measurement Value field, which contains the heart rate measure in beats per minute, has two possible forms. It can either be a uint8 field or a uint16 field. The Flags field indicates which of these it is. The uint16 variant is intended to hold values greater than 255 as might be required if you’ve strapped your heart rate monitor around the chest of a humming bird or a tiny, frightened hamster. We decided we were writing our app for humans and so coded specifically for the uint8 variant!

 

hamster_with_HR.png

Figure 9 - Hamster (non-frightened variety) with heart rate monitor 

 

Please note: Hamsters, frightened or otherwise, are not supported users of the HeartMonitor app.

 

Sniff it. If you’re into that kind of thing.....

 
There’s no actual hard and fast necessity for this step but if you’re at all like John and Martin, you’re the sort of person who likes to look under the covers to see how things really work. During our research into Bluetooth LE, we looked into the kinds of tools that might usefully supplement the standard developer tools and have acquired various gizmos including a Bluetooth development kit from Texas Instruments which lets us do a variety of useful things. For example, we can connect to and send commands to a Bluetooth device and we can capture the “over the air” protocol messages and examine them. So we hooked up the Texas Instruments® kit, pointed it at the Wahoo monitor and, by sending the correct commands, persuaded the Wahoo HR monitor to start delivering heart rate measurement notifications which we were able to capture and examine. This just helped cement the knowledge we’d gained from the specifications. A screen shot appears below.

 

03_hr_notifications.png

Figure 10 - Sniffing the Bluetooth GATT data

 

User Interface Design

 

We sketched out a simple UI design using tabs, providing the user with a screen from which to select the heart rate device to connect to, a screen which would display live heart rate data, as a numeric value and as a line graph which would update in real time and the usual “About” screen. This was done quickly and we refined our design ideas during coding and testing as ideas came to us.

 

Heart Monitor UI design  - R1.png

Figure 11 - HeartMonitor initial UI design

 

Implementing the HeartMonitor application using the BlackBerry 10 NDK with Cascades

 
So, we read the specs, we satisfied our curiosity regarding the protocol by snooping on a live session and we sketched out a rough UI design. All that left was to code our application!

 
APIs for Bluetooth are available from the BlackBerry 10® Native SDK (NDK) and as such you can utilise the Cascades™ UI framework with Bluetooth applications. We won’t dwell on UI issues here, rather we’ll seek to explain the Bluetooth APIs that were used in the development of the HeartMonitor application and how we exploited them in terms of patterns and other aspects of our app.

 
We had to implement for two distinct use cases. Let’s call them “Device Discovery and Selection” and “Monitoring”.

 
Device Discovery and Selection is concerned with determining what Bluetooth LE devices that support the Heart Rate Profile are in range, have been paired with by the user on some previous occasion and then deciding which one to connect to.

 
Monitoring is concerned with receiving and processing Heart Rate Measurement characteristics in Bluetooth notification messages from the device that we paired with in the first use case.

 
We decided to make life easier for the user so that once a heart rate monitor had been selected for monitoring purposes; we persisted this choice and assumed that the user would be happy to use the same device in the future. We gave the user the ability to re-scan for devices and select one for use should they change their minds however.

 
So let’s start by presenting a basic “recipe” for a Bluetooth LE application that includes scanning for devices that support the service we are interested in, connecting to a selected device and then receiving notifications from it.
To make it easier to present and review, we’ve taken a top down approach here, and start by presenting a high level depiction of the most fundamental, large grained steps. We then proceed by drilling down on each of these steps.

 

BT LE GATT Recipe 1.png 

Figure 12 - High level overview of steps

 

The Bluetooth aspects of our application breakdown, therefore, into three sets of operations; some initialisation steps, those related to Device Discovery and Selection and finally Monitoring, where we actually receive data from the Bluetooth LE device. Let’s now look at each of these in more detail. When reading what follows, bear in mind that for clarity’s sake, we’ve simplified some of the code related information and you should complete your review of this topic by looking at the corresponding code in the HeartMonitor application, which you will find in our GitHub repository (details at the end of this article).

 

BT LE GATT Recipe 2.png

Figure 13 - Bluetooth initialization steps 

 

An application will usually initialize the Bluetooth stack and libraries once only. You’ll need to ensure that Bluetooth is switched on and can query its state with bt_ldev_get_power before called bt_ldev_set_power if necessary. We have an extra initialization step which is specific to our use of GATT whereby we must use bt_gatt_init to provide pointers to a number of call back functions which the system will invoke during the set up of our GATT connection to the heart rate service. Call back functions are used in a number of places in the BlackBerry 10 Bluetooth APIs.

 

// positional parameters indicating the implementations of each of the function types in bt_gatt_callbacks_t

bt_gatt_callbacks_t gatt_callbacks = { gatt_service_connected, gatt_service_disconnected, gatt_service_updated };

.....

bt_gatt_init(&gatt_callbacks);

Figure 14 - Setting up GATT service call back functions 

 

Now, let’s review the Device Discovery and Selection steps and the API functions which are typically used. 

 

BT LE GATT Recipe 3.png

Figure 15 - API use in Device Discovery and Selection

 

In Device Discovery and Selection, we kick things off by calling bt_disc_start_inquiry and then pausing for 5 seconds. We pause because bt_disc_start_inquiry initiates the scanning for Bluetooth devices in a background thread and details of devices discovered are captured asynchronously in the Bluetooth library. 5 seconds seems a reasonable amount of time to allow for scanning and device information collection to take place. Once we’ve finished waiting for this to happen, we cancel the scan with a call to bt_disc_cancel_inquiry and then retrieve those device details so we can proceed to determine which of them support the heart rate service so that we can filter out those that do not before presenting the user with a list of devices to choose from.

 

bt_remote_device_t **remoteDeviceArray;

remoteDeviceArray = bt_disc_retrieve_devices(BT_DISCOVERY_ALL, 0);

Figure 16 - Retrieving details of Bluetooth devices discovered by scanning 

 

bt_disc_retrieve_devices gives us an array of bt_remote_device_t structs to process. Not all detected Bluetooth devices in that array will be LE devices and not all of the LE devices will support the Heart Rate Profile. So we next proceed by iterating through the array and taking a closer look at each of the devices. We’re looking for LE devices that include the Heart Rate Service amongst the services that they run.

 

// in our constructor initialiser list we set up a constant containing the UUID of the Heart Rate Service:
// HR_SERVICE_UUID("180D")

// iterate through the array of devices discovered

        for (int i = 0; (nextRemoteDevice = remoteDeviceArray[i]); ++i) {
....

            // isAHeartRateMonitor(..) is our own method for determining whether or not a
            // device supports the heart rate service

            if (isAHeartRateMonitor(nextRemoteDevice)) {
     		// this device has the Heart Rate Service so include it in our device list
            
            }
 	 }

// here’s a simplified version of our isAHeartRateMonitor method
				
bool DeviceListing::isAHeartRateMonitor(bt_remote_device_t * remoteDevice) {

   const int deviceType = bt_rdev_get_type(remoteDevice);

   if ((deviceType == BT_DEVICE_TYPE_LE_PUBLIC) 
    || (deviceType == BT_DEVICE_TYPE_LE_PRIVATE)) {

	 char **servicesArray = bt_rdev_get_services_gatt(remoteDevice);

        for (int i = 0; servicesArray[i]; i++) {
	      if (strcasecmp(servicesArray[i], HR_SERVICE_UUID.toAscii().constData()) == 0) {
		   foundMonitor = true;
		   break;
	      }
	}
	return foundMonitor;
}

Figure 17 - Selecting devices that have the heart rate service 

 

Let’s review the code fragment in Figure 17. In the comments at the top, we begin by mentioning that elsewhere in our DeviceListing class, we’ve created a constant called HR_SERVICE_UUID and given it a value of "180D". Bluetooth LE services are identified by a UUID whose value is defined in the Bluetooth specifications. So this is how we determine whether or not a device has the Heart Rate Service or not; we examine its services and look for one that has a UUID of 180D.

 
For each device we discovered we apply two checks. We use the API method bt_rdev_get_type to find out whether or not it’s a device that supports Bluetooth LE or not. The btdevice.h header file defines constants we can use in this condition. The two flags: BT_DEVICE_TYPE_LE_PUBLIC and BT_DEVICE_TYPE_LE_PRIVATE relate to a Bluetooth LE Privacy Feature. Bluetooth LE supports a feature that reduces the ability to track a LE device over a period of time by changing the Bluetooth device address on a frequent basis. Its use is really outside the scope of this article but we check for both public and private addresses for completeness.

 

If a device is found to be an LE device then we obtain a list of its services using the API method bt_rdev_get_services_gatt. This gives us another array to iterate through. For each service in the array, we check its UUID to see if it matches that of the HR_SERVICE_UUID constant. If it does, this device is a heart rate monitor and we include it in our list of devices to present to the user.

 
Let’s now move on to review the Monitoring step, the recipe for which is presented below.

 

BT LE GATT Recipe 4.png

Figure 18 - API and Call back functions in Monitoring

 

The key classes concerned with monitoring in our HeartMonitor application are BluetoothHandler and HrDataContainer. BluetoothHandler contains (not surprisingly) most of our Bluetooth API functionality (along with the DeviceListing class introduced above) whilst HrDataContainer is a singleton which acts as a central container for the data we accrue and which we need to use elsewhere, such as in the UI. We kick the whole thing off from within the HeartMonitor class and execute our Bluetooth operations in a background thread:

 

void HeartMonitor::monitorHeartRate(QString device_addr, QString device_name) {

	HrDataContainer* hrdc = HrDataContainer::getInstance();
	hrdc->setCurrentDeviceAddr(device_addr);
	hrdc->setCurrentDeviceName(device_name);

	_future = new QFuture<void>;
	_watcher = new QFutureWatcher<void>;
	_mutex.lock();
	*_future = QtConcurrent::run(_handler, &BluetoothHandler::receiveHrNotifications);
	_watcher->setFuture(*_future);
	QObject::connect(_watcher, SIGNAL(finished()), this, SLOT(finishedReceiving()));

}

Figure 19 - running Bluetooth operations in a background thread

 

We start by using bt_gatt_connect_service to connect to the Heart Rate Service on the device that the user selected.

 

errno= 0;

if (bt_gatt_connect_service(device_addr.toAscii().constData(), HR_SERVICE_UUID, NULL, &conParm, this) < 0) {

    qDebug() << "YYYY GATT connect service request failed: " + QString::number(errno) + " (" + QString(strerror(errno)) + ")";

} else {

    qDebug() << "YYYY requested connection to HR service OK";

}

 Figure 20 - Connecting to the heart rate service

 

Note the use of the errno variable. Bluetooth API functions will set errno to a non-zero value in the event that a call to the function fails and errno and strerror(errno) will give you further information on the nature and cause of the failure. Make sure you set errno to zero before making the API call.

 
The act of connecting to the Bluetooth service is handled asynchronously and successfully invoking bt_gatt_connect_service does not indicate you’ve connected, just that your request was accepted. An indication of successful connection to the service comes later, in the form of a call back to the function which was specified for the “service connected event” when calling bt_gatt_init. In our HeartMonitor application we called this function gatt_service_connected.

 
In our gatt_service_connected function, we proceeded as follows; first we registered for GATT notifications. We do this by calling bt_gatt_reg_notifications and specifying an identifier for the service using the int parameter called instance which our gatt_service_connected function receives from the system, and by providing a call back function which the system will call whenever it has a notification it wishes to deliver to us.

 

void gatt_service_connected(
   const char *bdaddr, 
   const char *service, 
   int instance, 
   int err, 
   uint16_t connInt, 
   uint16_t latency, 
   uint16_t superTimeout, 
   void *userData) {

	errno = 0;
	int rc = bt_gatt_reg_notifications(instance, notifications_cb);
	qDebug() << "YYYY rc from bt_gatt_reg_notifications=" << rc;
	if (rc != 0) {
		qDebug() << "YYYY bt_gatt_reg_notifications errno=" << errno;
		qDebug() << "YYYY bt_gatt_reg_notifications errno=" << strerror(errno);
	} else {
		qDebug() << "YYYY bt_gatt_reg_notifications was presumably OK";
	} 

}

 Figure 21 - Service connected call back function

  

Our next task in setting up notifications is to tell the system precisely which characterictic(s) we want to be notified about. There’s slightly more to this than meets the eye. A characteristic can be thought of as a type rather than a concrete data item containing a value. In some cases, a device will have multiple instances of a characteristic available. In our code, we have to indicate specifically which of these data items we want to receive notifications for.

 
So how is this achieved? Well, a characteristic has a UUID defined in the specification, so this is how we know that a data item is an instance of, say, the heart rate measurement value that we want. In addition to this, a given server device will allocate a unique (to the device) “handle” value at run-time to each data item. The handle values are not specified and are left to the device to allocate. Handle values may vary each time you use a device.

 
Now in the case of the heart rate measurement value characteristic, there will only ever be one instance of this characteristic because this is indicated in the specification so in some ways, life is easier for us in the case of the heart rate service. But we still need to obtain and make a note of the handle that the device allocated to the heart rate measurement value characteristic since it’s the handle value that is passed as an identifier to our notification call back and we’ll come to this shortly. 

 
To acquire the handle for our characteristic and to enable notifications for it, we proceed according to the final recipe we present in this article, which drills down on the previous recipe:

 

BT LE GATT Recipe 5.png 

 Figure 22 - Enabling notification for a specific characteristic

 

We proceed by finding out how many characteristics our service offers, using this to allocate sufficient memory for the list of characteristics and obtaining that list. We then examine the list for the UUID of our characteristic, in this case the Heart Rate Measurement Value and then obtain its handle value and use this when registering for notifications. Let’s see what the code looks like:

 

// instance references the connected service
int num_characteristics = bt_gatt_characteristics_count(instance);

// allocate memory for the characteristic list
bt_gatt_characteristic_t *characteristicList;
characteristicList = (bt_gatt_characteristic_t*) malloc(num_characteristics * sizeof(bt_gatt_characteristic_t));

// get the characteristic list
number = bt_gatt_characteristics(instance, characteristicList, num_characteristics);

// find the characteristic with the heart rate measurement value UUID
for (int i = 0; i < characteristicListSize; i++) {

  if (strcmp(characteristicList[i].uuid, HEART_RATE_MEASUREMENT) == 0) {
      HrDataContainer* hrdc = HrDataContainer::getInstance();
      hrdc->setHrHandle(characteristicList[i].handle);
      hrdc->setHrValueHandle(characteristicList[i].value_handle);
      errno= 0;
      rc = bt_gatt_enable_notify(instance, &characteristicList[i], 1);
      ........

// free memory
if (characteristicList != NULL) {
    free(characteristicList);
    characteristicList = NULL;
}

Figure 23 - find characteristic handle and register for notifications  

 

Note that we use our HrDataContainer singleton to store details of the selected characteristic. Note also that we have two distinct handles for our characteristic. This is because a characteristic is a struct which itself has a handle but which contains a value sub-field, with its own handle. The following table from the Bluetooth core specification illustrates this: 

 

characteristic_spec.png 

 

All that remains now is to illustrate how we actually receive a notification. This is pretty simple, as illustrated in the next code fragment.

 

void notifications_cb(int instance, uint16_t handle, const uint8_t *val, uint16_t len, void *userData) {

	if (NULL == val)
		return;

	HrDataContainer* hrdc = HrDataContainer::getInstance();

	if (handle == hrdc->getHrValueHandle()) {
		hrdc->addNotification(val,len);
		hrdc->logMostRecentNotification();
	}

}

 Figure 24 - Receiving a GATT notification

 

As you can see, all we do is hand the value received to our HrDataContainer. This stores the data and triggers a UI update. Note that the handle parameter contains the value of the characteristic value as opposed to the containing characteristic struct as a whole.

 

Architecture

 
Of course we also had to consider our architecture. We arrived at the following, simple object model which we’ve presented here in the form of a class diagram. We suspect this pattern may be generally useful for similar applications that process Bluetooth LE notifications.

 

heart_monitor_class_diagram.png

Figure 25 - Simplified class diagram for HeartMonitor 

 

The HeartMonitor application in action!

 
So here’s what the application looks like:

 

device_discovery_and_selection.png 

Figure 26 - Device Discovery and Selection

 

Figure 26 shows the device discovery phase, initiated by selecting the Scan tab. 

 

the_3_test_cases.png 

Figure 27 – Monitoring

 

Figure 27 shows monitoring in progress, from three different test sessions. The first was produced by Martin. Most of the “session” was spent seated, hence the nice low heart rate. But to spice things up a bit, Martin went running... for a very short period of time, just enough to drive his heart rate up to 107 bpm!

 
The other two are more novel and were produced by John. He was actually able to patiently attach the Wahoo device, first to a live humming bird and secondly to a sleepy, hibernating hedgehog he found in his garden. The “blip” in the data was caused when the hedgehog stirred in its sleep as John attached the monitor.

 
OK, confession time  No humming birds or hedgehogs were involved in our testing other than in our imagination! John doesn’t have a heart rate monitor device so he was using a nice bit of kit that is able to *simulate* a real device and from the point of view of our app and the BlackBerry 10 smart phone, was running a Bluetooth heart rate service, indistinguishable from the real thing. So he was able to tweak the settings and generate data in whatever range he chose. Let the records show that neither John nor Martin would ever dream of disturbing a sleeping hedgehog!

 

Note about the UI

 
We said we wouldn’t dwell on user interface issues in this article, but we’d like to point out a few aspects of the HeartMonitor application that you might find useful or interesting.

 
The line graph, which updates in real time, was implemented as a Cascades WebView component and uses the excellent FLOT JavaScript® library. See http://www.flotcharts.org/

 

The heart rate value itself “pulses” at a rate equivalent to the current heart rate measurement. So as your heart beats faster, the value displayed pulses faster. We thought this was a nice touch! It uses Cascades animations to create the pulsing effect.

 

Additional tools

 
The supplementary tools mentioned in this article are:

 
Texas Instruments Wireless Connectivity Development Kit:

 

http://www.ti.com/tool/cc2540dk-mini?DCMP=blestack&HQS=ble-pr-lp

 
Bluegiga development kit DKBLE112/113: 

 

http://www.bluegiga.com/evaluation_BLE113 


We should just note that these tools are ones that we found useful in researching this area and our use of them shouldn’t be interpreted as an endorsement, whether implied or explicit, by BlackBerry.

 

Future releases

 
You will notice that there are some additional features lurking in the code for HeartMonitor but which are currently hidden from the UI. That’s because we didn’t finish everything we had in mind but didn’t want to delay releasing the application and article. So watch out for future releases of HeartMonitor in our GitHub repository.

 

Video

 

We created a video which mirrors this article too. You'll find it in our BlackBerry developer YouTube® channel:

 

http://youtu.be/dcrJZSL7dpg

 
Summary

 

We hope that this article helps you exploit the BlackBerry 10 Bluetooth APIs in your BlackBerry 10 applications.

 

You can download HeartMonitor, including its full source code from:

https://github.com/blackberry/Cascades-Community-Samples

HeartMonitor was written for the BlackBerry 10 Z10 and Q10 devices and requires the following versions of the NDK and device software to build and run:

 

  • BlackBerry 10 Native SDK 10.0.9
  • BlackBerry 10 Software 10.0.9

 
You can contact Martin or John either through the BlackBerry support forums or through Twitter®:

 

 

     
Martin mwoolley @mdwrim
John jomurray @jcmrim