Bluetooth LE Example on mbed - Health Thermometer!


Now the mbed Bluetooth APIs are almost complete and the mbed-enabled Nordic nRF51822 single-chip Bluetooth LE hardware is available for pre-order, here is a peek at how we can use the mbed BLE APIs to easily implement a Bluetooth LE Health Thermometer.

Before we get in to the details, this video shows the Bluetooth LE Health Thermometer program in action:

The Example

Here we are going to advertise and respond as a thermometer to devices that choose to connect to us. Bluetooth LE Health Thermometers implement the 'Health Thermometer' service, exposing temperature and other data from a BLE device intended for healthcare and fitness applications. Full details on the service can be found here:

Key Bluetooth LE Concepts

Lets get a basic understanding of BLE and it's core concepts:

BLE - Bluetooth Low Energy, also known as Bluetooth Smart, is a wireless protocol designed specifically for low power applications, such as those for healthcare, security, fitness and home entertainment / energy monitoring. BLE makes up one form of the Bluetooth 4 specification, the other form is Bluetooth Classic (or Bluetooth Basic Rate) which is that standard Bluetooth interface you will have used for connecting with Bluetooth headsets, transferring files to/from your mobile device, etc.

Generic Access Profile (GAP) - This is the first of two profiles that all BLE devices must implement. It defines the basic functionality required for a BLE device (physical/link layers, security, etc), and the basic characteristics of that device, such as device name, it's appearance/role (Heart Rate Monitor, keyboard, blood pressure monitor, etc), connection parameters, etc.

Generic Attribute Profile (GATT) - This is the second profile that all BLE devices must implement, and is the high-level protocol that BLE devices use to communicate. GATT defines two roles: Server and Client. The Server is typically the low power device that is exposing various services (e.g. temperature monitoring, speed monitoring, etc) which are consumed by a client (computer, mobile phone, etc).

Service - A service is a particular functionality set that a device supports. A single device can support multiple services - as stated above, GATT and GAP are two services that all BLE devices must support. In addition to these, a Heart Rate Monitor device would support 'Heart Rate' service and, perhaps, a 'Battery Service'. A full list of BLE services can be found on the Bluetooth Developer Portal here.

Characteristic - Each service has a set of characteristics assosicated with it, some mandatory and some optional. These characteristics define the data that is to be transmitted by that service. E.g. temperature measurement, temperature type, measurement interval, etc.

Bluetooth LE Health Thermometer Services

When a BLE client device requests the list of supported services and their characteristics from our Bluetooth LE Health Thermometer running on the Nordic nRF51822 evaluation board, it will receive the following:

  • Generic Access (GAP)
    • DeviceName Characteristic - The name of our device.
    • Appearance Characteristic - The role that this device takes. A full list of possible appearances can be found here.
    • SlavePreferredConnectionParameters Characteristic- Various parameters related to how this device connects to clients. See here.
  • Generic Attribute (GATT)
    • ServiceChanged Characteristic
  • Health Thermometer
    • Temperature Measurement Characteristic - This mandatory characteristic is used to present a temperature measurement to the client.
  • Battery Service
    • Battery Level Characteristic - this mandatory characteristic is used to read the battery level as a percentage.

For the sake of brievety we have not included in our example the optional Characteristics supported by the Health Thermometer service, the full list can be seen here.

Kit

We will be using the following to kit for our Bluetooth LE Health Thermometer:

In this example, I'm using a pre-production version of the board ahead of it being available.

Hardware Setup

We connect our TMP102 to the nRF51822 Eval Board using the Jumper wires:

TMP102 PinnRF51822 Eval Board Pin
GNDGND
V+VCC
SCLP0.20
SDAP0.22
ADD0GND

Our board and sensor will look like this:

/media/uploads/donalm/health_temperature_ble.jpg

Software

We will run through our example application from here:

Import programBLE_Health_Thermometer_Blog

