Posted by Donal Morrissey
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)
- 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:
- 1 x mbed-enabled Nordic nRF51822 board - Pre-order Now!!
- 1 x TMP102 temperature sensor with jumper wires
- A Bluetooth LE enabled iOS or Android device running the free Nordic nRF Utility mobile app
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 Pin | nRF51822 Eval Board Pin |
---|---|
GND | GND |
V+ | VCC |
SCL | P0.20 |
SDA | P0.22 |
ADD0 | GND |
Our board and sensor will look like this:
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:
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