IoT Triad Part II – Mobile (Cordova), Device (nRF51822) and a bit of Cloud (ThingSpeak)

Introduction
Last year, I had written a rather ponderous article on the Device part of the IoT triad consisting of Device, Mobile and Cloud. This time, I want to focus on the Mobile part. In this article, I’ll go through the construction of a cross platform mobile app that displays heart rate measurement data sent by a Nordic nRF51822 device via BLE. I’ll throw in a bit of cloud as well – by periodically posting this data to thingspeak.
nRF51 Programming
There is no firmware development in this article. I’ll be assuming that you have an nRF51 device, and that you have programmed it with the ble_app_hrs example from Nordic.
If you are unfamiliar with nRF51 development, you might want to look through some of my previous articles on the subject:
- Hacking a Cheap LED Lamp with nRF51822
- nRF51822 Begins – nRF-DK, GCC, ADC, UART/BLE
- External nRF51822 SWD Programming using the nRF51-DK
- nRF51-DK PWM & GPIOTE test with S110 SoftDevice
- Talking to Ultrasonic Distance Sensor HC-SR04 using nRF51822
- Controlling an RGB LED with Nordic nRF51-DK (nRF51822/nRF51422)
- Motor Control over BLE with nRF51822 and TB6612FNG
- BLEBot – nRF51822 based BLE Robot
Now let’s look at the mobile part.
Cross Platform Mobile Apps
At the beginning of my career in the late 90s, I spent most of my time porting applications from Unix to Windows. Cross Platform development was a mess, and my job felt like taking apart one watch to stuff it into another, losing parts in the process. In 2016, writing portable code is still a wretched affair, but there is a glimmer of hope as Web Tech, with JavaScript at the helm, is emerging as the winner. (I don’t know about you, but I am tired of learning syntax for yet another programming language.)
For the Mobile universe, there are various cross platform solutions out there – PhoneGap, React Native, Ionic/Angular, Xamarin – just to name a few. After reading through some of this technology (and doubling my number of grey hairs), I decided to go with plain Cordova – which is actually the basis for many of the frameworks above, and uses HTML5, CSS and JavaScript to help you develop cross platform mobile apps. (For folks who want a quick start for their IoT devices, I recommend exploring evothings, which is also based on cordova.)
Mobile App Development
Now let’s get into the nitty-gritty of developing the mobile app.
Objective
Our goal is to create a mobile app that does the following:
- Scan for BLE devices
- Connect to Heart Rate Service on a device
- Enable notifications
- Display current heart rate
- Plot heart rate as a function of time
- Post data periodically to ThingSpeak
Let’s get on with the setup.
Cordova setup
I won’t cover cordova here, but the official documentation is quite good, and you can start there. For this project, the most important thing is that you need to get the cordova-plugin-ble-central plugin. Your setup will roughly look like the following:
$cordova create nrf51HRM com.electronut.nrf51HRM nrf51HRM
$cd nrf51HRM
$cordova plugin add cordova-plugin-ble-central
$cordova platform add android
The above was done on OS X which required sudo on cordova commands. (Fixable, but life is short, and there’s too much to do.) Also, a long term iPhone user, I finally decided to ditch iOS and get an Android (Nexus 5X) using this project as an excuse – the main reason being that I am tired of paying developer fees to the demigods at Infinite Loop just so I can put apps on my own darn device. If you use iOS, you will need to add that platform above.
Next, you’ll need to setup your platform (iOS or Android) for development. That’s beyond the scope of this article, but there are many resources to help you out there. Again, the Cordova official documentation has nice sections on platform setup.
After the setup, here’s what the directory structure looks like:
$ tree -L 2 nrf51HRM/
nrf51HRM/
├── config.xml
├── hooks
│ └── README.md
├── platforms
│ ├── android
│ └── platforms.json
├── plugins
│ ├── android.json
│ ├── cordova-plugin-ble-central
│ ├── cordova-plugin-compat
│ ├── cordova-plugin-whitelist
│ └── fetch.json
└── www
├── css
├── index.html
└── js
All our action is in the www directory. Once you are done with setup, you need to replace the contents of the www directory with my code which you can find in the Downloads section below.
Here’s what my www looks like:
$ tree -L 4 nrf51HRM/www
nrf51HRM/www
├── css
│ └── index.css
├── index.html
└── js
├── index.js
└── jquery
├── jquery.js
└── version.txt
index.html contains the layout of your app, which is styled by index.css. The logic for your app resides in index.js. We use jquery in this project, so you need that as well.
Now that you are setup, here’s how you build and upload the code to your mobile device:
$cordova build android
$cordova run android
I had trouble running this on the emulator, and BLE may not work with the emulator anyway. So I suggest that you just plug in the mobile and work with it right from start.
Debugging
Having a good debugging tool saves you a lot of time on development. Midway through this project, I was delighted to discover this Chrome trick – with your mobile connected via USB to your computer and running the app, go to chrome://inspect/?#devices on your computer. It will list your app, and you can click on inspect to get a developer console. How cool is that?! Here’s a session:

