Chanel's edits
Dependencies: max32630fthr USBDevice
main.cpp@16:f6bfa6b66e96, 2020-03-11 (annotated)
- Committer:
- saleiferis
- Date:
- Wed Mar 11 00:54:39 2020 +0000
- Revision:
- 16:f6bfa6b66e96
- Parent:
- 15:b15b4b6c6da8
- Child:
- 17:65a8b29f6bac
preprocessing+PanTompkins Heart Rate + BLE
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
saleiferis | 0:89ec48e52250 | 1 | #include "mbed.h" |
saleiferis | 0:89ec48e52250 | 2 | #include "math.h" |
saleiferis | 0:89ec48e52250 | 3 | #include "max32630fthr.h" |
saleiferis | 0:89ec48e52250 | 4 | #include "max86150.h" |
saleiferis | 0:89ec48e52250 | 5 | #include "I2C.h" |
saleiferis | 0:89ec48e52250 | 6 | #include "ble/BLE.h" |
saleiferis | 0:89ec48e52250 | 7 | #include "ble/Gap.h" |
saleiferis | 7:4debec043316 | 8 | //#include "ble/services/HeartRateService.h" |
saleiferis | 7:4debec043316 | 9 | #include "ECGService.h" |
saleiferis | 1:6e6f7e3cc1e1 | 10 | #include <events/mbed_events.h> |
saleiferis | 1:6e6f7e3cc1e1 | 11 | #include "bt32630.h" |
saleiferis | 16:f6bfa6b66e96 | 12 | #include "panTompkins.h" |
saleiferis | 14:ee2175578993 | 13 | //#include "filters.h" |
saleiferis | 0:89ec48e52250 | 14 | |
saleiferis | 0:89ec48e52250 | 15 | //Register definitions |
saleiferis | 0:89ec48e52250 | 16 | #define MAX86150_Addr 0xBC //updated per I2Cscanner, 8 bit version of 7 bit code 0x5E |
saleiferis | 0:89ec48e52250 | 17 | #define maxi2cFreq 1000000 |
saleiferis | 0:89ec48e52250 | 18 | #define recommendedi2cFreq 400000 |
saleiferis | 10:28b8729cf5dc | 19 | |
saleiferis | 10:28b8729cf5dc | 20 | #define BaudRate 115200 |
saleiferis | 0:89ec48e52250 | 21 | |
saleiferis | 13:a555fd1253e7 | 22 | #define WINDOW_SIZE 25 // number of samples for moving winfow integration for HR algo preprocessing. Effective window_size is half of this value |
saleiferis | 14:ee2175578993 | 23 | #define BUFF_SIZE 136 // TODO: decouple BUFF_SIZE and FIR_SIZE. now program crashes if != 136 |
saleiferis | 14:ee2175578993 | 24 | #define FIR_SIZE 136 |
saleiferis | 0:89ec48e52250 | 25 | |
saleiferis | 10:28b8729cf5dc | 26 | Serial pc(USBTX,USBRX,NULL,BaudRate); |
saleiferis | 10:28b8729cf5dc | 27 | InterruptIn intPin(P5_5); // interrupts currently not used |
saleiferis | 0:89ec48e52250 | 28 | I2C i2c(I2C2_SDA, I2C2_SCL); |
saleiferis | 8:2005014df05c | 29 | MAX86150 max86150Sensor; |
saleiferis | 0:89ec48e52250 | 30 | |
saleiferis | 10:28b8729cf5dc | 31 | ECGService *hrServicePtr; |
saleiferis | 14:ee2175578993 | 32 | int k; // loop iteration |
saleiferis | 14:ee2175578993 | 33 | float signal[BUFF_SIZE]={0}; //store signal segment to be filtered |
saleiferis | 14:ee2175578993 | 34 | float bp_signal[BUFF_SIZE]={0}; |
saleiferis | 14:ee2175578993 | 35 | float derivative[BUFF_SIZE]={0}; |
saleiferis | 14:ee2175578993 | 36 | float squared[BUFF_SIZE]={0}; |
saleiferis | 14:ee2175578993 | 37 | float integral[BUFF_SIZE] = {0}; |
saleiferis | 16:f6bfa6b66e96 | 38 | bool outputSignal[BUFF_SIZE]={0}; |
saleiferis | 14:ee2175578993 | 39 | |
saleiferis | 14:ee2175578993 | 40 | // Variables for preprocessing |
saleiferis | 14:ee2175578993 | 41 | float y = 0.0; //filtered sample to be displayed |
saleiferis | 14:ee2175578993 | 42 | float prev_y = 0.0; // keep track of previous output to calculate derivative |
saleiferis | 14:ee2175578993 | 43 | float sq_y = 0.0; |
saleiferis | 14:ee2175578993 | 44 | float movmean = 0.0; // result of moving window integration |
saleiferis | 14:ee2175578993 | 45 | |
saleiferis | 14:ee2175578993 | 46 | |
saleiferis | 14:ee2175578993 | 47 | // Variables for peak-finding |
saleiferis | 14:ee2175578993 | 48 | int rr1[8], rr2[8], rravg1, rravg2, rrlow = 0, rrhigh = 0, rrmiss = 0; |
saleiferis | 16:f6bfa6b66e96 | 49 | long unsigned int i, j, sample = 0, current = 0, lastQRS = 0, lastSlope = 0, currentSlope = 0; |
saleiferis | 14:ee2175578993 | 50 | float peak_i = 0, peak_f = 0, threshold_i1 = 0, threshold_i2 = 0, threshold_f1 = 0, threshold_f2 = 0, spk_i = 0, spk_f = 0, npk_i = 0, npk_f = 0; |
saleiferis | 14:ee2175578993 | 51 | bool qrs, regular = true, prevRegular; |
saleiferis | 14:ee2175578993 | 52 | |
saleiferis | 9:171171516ebd | 53 | |
saleiferis | 12:ad628acddbf7 | 54 | //Timer timer; |
saleiferis | 9:171171516ebd | 55 | |
saleiferis | 1:6e6f7e3cc1e1 | 56 | static events::EventQueue event_queue(/* event count */ 16 * EVENTS_EVENT_SIZE); |
saleiferis | 1:6e6f7e3cc1e1 | 57 | |
saleiferis | 15:b15b4b6c6da8 | 58 | |
saleiferis | 9:171171516ebd | 59 | |
saleiferis | 15:b15b4b6c6da8 | 60 | |
saleiferis | 10:28b8729cf5dc | 61 | |
saleiferis | 0:89ec48e52250 | 62 | ////////// |
saleiferis | 10:28b8729cf5dc | 63 | int main(){ |
saleiferis | 14:ee2175578993 | 64 | /* |
saleiferis | 0:89ec48e52250 | 65 | max86150Sensor.begin(i2c, recommendedi2cFreq, MAX86150_Addr); |
saleiferis | 0:89ec48e52250 | 66 | wait_ms(300); |
saleiferis | 4:4233f5538abf | 67 | |
saleiferis | 0:89ec48e52250 | 68 | //unsigned char partID = max86150Sensor.readPartID(); |
saleiferis | 0:89ec48e52250 | 69 | unsigned char partID = max86150Sensor.readRegister8(MAX86150_Addr,0xFF); |
saleiferis | 0:89ec48e52250 | 70 | pc.printf("Part ID is: %X\n",partID); |
saleiferis | 14:ee2175578993 | 71 | while (partID != 0x1E) { } // Connection to sensor is not established |
saleiferis | 2:a96a53e6c6a3 | 72 | |
saleiferis | 2:a96a53e6c6a3 | 73 | |
saleiferis | 14:ee2175578993 | 74 | // SETUP SENSOR |
saleiferis | 10:28b8729cf5dc | 75 | max86150Sensor.setup(); //Configure sensor |
saleiferis | 10:28b8729cf5dc | 76 | wait_ms(300); |
saleiferis | 10:28b8729cf5dc | 77 | pc.printf("SYSCONTOL REG: %x\n", max86150Sensor.readRegister8(MAX86150_Addr,0x0D)); |
saleiferis | 10:28b8729cf5dc | 78 | pc.printf("FIFO CONFIG: %X\n",max86150Sensor.readRegister8(MAX86150_Addr,0x08)); |
saleiferis | 10:28b8729cf5dc | 79 | pc.printf("INT_EN1: %X\n", max86150Sensor.readRegister8(MAX86150_Addr,0x02)); |
saleiferis | 10:28b8729cf5dc | 80 | pc.printf("INT_EN2: %X\n", max86150Sensor.readRegister8(MAX86150_Addr,0x03)); |
saleiferis | 10:28b8729cf5dc | 81 | pc.printf("INT STATUS1: %X\n",max86150Sensor.readRegister8(MAX86150_Addr,0x00)); |
saleiferis | 10:28b8729cf5dc | 82 | pc.printf("INT STATUS2: %X\n",max86150Sensor.readRegister8(MAX86150_Addr,0x01)); |
saleiferis | 10:28b8729cf5dc | 83 | |
saleiferis | 14:ee2175578993 | 84 | //// |
saleiferis | 10:28b8729cf5dc | 85 | |
saleiferis | 10:28b8729cf5dc | 86 | max86150Sensor.clearFIFO(); |
saleiferis | 10:28b8729cf5dc | 87 | max86150Sensor.writeRegister8(MAX86150_Addr,0x0D,0x04); //start FIFO |
saleiferis | 14:ee2175578993 | 88 | */ |
saleiferis | 10:28b8729cf5dc | 89 | //******* SETUP BLUETOOTH ********* |
saleiferis | 10:28b8729cf5dc | 90 | eventQueue.call_every(1, periodicCallback); // poll sensor every 1ms. New samples come every 5ms, so polling freq can potentially be decreased |
saleiferis | 1:6e6f7e3cc1e1 | 91 | BLE &ble = BLE::Instance(); |
saleiferis | 10:28b8729cf5dc | 92 | ble.onEventsToProcess(scheduleBleEventsProcessing); |
saleiferis | 14:ee2175578993 | 93 | ble.init(bleInitComplete); |
saleiferis | 14:ee2175578993 | 94 | eventQueue.dispatch_forever(); |
saleiferis | 14:ee2175578993 | 95 | |
saleiferis | 13:a555fd1253e7 | 96 | |
saleiferis | 14:ee2175578993 | 97 | /* |
saleiferis | 14:ee2175578993 | 98 | int16_t ecgsigned16; |
saleiferis | 13:a555fd1253e7 | 99 | while(1){ |
saleiferis | 13:a555fd1253e7 | 100 | if(max86150Sensor.check()>0){ |
saleiferis | 13:a555fd1253e7 | 101 | |
saleiferis | 13:a555fd1253e7 | 102 | ecgsigned16 = (int16_t) (max86150Sensor.getFIFOECG()>>2); |
saleiferis | 13:a555fd1253e7 | 103 | max86150Sensor.nextSample(); |
saleiferis | 13:a555fd1253e7 | 104 | ble.updateHeartRate(ecgsigned16); |
saleiferis | 14:ee2175578993 | 105 | pc.printf("%f\n",(float)ecgsigned16); |
saleiferis | 14:ee2175578993 | 106 | */ |
saleiferis | 13:a555fd1253e7 | 107 | |
saleiferis | 4:4233f5538abf | 108 | |
saleiferis | 10:28b8729cf5dc | 109 | // Below code is for testing interrupts and sensor polling without involving bluetooth API |
saleiferis | 14:ee2175578993 | 110 | /* UNCOMMENT HERE |
saleiferis | 10:28b8729cf5dc | 111 | // max86150Sensor.clearFIFO(); |
saleiferis | 10:28b8729cf5dc | 112 | //intPin.fall(&ISR_DATA_READY); |
saleiferis | 10:28b8729cf5dc | 113 | // max86150Sensor.writeRegister8(MAX86150_Addr,0x0D,0x04); |
saleiferis | 10:28b8729cf5dc | 114 | int16_t ecgsigned16; |
saleiferis | 13:a555fd1253e7 | 115 | int k; // loop iteration |
saleiferis | 12:ad628acddbf7 | 116 | float signal[BUFF_SIZE]={0}; //store signal segment to be filtered |
saleiferis | 12:ad628acddbf7 | 117 | float bp_signal[BUFF_SIZE]={0}; |
saleiferis | 12:ad628acddbf7 | 118 | float derivative[BUFF_SIZE]={0}; |
saleiferis | 12:ad628acddbf7 | 119 | float squared[BUFF_SIZE]={0}; |
saleiferis | 12:ad628acddbf7 | 120 | float integral[BUFF_SIZE] = {0}; |
saleiferis | 12:ad628acddbf7 | 121 | |
saleiferis | 12:ad628acddbf7 | 122 | // Variables for preprocessing |
saleiferis | 11:cbd9182d14d2 | 123 | float y = 0.0; //filtered sample to be displayed |
saleiferis | 12:ad628acddbf7 | 124 | float prev_y = 0.0; // keep track of previous output to calculate derivative |
saleiferis | 12:ad628acddbf7 | 125 | float sq_y = 0.0; |
saleiferis | 12:ad628acddbf7 | 126 | float movmean = 0.0; // result of moving window integration |
saleiferis | 12:ad628acddbf7 | 127 | |
saleiferis | 12:ad628acddbf7 | 128 | // Variables for peak-finding |
saleiferis | 12:ad628acddbf7 | 129 | int rr1[8], rr2[8], rravg1, rravg2, rrlow = 0, rrhigh = 0, rrmiss = 0; |
saleiferis | 16:f6bfa6b66e96 | 130 | long unsigned int i, j, sample = 0, current = 0, lastQRS = 0, lastSlope = 0, currentSlope = 0; |
saleiferis | 12:ad628acddbf7 | 131 | float peak_i = 0, peak_f = 0, threshold_i1 = 0, threshold_i2 = 0, threshold_f1 = 0, threshold_f2 = 0, spk_i = 0, spk_f = 0, npk_i = 0, npk_f = 0; |
saleiferis | 12:ad628acddbf7 | 132 | bool qrs, regular = true, prevRegular; |
saleiferis | 12:ad628acddbf7 | 133 | // Initializing the RR averages |
saleiferis | 12:ad628acddbf7 | 134 | for (i = 0; i < 8; i++) |
saleiferis | 12:ad628acddbf7 | 135 | { |
saleiferis | 12:ad628acddbf7 | 136 | rr1[i] = 0; |
saleiferis | 12:ad628acddbf7 | 137 | rr2[i] = 0; |
saleiferis | 12:ad628acddbf7 | 138 | } |
saleiferis | 12:ad628acddbf7 | 139 | |
saleiferis | 9:171171516ebd | 140 | |
saleiferis | 8:2005014df05c | 141 | while(1){ |
saleiferis | 10:28b8729cf5dc | 142 | if(max86150Sensor.check()>0){ |
saleiferis | 10:28b8729cf5dc | 143 | |
saleiferis | 11:cbd9182d14d2 | 144 | //ecgsigned16 = (int16_t) (max86150Sensor.getFIFOECG()>>2); |
saleiferis | 11:cbd9182d14d2 | 145 | //max86150Sensor.nextSample(); |
saleiferis | 11:cbd9182d14d2 | 146 | //pc.printf("%f\n",(float)ecgsigned16); |
saleiferis | 11:cbd9182d14d2 | 147 | |
saleiferis | 12:ad628acddbf7 | 148 | // if buffer is full |
saleiferis | 16:f6bfa6b66e96 | 149 | if (sample >= BUFF_SIZE){ |
saleiferis | 13:a555fd1253e7 | 150 | for (k=0; k<BUFF_SIZE-1; k++){ //discard oldest sample, shift samples in buffer |
saleiferis | 13:a555fd1253e7 | 151 | signal[k] = signal[k+1]; |
saleiferis | 13:a555fd1253e7 | 152 | bp_signal[k] = bp_signal[k+1]; |
saleiferis | 13:a555fd1253e7 | 153 | derivative[k] = derivative[k+1]; |
saleiferis | 13:a555fd1253e7 | 154 | integral[k] = integral[k+1]; |
saleiferis | 13:a555fd1253e7 | 155 | squared[k] = squared[k+1]; |
saleiferis | 12:ad628acddbf7 | 156 | |
saleiferis | 11:cbd9182d14d2 | 157 | } |
saleiferis | 16:f6bfa6b66e96 | 158 | current = BUFF_SIZE-1; // indicates that buffer is full |
saleiferis | 12:ad628acddbf7 | 159 | prev_y = y; |
saleiferis | 11:cbd9182d14d2 | 160 | y = 0.0; // reset filter output for current sample |
saleiferis | 11:cbd9182d14d2 | 161 | }else{ //Buffer is not full yet |
saleiferis | 16:f6bfa6b66e96 | 162 | current = sample; //position at buffer is same as sample number |
saleiferis | 11:cbd9182d14d2 | 163 | } |
saleiferis | 11:cbd9182d14d2 | 164 | ecgsigned16 = (int16_t) (max86150Sensor.getFIFOECG()>>2); //read a sample |
saleiferis | 11:cbd9182d14d2 | 165 | max86150Sensor.nextSample(); // advance tail to get sample in next iteration |
saleiferis | 16:f6bfa6b66e96 | 166 | signal[current] = (float)ecgsigned16; //add sample to buffer |
saleiferis | 16:f6bfa6b66e96 | 167 | sample++; |
saleiferis | 11:cbd9182d14d2 | 168 | |
saleiferis | 16:f6bfa6b66e96 | 169 | if (current< FIR_SIZE-1){ |
saleiferis | 11:cbd9182d14d2 | 170 | // buffer is not full yet |
saleiferis | 11:cbd9182d14d2 | 171 | }else{ //buffer is full, filter current sample |
saleiferis | 14:ee2175578993 | 172 | for(k = 0; k < FIR_SIZE; k++){ //FIR bandpass filter |
saleiferis | 16:f6bfa6b66e96 | 173 | y = y + FIR_BP[k] * signal[current-k]; |
saleiferis | 11:cbd9182d14d2 | 174 | } |
saleiferis | 16:f6bfa6b66e96 | 175 | bp_signal[current] = y; |
saleiferis | 12:ad628acddbf7 | 176 | } |
saleiferis | 12:ad628acddbf7 | 177 | sq_y = pow(y-prev_y,2); |
saleiferis | 16:f6bfa6b66e96 | 178 | squared[current] = sq_y; |
saleiferis | 12:ad628acddbf7 | 179 | //moving window integration |
saleiferis | 12:ad628acddbf7 | 180 | movmean = 0.0; // reset for current sample |
saleiferis | 13:a555fd1253e7 | 181 | for(k=0; k<(int)WINDOW_SIZE/2; k++){ |
saleiferis | 16:f6bfa6b66e96 | 182 | //pc.printf("%d\n",(int)current - (int)(WINDOW_SIZE)); |
saleiferis | 16:f6bfa6b66e96 | 183 | if ((int)current - (int)(WINDOW_SIZE) > k){ //there are enough samples in squared[] |
saleiferis | 16:f6bfa6b66e96 | 184 | // movmean = movmean + squared[(current-k-(int)WINDOW_SIZE/2)] + squared[current+k-(int)WINDOW_SIZE/2]; |
saleiferis | 16:f6bfa6b66e96 | 185 | movmean = movmean + squared[current-k]; |
saleiferis | 12:ad628acddbf7 | 186 | |
saleiferis | 12:ad628acddbf7 | 187 | }else{ |
saleiferis | 12:ad628acddbf7 | 188 | break; |
saleiferis | 12:ad628acddbf7 | 189 | } |
saleiferis | 11:cbd9182d14d2 | 190 | } |
saleiferis | 13:a555fd1253e7 | 191 | movmean = movmean/(float)(k+1); |
saleiferis | 16:f6bfa6b66e96 | 192 | integral[current] = movmean; |
saleiferis | 12:ad628acddbf7 | 193 | |
saleiferis | 12:ad628acddbf7 | 194 | pc.printf("%f %f\n",movmean/1000, y/50); |
saleiferis | 12:ad628acddbf7 | 195 | |
saleiferis | 12:ad628acddbf7 | 196 | // Preprocessing done |
saleiferis | 14:ee2175578993 | 197 | |
saleiferis | 12:ad628acddbf7 | 198 | // Start peak-finding |
saleiferis | 13:a555fd1253e7 | 199 | qrs = false; |
saleiferis | 16:f6bfa6b66e96 | 200 | if (integral[current] >= threshold_i1 || highpass[current] >= threshold_f1){ |
saleiferis | 16:f6bfa6b66e96 | 201 | peak_i = integral[current]; |
saleiferis | 16:f6bfa6b66e96 | 202 | peak_f = bp_signal[current]; |
saleiferis | 14:ee2175578993 | 203 | } |
saleiferis | 12:ad628acddbf7 | 204 | |
saleiferis | 12:ad628acddbf7 | 205 | |
saleiferis | 12:ad628acddbf7 | 206 | |
saleiferis | 12:ad628acddbf7 | 207 | |
saleiferis | 10:28b8729cf5dc | 208 | } |
saleiferis | 14:ee2175578993 | 209 | } end while(1)*/ |
saleiferis | 10:28b8729cf5dc | 210 | |
saleiferis | 0:89ec48e52250 | 211 | } |