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