BLE Heart Rate Sample Program for HRM1017 which is using Nordic nRF51822 confirmed the connection of nRFToolbox on Android.

Dependencies:   BLE_API mbed nRF51822 color_pixels

Fork of BLE_HTM_HRM1017 by Switch Science

Committer:
YoshinoTaro
Date:
Sun Oct 16 14:25:59 2016 +0000
Revision:
11:d32f4f43161d
Parent:
10:8a67578c3ef0
Enable Heart Rate Sensor and PixelLeds;

Who changed what in which revision?

UserRevisionLine numberNew contents of line
todotani0:5e4210d108ac 1#include "mbed.h"
ytsuboi8:f753b4b022a8 2#include "BLE.h"
YoshinoTaro11:d32f4f43161d 3#include "color_pixels.h"
YoshinoTaro10:8a67578c3ef0 4#include <math.h>
todotani0:5e4210d108ac 5
YoshinoTaro11:d32f4f43161d 6#include "PulseSensor.h"
todotani0:5e4210d108ac 7
YoshinoTaro11:d32f4f43161d 8#define NEED_DEBUG
YoshinoTaro11:d32f4f43161d 9#ifdef NEED_DEBUG
YoshinoTaro10:8a67578c3ef0 10#define DEBUG(...) { printf(__VA_ARGS__); }
todotani0:5e4210d108ac 11#else
todotani0:5e4210d108ac 12#define DEBUG(...) /* nothing */
YoshinoTaro10:8a67578c3ef0 13#endif
todotani0:5e4210d108ac 14
YoshinoTaro10:8a67578c3ef0 15
YoshinoTaro10:8a67578c3ef0 16const static char DEVICE_NAME[] = "mbed HRM1017";
todotani2:daf2344afc28 17static volatile bool triggerSensorPolling = false;
todotani0:5e4210d108ac 18
YoshinoTaro10:8a67578c3ef0 19
todotani0:5e4210d108ac 20BLEDevice ble;
todotani0:5e4210d108ac 21
todotani0:5e4210d108ac 22
YoshinoTaro10:8a67578c3ef0 23/* Heart Rate Service */
YoshinoTaro10:8a67578c3ef0 24static uint8_t hrmCounter = 100;
YoshinoTaro10:8a67578c3ef0 25static uint8_t bpm[2] = {0x00, hrmCounter};
YoshinoTaro10:8a67578c3ef0 26GattCharacteristic hrmChar(GattCharacteristic::UUID_HEART_RATE_MEASUREMENT_CHAR
YoshinoTaro10:8a67578c3ef0 27 ,bpm, sizeof(bpm) ,sizeof(bpm)
YoshinoTaro10:8a67578c3ef0 28 ,GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY);
YoshinoTaro10:8a67578c3ef0 29static uint8_t location = 0x05; /* Ear Lobe */
YoshinoTaro10:8a67578c3ef0 30GattCharacteristic hrmLocation(GattCharacteristic::UUID_BODY_SENSOR_LOCATION_CHAR
YoshinoTaro10:8a67578c3ef0 31 ,(uint8_t *)&location ,sizeof(location) ,sizeof(location)
YoshinoTaro10:8a67578c3ef0 32 ,GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ);
YoshinoTaro10:8a67578c3ef0 33GattCharacteristic *hrmChars[] = {&hrmChar, &hrmLocation,};
YoshinoTaro10:8a67578c3ef0 34GattService hrmService(GattService::UUID_HEART_RATE_SERVICE
YoshinoTaro10:8a67578c3ef0 35 ,hrmChars ,sizeof(hrmChars)/sizeof(GattCharacteristic *));
todotani0:5e4210d108ac 36
todotani0:5e4210d108ac 37
YoshinoTaro10:8a67578c3ef0 38
todotani0:5e4210d108ac 39/* Battery Level Service */
YoshinoTaro10:8a67578c3ef0 40static uint8_t batt = 100;
YoshinoTaro10:8a67578c3ef0 41GattCharacteristic battLevel(GattCharacteristic::UUID_BATTERY_LEVEL_CHAR
YoshinoTaro10:8a67578c3ef0 42 ,(uint8_t *)&batt ,sizeof(batt) ,sizeof(batt)
YoshinoTaro10:8a67578c3ef0 43 ,GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY);
YoshinoTaro10:8a67578c3ef0 44GattCharacteristic *battChars[] = {&battLevel,};
YoshinoTaro10:8a67578c3ef0 45GattService battService(GattService::UUID_BATTERY_SERVICE
YoshinoTaro10:8a67578c3ef0 46 ,battChars ,sizeof(battChars)/sizeof(GattCharacteristic *));
todotani0:5e4210d108ac 47
todotani0:5e4210d108ac 48
YoshinoTaro10:8a67578c3ef0 49
YoshinoTaro10:8a67578c3ef0 50/* Device Information service */
YoshinoTaro10:8a67578c3ef0 51static uint8_t deviceName[] = {'H', 'R', 'M', '1', '0', '1', '7'};
YoshinoTaro10:8a67578c3ef0 52GattCharacteristic deviceManufacturer(GattCharacteristic::UUID_MANUFACTURER_NAME_STRING_CHAR
YoshinoTaro10:8a67578c3ef0 53 ,(uint8_t *)deviceName ,sizeof(deviceName) ,sizeof(deviceName)
YoshinoTaro10:8a67578c3ef0 54 ,GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ);
YoshinoTaro10:8a67578c3ef0 55GattCharacteristic *devInfoChars[] = {&deviceManufacturer,};
YoshinoTaro10:8a67578c3ef0 56GattService deviceInformationService(GattService::UUID_DEVICE_INFORMATION_SERVICE
YoshinoTaro10:8a67578c3ef0 57 ,devInfoChars ,sizeof(devInfoChars)/sizeof(GattCharacteristic *));
YoshinoTaro10:8a67578c3ef0 58
YoshinoTaro10:8a67578c3ef0 59
YoshinoTaro10:8a67578c3ef0 60static uint16_t uuid16_list[] = {
YoshinoTaro10:8a67578c3ef0 61 GattService::UUID_HEART_RATE_SERVICE
YoshinoTaro10:8a67578c3ef0 62 ,GattService::UUID_BATTERY_SERVICE
YoshinoTaro10:8a67578c3ef0 63 ,GattService::UUID_DEVICE_INFORMATION_SERVICE
YoshinoTaro10:8a67578c3ef0 64 };
YoshinoTaro10:8a67578c3ef0 65
YoshinoTaro10:8a67578c3ef0 66
todotani0:5e4210d108ac 67void updateServiceValues(void);
todotani0:5e4210d108ac 68static Gap::ConnectionParams_t connectionParams;
todotani0:5e4210d108ac 69
YoshinoTaro10:8a67578c3ef0 70void disconnectionCallback(const Gap::DisconnectionCallbackParams_t *params)
todotani0:5e4210d108ac 71{
mbed_tw_hoehoe9:554af3c63d0c 72 DEBUG("Disconnected handle %u, reason %u\r\n", params->handle, params->reason);
ytsuboi8:f753b4b022a8 73 DEBUG("Restarting the advertising process\r\n");
ytsuboi8:f753b4b022a8 74 ble.gap().startAdvertising();
todotani0:5e4210d108ac 75}
todotani0:5e4210d108ac 76
ytsuboi8:f753b4b022a8 77void onConnectionCallback(const Gap::ConnectionCallbackParams_t *params) //Mod
todotani0:5e4210d108ac 78{
ytsuboi8:f753b4b022a8 79 DEBUG("connected. Got handle %u\r\n", params->handle);
todotani0:5e4210d108ac 80
todotani0:5e4210d108ac 81 connectionParams.slaveLatency = 1;
ytsuboi8:f753b4b022a8 82 if (ble.gap().updateConnectionParams(params->handle, &connectionParams) != BLE_ERROR_NONE) {
todotani0:5e4210d108ac 83 DEBUG("failed to update connection paramter\r\n");
todotani0:5e4210d108ac 84 }
todotani0:5e4210d108ac 85}
todotani0:5e4210d108ac 86
todotani2:daf2344afc28 87void periodicCallback(void)
todotani2:daf2344afc28 88{
todotani2:daf2344afc28 89 triggerSensorPolling = true;
todotani2:daf2344afc28 90}
todotani2:daf2344afc28 91
YoshinoTaro11:d32f4f43161d 92ColorPixels pixels(6,8);
YoshinoTaro11:d32f4f43161d 93int ledfade = 0;
YoshinoTaro11:d32f4f43161d 94void pixelPattern(int n, uint8_t r, uint8_t g, uint8_t b);
YoshinoTaro11:d32f4f43161d 95
YoshinoTaro11:d32f4f43161d 96
todotani0:5e4210d108ac 97int main(void)
YoshinoTaro11:d32f4f43161d 98{
todotani2:daf2344afc28 99 Ticker ticker;
todotani2:daf2344afc28 100 ticker.attach(periodicCallback, 1);
YoshinoTaro11:d32f4f43161d 101
ytsuboi8:f753b4b022a8 102 DEBUG("Initialising the nRF51822\r\n");
todotani0:5e4210d108ac 103 ble.init();
ytsuboi8:f753b4b022a8 104 DEBUG("Init done\r\n");
ytsuboi8:f753b4b022a8 105 ble.gap().onDisconnection(disconnectionCallback);
ytsuboi8:f753b4b022a8 106 ble.gap().onConnection(onConnectionCallback);
todotani0:5e4210d108ac 107
ytsuboi8:f753b4b022a8 108 ble.gap().getPreferredConnectionParams(&connectionParams);
todotani0:5e4210d108ac 109
todotani0:5e4210d108ac 110 /* setup advertising */
ytsuboi8:f753b4b022a8 111 ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
ytsuboi8:f753b4b022a8 112 ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, (uint8_t*)uuid16_list, sizeof(uuid16_list));
YoshinoTaro10:8a67578c3ef0 113 ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::GENERIC_HEART_RATE_SENSOR);
ytsuboi8:f753b4b022a8 114 ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)DEVICE_NAME, sizeof(DEVICE_NAME));
ytsuboi8:f753b4b022a8 115 ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
ytsuboi8:f753b4b022a8 116 ble.gap().setAdvertisingInterval(160); /* 100ms; in multiples of 0.625ms. */
ytsuboi8:f753b4b022a8 117 ble.gap().startAdvertising();
ytsuboi8:f753b4b022a8 118 DEBUG("Start Advertising\r\n");
todotani0:5e4210d108ac 119
YoshinoTaro10:8a67578c3ef0 120 ble.gattServer().addService(hrmService);
ytsuboi8:f753b4b022a8 121 ble.gattServer().addService(battService);
YoshinoTaro10:8a67578c3ef0 122 ble.gattServer().addService(deviceInformationService);
ytsuboi8:f753b4b022a8 123 DEBUG("Add Service\r\n");
YoshinoTaro11:d32f4f43161d 124
YoshinoTaro11:d32f4f43161d 125 initPulseSensor();
YoshinoTaro11:d32f4f43161d 126
YoshinoTaro11:d32f4f43161d 127 Ticker hrm_ticker;
YoshinoTaro11:d32f4f43161d 128 hrm_ticker.attach(calcHeartRate, 0.02);
YoshinoTaro11:d32f4f43161d 129
YoshinoTaro11:d32f4f43161d 130 int counter = 0;
YoshinoTaro11:d32f4f43161d 131 uint8_t cl = 193;
todotani0:5e4210d108ac 132
todotani2:daf2344afc28 133 while (true) {
todotani2:daf2344afc28 134 if (triggerSensorPolling) {
todotani2:daf2344afc28 135 triggerSensorPolling = false;
todotani2:daf2344afc28 136 updateServiceValues();
todotani2:daf2344afc28 137 } else {
todotani2:daf2344afc28 138 ble.waitForEvent();
todotani2:daf2344afc28 139 }
YoshinoTaro11:d32f4f43161d 140 if (counter % 3 == 0) {
YoshinoTaro11:d32f4f43161d 141 if (ledfade) {
YoshinoTaro11:d32f4f43161d 142 --ledfade;
YoshinoTaro11:d32f4f43161d 143 pixelPattern(ledfade, cl, cl, cl);
YoshinoTaro11:d32f4f43161d 144 counter = 0;
YoshinoTaro11:d32f4f43161d 145 }
YoshinoTaro11:d32f4f43161d 146 }
YoshinoTaro11:d32f4f43161d 147 ++counter;
todotani0:5e4210d108ac 148 }
YoshinoTaro11:d32f4f43161d 149
todotani0:5e4210d108ac 150}
todotani0:5e4210d108ac 151
YoshinoTaro11:d32f4f43161d 152
YoshinoTaro11:d32f4f43161d 153
todotani0:5e4210d108ac 154void updateServiceValues(void)
todotani0:5e4210d108ac 155{
YoshinoTaro10:8a67578c3ef0 156 /* Decrement the battery level. */
YoshinoTaro10:8a67578c3ef0 157 batt <= 50 ? batt = 100 : batt--;
YoshinoTaro10:8a67578c3ef0 158 ble.gattServer().write(battLevel.getValueAttribute().getHandle(), (uint8_t *)&batt, sizeof(batt));
YoshinoTaro10:8a67578c3ef0 159
YoshinoTaro10:8a67578c3ef0 160 /* Randomize the heart rate. */
YoshinoTaro11:d32f4f43161d 161 // hrmCounter = (rand() % 150) + 30;
YoshinoTaro11:d32f4f43161d 162 if (isQS()) {
YoshinoTaro11:d32f4f43161d 163 hrmCounter = getBPM();
YoshinoTaro11:d32f4f43161d 164 DEBUG("BPM: %d\r\n", hrmCounter);
YoshinoTaro11:d32f4f43161d 165 ledfade = 5;
YoshinoTaro11:d32f4f43161d 166 }
YoshinoTaro11:d32f4f43161d 167
YoshinoTaro10:8a67578c3ef0 168 bpm[1] = hrmCounter;
YoshinoTaro10:8a67578c3ef0 169 ble.gattServer().write(hrmChar.getValueAttribute().getHandle(), bpm, sizeof(bpm));
todotani0:5e4210d108ac 170}
YoshinoTaro11:d32f4f43161d 171
YoshinoTaro11:d32f4f43161d 172
YoshinoTaro11:d32f4f43161d 173void pixelPattern(int n, uint8_t r, uint8_t g, uint8_t b) {
YoshinoTaro11:d32f4f43161d 174
YoshinoTaro11:d32f4f43161d 175 if (n < 0 || n > 4) return;
YoshinoTaro11:d32f4f43161d 176
YoshinoTaro11:d32f4f43161d 177 switch (n) {
YoshinoTaro11:d32f4f43161d 178 case 4:
YoshinoTaro11:d32f4f43161d 179 pixels.set_color(7, r, g, b);
YoshinoTaro11:d32f4f43161d 180 pixels.set_color(6, r, g, b);
YoshinoTaro11:d32f4f43161d 181 pixels.set_color(5, r, g, b);
YoshinoTaro11:d32f4f43161d 182 pixels.set_color(4, r, g, b);
YoshinoTaro11:d32f4f43161d 183 pixels.set_color(3, r, g, b);
YoshinoTaro11:d32f4f43161d 184 pixels.set_color(2, r, g, b);
YoshinoTaro11:d32f4f43161d 185 pixels.set_color(1, r, g, b);
YoshinoTaro11:d32f4f43161d 186 pixels.set_color(0, r, g, b);
YoshinoTaro11:d32f4f43161d 187 pixels.update();
YoshinoTaro11:d32f4f43161d 188 break;
YoshinoTaro11:d32f4f43161d 189 case 3:
YoshinoTaro11:d32f4f43161d 190 pixels.set_color(7, 0, 0, 0);
YoshinoTaro11:d32f4f43161d 191 pixels.set_color(6, r/2, g/2, b/2);
YoshinoTaro11:d32f4f43161d 192 pixels.set_color(5, r*3/4, g*3/4, b*3/4);
YoshinoTaro11:d32f4f43161d 193 pixels.set_color(4, r, g, b);
YoshinoTaro11:d32f4f43161d 194 pixels.set_color(3, r, g, b);
YoshinoTaro11:d32f4f43161d 195 pixels.set_color(2, r*3/4, g*3/4, b*3/4);
YoshinoTaro11:d32f4f43161d 196 pixels.set_color(1, r/2, g/2, b/2);
YoshinoTaro11:d32f4f43161d 197 pixels.set_color(0, 0, 0, 0);
YoshinoTaro11:d32f4f43161d 198 pixels.update();
YoshinoTaro11:d32f4f43161d 199 break;
YoshinoTaro11:d32f4f43161d 200 case 2:
YoshinoTaro11:d32f4f43161d 201 pixels.set_color(7, 0, 0, 0);
YoshinoTaro11:d32f4f43161d 202 pixels.set_color(6, 0, 0, 0);
YoshinoTaro11:d32f4f43161d 203 pixels.set_color(5, r/2, g/2, b/2);
YoshinoTaro11:d32f4f43161d 204 pixels.set_color(4, r*3/4, g*3/4, b*3/4);
YoshinoTaro11:d32f4f43161d 205 pixels.set_color(3, r*3/4, g*3/4, b*3/4);
YoshinoTaro11:d32f4f43161d 206 pixels.set_color(2, r/2, g/2, b/2);
YoshinoTaro11:d32f4f43161d 207 pixels.set_color(1, 0, 0, 0);
YoshinoTaro11:d32f4f43161d 208 pixels.set_color(0, 0, 0, 0);
YoshinoTaro11:d32f4f43161d 209 pixels.update();
YoshinoTaro11:d32f4f43161d 210 break;
YoshinoTaro11:d32f4f43161d 211 case 1:
YoshinoTaro11:d32f4f43161d 212 pixels.set_color(7, 0, 0, 0);
YoshinoTaro11:d32f4f43161d 213 pixels.set_color(6, 0, 0, 0);
YoshinoTaro11:d32f4f43161d 214 pixels.set_color(5, 0, 0, 0);
YoshinoTaro11:d32f4f43161d 215 pixels.set_color(4, r/2, g/2, b/4);
YoshinoTaro11:d32f4f43161d 216 pixels.set_color(3, r/2, g/2, b/4);
YoshinoTaro11:d32f4f43161d 217 pixels.set_color(2, 0, 0, 0);
YoshinoTaro11:d32f4f43161d 218 pixels.set_color(1, 0, 0, 0);
YoshinoTaro11:d32f4f43161d 219 pixels.set_color(0, 0, 0, 0);
YoshinoTaro11:d32f4f43161d 220 pixels.update();
YoshinoTaro11:d32f4f43161d 221 break;
YoshinoTaro11:d32f4f43161d 222 case 0:
YoshinoTaro11:d32f4f43161d 223 default:
YoshinoTaro11:d32f4f43161d 224 pixels.set_color(7, 0, 0, 0);
YoshinoTaro11:d32f4f43161d 225 pixels.set_color(6, 0, 0, 0);
YoshinoTaro11:d32f4f43161d 226 pixels.set_color(5, 0, 0, 0);
YoshinoTaro11:d32f4f43161d 227 pixels.set_color(4, 0, 0, 0);
YoshinoTaro11:d32f4f43161d 228 pixels.set_color(3, 0, 0, 0);
YoshinoTaro11:d32f4f43161d 229 pixels.set_color(2, 0, 0, 0);
YoshinoTaro11:d32f4f43161d 230 pixels.set_color(1, 0, 0, 0);
YoshinoTaro11:d32f4f43161d 231 pixels.set_color(0, 0, 0, 0);
YoshinoTaro11:d32f4f43161d 232 pixels.update();
YoshinoTaro11:d32f4f43161d 233 }
YoshinoTaro11:d32f4f43161d 234}