This application transmits a heart rate value using the Bluetooth SIG Heart Rate Profile. The heart rate value is provided by the application itself, not by a sensor, so that you don't have to get a sensor just to run the example. The canonical source for this example lives at https://github.com/ARMmbed/mbed-os-example-ble/tree/master/BLE_HeartRate

BLE Heart Rate Monitor

This application transmits a heart rate value using the Bluetooth SIG Heart Rate Profile. The heart rate value is provided by the application itself, not by a sensor, so that you don't have to get a sensor just to run the example.

Technical details are better presented in the mbed Classic equivalent of this example.

Running the application

Requirements

To see the heart rate information on your phone, download Panobike for iOS or Android.

You could also use a generic BLE scanners:

- nRF Master Control Panel for Android.

- LightBlue for iPhone.

Hardware requirements are in the main readme.

Building instructions

Building with mbed-cli

If you'd like to use mbed-cli to build this, then you should refer to the main readme. The instructions here relate to using the developer.mbed.org Online Compiler

In order to build this example in the mbed Online Compiler, first import the example using the ‘Import’ button on the right hand side.

Next, select a platform to build for. This must either be a platform that supports BLE, for example the NRF51-DK, or one of the following:

List of platforms supporting Bluetooth Low Energy

Or you must also add a piece of hardware and the supporting library that includes a Bluetooth Low Energy driver for that hardware, for example the K64F or NUCLEO_F401RE with the X-NUCLEO-IDB05A1

List of components supporting Bluetooth Low Energy.

Once you have selected your platform, compile the example and drag and drop the resulting binary onto your board.

For general instructions on using the mbed Online Compiler, please see the mbed Handbook

Checking for success

Note: Screens captures depicted below show what is expected from this example if the scanner used is nRF Master Control Panel version 4.0.5. If you encounter any difficulties consider trying another scanner or another version of nRF Master Control Panel. Alternative scanners may require reference to their manuals.

  • Build the application and install it on your board as explained in the building instructions.
  • Open the BLE scanner on your phone.
  • Start a scan.

/media/uploads/vcoubard/start_scan.png

figure 1 How to start scan using nRF Master Control Panel 4.0.5

  • Find your device; it should be named `HRM`.

/media/uploads/vcoubard/scan_result.png

figure 2 Scan results using nRF Master Control Panel 4.0.5

  • Establish a connection with your device.

/media/uploads/vcoubard/connection.png

figure 3 How to establish a connection using Master Control Panel 4.0.5

  • Discover the services and the characteristics on the device. The *Heart Rate* service has the UUID `0x180D` and includes the *Heart Rate Measurement* characteristic which has the UUID `0x2A37`.

/media/uploads/vcoubard/discovery.png

figure 4 Representation of the Heart Rate service using Master Control Panel 4.0.5

  • Register for the notifications sent by the Heart Rate Measurement characteristic.

/media/uploads/vcoubard/register_to_notifications.png

figure 5 How to register to notifications using Master Control Panel 4.0.5

  • You should see the heart rate value change every half second. It begins at 100, goes up to 175 (in steps of 1), resets to 100 and so on.

/media/uploads/vcoubard/notifications.png

figure 6 Notifications view using Master Control Panel 4.0.5

Committer:
Vincent Coubard
Date:
Tue Jul 26 14:45:32 2016 +0100
Revision:
0:fb66cae3e9a1
Child:
1:72c60abef7e7
Update example at tag mbed-os-5.0.1-rc1

Who changed what in which revision?

