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.
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
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!
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:
- Scan and find “VIBER” device
- Connect to “VIBER” device
- Find the service for the button
- Read the characteristic for the button
- Read the value for the button
- Decide if we should vibrate
- Disconnect and wait for 1000 ms before starting over
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!