Web Bluetooth meets ARM mbed OS

Göran KrampeBlogs, Tutorials

mbedos
Tweet about this on TwitterShare on FacebookShare on LinkedInShare on Google+

At Evothings we are following the new mbed OS (not to be confused with the old mbed “classic”) closely as we think it is perhaps the most promising professional eco system for making embedded IoT software that works across many different devices. We are also looking at making integrations with the mbed OS toolset in the Evothings Workbench!

In this tutorial we are going through the “moves” in building a mobile hybrid app in JavaScript that runs on both iOS and Android and talks BLE using the new Web Bluetooth API to communicate with an nRF51-DK running a simple mbed OS application.

I am using Ubuntu 14.04 (64 bit Linux) in this tutorial, but I have also added notes for doing it all from OSX.

Diving into mbed OS

Let’s start on the embedded side. In order to build mbed OS applications we need to install the tools for it. The people at ARM mbed have been very nice to produce clear and precise installation instructions for Linux, no sweat, I just followed it.

NOTE OSX: I also ran this on my OSX machine (10.9.5) and there I followed the manual homebrew installation. I did stumble on a b0rken pip, but a quick brew --link overwrite python got me through that one, your mileage may vary!

This means we now have a proper GCC for ARM and we have the yotta (or yt for less typing) build tool. We can even enable command completion for it. Let’s do the good ol Blinky to get started, we are basically following the official documentation:

$ mkdir blinky
$ cd blinky
$ yt init 
Enter the module name:  
Enter the initial version: <0.0.0> 
Is this an executable (instead of a re-usable library module)?  yes
Short description: Blinky, blinky little star
Author: Göran Krampe
What is the license for this project (Apache-2.0, ISC, MIT etc.)?  

$ tree
.
├── module.json
├── source
└── test

2 directories, 1 file

When running yt init just make sure to answer “yes” about this being an executable.
As can be seen the above questions produces a module.json file containing the collected metadata:

{
  "name": "blinky",
  "version": "0.0.0",
  "bin": "./source",
  "private": true,
  "description": "Blinky, blinky little star",
  "author": "G\u00f6ran Krampe",
  "license": "Apache-2.0",
  "dependencies": {}
}

Of course, we could have created this file manually too, it’s just a JSON file.

Next up we need to select a supported target for this project. I tried using the recently created nrf52dk-gcc target together with the newer nRF52-DK but… it’s not yet an operational target for mbed, but I did chat a bit with a friendly developer at Nordic who is working on the mbed OS support for it and I suspect it will soon arrive since the 52 seems to be better in all respects.

mbed-nordic-nrf51-dk

Thus we fall back on the venerable Nordic Semiconductor nRF51-DK. We can look for valid targets using yotta:

$ yotta search target nrf51dk
nrf51dk-gcc 1.0.0:
    Official mbed build target for the nRF51-DK 32KB platform.
    mbed-official, mbed-target:nrf51_dk, gcc
nrf51dk-armcc 1.0.0:
    Official mbed build target for the nRF51-DK 32KB platform.
    mbed-official, mbed-target:nrf51_dk, armcc
$ 

We select the first one and enable it like below. At this point you will probably be asked to register/login to mbed etc, and … yes, you will need to get that done :)

Then it should eventually spit out something like this:

$ yotta target nrf51dk-gcc
info: get versions for nrf51dk-gcc
info: download nrf51dk-gcc@1.0.0 from the public module registry
info: get versions for nordic-nrf51822-gcc
info: download nordic-nrf51822-gcc@1.0.0 from the public module registry
info: get versions for mbed-gcc
info: download mbed-gcc@1.1.0 from the public module registry
$

Let’s also just verify the target is now set:

$ yotta target
nrf51dk-gcc 1.0.0
nordic-nrf51822-gcc 1.0.0
mbed-gcc 1.1.0
$

Here we see that there is a yotta_targets sub directory with three different targets in it, inheriting each other. The mbed-gcc target is an abstract target for all GCC based targets and is inherited by nordic-nrf51822-gcc which is also abstract and covers building for devices using this chip. The nrf51dk-gcc inherits from nordic-nrf51822-gcc and is the concrete target we are using for our nRF51-DK device.

Evidently any mbed app needs the mbed-drivers as a dependency and we can add that using… you guessed it, yotta:

$ yotta install mbed-drivers
info: get versions for mbed-drivers
info: download mbed-drivers@0.12.1 from the public module registry
info: dependency mbed-drivers: ~0.12.1 written to module.json
info: get versions for mbed-hal
info: download mbed-hal@1.2.2 from the public module registry
info: get versions for cmsis-core
info: download cmsis-core@1.1.2 from the public module registry
info: get versions for ualloc
info: download ualloc@1.0.3 from the public module registry
info: get versions for minar
info: download minar@1.0.4 from the public module registry
info: get versions for core-util
info: download core-util@1.3.0 from the public module registry
info: get versions for compiler-polyfill
info: download compiler-polyfill@1.2.1 from the public module registry
info: get versions for mbed-hal-nordic
info: download mbed-hal-nordic@2.0.0 from the public module registry
info: get versions for mbed-hal-nrf51822-mcu
info: download mbed-hal-nrf51822-mcu@2.1.5 from the public module registry
info: get versions for nrf51-sdk
info: download nrf51-sdk@2.2.1 from the public module registry
info: get versions for mbed-hal-nrf51dk
info: download mbed-hal-nrf51dk@2.0.0 from the public module registry
info: get versions for cmsis-core-nordic
info: download cmsis-core-nordic@1.0.1 from the public module registry
info: get versions for cmsis-core-nrf51822
info: download cmsis-core-nrf51822@1.3.2 from the public module registry
info: get versions for dlmalloc
info: download dlmalloc@1.0.0 from the public module registry
info: get versions for minar-platform
info: download minar-platform@1.0.0 from the public module registry
info: get versions for minar-platform-mbed
info: download minar-platform-mbed@1.1.2 from the public module registry
$

