Creating mbed OS 3.0 BLE applications

This chapter explains how to use BLE on mbed OS 3.0 for mbed-enabled boards.

The BLE libraries in mbed OS 3.0 work as they did in mbed 2.0: the BLE API abstracts the BLE protocol, so that no matter which manufacturer’s stack you’re using, the API remains the same and you don’t need to rewrite your code. By moving to yotta, however, we have gained the ability to switch between API implementations depending on which target we select.

Instructions for the impatient

If you want to jump straight in - and already have some background with mbed or BLE - this section allows you to get a BLE example running on an existing supported mbed OS 3.0 board.

  1. Install yotta by following the docs here.
  2. Clone the ble-examples repository:
    git clone https://github.com/ARMmbed/ble-examples.git
  3. Choose your example (here, we use the heart rate example):
    cd ble-examples/BLE_Heartrate/
  4. Choose your target:
    yt target bbc-microbit-gcc #or any of the other supported targets; see below
  5. Build the example for the target:
    yt build
  6. Copy the built file onto your device:
    cp build/bbc-microbit-gcc/source/BLE_Beacon-combined.hex /Volumes/MICROBIT/ #Update the path if you’re not on Mac OS X

Verify that the program is running by scanning for a Heart Rate monitor with a BLE scanning app on your computer or phone (see below for more).

Migrating from mbed OS 2.0 and the original mbed IDE

You have to understand a few basic concepts of programming for mbed OS 3.0 if you want to make the transition from mbed OS 2.0, because the two are very different. You can then either write new applications, or port your mbed OS 2.0 applications to mbed OS 3.0.

Note: To learn more about mbed OS 3.0, see the user guide.

Moving to asynchronous programming

In mbed OS 2.0, all application callbacks execute in handler mode (interrupt context). mbed OS 3.0 comes with its own scheduler, MINAR, which encourages an asynchronous programming style based on thread-mode callbacks (non-interrupt user context). With mbed OS 3.0, application code is made up entirely of callback handlers. We don’t even expose main() to users; instead; you should use app_start(). Please refer to MINAR’s documentation to understand its impact.

Tip: An expended version of the MINAR documentation is avaialble in the mbed OS 3.0 user guide.

Guidelines for application code

If you’re porting an mbed OS 2.0 application to mbed OS 3.0, please:

  • Replace main() with void app_start(int argc, char *argv[]). app_start() receives control after system initialization, but should finish quickly without blocking (like any other callback handler). If application initialization needs to issue blocking calls, app_start() can pend callbacks for later activity.

  • Unlike the former main(), app_start() should not finish with an infinite wait loop for system events or for entering sleep. mbed OS 3.0 expects app_start() to return quickly and yield to the scheduler for callback execution. The application should handle events by posting callbacks, and when there are no pending callbacks the system is automatically put to low-power sleep (the scheduler implicitly calls the equivalent of BLE::waitForEvent()). Please remove any equivalent of the following from your app_start():

    while (true) { ble.waitForEvent(); }

  • If you expect objects to persist across callbacks, you need to allocate them either from the global static context or from the free-store (that is, using malloc() or new()). This is similar to the situation in mbed OS 2.0 - the key difference is that objects allocated in app_start() do not persist over the lifetime of the programme as they would if created in main().

  • Migrate your applications to newer system APIs. For instance, with mbed OS 2.0, applications use the Ticker to post time-deferred callbacks. You should now use MINAR’s postCallback APIs directly. Refer to https://github.com/ARMmbed/minar#using-events. The replacement code would look something like:

    minar::Scheduler::postCallback(callback).delay(minar::milliseconds(DELAY));

    Or, if we are more explicit:

    Event e(FunctionPointer0<void>(callback).bind()); minar::Scheduler::postCallback(e).delay(minar::milliseconds(DELAY));

    Using MINAR to schedule callbacks means that the callback handler executes in thread mode (non-interrupt context), which results in a more stable system.

    Again, you might find it useful to study the documentation covering MINAR.

Including BLE functionality in an application

To help reduce the size of applications that use only other connectivity methods, mbed OS 3.0 doesn’t include BLE automatically. In fact, BLE functionality is ‘just another module’ to an mbed OS 3.0 application. To use BLE in your application, you will therefore need to explicitly include the BLE API in both the application code and the project’s module.json file (as you’ll see below).

General BLE functionality

To include BLE functionality, add the following to your application’s `main.cpp file:

#include "ble/BLE.h"

If you’re using one of the standard Bluetooth services that come with BLE API, include its header as well:

#include "ble/services/iBeacon.h" 

You will also need to add these dependencies to your project’s module.json file:

"dependencies": {
    "ble": "^2.0.0" 
}

Tip: ble has mbed-drivers as its own dependency, so there is no need to explicitly list mbed-drivers in module.json.

The version qualification for the BLE dependency (above) indicates that any implementation of ble at major API version 2 would suffice. Ideally, new applications should depend on the latest version of BLE, which can be deduced from the module.json of the master branch of the ble repository on Github: https://github.com/ARMmbed/ble/blob/master/module.json#L3

For more information about versions in module.json, please see the mbed OS 3.0 User Guide.

BLE profiles and services

The BLE API offers standard BLE profiles and services through headers in the ble module. These include GATT and GAP functionality and some of the BLE services.

Here is the BLE Heart Rate example including the headers it requires:

#include "mbed.h"
#include "ble/BLE.h"
#include "ble/Gap.h"
#include "ble/services/HeartRateService.h"

Where next

A good way to understand the difference between mbed OS 3.0 and mbed OS 2.0 BLE applications is to compare their examples.

You might also want to look at the mbed OS 3.0 User Guide.