Now let’s look at the code.
The Layout
Everything starts at index.html, which starts by setting up some parameters and loading scripts inside <head>.
<head>
<meta charset="utf-8" />
<meta name="format-detection" content="telephone=no" />
<meta name="msapplication-tap-highlight" content="no" />
<meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height, target-densitydpi=device-dpi" />
<link rel="stylesheet" type="text/css" href="css/index.css" />
<title>Heart Rate</title>
<script type="text/javascript" src="cordova.js"></script>
<script type="text/javascript" src="js/jquery/jquery.js"></script>
<script type="text/javascript" src="js/index.js"></script>
</head>
You can see above where the JavaScript files are loaded. Now for the main content:
<body>
<div>
<h2>Heart Rate</h2>
<div id="beatsPerMinute">...</div>
<div id="statusDiv"></div>
<button id="button-connect" onclick="app.connectBtn()">CONNECT</button>
</div>
<canvas id="canvas" width="600" height="200"></canvas>
</body>
The layout above is simple – just a heading, <div>s for heart rate and status, a connect button, and an HTML5 canvas for drawing the graph.
Now let’s go to the action part.
JS Action
index.js is where the action happens. Let’s look at the important snippets within.
Talking to BLE
When you click the connect button, the BLE scan is started here:
app.scan = function() {
app.status("Scanning for Heart Rate Monitor");
// hanlder for scan success
function onScan(peripheral) {
// assume only one peripheral sending heart rate
console.log("Found " + JSON.stringify(peripheral));
app.status("Found " + peripheral.name);
// save peripheral
app.peripheral = peripheral;
// on successful connection
function onConnect(peripheral) {
app.status("Connected to " + peripheral.name);
// start heart rate notification
ble.startNotification(peripheral.id, '180D', '2A37', app.onData, app.onError);
// set flag
app.connected = true;
}
// on connection failure
function onFailure (reason) {
beatsPerMinute.innerHTML = "...";
console.log("disconnected: " + reason);
app.status("Disconnected!");
app.connected = false;
$('#button-connect').html('CONNECT');
}
// connect to peripheral
ble.connect(peripheral.id, onConnect, onFailure);
// set button text
$('#button-connect').html('DISCONNECT');
}
// handler for scan failure
function scanFailure(reason) {
app.status("Did not find a heart rate monitor.");
$('#button-connect').html('CONNECT');
}
// scan for heartrate service, 0x180D
// https://developer.bluetooth.org/gatt/services/Pages/ServiceViewer.aspx?u=org.bluetooth.service.heart_rate.xml
ble.scan(['180D'], 5, onScan, scanFailure);
};
If you are new to JavaScript, the “functions within functions” style of writing code as seen above might look incomprehensible, till you realize as Douglas Crockford said, that JavaScript is Lisp in C’s clothing. I highly recommend that you read his book JavaScript: The Good Parts to get a feel for the actual power of this language.
In the above code, ble.scan() starts looking for BLE devices, and we’re filtering for the service id 0x180D, which is a predefined bluetooth service for sending heart rate information. Each time a device with this service is found, the onConnect() function is called, which calls ble.startNotification() on the heart rate characteristic with id 0x2A37 – this is the actual heart rate measurement. Setting the notification will make the device send the data periodically to the app. This data will be made available through the onData() function passed in. Here’s what it looks like:
// called on receiving characteristic data
app.onData = function(buffer) {
var hrm = new Uint8Array(buffer);
// parse heart rate
// see:
// https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.heart_rate_measurement.xml
if(hrm[0] & 0x1) {
// 16-bit
app.heartRate = (hrm[2] << 8) + hrm[1];
}
else {
// 8-bit
app.heartRate = hrm[1];
}
// set heart rate display
beatsPerMinute.innerHTML = app.heartRate;
// draw graph
app.plot(app.heartRate);
};
To understand the format of the data sent by the heart rate measurement characteristic, please take a look at the official specification. The Nordic ble_app_hrs firmware sends heart rate in 16-bit format, and the code above handles it by combining data from the two adjacent bytes. Once parsed, the value is set to the text display and then passed on to the app.plot() function.
In addition to the above, the status messages are set based on various conditions, like connect, disconnect, failures, etc.
Now let’s look at plotting a graph using the heart rate data.
Drawing a Graph
For display the heart rate data, we create a “rolling graph” of points (represented as filled circles). As new data comes in, old data moves to the right.
Here’s the app.plot() function where the drawing to the HTML5 canvas happens.
app.plot = function(heartRate) {
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
var dataPoints = app.dataPoints;
var maxLen = 50;
// add data
dataPoints.push(heartRate);
// cap length
if (dataPoints.length > maxLen) {
// remove first
dataPoints.splice(0, 1);
}
// maximum value
var maxVal = 400;
function drawPoints(color)
{
// draw dots
context.fillStyle = color;
context.strokeStyle = color;
var x = 0;
for (var i = dataPoints.length-1; i> 0; i--) {
context.beginPath();
var y = canvas.height - (dataPoints[i] * canvas.height) / maxVal;
context.arc(x, y, 4, 0, 2 * Math.PI);
context.fill();
x += 10;
}
}
// clear previous
context.clearRect(0, 0, canvas.width, canvas.height);
drawPoints("green");
};
In the above code, new data is added to the dataPoints array as they come in. A splice is done when the count exceeds maxLen to keep the number of points drawn constant. The function drawPoints() does the actual drawing, using the arc() method of the canvas context. x+=10; ensures a certain distance between the points on the horizontal axis.
Now for some cloud mischief.
Posting Data to ThingSpeak
Thingspeak is a great platform for plotting your IoT data. It’s free, and lets you create channels for your data, and provides convenient export options for all your data. You’re allowed to update your channel every 15 seconds only – something to remember. Here’s the code that posts data there, from the $(document).ready() function:
// AJAX callback
function onDataReceived(jsonData) {
app.status("Thingspeak post: " + JSON.stringify(jsonData));
}
// AJAX error handler
function onError(){
app.status("Ajax error!");
}
// get data from server
function getData() {
if(app.connected) {
// prepare thingspeak URL
// set up a thingspeak channel and change the write key below
var key = 'IKYH9WWZLG5TVYF2'
var urlTS = 'https://api.thingspeak.com/update?api_key=' + key + '&field1=' + app.heartRate;
// make the AJAX call
$.ajax({
url: urlTS,
type: "GET",
dataType: "json",
success: onDataReceived,
error: onError
});
}
}
// define an update function
var count = 0;
function update() {
// get data
getData();
// set timeout - thingspeak can only update every 15 seconds
setTimeout(update, 16000);
}
// call update
update();
In the above code, we’re using a simple AJAX setup to make a ‘GET’ call into the thingspeak channel to update our channel. jquery makes this job easy. We use the setTimeout() method to call this code every 16 seconds. The data returned from the GET call is displayed in the status text.
You’ll need to setup your own thingspeak channel for testing, as I’ve changed the write API key to mine – no naughty business. 🙂
Here’s a sample output:

And thus we have our BLE device not only talking to our own mobile app, but using it as a conduit to post data on to the cloud yonder.
Downloads
You can get the complete source code for this project here:
https://github.com/electronut/nrf51-codova
In Action
Here’s the app in action:
Conclusion
We’ve touched on the Mobile part of the IoT triad by building a cross platform mobile application that talks to a BLE device. I think cordova is a good place to start – especially in an IoT context. The concepts learned here can be leveraged as you move to other platforms like Ionic/Angular.