UserRevisionLine numberNew contents of line
Vincent Coubard 0:fb66cae3e9a1 1 /* mbed Microcontroller Library
Vincent Coubard 0:fb66cae3e9a1 2 * Copyright (c) 2006-2015 ARM Limited
Vincent Coubard 0:fb66cae3e9a1 3 *
Vincent Coubard 0:fb66cae3e9a1 4 * Licensed under the Apache License, Version 2.0 (the "License");
Vincent Coubard 0:fb66cae3e9a1 5 * you may not use this file except in compliance with the License.
Vincent Coubard 0:fb66cae3e9a1 6 * You may obtain a copy of the License at
Vincent Coubard 0:fb66cae3e9a1 7 *
Vincent Coubard 0:fb66cae3e9a1 8 * http://www.apache.org/licenses/LICENSE-2.0
Vincent Coubard 0:fb66cae3e9a1 9 *
Vincent Coubard 0:fb66cae3e9a1 10 * Unless required by applicable law or agreed to in writing, software
Vincent Coubard 0:fb66cae3e9a1 11 * distributed under the License is distributed on an "AS IS" BASIS,
Vincent Coubard 0:fb66cae3e9a1 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Vincent Coubard 0:fb66cae3e9a1 13 * See the License for the specific language governing permissions and
Vincent Coubard 0:fb66cae3e9a1 14 * limitations under the License.
Vincent Coubard 0:fb66cae3e9a1 15 */
Vincent Coubard 0:fb66cae3e9a1 16
Vincent Coubard 0:fb66cae3e9a1 17 #include <mbed-events/events.h>
Vincent Coubard 0:fb66cae3e9a1 18 #include <mbed.h>
Vincent Coubard 0:fb66cae3e9a1 19 #include "ble/BLE.h"
Vincent Coubard 0:fb66cae3e9a1 20 #include "ble/Gap.h"
Vincent Coubard 0:fb66cae3e9a1 21 #include "ble/services/HeartRateService.h"
Vincent Coubard 0:fb66cae3e9a1 22
Vincent Coubard 0:fb66cae3e9a1 23 DigitalOut led1(LED1, 1);
Vincent Coubard 0:fb66cae3e9a1 24
Vincent Coubard 0:fb66cae3e9a1 25 const static char DEVICE_NAME[] = "HRM";
Vincent Coubard 0:fb66cae3e9a1 26 static const uint16_t uuid16_list[] = {GattService::UUID_HEART_RATE_SERVICE};
Vincent Coubard 0:fb66cae3e9a1 27
Vincent Coubard 0:fb66cae3e9a1 28 static uint8_t hrmCounter = 100; // init HRM to 100bps
Vincent Coubard 0:fb66cae3e9a1 29 static HeartRateService *hrServicePtr;
Vincent Coubard 0:fb66cae3e9a1 30
Vincent Coubard 0:fb66cae3e9a1 31 static EventQueue eventQueue(
Vincent Coubard 0:fb66cae3e9a1 32 /* event count */ 16 * /* event size */ 32
Vincent Coubard 0:fb66cae3e9a1 33 );
Vincent Coubard 0:fb66cae3e9a1 34
Vincent Coubard 0:fb66cae3e9a1 35 void disconnectionCallback(const Gap::DisconnectionCallbackParams_t *params)
Vincent Coubard 0:fb66cae3e9a1 36 {
Vincent Coubard 0:fb66cae3e9a1 37 BLE::Instance().gap().startAdvertising(); // restart advertising
Vincent Coubard 0:fb66cae3e9a1 38 }
Vincent Coubard 0:fb66cae3e9a1 39
Vincent Coubard 0:fb66cae3e9a1 40 void updateSensorValue() {
Vincent Coubard 0:fb66cae3e9a1 41 // Do blocking calls or whatever is necessary for sensor polling.
Vincent Coubard 0:fb66cae3e9a1 42 // In our case, we simply update the HRM measurement.
Vincent Coubard 0:fb66cae3e9a1 43 hrmCounter++;
Vincent Coubard 0:fb66cae3e9a1 44
Vincent Coubard 0:fb66cae3e9a1 45 // 100 <= HRM bps <=175
Vincent Coubard 0:fb66cae3e9a1 46 if (hrmCounter == 175) {
Vincent Coubard 0:fb66cae3e9a1 47 hrmCounter = 100;
Vincent Coubard 0:fb66cae3e9a1 48 }
Vincent Coubard 0:fb66cae3e9a1 49
Vincent Coubard 0:fb66cae3e9a1 50 hrServicePtr->updateHeartRate(hrmCounter);
Vincent Coubard 0:fb66cae3e9a1 51 }
Vincent Coubard 0:fb66cae3e9a1 52
Vincent Coubard 0:fb66cae3e9a1 53 void periodicCallback(void)
Vincent Coubard 0:fb66cae3e9a1 54 {
Vincent Coubard 0:fb66cae3e9a1 55 led1 = !led1; /* Do blinky on LED1 while we're waiting for BLE events */
Vincent Coubard 0:fb66cae3e9a1 56
Vincent Coubard 0:fb66cae3e9a1 57 if (BLE::Instance().getGapState().connected) {
Vincent Coubard 0:fb66cae3e9a1 58 eventQueue.post(updateSensorValue);
Vincent Coubard 0:fb66cae3e9a1 59 }
Vincent Coubard 0:fb66cae3e9a1 60 }
Vincent Coubard 0:fb66cae3e9a1 61
Vincent Coubard 0:fb66cae3e9a1 62 void onBleInitError(BLE &ble, ble_error_t error)
Vincent Coubard 0:fb66cae3e9a1 63 {
Vincent Coubard 0:fb66cae3e9a1 64 (void)ble;
Vincent Coubard 0:fb66cae3e9a1 65 (void)error;
Vincent Coubard 0:fb66cae3e9a1 66 /* Initialization error handling should go here */
Vincent Coubard 0:fb66cae3e9a1 67 }
Vincent Coubard 0:fb66cae3e9a1 68
Vincent Coubard 0:fb66cae3e9a1 69 void bleInitComplete(BLE::InitializationCompleteCallbackContext *params)
Vincent Coubard 0:fb66cae3e9a1 70 {
Vincent Coubard 0:fb66cae3e9a1 71 BLE& ble = params->ble;
Vincent Coubard 0:fb66cae3e9a1 72 ble_error_t error = params->error;
Vincent Coubard 0:fb66cae3e9a1 73
Vincent Coubard 0:fb66cae3e9a1 74 if (error != BLE_ERROR_NONE) {
Vincent Coubard 0:fb66cae3e9a1 75 onBleInitError(ble, error);
Vincent Coubard 0:fb66cae3e9a1 76 return;
Vincent Coubard 0:fb66cae3e9a1 77 }
Vincent Coubard 0:fb66cae3e9a1 78
Vincent Coubard 0:fb66cae3e9a1 79 if (ble.getInstanceID() != BLE::DEFAULT_INSTANCE) {
Vincent Coubard 0:fb66cae3e9a1 80 return;
Vincent Coubard 0:fb66cae3e9a1 81 }
Vincent Coubard 0:fb66cae3e9a1 82
Vincent Coubard 0:fb66cae3e9a1 83 ble.gap().onDisconnection(disconnectionCallback);
Vincent Coubard 0:fb66cae3e9a1 84
Vincent Coubard 0:fb66cae3e9a1 85 /* Setup primary service. */
Vincent Coubard 0:fb66cae3e9a1 86 hrServicePtr = new HeartRateService(ble, hrmCounter, HeartRateService::LOCATION_FINGER);
Vincent Coubard 0:fb66cae3e9a1 87
Vincent Coubard 0:fb66cae3e9a1 88 /* Setup advertising. */
Vincent Coubard 0:fb66cae3e9a1 89 ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
Vincent Coubard 0:fb66cae3e9a1 90 ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, (uint8_t *)uuid16_list, sizeof(uuid16_list));
Vincent Coubard 0:fb66cae3e9a1 91 ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::GENERIC_HEART_RATE_SENSOR);
Vincent Coubard 0:fb66cae3e9a1 92 ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)DEVICE_NAME, sizeof(DEVICE_NAME));
Vincent Coubard 0:fb66cae3e9a1 93 ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
Vincent Coubard 0:fb66cae3e9a1 94 ble.gap().setAdvertisingInterval(1000); /* 1000ms */
Vincent Coubard 0:fb66cae3e9a1 95 ble.gap().startAdvertising();
Vincent Coubard 0:fb66cae3e9a1 96 }
Vincent Coubard 0:fb66cae3e9a1 97
Vincent Coubard 0:fb66cae3e9a1 98 void scheduleBleEventsProcessing(BLE::OnEventsToProcessCallbackContext* context) {
Vincent Coubard 0:fb66cae3e9a1 99 BLE &ble = BLE::Instance();
Vincent Coubard 0:fb66cae3e9a1 100 eventQueue.post(Callback<void()>(&ble, &BLE::processEvents));
Vincent Coubard 0:fb66cae3e9a1 101 }
Vincent Coubard 0:fb66cae3e9a1 102
Vincent Coubard 0:fb66cae3e9a1 103 int main()
Vincent Coubard 0:fb66cae3e9a1 104 {
Vincent Coubard 0:fb66cae3e9a1 105 eventQueue.post_every(periodicCallback, 500);
Vincent Coubard 0:fb66cae3e9a1 106
Vincent Coubard 0:fb66cae3e9a1 107 BLE &ble = BLE::Instance();
Vincent Coubard 0:fb66cae3e9a1 108 ble.onEventsToProcess(scheduleBleEventsProcessing);
Vincent Coubard 0:fb66cae3e9a1 109 ble.init(bleInitComplete);
Vincent Coubard 0:fb66cae3e9a1 110
Vincent Coubard 0:fb66cae3e9a1 111 while (true) {
Vincent Coubard 0:fb66cae3e9a1 112 eventQueue.dispatch();
Vincent Coubard 0:fb66cae3e9a1 113 }
Vincent Coubard 0:fb66cae3e9a1 114
Vincent Coubard 0:fb66cae3e9a1 115 return 0;
Vincent Coubard 0:fb66cae3e9a1 116 }