I've got some basic filter code setup (but not yet tested).
Dependencies: BLE_API Queue mbed nRF51822
Fork of BLE_HeartRate by
main.cpp@62:8e2fbe131b53, 2015-06-28 (annotated)
- Committer:
- roysandberg
- Date:
- Sun Jun 28 03:06:00 2015 +0000
- Revision:
- 62:8e2fbe131b53
- Parent:
- 61:1de72bdab0ef
Working Beat Detection and Analysis
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
ktownsend | 0:87a7fc231fae | 1 | /* mbed Microcontroller Library |
ktownsend | 0:87a7fc231fae | 2 | * Copyright (c) 2006-2013 ARM Limited |
ktownsend | 0:87a7fc231fae | 3 | * |
ktownsend | 0:87a7fc231fae | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
ktownsend | 0:87a7fc231fae | 5 | * you may not use this file except in compliance with the License. |
ktownsend | 0:87a7fc231fae | 6 | * You may obtain a copy of the License at |
ktownsend | 0:87a7fc231fae | 7 | * |
ktownsend | 0:87a7fc231fae | 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
ktownsend | 0:87a7fc231fae | 9 | * |
ktownsend | 0:87a7fc231fae | 10 | * Unless required by applicable law or agreed to in writing, software |
ktownsend | 0:87a7fc231fae | 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
ktownsend | 0:87a7fc231fae | 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
ktownsend | 0:87a7fc231fae | 13 | * See the License for the specific language governing permissions and |
ktownsend | 0:87a7fc231fae | 14 | * limitations under the License. |
ktownsend | 0:87a7fc231fae | 15 | */ |
ktownsend | 0:87a7fc231fae | 16 | |
ktownsend | 0:87a7fc231fae | 17 | #include "mbed.h" |
roysandberg | 62:8e2fbe131b53 | 18 | #include <qrsdet.h> |
roysandberg | 62:8e2fbe131b53 | 19 | #include "queue.h" |
roysandberg | 62:8e2fbe131b53 | 20 | |
Rohit Grover |
10:2436164b692e | 21 | #include "BLEDevice.h" |
rgrover1 | 39:6390604f904c | 22 | #include "HeartRateService.h" |
rgrover1 | 42:06ebef2e0e44 | 23 | #include "DeviceInformationService.h" |
ktownsend | 0:87a7fc231fae | 24 | |
rgrover1 | 52:6bbf62943106 | 25 | /* Enable the following if you need to throttle the connection interval. This has |
rgrover1 | 52:6bbf62943106 | 26 | * the effect of reducing energy consumption after a connection is made; |
rgrover1 | 52:6bbf62943106 | 27 | * particularly for applications where the central may want a fast connection |
rgrover1 | 52:6bbf62943106 | 28 | * interval.*/ |
rgrover1 | 52:6bbf62943106 | 29 | #define UPDATE_PARAMS_FOR_LONGER_CONNECTION_INTERVAL 0 |
rgrover1 | 52:6bbf62943106 | 30 | |
roysandberg | 62:8e2fbe131b53 | 31 | // SAMPLE_RATE is defined in qrsdet.h |
roysandberg | 62:8e2fbe131b53 | 32 | #define MS_PER_SAMPLE (1000/SAMPLE_RATE) |
roysandberg | 62:8e2fbe131b53 | 33 | |
roysandberg | 61:1de72bdab0ef | 34 | extern void setup_sampler(void (*function)(uint32_t)); |
roysandberg | 62:8e2fbe131b53 | 35 | extern int BeatDetectAndClassify(int ecgSample, int *beatType, int *beatMatch); // defined in bdac.cpp |
roysandberg | 62:8e2fbe131b53 | 36 | extern void ResetBDAC(void); |
roysandberg | 62:8e2fbe131b53 | 37 | |
Rohit Grover |
10:2436164b692e | 38 | BLEDevice ble; |
rgrover1 | 47:430545f41113 | 39 | DigitalOut led1(LED1); |
roysandberg | 62:8e2fbe131b53 | 40 | PwmOut LRA(P0_15); |
roysandberg | 62:8e2fbe131b53 | 41 | InterruptIn button1(P0_17); // button 1 |
roysandberg | 62:8e2fbe131b53 | 42 | InterruptIn button2(P0_18); // button 2 |
roysandberg | 62:8e2fbe131b53 | 43 | InterruptIn button3(P0_19); // button 3 |
roysandberg | 62:8e2fbe131b53 | 44 | InterruptIn button4(P0_20); // button 4 |
ktownsend | 0:87a7fc231fae | 45 | |
roysandberg | 62:8e2fbe131b53 | 46 | |
roysandberg | 62:8e2fbe131b53 | 47 | //Serial pc (P0_9, P0_11); // TX, RX |
roysandberg | 62:8e2fbe131b53 | 48 | |
roysandberg | 62:8e2fbe131b53 | 49 | volatile uint8_t hrmCounter = 128; |
roysandberg | 62:8e2fbe131b53 | 50 | volatile uint16_t ecgSample = 0; |
roysandberg | 62:8e2fbe131b53 | 51 | volatile int sampleCounter=0; |
roysandberg | 62:8e2fbe131b53 | 52 | |
roysandberg | 62:8e2fbe131b53 | 53 | #define ITEMS_IN_QUEUE 50 |
roysandberg | 62:8e2fbe131b53 | 54 | Queue ecgQueue ( sizeof(int), ITEMS_IN_QUEUE ); // queue of hrmCounter values |
roysandberg | 62:8e2fbe131b53 | 55 | bool itemAddedToQueue = true; |
roysandberg | 62:8e2fbe131b53 | 56 | |
roysandberg | 61:1de72bdab0ef | 57 | |
mbedAustin | 55:3a7d497a3e03 | 58 | const static char DEVICE_NAME[] = "HRM1"; |
rgrover1 | 42:06ebef2e0e44 | 59 | static const uint16_t uuid16_list[] = {GattService::UUID_HEART_RATE_SERVICE, |
roysandberg | 61:1de72bdab0ef | 60 | GattService::UUID_DEVICE_INFORMATION_SERVICE |
roysandberg | 61:1de72bdab0ef | 61 | }; |
Rohit Grover |
36:ea2a1b4f51c1 | 62 | |
rgrover1 | 41:9cef0129da5f | 63 | void disconnectionCallback(Gap::Handle_t handle, Gap::DisconnectionReason_t reason) |
ktownsend | 0:87a7fc231fae | 64 | { |
rgrover1 | 46:ee7c55907f36 | 65 | ble.startAdvertising(); // restart advertising |
rgrover1 | 7:daab8ba5139e | 66 | } |
Rohit Grover |
3:24e2b056d229 | 67 | |
roysandberg | 62:8e2fbe131b53 | 68 | |
roysandberg | 62:8e2fbe131b53 | 69 | void power0() { |
roysandberg | 62:8e2fbe131b53 | 70 | LRA.write(0.0); |
roysandberg | 62:8e2fbe131b53 | 71 | } |
roysandberg | 62:8e2fbe131b53 | 72 | void power1() { |
roysandberg | 62:8e2fbe131b53 | 73 | LRA.write(0.55); |
roysandberg | 62:8e2fbe131b53 | 74 | } |
roysandberg | 62:8e2fbe131b53 | 75 | void power2() { |
roysandberg | 62:8e2fbe131b53 | 76 | LRA.write(0.85); |
roysandberg | 62:8e2fbe131b53 | 77 | } |
roysandberg | 62:8e2fbe131b53 | 78 | void power3() { |
roysandberg | 62:8e2fbe131b53 | 79 | LRA.write(1.0); |
roysandberg | 62:8e2fbe131b53 | 80 | } |
roysandberg | 61:1de72bdab0ef | 81 | |
roysandberg | 61:1de72bdab0ef | 82 | void sampler_callback(uint32_t value) |
Rohit Grover |
11:1d9aafee4984 | 83 | { |
roysandberg | 62:8e2fbe131b53 | 84 | //hrmCounter = (uint8_t) ((value+127) % 255); |
roysandberg | 62:8e2fbe131b53 | 85 | ecgSample = (uint16_t) value; |
roysandberg | 62:8e2fbe131b53 | 86 | itemAddedToQueue = ecgQueue.PutIrq((void*)&ecgSample); |
Rohit Grover |
11:1d9aafee4984 | 87 | } |
Rohit Grover |
11:1d9aafee4984 | 88 | |
roysandberg | 62:8e2fbe131b53 | 89 | |
ktownsend | 0:87a7fc231fae | 90 | int main(void) |
ktownsend | 0:87a7fc231fae | 91 | { |
roysandberg | 62:8e2fbe131b53 | 92 | uint16_t value; |
roysandberg | 62:8e2fbe131b53 | 93 | int beatType; |
roysandberg | 62:8e2fbe131b53 | 94 | int beatMatch; |
roysandberg | 62:8e2fbe131b53 | 95 | int samplesSinceLastR; |
roysandberg | 62:8e2fbe131b53 | 96 | int lastSamplesSinceLastR=0; |
roysandberg | 62:8e2fbe131b53 | 97 | int sampleOffset=0; |
roysandberg | 62:8e2fbe131b53 | 98 | char* beatName; |
roysandberg | 62:8e2fbe131b53 | 99 | |
rgrover1 | 47:430545f41113 | 100 | led1 = 1; |
roysandberg | 62:8e2fbe131b53 | 101 | |
roysandberg | 61:1de72bdab0ef | 102 | // Ticker ticker; |
roysandberg | 61:1de72bdab0ef | 103 | //ticker.attach(periodicCallback, 0.1); // blink LED every second |
roysandberg | 61:1de72bdab0ef | 104 | |
roysandberg | 62:8e2fbe131b53 | 105 | button1.rise(&power0); |
roysandberg | 62:8e2fbe131b53 | 106 | button2.rise(&power1); |
roysandberg | 62:8e2fbe131b53 | 107 | button3.rise(&power2); |
roysandberg | 62:8e2fbe131b53 | 108 | button4.rise(&power3); |
ktownsend | 0:87a7fc231fae | 109 | |
roysandberg | 62:8e2fbe131b53 | 110 | LRA.period_us(50); // 50 uS -> 20 Khz (needs to be between 10Khz and 250Khz) |
roysandberg | 62:8e2fbe131b53 | 111 | LRA.write(0.0); // Off. < 50% = 0ff |
roysandberg | 62:8e2fbe131b53 | 112 | |
roysandberg | 62:8e2fbe131b53 | 113 | printf("Initializing BLE...\r\n"); |
roysandberg | 62:8e2fbe131b53 | 114 | |
Rohit Grover |
15:7ba28817e31e | 115 | ble.init(); |
rgrover1 | 7:daab8ba5139e | 116 | ble.onDisconnection(disconnectionCallback); |
ktownsend | 0:87a7fc231fae | 117 | |
rgrover1 | 45:98c5a34b07a4 | 118 | /* Setup primary service. */ |
roysandberg | 61:1de72bdab0ef | 119 | //uint8_t hrmCounter = 100; // init HRM to 100bps |
rgrover1 | 45:98c5a34b07a4 | 120 | HeartRateService hrService(ble, hrmCounter, HeartRateService::LOCATION_FINGER); |
rgrover1 | 45:98c5a34b07a4 | 121 | |
mbedAustin | 55:3a7d497a3e03 | 122 | /* Setup auxiliary service. */ |
rgrover1 | 45:98c5a34b07a4 | 123 | DeviceInformationService deviceInfo(ble, "ARM", "Model1", "SN1", "hw-rev1", "fw-rev1", "soft-rev1"); |
rgrover1 | 45:98c5a34b07a4 | 124 | |
rgrover1 | 45:98c5a34b07a4 | 125 | /* Setup advertising. */ |
roysandberg | 62:8e2fbe131b53 | 126 | printf("Setting up advertising...\r\n"); |
Rohit Grover |
29:76d865c718a6 | 127 | ble.accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE); |
rgrover1 | 40:e73130c6f2bb | 128 | ble.accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, (uint8_t *)uuid16_list, sizeof(uuid16_list)); |
rgrover1 | 42:06ebef2e0e44 | 129 | ble.accumulateAdvertisingPayload(GapAdvertisingData::GENERIC_HEART_RATE_SENSOR); |
Rohit Grover |
29:76d865c718a6 | 130 | ble.accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)DEVICE_NAME, sizeof(DEVICE_NAME)); |
rgrover1 | 7:daab8ba5139e | 131 | ble.setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED); |
rgrover1 | 56:83623419d5e4 | 132 | ble.setAdvertisingInterval(1000); |
rgrover1 | 7:daab8ba5139e | 133 | ble.startAdvertising(); |
Rohit Grover |
3:24e2b056d229 | 134 | |
roysandberg | 62:8e2fbe131b53 | 135 | setup_sampler(&sampler_callback); |
roysandberg | 62:8e2fbe131b53 | 136 | |
roysandberg | 62:8e2fbe131b53 | 137 | ResetBDAC(); // reset the beat detector and classifier |
roysandberg | 62:8e2fbe131b53 | 138 | |
roysandberg | 62:8e2fbe131b53 | 139 | printf("Starting...\r\n"); |
roysandberg | 62:8e2fbe131b53 | 140 | |
mbedAustin | 55:3a7d497a3e03 | 141 | // infinite loop |
mbedAustin | 55:3a7d497a3e03 | 142 | while (1) { |
mbedAustin | 55:3a7d497a3e03 | 143 | // check for trigger from periodicCallback() |
roysandberg | 62:8e2fbe131b53 | 144 | if (ble.getGapState().connected) { |
roysandberg | 62:8e2fbe131b53 | 145 | while (ecgQueue.GetNumberOfItems()>0) { |
roysandberg | 62:8e2fbe131b53 | 146 | ecgQueue.Get(&value); |
roysandberg | 62:8e2fbe131b53 | 147 | samplesSinceLastR = BeatDetectAndClassify(value, &beatType, &beatMatch); |
roysandberg | 62:8e2fbe131b53 | 148 | if (samplesSinceLastR != 0) { |
roysandberg | 62:8e2fbe131b53 | 149 | printf("[C] samplesSinceLastR=%d, type=%d\r\n", value, beatType); |
roysandberg | 62:8e2fbe131b53 | 150 | } |
roysandberg | 62:8e2fbe131b53 | 151 | //hrService.updateHeartRate(value); |
roysandberg | 62:8e2fbe131b53 | 152 | led1 = !led1; |
roysandberg | 62:8e2fbe131b53 | 153 | } |
Rohit Grover |
36:ea2a1b4f51c1 | 154 | } else { |
roysandberg | 62:8e2fbe131b53 | 155 | if (!itemAddedToQueue) { |
roysandberg | 62:8e2fbe131b53 | 156 | printf("Queue overflow.\n\r"); |
roysandberg | 62:8e2fbe131b53 | 157 | itemAddedToQueue = true; |
roysandberg | 62:8e2fbe131b53 | 158 | } |
roysandberg | 62:8e2fbe131b53 | 159 | while (ecgQueue.GetNumberOfItems()>0) { |
roysandberg | 62:8e2fbe131b53 | 160 | sampleCounter++; |
roysandberg | 62:8e2fbe131b53 | 161 | ecgQueue.Get(&value); |
roysandberg | 62:8e2fbe131b53 | 162 | samplesSinceLastR = BeatDetectAndClassify(value, &beatType, &beatMatch); |
roysandberg | 62:8e2fbe131b53 | 163 | if (samplesSinceLastR != 0) { |
roysandberg | 62:8e2fbe131b53 | 164 | sampleOffset = lastSamplesSinceLastR - samplesSinceLastR; |
roysandberg | 62:8e2fbe131b53 | 165 | |
roysandberg | 62:8e2fbe131b53 | 166 | if (beatType == 1) { |
roysandberg | 62:8e2fbe131b53 | 167 | beatName = "Normal"; |
roysandberg | 62:8e2fbe131b53 | 168 | } else if (beatType == 5) { |
roysandberg | 62:8e2fbe131b53 | 169 | beatName = "Ectopic"; |
roysandberg | 62:8e2fbe131b53 | 170 | } else { |
roysandberg | 62:8e2fbe131b53 | 171 | beatName = "Unknown"; |
roysandberg | 62:8e2fbe131b53 | 172 | } |
roysandberg | 62:8e2fbe131b53 | 173 | printf("[NC] interval_ms=%d, bpm=%d, samplesSinceLastR=%d (%s)\r\n", ((sampleCounter-sampleOffset)*MS_PER_SAMPLE), (60000/((sampleCounter-sampleOffset)*MS_PER_SAMPLE)), value, beatName); |
roysandberg | 62:8e2fbe131b53 | 174 | sampleCounter=0; |
roysandberg | 62:8e2fbe131b53 | 175 | lastSamplesSinceLastR = samplesSinceLastR; |
roysandberg | 62:8e2fbe131b53 | 176 | } |
roysandberg | 62:8e2fbe131b53 | 177 | } |
mbedAustin | 55:3a7d497a3e03 | 178 | ble.waitForEvent(); // low power wait for event |
Rohit Grover |
36:ea2a1b4f51c1 | 179 | } |
ktownsend | 0:87a7fc231fae | 180 | } |
ktownsend | 0:87a7fc231fae | 181 | } |