Heart Rate Monitor example for the BLE API using ST BlueNRG native drivers
Dependencies: BLE_API X_NUCLEO_IDB0XA1 mbed
Getting started with X-NUCLEO-IDB05A1
This example demonstrates how to use the X-NUCLEO-IDB05A1 component with one of the STM32 Nucleo platforms tested with the to obtain heart rate information:
First, attach the X-NUCLEO-IDB05A1 to the STM32 Nucleo platform as shown here:
Next, connect the STM32 Nucleo platform to your PC with a USB-mini USB cable.
Now return to the BLE_HeartRate_IDB0XA1 project homepage and import the project into your mbed compiler:
Click “Import” button on the pop up window shown below
Compile and load the image onto the STM32 Nucleo platform
Upon a successful compilation, a binary image will be created and a dialog box will ask you where to save it.
Now you can load the compiled code onto your Nucleo platform by saving the newly created binary file to your STM32 Nucleo drive.
Open a terminal window to display the status of your Bluetooth stack. For this example, set the terminal BAUD rate to 9600
Note
Included within the USB interface of the STM32 Nucleo platform is a Virtual COM port that can be used to send back messages to your PC within your embedded code.
You need a terminal emulator installed on your PC to perform serial communications with your STM32 Nucleo platform. If you do not have a terminal emulation program on your PC we recommend you download and install one of the following terminal emulation programs:
Upon a successful serial connection, you should now see the various Bluetooth stack status displayed on your console output.
Test your application with an Android or iOS device
- Install STM32 BLE Profiles available on Google Play and iTunes App Store
- On the STM32 BLE Profiles app select HeartRateSensor
- Select Heart Rate
main.cpp@1:11cd427bbc8b, 2015-09-29 (annotated)
- Committer:
- apalmieri
- Date:
- Tue Sep 29 09:11:55 2015 +0000
- Revision:
- 1:11cd427bbc8b
- Parent:
- 0:eb7f02ad28a7
- Child:
- 2:bc0c0d442a24
Do not use LED when compiling for 'IDB0XA1_D13_PATCH'
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
screamer | 0:eb7f02ad28a7 | 1 | /* mbed Microcontroller Library |
screamer | 0:eb7f02ad28a7 | 2 | * Copyright (c) 2006-2015 ARM Limited |
screamer | 0:eb7f02ad28a7 | 3 | * |
screamer | 0:eb7f02ad28a7 | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
screamer | 0:eb7f02ad28a7 | 5 | * you may not use this file except in compliance with the License. |
screamer | 0:eb7f02ad28a7 | 6 | * You may obtain a copy of the License at |
screamer | 0:eb7f02ad28a7 | 7 | * |
screamer | 0:eb7f02ad28a7 | 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
screamer | 0:eb7f02ad28a7 | 9 | * |
screamer | 0:eb7f02ad28a7 | 10 | * Unless required by applicable law or agreed to in writing, software |
screamer | 0:eb7f02ad28a7 | 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
screamer | 0:eb7f02ad28a7 | 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
screamer | 0:eb7f02ad28a7 | 13 | * See the License for the specific language governing permissions and |
screamer | 0:eb7f02ad28a7 | 14 | * limitations under the License. |
screamer | 0:eb7f02ad28a7 | 15 | */ |
screamer | 0:eb7f02ad28a7 | 16 | |
screamer | 0:eb7f02ad28a7 | 17 | #include "mbed.h" |
screamer | 0:eb7f02ad28a7 | 18 | #include "ble/BLE.h" |
screamer | 0:eb7f02ad28a7 | 19 | #include "ble/services/HeartRateService.h" |
screamer | 0:eb7f02ad28a7 | 20 | #include "ble/services/BatteryService.h" |
screamer | 0:eb7f02ad28a7 | 21 | #include "ble/services/DeviceInformationService.h" |
screamer | 0:eb7f02ad28a7 | 22 | |
screamer | 0:eb7f02ad28a7 | 23 | BLE ble; |
apalmieri | 1:11cd427bbc8b | 24 | |
apalmieri | 1:11cd427bbc8b | 25 | #if !defined(IDB0XA1_D13_PATCH) |
screamer | 0:eb7f02ad28a7 | 26 | DigitalOut led1(LED1); |
apalmieri | 1:11cd427bbc8b | 27 | #endif |
screamer | 0:eb7f02ad28a7 | 28 | |
screamer | 0:eb7f02ad28a7 | 29 | const static char DEVICE_NAME[] = "HRM1"; |
screamer | 0:eb7f02ad28a7 | 30 | static const uint16_t uuid16_list[] = {GattService::UUID_HEART_RATE_SERVICE, |
screamer | 0:eb7f02ad28a7 | 31 | GattService::UUID_DEVICE_INFORMATION_SERVICE}; |
screamer | 0:eb7f02ad28a7 | 32 | static volatile bool triggerSensorPolling = false; |
screamer | 0:eb7f02ad28a7 | 33 | |
screamer | 0:eb7f02ad28a7 | 34 | void disconnectionCallback(Gap::Handle_t handle, Gap::DisconnectionReason_t reason) |
screamer | 0:eb7f02ad28a7 | 35 | { |
screamer | 0:eb7f02ad28a7 | 36 | ble.gap().startAdvertising(); // restart advertising |
screamer | 0:eb7f02ad28a7 | 37 | } |
screamer | 0:eb7f02ad28a7 | 38 | |
screamer | 0:eb7f02ad28a7 | 39 | void periodicCallback(void) |
screamer | 0:eb7f02ad28a7 | 40 | { |
apalmieri | 1:11cd427bbc8b | 41 | |
apalmieri | 1:11cd427bbc8b | 42 | #if !defined(IDB0XA1_D13_PATCH) |
screamer | 0:eb7f02ad28a7 | 43 | led1 = !led1; /* Do blinky on LED1 while we're waiting for BLE events */ |
apalmieri | 1:11cd427bbc8b | 44 | #endif |
screamer | 0:eb7f02ad28a7 | 45 | /* Note that the periodicCallback() executes in interrupt context, so it is safer to do |
screamer | 0:eb7f02ad28a7 | 46 | * heavy-weight sensor polling from the main thread. */ |
screamer | 0:eb7f02ad28a7 | 47 | triggerSensorPolling = true; |
screamer | 0:eb7f02ad28a7 | 48 | } |
screamer | 0:eb7f02ad28a7 | 49 | |
screamer | 0:eb7f02ad28a7 | 50 | int main(void) |
screamer | 0:eb7f02ad28a7 | 51 | { |
apalmieri | 1:11cd427bbc8b | 52 | |
apalmieri | 1:11cd427bbc8b | 53 | #if !defined(IDB0XA1_D13_PATCH) |
screamer | 0:eb7f02ad28a7 | 54 | led1 = 1; |
apalmieri | 1:11cd427bbc8b | 55 | #endif |
screamer | 0:eb7f02ad28a7 | 56 | Ticker ticker; |
screamer | 0:eb7f02ad28a7 | 57 | ticker.attach(periodicCallback, 1); // blink LED every second |
screamer | 0:eb7f02ad28a7 | 58 | |
screamer | 0:eb7f02ad28a7 | 59 | ble.init(); |
screamer | 0:eb7f02ad28a7 | 60 | ble.gap().onDisconnection(disconnectionCallback); |
screamer | 0:eb7f02ad28a7 | 61 | |
screamer | 0:eb7f02ad28a7 | 62 | /* Setup primary service. */ |
screamer | 0:eb7f02ad28a7 | 63 | uint8_t hrmCounter = 100; // init HRM to 100bps |
screamer | 0:eb7f02ad28a7 | 64 | HeartRateService hrService(ble, hrmCounter, HeartRateService::LOCATION_FINGER); |
screamer | 0:eb7f02ad28a7 | 65 | |
screamer | 0:eb7f02ad28a7 | 66 | /* Setup auxiliary service. */ |
screamer | 0:eb7f02ad28a7 | 67 | DeviceInformationService deviceInfo(ble, "ARM", "Model1", "SN1", "hw-rev1", "fw-rev1", "soft-rev1"); |
screamer | 0:eb7f02ad28a7 | 68 | |
screamer | 0:eb7f02ad28a7 | 69 | /* Setup advertising. */ |
screamer | 0:eb7f02ad28a7 | 70 | ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE); |
screamer | 0:eb7f02ad28a7 | 71 | ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, (uint8_t *)uuid16_list, sizeof(uuid16_list)); |
screamer | 0:eb7f02ad28a7 | 72 | ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::GENERIC_HEART_RATE_SENSOR); |
screamer | 0:eb7f02ad28a7 | 73 | ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)DEVICE_NAME, sizeof(DEVICE_NAME)); |
screamer | 0:eb7f02ad28a7 | 74 | ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED); |
screamer | 0:eb7f02ad28a7 | 75 | ble.gap().setAdvertisingInterval(1000); /* 1000ms */ |
screamer | 0:eb7f02ad28a7 | 76 | ble.gap().startAdvertising(); |
screamer | 0:eb7f02ad28a7 | 77 | |
screamer | 0:eb7f02ad28a7 | 78 | // infinite loop |
screamer | 0:eb7f02ad28a7 | 79 | while (1) { |
screamer | 0:eb7f02ad28a7 | 80 | // check for trigger from periodicCallback() |
screamer | 0:eb7f02ad28a7 | 81 | if (triggerSensorPolling && ble.getGapState().connected) { |
screamer | 0:eb7f02ad28a7 | 82 | triggerSensorPolling = false; |
screamer | 0:eb7f02ad28a7 | 83 | |
screamer | 0:eb7f02ad28a7 | 84 | // Do blocking calls or whatever is necessary for sensor polling. |
screamer | 0:eb7f02ad28a7 | 85 | // In our case, we simply update the HRM measurement. |
screamer | 0:eb7f02ad28a7 | 86 | hrmCounter++; |
screamer | 0:eb7f02ad28a7 | 87 | |
screamer | 0:eb7f02ad28a7 | 88 | // 100 <= HRM bps <=175 |
screamer | 0:eb7f02ad28a7 | 89 | if (hrmCounter == 175) { |
screamer | 0:eb7f02ad28a7 | 90 | hrmCounter = 100; |
screamer | 0:eb7f02ad28a7 | 91 | } |
screamer | 0:eb7f02ad28a7 | 92 | |
screamer | 0:eb7f02ad28a7 | 93 | // update bps |
screamer | 0:eb7f02ad28a7 | 94 | hrService.updateHeartRate(hrmCounter); |
screamer | 0:eb7f02ad28a7 | 95 | } else { |
screamer | 0:eb7f02ad28a7 | 96 | ble.waitForEvent(); // low power wait for event |
screamer | 0:eb7f02ad28a7 | 97 | } |
screamer | 0:eb7f02ad28a7 | 98 | } |
screamer | 0:eb7f02ad28a7 | 99 | } |