An example BLE Health Thermometer using the mbed BLE API and Nordic NRF51822

We start off by creating instances of our nRF51822 and TMP102 objects. Along with some LEDs for indication:

main.cpp

nRF51822n   nrf;                               /* BLE radio driver */
TMP102      healthThemometer(p22, p20, 0x90);  /* The TMP102 connected to our board */

/* LEDs for indication: */
DigitalOut  oneSecondLed(LED1);        /* LED1 is toggled every second. */
DigitalOut  advertisingStateLed(LED2); /* LED2 is on when we are advertising, otherwise off. */

We then instantiate our Health Thermometer and Battery Level Service, including their mandatory characteristics. Note that we are not concerned with the GATT and GAP services, as they are automatically created by the BLE API interface.

main.cpp

/* Health Thermometer Service */ 
uint8_t             thermTempPayload[5] = { 0, 0, 0, 0, 0 };
GattService         thermService (GattService::UUID_HEALTH_THERMOMETER_SERVICE);
GattCharacteristic  thermTemp (GattCharacteristic::UUID_TEMPERATURE_MEASUREMENT_CHAR,
                               5, 5, GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_INDICATE);

/* Battery Level Service */
uint8_t            batt = 100;     /* Battery level */
uint8_t            read_batt = 0; /* Variable to hold battery level reads */
GattService        battService ( GattService::UUID_BATTERY_SERVICE );
GattCharacteristic battLevel   ( GattCharacteristic::UUID_BATTERY_LEVEL_CHAR, 1, 1,
                                 GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY |
                                 GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ);

Next up is the Advertising data and parameters, along with a list of services the device will support:

main.cpp

/* Advertising data and parameters */
GapAdvertisingData   advData;
GapAdvertisingData   scanResponse;
GapAdvertisingParams advParams ( GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED );

uint16_t             uuid16_list[] = {GattService::UUID_HEALTH_THERMOMETER_SERVICE,
                                      GattService::UUID_BATTERY_SERVICE};

The following is the application's main function. The code is straight forward enough: We initialise the BLE nrf object, adding the Appearance and various services that are supported. Then start advertising.

main.cpp

int main(void)
{
    
    /* Setup blinky led */
    oneSecondLed=1;
    
    /* Setup an event handler for GAP events i.e. Client/Server connection events. */
    nrf.getGap().setEventHandler(new GapEventHandler());
    
    /* Initialise the nRF51822 */
    nrf.init();

    /* Make sure we get a clean start */
    nrf.reset();    

    /* Add BLE-Only flag and complete service list to the advertising data */
    advData.addFlags(GapAdvertisingData::BREDR_NOT_SUPPORTED);
    advData.addData(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, 
                   (uint8_t*)uuid16_list, sizeof(uuid16_list));
    advData.addAppearance(GapAdvertisingData::GENERIC_THERMOMETER);
    nrf.getGap().setAdvertisingData(advData, scanResponse);

    /* Health Thermometer Service */
    thermService.addCharacteristic(thermTemp);
    nrf.getGattServer().addService(thermService);
    
    /* Add the Battery Level service */
    battService.addCharacteristic(battLevel);
    nrf.getGattServer().addService(battService);

    /* Start advertising (make sure you've added all your data first) */
    nrf.getGap().startAdvertising(advParams);
    advertisingStateLed = 1;
    

    for (;;)
    {
        /* Now that we're live, update the battery level & temperature characteristics */
        updateServiceValues();
        wait(1);
    }
}

Every second we update the values for the battery level and temperature characteristics:

main.cpp

void updateServiceValues(void)
{
      /* Toggle the one second LED */
      oneSecondLed = !oneSecondLed;
      
      /* Update battery level */
      nrf.getGattServer().updateValue(battLevel.handle, (uint8_t*)&batt, sizeof(batt));
      /* Decrement the battery level. */
      batt <=50 ? batt=100 : batt--;;
      
      /* Update the temperature. Note that we need to convert to an ieee11073 format float. */
      float temperature = healthThemometer.read();
      uint32_t temp_ieee11073 = quick_ieee11073_from_float(temperature);
      memcpy(thermTempPayload+1, &temp_ieee11073, 4);
      nrf.getGattServer().updateValue(thermTemp.handle, thermTempPayload, sizeof(thermTempPayload));
}