Now we can verify that yotta indeed installed a whole bunch of modules and also added this dependency to our modules.json file:

$ ls yotta_modules/
cmsis-core           core-util     mbed-hal-nordic        minar-platform
cmsis-core-nordic    dlmalloc      mbed-hal-nrf51822-mcu  minar-platform-mbed
cmsis-core-nrf51822  mbed-drivers  mbed-hal-nrf51dk       nrf51-sdk
compiler-polyfill    mbed-hal      minar                  ualloc
$ cat module.json 
{
  "name": "blinky",
  "version": "0.0.0",
  "bin": "./source",
  "private": true,
  "description": "Blinky, blinky little star",
  "author": "G\u00f6ran Krampe",
  "license": "Apache-2.0",
  "dependencies": {
    "mbed-drivers": "~0.12.1"
  }
}
$

Of course, we also need some code for blinky, shamefully ripped from mbed’s documentation we add a file source/app.cpp with this content:

#include "mbed-drivers/mbed.h"

static void blinky(void) {
    static DigitalOut led(LED1);
    led = !led;
    printf("LED = %d \r\n",led.read());
}

void app_start(int, char**) {
    minar::Scheduler::postCallback(blinky).period(minar::milliseconds(500));
}

Comparing the above code with Arduino style sketches we can conclude some obvious details:

  • ARM and mbed buys into C++ more clearly and is a bit less worried about verbosity :)
  • We have a more regular startup where we ourselves can set up periodic callbacks instead of doing all in a single loop() function.
  • The stronger C++ focus is clearly also seen with the DigitalOut class that implements the = operator

C++

Build it!

Finally, time to build. As you might have realized by now, yotta is our swiss army chain saw – it not only performs duties to install and maintain dependencies, it also handles the build for us. Underneath the hood it generates CMake files which in turn generates Ninja “make files” to drive the actual compilation and linking process. When we run yt build it should hopefully end with something like this at the end:

[118/118] Linking CXX executable source/blinky
Memory usage for 'blinky'
section             size
.data                128
.bss                 760
.heap              19592
.stack              2048

And we can verify that we got some binaries out of this:

$ ls -lart build/nrf51dk-gcc/source/
total 1072
-rw-rw-r-- 1 gokr gokr    707 Feb 22 13:27 CMakeLists.txt
-rw-rw-r-- 1 gokr gokr    319 Feb 22 13:27 CTestTestfile.cmake
-rw-rw-r-- 1 gokr gokr   1017 Feb 22 13:27 cmake_install.cmake
drwxrwxr-x 3 gokr gokr   4096 Feb 22 13:27 CMakeFiles
drwxrwxr-x 6 gokr gokr   4096 Feb 22 13:27 ..
-rw-rw-r-- 1 gokr gokr 245931 Feb 22 13:27 blinky.map
-rwxrwxr-x 1 gokr gokr 391064 Feb 22 13:27 blinky
-rw-rw-r-- 1 gokr gokr  72388 Feb 22 13:27 blinky.hex
-rw-rw-r-- 1 gokr gokr 372516 Feb 22 13:27 blinky-combined.hex
-rwxrwxr-x 1 gokr gokr  25708 Feb 22 13:27 blinky.bin

…which one to use? Seems to be four reasonable candidates, and I am not sure how you are meant to know which one… aaaah, the suspense! Read on to find out!

Prepping the board

Before we can flash the binary by simply copying over the correct file to the board mounted as a USB drive, we first need to perform a firmware update of the board to “mbed enable” it. This particular step was not obvious to me when reading the mbed OS documentation! To seasoned mbed classic developers it’s probably obvious.

This firmware is referred to as an “mbed interface upgrade file”. Hook up your nRF51DK with a USB cable and follow the steps under the heading “Firmware Update” and you should end up with the board mounted as “MBED”, it worked fine for me at least, on Ubuntu Linux that is, I haven’t tried the procedure on OSX.

Now we can simply copy a file (which one? which one?) over to this device in order to flash it, and press the reset button to get blinking glory!

Flashing blinky

So… in the build/nrf51dk-gcc/source/ directory we have the following to choose from:

-rwxrwxr-x 1 gokr gokr 391064 Feb 17 08:38 blinky
-rw-rw-r-- 1 gokr gokr  72388 Feb 17 08:38 blinky.hex
-rw-rw-r-- 1 gokr gokr 372516 Feb 17 08:38 blinky-combined.hex
-rwxrwxr-x 1 gokr gokr  25708 Feb 17 08:38 blinky.bin

I tried blinky.bin and blinky.hex before caving in and asking someone, but of course, the correct choice was blinky-combined.hex! Oh well, you learn as long as you live :)

cp build/nrf51dk-gcc/source/blinky-combined.hex /media/gokr/MBED/

…and then pressing the reset button on the nRF51DK, and we have blinking!

muahaha

Ok, yeah, not that impressive, but still :)

NOTE OSX: Flashing works fine, just replace /media/gokr/MBED with /Volumes/MBED/.

Stepping up the game

Now we are in the zone and know how to hack code. Let’s combine some sample code so that we can control the board over BLE instead!

I cheated of course and went hunting for sample code. Obviously the BLE examples sounded promising. Then I took the code from BLE LED and BLE Button and combined them.

At first when I tried yt build I got an error:

$ yt build
...blabla...
/home/gokr/blinky/source/main.cpp:18:21: fatal error: ble/BLE.h: No such file or directory
compilation terminated.
ninja: build stopped: subcommand failed.
error: command ['ninja'] failed
$

Aha… dependencies! Yotta to the rescue:

$ yt install ble
info: get versions for ble
info: download ble@2.5.0 from the public module registry
info: dependency ble: ^2.5.0 written to module.json
info: get versions for ble-nrf51822
info: download ble-nrf51822@2.5.0 from the public module registry
$ 

A bunch of warnings later – success!

Ok, so… at this point I am not going to force you to copy/paste anymore, let’s just back up a level out of the blinky directory and clone my demos repo fork, go into mbedos-nrf51dk-webbluetooth and build it:

$ git clone https://github.com/gokr/evothings-demos.git
$ cd evothings-demos/mbedos-nrf51dk-webbluetooth
$ yt build

You can take a look at the code of course, it’s just an adapted/combined variant of the example code from ARM which both has a blink-a-LED service and a read-a-button service. And smack it onto the nRF51DK:

cp build/nrf51dk-gcc/source/viber-combined.hex /media/gokr/MBED/

To get it running you should also press the reset button on the nRF51DK, it should start blinking to show its alive.

Going Mobile

Now we want to talk to this puppy using a mobile Evothings application. The new Evothings 2.1 has support for ES6 (ECMAScript 2015) and Web Bluetooth! It’s available as a first working alpha release and you can install (unzip somewhere) it alongside the regular 2.0 version without problems.

In the git clone we have a directory called evothings, in there is the Evothings hybrid mobile application. So first you need to download and install the 2.1 version of the Workbench. Just unzip it in your home directory and start it up by running ./EvothingsWorkbench in tere. Proceed by following the instructions in the Connect tab to get a phone/tablet connected.

Next you can open up the Ubuntu file browser tool, find the evothings-deoms/mbedos-nrf51dk-webbluetooth/evothings/evothings.json file and drag it onto the Workbench. This should get the application listed under the MyApps tab.

Now you can press RUN on it, and it should fire up on your phone. When playing with the application it is useful to also have the Javascript Workbench open, so press the “Tools” button in the upper right corner to open it. In the bottom pane you will see any logging that is done in the mobile app.

Ok, so where is the code then? It’s the evothings/app/app.js file which you can also view on github. This sourcecode is written in ES6 (ECMAScript 2015) and when you edit and save it using your favorite editor the Evothings Workbench will notice and rebuild it using Babel and place the resulting file in evothings/www/app.js and also trigger a reload of it on the phone!

If you have your nRF51DK running as described above, it should appear as “VIBER” in any BLE scanner, and have two primary services – one for manipulating the LED and one for reading the state of the button (the one closest to the center of the board).

The mobile application, when you press start, does the following:

  1. Scan and find “VIBER” device
  2. Connect to “VIBER” device
  3. Find the service for the button
  4. Read the characteristic for the button
  5. Read the value for the button
  6. Decide if we should vibrate
  7. Disconnect and wait for 1000 ms before starting over

Viber

Crazy app! But… a bit interesting to see how it behaves with say 5-10 phones running it. In theory they should all (eventually) start to vibrate if the button is held down on the nRF51DK, and stop vibrating when its released. So we get a sense of how a crowd of BLE central devices can “poll” a BLE device for some state. We could also do something with the LED of course, but that’s left as an exercise.

BEWARE: This application sometimes behaves fine and sometimes very much less so getting stuck etc. I have not yet managed to figure out exactly why!

Conclusion

Building and coding for mbed OS was not hard to get starteed with. I like the command line focus and the yotta tool is quite neat. It should only get better with more targets and the libraries seem to be growing quite strong.

The new Web Bluetooth API is interesting and will most surely be established as a standard way of using BLE from JavaScript, in a hybrid mobile app, or in a browser. It’s very Early In The Game but we are working closely with its development.

And Evothings Workbench? It rocks of course, but I am partial since I work at Evothings :)

Hope you found this article interesting, download Evothings Studio 2.1 alpha today and start hacking Web Bluetooth!

download