Bluetooth Heart Rate Monitor using Maxim MAX32630FTHR & MAXREFDES117

Fork of mbed-os-example-ble-HeartRate by mbed-os-examples

Committer:
Brakkion
Date:
Mon Dec 11 16:22:50 2017 +0000
Revision:
49:5be03f287022
Parent:
47:8ae30057edc0
Created using MAX32630FTHR & MAXREFDES117

Who changed what in which revision?

UserRevisionLine numberNew contents of line
mbed_official 1:72c60abef7e7 1 /* mbed Microcontroller Library
mbed_official 1:72c60abef7e7 2 * Copyright (c) 2006-2015 ARM Limited
mbed_official 1:72c60abef7e7 3 *
mbed_official 1:72c60abef7e7 4 * Licensed under the Apache License, Version 2.0 (the "License");
mbed_official 1:72c60abef7e7 5 * you may not use this file except in compliance with the License.
mbed_official 1:72c60abef7e7 6 * You may obtain a copy of the License at
mbed_official 1:72c60abef7e7 7 *
mbed_official 1:72c60abef7e7 8 * http://www.apache.org/licenses/LICENSE-2.0
mbed_official 1:72c60abef7e7 9 *
mbed_official 1:72c60abef7e7 10 * Unless required by applicable law or agreed to in writing, software
mbed_official 1:72c60abef7e7 11 * distributed under the License is distributed on an "AS IS" BASIS,
mbed_official 1:72c60abef7e7 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
mbed_official 1:72c60abef7e7 13 * See the License for the specific language governing permissions and
mbed_official 1:72c60abef7e7 14 * limitations under the License.
mbed_official 1:72c60abef7e7 15 */
mbed_official 1:72c60abef7e7 16
mbed_official 10:ac3615194d04 17 #include <events/mbed_events.h>
mbed_official 1:72c60abef7e7 18 #include <mbed.h>
mbed_official 1:72c60abef7e7 19 #include "ble/BLE.h"
mbed_official 1:72c60abef7e7 20 #include "ble/Gap.h"
mbed_official 1:72c60abef7e7 21 #include "ble/services/HeartRateService.h"
Brakkion 47:8ae30057edc0 22 #include "algorithm.h"
Brakkion 47:8ae30057edc0 23 #include "MAX30102.h"
mbed_official 1:72c60abef7e7 24
Brakkion 47:8ae30057edc0 25 #define MAX_BRIGHTNESS 255
Brakkion 47:8ae30057edc0 26
Brakkion 47:8ae30057edc0 27
Brakkion 47:8ae30057edc0 28
Brakkion 47:8ae30057edc0 29 DigitalOut led2(LED2, 1);
Brakkion 47:8ae30057edc0 30
Brakkion 47:8ae30057edc0 31 uint32_t aun_ir_buffer[500]; //IR LED sensor data
Brakkion 47:8ae30057edc0 32 int32_t n_ir_buffer_length; //data length
Brakkion 47:8ae30057edc0 33 uint32_t aun_red_buffer[500]; //Red LED sensor data
Brakkion 47:8ae30057edc0 34 int32_t n_sp02; //SPO2 value
Brakkion 47:8ae30057edc0 35 int8_t ch_spo2_valid; //indicator to show if the SP02 calculation is valid
Brakkion 47:8ae30057edc0 36 int32_t n_heart_rate; //heart rate value
Brakkion 47:8ae30057edc0 37 int8_t ch_hr_valid; //indicator to show if the heart rate calculation is valid
Brakkion 47:8ae30057edc0 38 uint8_t uch_dummy;
Brakkion 49:5be03f287022 39 uint32_t un_min, un_max, un_prev_data; //variables to calculate the on-board LED brightness that reflects the heartbeats
Brakkion 49:5be03f287022 40 int i;
Brakkion 49:5be03f287022 41 int32_t n_brightness;
Brakkion 49:5be03f287022 42 float f_temp;
Brakkion 49:5be03f287022 43 uint8_t hrmCounter;
Brakkion 47:8ae30057edc0 44
Brakkion 49:5be03f287022 45
Brakkion 47:8ae30057edc0 46 #ifdef TARGET_MAX32630FTHR
Brakkion 47:8ae30057edc0 47 PwmOut led1(LED_RED); //initializes the pwm output that connects to the on board LED
Brakkion 47:8ae30057edc0 48 DigitalIn INT(P3_0); //pin P30 connects to the interrupt output pin of the MAX30102
Brakkion 47:8ae30057edc0 49 #endif
mbed_official 1:72c60abef7e7 50
mbed_official 1:72c60abef7e7 51 const static char DEVICE_NAME[] = "HRM";
mbed_official 1:72c60abef7e7 52 static const uint16_t uuid16_list[] = {GattService::UUID_HEART_RATE_SERVICE};
mbed_official 1:72c60abef7e7 53
mbed_official 1:72c60abef7e7 54 static HeartRateService *hrServicePtr;
mbed_official 1:72c60abef7e7 55
mbed_official 26:d7dd71a8aea1 56 static EventQueue eventQueue(/* event count */ 16 * EVENTS_EVENT_SIZE);
mbed_official 1:72c60abef7e7 57
mbed_official 1:72c60abef7e7 58 void disconnectionCallback(const Gap::DisconnectionCallbackParams_t *params)
mbed_official 1:72c60abef7e7 59 {
mbed_official 1:72c60abef7e7 60 BLE::Instance().gap().startAdvertising(); // restart advertising
mbed_official 1:72c60abef7e7 61 }
mbed_official 1:72c60abef7e7 62
Brakkion 49:5be03f287022 63 void checkHR(){
Brakkion 49:5be03f287022 64
Brakkion 49:5be03f287022 65 i=0;
Brakkion 49:5be03f287022 66 un_min=0x3FFFF;
Brakkion 49:5be03f287022 67 un_max=0;
Brakkion 49:5be03f287022 68
Brakkion 49:5be03f287022 69 //dumping the first 100 sets of samples in the memory and shift the last 400 sets of samples to the top
Brakkion 49:5be03f287022 70 for(i=100;i<500;i++)
Brakkion 49:5be03f287022 71 {
Brakkion 49:5be03f287022 72 aun_red_buffer[i-100]=aun_red_buffer[i];
Brakkion 49:5be03f287022 73 aun_ir_buffer[i-100]=aun_ir_buffer[i];
Brakkion 49:5be03f287022 74
Brakkion 49:5be03f287022 75 //update the signal min and max
Brakkion 49:5be03f287022 76 if(un_min>aun_red_buffer[i])
Brakkion 49:5be03f287022 77 un_min=aun_red_buffer[i];
Brakkion 49:5be03f287022 78 if(un_max<aun_red_buffer[i])
Brakkion 49:5be03f287022 79 un_max=aun_red_buffer[i];
Brakkion 49:5be03f287022 80 }
Brakkion 49:5be03f287022 81
Brakkion 49:5be03f287022 82 //take 100 sets of samples before calculating the heart rate.
Brakkion 49:5be03f287022 83 for(i=400;i<500;i++)
Brakkion 49:5be03f287022 84 {
Brakkion 49:5be03f287022 85 un_prev_data=aun_red_buffer[i-1];
Brakkion 49:5be03f287022 86 while(INT.read()==1);
Brakkion 49:5be03f287022 87 maxim_max30102_read_fifo((aun_red_buffer+i), (aun_ir_buffer+i));
Brakkion 49:5be03f287022 88
Brakkion 49:5be03f287022 89 if(aun_red_buffer[i]>un_prev_data)
Brakkion 49:5be03f287022 90 {
Brakkion 49:5be03f287022 91 f_temp=aun_red_buffer[i]-un_prev_data;
Brakkion 49:5be03f287022 92 f_temp/=(un_max-un_min);
Brakkion 49:5be03f287022 93 f_temp*=MAX_BRIGHTNESS;
Brakkion 49:5be03f287022 94 n_brightness-=(int)f_temp;
Brakkion 49:5be03f287022 95 if(n_brightness<0)
Brakkion 49:5be03f287022 96 n_brightness=0;
Brakkion 49:5be03f287022 97 }
Brakkion 49:5be03f287022 98 else
Brakkion 49:5be03f287022 99 {
Brakkion 49:5be03f287022 100 f_temp=un_prev_data-aun_red_buffer[i];
Brakkion 49:5be03f287022 101 f_temp/=(un_max-un_min);
Brakkion 49:5be03f287022 102 f_temp*=MAX_BRIGHTNESS;
Brakkion 49:5be03f287022 103 n_brightness+=(int)f_temp;
Brakkion 49:5be03f287022 104 if(n_brightness>MAX_BRIGHTNESS)
Brakkion 49:5be03f287022 105 n_brightness=MAX_BRIGHTNESS;
Brakkion 49:5be03f287022 106 }
Brakkion 49:5be03f287022 107 #if defined(TARGET_KL25Z) || defined(TARGET_MAX32630FTHR)
Brakkion 49:5be03f287022 108 led1.write(1-(float)n_brightness/256);
Brakkion 49:5be03f287022 109 #endif
Brakkion 49:5be03f287022 110
Brakkion 49:5be03f287022 111
Brakkion 49:5be03f287022 112 }
Brakkion 49:5be03f287022 113 maxim_heart_rate_and_oxygen_saturation(aun_ir_buffer, n_ir_buffer_length, aun_red_buffer, &n_sp02, &ch_spo2_valid, &n_heart_rate, &ch_hr_valid);
Brakkion 49:5be03f287022 114 }
Brakkion 49:5be03f287022 115
mbed_official 1:72c60abef7e7 116 void updateSensorValue() {
mbed_official 1:72c60abef7e7 117 // Do blocking calls or whatever is necessary for sensor polling.
mbed_official 1:72c60abef7e7 118 // In our case, we simply update the HRM measurement.
mbed_official 1:72c60abef7e7 119
Brakkion 49:5be03f287022 120
Brakkion 49:5be03f287022 121 checkHR();
Brakkion 49:5be03f287022 122
Brakkion 49:5be03f287022 123 hrmCounter = n_heart_rate;
Brakkion 49:5be03f287022 124
mbed_official 1:72c60abef7e7 125 hrServicePtr->updateHeartRate(hrmCounter);
mbed_official 1:72c60abef7e7 126 }
mbed_official 1:72c60abef7e7 127
mbed_official 1:72c60abef7e7 128 void periodicCallback(void)
mbed_official 1:72c60abef7e7 129 {
mbed_official 1:72c60abef7e7 130
mbed_official 1:72c60abef7e7 131 if (BLE::Instance().getGapState().connected) {
mbed_official 10:ac3615194d04 132 eventQueue.call(updateSensorValue);
mbed_official 1:72c60abef7e7 133 }
mbed_official 1:72c60abef7e7 134 }
mbed_official 1:72c60abef7e7 135
Brakkion 49:5be03f287022 136
Brakkion 49:5be03f287022 137
mbed_official 1:72c60abef7e7 138 void onBleInitError(BLE &ble, ble_error_t error)
mbed_official 1:72c60abef7e7 139 {
mbed_official 1:72c60abef7e7 140 (void)ble;
mbed_official 1:72c60abef7e7 141 (void)error;
mbed_official 1:72c60abef7e7 142 /* Initialization error handling should go here */
mbed_official 1:72c60abef7e7 143 }
mbed_official 1:72c60abef7e7 144
mbed_official 43:fb2855f7754b 145 void printMacAddress()
mbed_official 43:fb2855f7754b 146 {
mbed_official 43:fb2855f7754b 147 /* Print out device MAC address to the console*/
mbed_official 43:fb2855f7754b 148 Gap::AddressType_t addr_type;
mbed_official 43:fb2855f7754b 149 Gap::Address_t address;
mbed_official 43:fb2855f7754b 150 BLE::Instance().gap().getAddress(&addr_type, address);
mbed_official 43:fb2855f7754b 151 printf("DEVICE MAC ADDRESS: ");
mbed_official 43:fb2855f7754b 152 for (int i = 5; i >= 1; i--){
mbed_official 43:fb2855f7754b 153 printf("%02x:", address[i]);
mbed_official 43:fb2855f7754b 154 }
mbed_official 43:fb2855f7754b 155 printf("%02x\r\n", address[0]);
mbed_official 43:fb2855f7754b 156 }
mbed_official 43:fb2855f7754b 157
mbed_official 1:72c60abef7e7 158 void bleInitComplete(BLE::InitializationCompleteCallbackContext *params)
mbed_official 1:72c60abef7e7 159 {
mbed_official 1:72c60abef7e7 160 BLE& ble = params->ble;
mbed_official 1:72c60abef7e7 161 ble_error_t error = params->error;
mbed_official 1:72c60abef7e7 162
mbed_official 1:72c60abef7e7 163 if (error != BLE_ERROR_NONE) {
mbed_official 1:72c60abef7e7 164 onBleInitError(ble, error);
mbed_official 1:72c60abef7e7 165 return;
mbed_official 1:72c60abef7e7 166 }
mbed_official 1:72c60abef7e7 167
mbed_official 1:72c60abef7e7 168 if (ble.getInstanceID() != BLE::DEFAULT_INSTANCE) {
mbed_official 1:72c60abef7e7 169 return;
mbed_official 1:72c60abef7e7 170 }
mbed_official 1:72c60abef7e7 171
mbed_official 1:72c60abef7e7 172 ble.gap().onDisconnection(disconnectionCallback);
mbed_official 1:72c60abef7e7 173
mbed_official 1:72c60abef7e7 174 /* Setup primary service. */
mbed_official 1:72c60abef7e7 175 hrServicePtr = new HeartRateService(ble, hrmCounter, HeartRateService::LOCATION_FINGER);
mbed_official 1:72c60abef7e7 176
mbed_official 1:72c60abef7e7 177 /* Setup advertising. */
mbed_official 1:72c60abef7e7 178 ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
mbed_official 1:72c60abef7e7 179 ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, (uint8_t *)uuid16_list, sizeof(uuid16_list));
mbed_official 1:72c60abef7e7 180 ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::GENERIC_HEART_RATE_SENSOR);
mbed_official 1:72c60abef7e7 181 ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)DEVICE_NAME, sizeof(DEVICE_NAME));
mbed_official 1:72c60abef7e7 182 ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
mbed_official 1:72c60abef7e7 183 ble.gap().setAdvertisingInterval(1000); /* 1000ms */
mbed_official 1:72c60abef7e7 184 ble.gap().startAdvertising();
mbed_official 43:fb2855f7754b 185
mbed_official 43:fb2855f7754b 186 printMacAddress();
mbed_official 1:72c60abef7e7 187 }
mbed_official 1:72c60abef7e7 188
mbed_official 1:72c60abef7e7 189 void scheduleBleEventsProcessing(BLE::OnEventsToProcessCallbackContext* context) {
mbed_official 1:72c60abef7e7 190 BLE &ble = BLE::Instance();
mbed_official 10:ac3615194d04 191 eventQueue.call(Callback<void()>(&ble, &BLE::processEvents));
mbed_official 1:72c60abef7e7 192 }
mbed_official 1:72c60abef7e7 193
mbed_official 1:72c60abef7e7 194 int main()
mbed_official 1:72c60abef7e7 195 {
Brakkion 47:8ae30057edc0 196 maxim_max30102_reset(); //resets the MAX30102
mbed_official 1:72c60abef7e7 197
Brakkion 47:8ae30057edc0 198
Brakkion 47:8ae30057edc0 199 //read and clear status register
Brakkion 47:8ae30057edc0 200 maxim_max30102_read_reg(0,&uch_dummy);
Brakkion 47:8ae30057edc0 201
Brakkion 47:8ae30057edc0 202
Brakkion 47:8ae30057edc0 203 maxim_max30102_init(); //initializes the MAX30102
Brakkion 47:8ae30057edc0 204
Brakkion 47:8ae30057edc0 205 n_brightness=0;
Brakkion 47:8ae30057edc0 206 un_min=0x3FFFF;
Brakkion 47:8ae30057edc0 207 un_max=0;
Brakkion 47:8ae30057edc0 208
Brakkion 47:8ae30057edc0 209 n_ir_buffer_length=500; //buffer length of 100 stores 5 seconds of samples running at 100sps
Brakkion 47:8ae30057edc0 210
Brakkion 47:8ae30057edc0 211 //read the first 500 samples, and determine the signal range
Brakkion 47:8ae30057edc0 212 for(i=0;i<n_ir_buffer_length;i++)
Brakkion 47:8ae30057edc0 213 {
Brakkion 47:8ae30057edc0 214 while(INT.read()==1); //wait until the interrupt pin asserts
Brakkion 47:8ae30057edc0 215
Brakkion 47:8ae30057edc0 216 maxim_max30102_read_fifo((aun_red_buffer+i), (aun_ir_buffer+i)); //read from MAX30102 FIFO
Brakkion 47:8ae30057edc0 217
Brakkion 47:8ae30057edc0 218 if(un_min>aun_red_buffer[i])
Brakkion 47:8ae30057edc0 219 un_min=aun_red_buffer[i]; //update signal min
Brakkion 47:8ae30057edc0 220 if(un_max<aun_red_buffer[i])
Brakkion 47:8ae30057edc0 221 un_max=aun_red_buffer[i]; //update signal max
Brakkion 47:8ae30057edc0 222
Brakkion 47:8ae30057edc0 223 }
Brakkion 47:8ae30057edc0 224 un_prev_data=aun_red_buffer[i];
Brakkion 47:8ae30057edc0 225
Brakkion 47:8ae30057edc0 226 //calculate heart rate and SpO2 after first 500 samples (first 5 seconds of samples)
Brakkion 47:8ae30057edc0 227 maxim_heart_rate_and_oxygen_saturation(aun_ir_buffer, n_ir_buffer_length, aun_red_buffer, &n_sp02, &ch_spo2_valid, &n_heart_rate, &ch_hr_valid);
Brakkion 47:8ae30057edc0 228
Brakkion 47:8ae30057edc0 229
Brakkion 47:8ae30057edc0 230 while(1)
Brakkion 47:8ae30057edc0 231 {
Brakkion 47:8ae30057edc0 232
Brakkion 47:8ae30057edc0 233 eventQueue.call_every(500, periodicCallback);
Brakkion 47:8ae30057edc0 234
Brakkion 47:8ae30057edc0 235 BLE &ble = BLE::Instance();
Brakkion 47:8ae30057edc0 236 ble.onEventsToProcess(scheduleBleEventsProcessing);
Brakkion 47:8ae30057edc0 237 ble.init(bleInitComplete);
Brakkion 47:8ae30057edc0 238
Brakkion 47:8ae30057edc0 239 eventQueue.dispatch_forever();
Brakkion 49:5be03f287022 240
Brakkion 49:5be03f287022 241
Brakkion 47:8ae30057edc0 242 }
mbed_official 1:72c60abef7e7 243 }