Finally we have our GAP events handler. When a client device connects to our device, our device will stop advertising. Once the client has disconnected, we need to instruct our device to start re-advertising.

main.cpp

class GapEventHandler : public GapEvents
{
    //virtual void onTimeout(void) {}   
     
    virtual void onConnected(void)
    {
        advertisingStateLed = 0;
    }

    /* When a client device disconnects we need to start advertising again. */
    virtual void onDisconnected(void)
    {
        nrf.getGap().startAdvertising(advParams);
        advertisingStateLed = 1;
    }
};

To test our Bluetooth LE Health Thermometer, we compile, download and flash our program onto the Nordic nRF51822 board using the standard mbed workflow. When the board is powered, we will see the one second LED blinking regularly, and the advertising LED switched permanently on. We can then launch the Nordic Utility mobile application, open the temperature app, and click connect. The advertising LED on the board will switch off and the app will show data from our board:

/media/uploads/donalm/temperature_monitor.jpg

Once we press the disconnect button on in the app, the app will stop displaying updates and the board's advertising LED will switch on again.

Final Notes

The whole mbed program is running on the single-chip Nordic Bluetooth device, and connecting up to the temperature sensor was as simple as pulling in a component from the component database, so hopefully this gives a good feel for how simple it should be to create a working Bluetooth LE device that can be made really cheaply.

If you want to get hold of one of the boards when they are released, take a look at:

Information

Check out the Bluetooth Developers site for a webinar about adding Bluetooth Smart using the mbed BLE API https://developer.bluetooth.org/DevelopmentResources/Pages/Webinars.aspx

12 comments on Bluetooth LE Example on mbed - Health Thermometer!:

28 Feb 2014

Sweet! Is this running natively on the nRF51822?

28 Feb 2014

Good~

28 Feb 2014

@Joris, yes it is!

07 Mar 2014

Do you know if it is possible to have the "mbed enabled" firmware. We have a project with these chips, and we'd like to switch from their native sdk to mbed IDE before we have written too much code. It doesn't matter if the firmware is only beta.

Thanks

07 Mar 2014

We are also starting a project with these chips and would love to be able to use mbed. Do you have a projected timeframe for when these boards will be available or even just the option to compile for this target as we have some dev boards with these chips at hand and could probably use the Segger tools to do the flashing.

Thanks

08 Mar 2014

Is there some way to add the mKit as a device yet? I would like to experiment with the code on a PCA10004 board using jlink to download the binary manually. However I can't find the "Add to your mbed compiler" link on the mKIT device page.

28 Mar 2014

Does the battery value get transmitted immediately upon calling nrf.getGattServer().updateValue(battLevel.handle, (uint8_t*)&batt, sizeof(batt)) or does the profile only send updates periodically according to some preset rate?

In our particular application, we are looking to send information with low latency but asynchronously driven by an interrupt (like a keyboard key press). Do only certain profiles support this functionality?

Thanks for any insight.

10 Apr 2014

Can I download code with my FRDM board based CMSIS-DAP debugger? And how?

17 Apr 2014

Can I flash the .HEX file generated from BLE_Health_Thermometer_Blog program onto a PCA10001 from the nRF51822 Development Kit??

17 Apr 2014

all i get when i compile and run the program is SWD ERROR file on the mbed

21 Apr 2014

Hi Donal, Are you using Nordic's SoftDevice or sometimes called the 'Soft Stack?'

if no, Where did the BLE stack come from?

if yes, Where can I find the BLE stack component in the SDK?

KK.

05 May 2014

It's working with the iOs app!