Chanel's edits
Dependencies: max32630fthr USBDevice
bt32630.h@15:b15b4b6c6da8, 2020-03-10 (annotated)
- Committer:
- saleiferis
- Date:
- Tue Mar 10 20:33:49 2020 +0000
- Revision:
- 15:b15b4b6c6da8
- Parent:
- 14:ee2175578993
- Child:
- 16:f6bfa6b66e96
preprocessing, BLE, and serial, before pan-tompkins
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
saleiferis | 7:4debec043316 | 1 | //#include "HeartRateService.h" |
saleiferis | 7:4debec043316 | 2 | #include "ECGService.h" |
saleiferis | 14:ee2175578993 | 3 | #include "PPGService.h" |
saleiferis | 14:ee2175578993 | 4 | #include "filters.h" |
saleiferis | 14:ee2175578993 | 5 | |
saleiferis | 14:ee2175578993 | 6 | //Register definitions |
saleiferis | 14:ee2175578993 | 7 | #define MAX86150_Addr 0xBC //updated per I2Cscanner, 8 bit version of 7 bit code 0x5E |
saleiferis | 14:ee2175578993 | 8 | #define maxi2cFreq 1000000 |
saleiferis | 14:ee2175578993 | 9 | #define recommendedi2cFreq 400000 |
saleiferis | 14:ee2175578993 | 10 | |
saleiferis | 14:ee2175578993 | 11 | #define BaudRate 115200 |
saleiferis | 14:ee2175578993 | 12 | |
saleiferis | 14:ee2175578993 | 13 | #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 | 14 | #define BUFF_SIZE 136 // TODO: decouple BUFF_SIZE and FIR_SIZE. now program crashes if != 136 |
saleiferis | 14:ee2175578993 | 15 | #define FIR_SIZE 136 |
saleiferis | 7:4debec043316 | 16 | |
saleiferis | 1:6e6f7e3cc1e1 | 17 | extern MAX86150 max86150Sensor; |
saleiferis | 1:6e6f7e3cc1e1 | 18 | extern Serial pc; |
saleiferis | 14:ee2175578993 | 19 | extern I2C i2c; |
saleiferis | 14:ee2175578993 | 20 | extern BLE &ble; |
saleiferis | 15:b15b4b6c6da8 | 21 | const static char DEVICE_NAME[] = "MAX32630"; |
saleiferis | 1:6e6f7e3cc1e1 | 22 | static const uint16_t uuid16_list[] = {GattService::UUID_HEART_RATE_SERVICE}; |
saleiferis | 1:6e6f7e3cc1e1 | 23 | |
saleiferis | 14:ee2175578993 | 24 | // |
saleiferis | 14:ee2175578993 | 25 | extern int k; // loop iteration |
saleiferis | 14:ee2175578993 | 26 | extern float signal[BUFF_SIZE]; //store signal segment to be filtered |
saleiferis | 14:ee2175578993 | 27 | extern float bp_signal[BUFF_SIZE]; |
saleiferis | 14:ee2175578993 | 28 | extern float derivative[BUFF_SIZE]; |
saleiferis | 14:ee2175578993 | 29 | extern float squared[BUFF_SIZE]; |
saleiferis | 14:ee2175578993 | 30 | extern float integral[BUFF_SIZE]; |
saleiferis | 14:ee2175578993 | 31 | |
saleiferis | 14:ee2175578993 | 32 | // |
saleiferis | 14:ee2175578993 | 33 | extern int rr1[8], rr2[8], rravg1, rravg2, rrlow , rrhigh , rrmiss ; |
saleiferis | 14:ee2175578993 | 34 | extern long unsigned int i, j, sample_idx , curr , lastQRS , lastSlope , currentSlope ; |
saleiferis | 14:ee2175578993 | 35 | extern float peak_i , peak_f , threshold_i1 , threshold_i2 , threshold_f1 , threshold_f2 , spk_i , spk_f , npk_i , npk_f ; |
saleiferis | 14:ee2175578993 | 36 | extern bool qrs, regular, prevRegular; |
saleiferis | 14:ee2175578993 | 37 | |
saleiferis | 14:ee2175578993 | 38 | // Variables for preprocessing |
saleiferis | 14:ee2175578993 | 39 | extern float y ; //filtered sample to be displayed |
saleiferis | 14:ee2175578993 | 40 | extern float prev_y ; // keep track of previous output to calculate derivative |
saleiferis | 14:ee2175578993 | 41 | extern float sq_y; |
saleiferis | 14:ee2175578993 | 42 | extern float movmean; // result of moving window integration |
saleiferis | 14:ee2175578993 | 43 | |
saleiferis | 7:4debec043316 | 44 | |
saleiferis | 10:28b8729cf5dc | 45 | static int16_t hrmCounter = 100; // init HRM to 100bps |
saleiferis | 7:4debec043316 | 46 | //static HeartRateService *hrServicePtr; |
saleiferis | 10:28b8729cf5dc | 47 | extern ECGService *hrServicePtr; |
saleiferis | 1:6e6f7e3cc1e1 | 48 | |
saleiferis | 1:6e6f7e3cc1e1 | 49 | static EventQueue eventQueue(/* event count */ 16 * EVENTS_EVENT_SIZE); |
saleiferis | 1:6e6f7e3cc1e1 | 50 | |
saleiferis | 1:6e6f7e3cc1e1 | 51 | |
saleiferis | 4:4233f5538abf | 52 | void connectionCallback(const Gap::ConnectionCallbackParams_t *params) |
saleiferis | 4:4233f5538abf | 53 | { |
saleiferis | 4:4233f5538abf | 54 | pc.printf("Connected to BLE Client...\n"); |
saleiferis | 14:ee2175578993 | 55 | max86150Sensor.begin(i2c, recommendedi2cFreq, MAX86150_Addr); |
saleiferis | 14:ee2175578993 | 56 | wait_ms(300); |
saleiferis | 14:ee2175578993 | 57 | |
saleiferis | 14:ee2175578993 | 58 | //unsigned char partID = max86150Sensor.readPartID(); |
saleiferis | 14:ee2175578993 | 59 | unsigned char partID = max86150Sensor.readRegister8(MAX86150_Addr,0xFF); |
saleiferis | 14:ee2175578993 | 60 | pc.printf("Part ID is: %X\n",partID); |
saleiferis | 14:ee2175578993 | 61 | while (partID != 0x1E) {/* Connection to sensor is not established */ } |
saleiferis | 14:ee2175578993 | 62 | |
saleiferis | 14:ee2175578993 | 63 | |
saleiferis | 14:ee2175578993 | 64 | //***** SETUP SENSOR */ |
saleiferis | 14:ee2175578993 | 65 | max86150Sensor.setup(); //Configure sensor |
saleiferis | 14:ee2175578993 | 66 | wait_ms(300); |
saleiferis | 14:ee2175578993 | 67 | pc.printf("SYSCONTOL REG: %x\n", max86150Sensor.readRegister8(MAX86150_Addr,0x0D)); |
saleiferis | 14:ee2175578993 | 68 | pc.printf("FIFO CONFIG: %X\n",max86150Sensor.readRegister8(MAX86150_Addr,0x08)); |
saleiferis | 14:ee2175578993 | 69 | pc.printf("INT_EN1: %X\n", max86150Sensor.readRegister8(MAX86150_Addr,0x02)); |
saleiferis | 14:ee2175578993 | 70 | pc.printf("INT_EN2: %X\n", max86150Sensor.readRegister8(MAX86150_Addr,0x03)); |
saleiferis | 14:ee2175578993 | 71 | pc.printf("INT STATUS1: %X\n",max86150Sensor.readRegister8(MAX86150_Addr,0x00)); |
saleiferis | 14:ee2175578993 | 72 | pc.printf("INT STATUS2: %X\n",max86150Sensor.readRegister8(MAX86150_Addr,0x01)); |
saleiferis | 14:ee2175578993 | 73 | //*************************************************************// |
saleiferis | 14:ee2175578993 | 74 | |
saleiferis | 14:ee2175578993 | 75 | max86150Sensor.clearFIFO(); |
saleiferis | 14:ee2175578993 | 76 | max86150Sensor.writeRegister8(MAX86150_Addr,0x0D,0x04); //start FIFO |
saleiferis | 14:ee2175578993 | 77 | |
saleiferis | 4:4233f5538abf | 78 | } |
saleiferis | 4:4233f5538abf | 79 | |
saleiferis | 1:6e6f7e3cc1e1 | 80 | void disconnectionCallback(const Gap::DisconnectionCallbackParams_t *params) |
saleiferis | 1:6e6f7e3cc1e1 | 81 | { |
saleiferis | 1:6e6f7e3cc1e1 | 82 | BLE::Instance().gap().startAdvertising(); // restart advertising |
saleiferis | 1:6e6f7e3cc1e1 | 83 | } |
saleiferis | 1:6e6f7e3cc1e1 | 84 | |
saleiferis | 1:6e6f7e3cc1e1 | 85 | void updateSensorValue() { |
saleiferis | 1:6e6f7e3cc1e1 | 86 | // Do blocking calls or whatever is necessary for sensor polling. |
saleiferis | 1:6e6f7e3cc1e1 | 87 | // In our case, we simply update the HRM measurement. |
saleiferis | 14:ee2175578993 | 88 | int16_t ecgsigned16; |
saleiferis | 14:ee2175578993 | 89 | uint32_t ppgRed16; |
saleiferis | 14:ee2175578993 | 90 | if(max86150Sensor.check()>0){ |
saleiferis | 14:ee2175578993 | 91 | // if buffer is full |
saleiferis | 14:ee2175578993 | 92 | if (sample_idx >= BUFF_SIZE){ |
saleiferis | 14:ee2175578993 | 93 | for (k=0; k<BUFF_SIZE-1; k++){ //discard oldest sample, shift samples in buffer |
saleiferis | 14:ee2175578993 | 94 | signal[k] = signal[k+1]; |
saleiferis | 14:ee2175578993 | 95 | bp_signal[k] = bp_signal[k+1]; |
saleiferis | 14:ee2175578993 | 96 | derivative[k] = derivative[k+1]; |
saleiferis | 14:ee2175578993 | 97 | integral[k] = integral[k+1]; |
saleiferis | 14:ee2175578993 | 98 | squared[k] = squared[k+1]; |
saleiferis | 14:ee2175578993 | 99 | |
saleiferis | 14:ee2175578993 | 100 | } |
saleiferis | 14:ee2175578993 | 101 | curr = BUFF_SIZE-1; // indicates that buffer is full |
saleiferis | 14:ee2175578993 | 102 | prev_y = y; |
saleiferis | 14:ee2175578993 | 103 | y = 0.0; // reset filter output for current sample |
saleiferis | 14:ee2175578993 | 104 | }else{ //Buffer is not full yet |
saleiferis | 14:ee2175578993 | 105 | curr = sample_idx; //position at buffer is same as sample number |
saleiferis | 14:ee2175578993 | 106 | } |
saleiferis | 14:ee2175578993 | 107 | ecgsigned16 = (int16_t) (max86150Sensor.getFIFOECG()>>2); //read an ECG sample |
saleiferis | 14:ee2175578993 | 108 | ppgRed16 = max86150Sensor.getFIFORed(); |
saleiferis | 14:ee2175578993 | 109 | max86150Sensor.nextSample(); // advance tail to get sample in next iteration |
saleiferis | 14:ee2175578993 | 110 | signal[curr] = (float)ecgsigned16; //add sample to buffer |
saleiferis | 14:ee2175578993 | 111 | sample_idx++; |
saleiferis | 14:ee2175578993 | 112 | |
saleiferis | 14:ee2175578993 | 113 | if (curr< FIR_SIZE-1){ |
saleiferis | 14:ee2175578993 | 114 | // buffer is not full yet |
saleiferis | 14:ee2175578993 | 115 | }else{ //buffer is full, filter current sample |
saleiferis | 14:ee2175578993 | 116 | for(k = 0; k < FIR_SIZE; k++){ //FIR bandpass filter |
saleiferis | 14:ee2175578993 | 117 | y = y + FIR_BP[k] * signal[curr-k]; |
saleiferis | 14:ee2175578993 | 118 | } |
saleiferis | 14:ee2175578993 | 119 | bp_signal[curr] = y; |
saleiferis | 14:ee2175578993 | 120 | } |
saleiferis | 14:ee2175578993 | 121 | sq_y = pow(y-prev_y,2); |
saleiferis | 14:ee2175578993 | 122 | squared[curr] = sq_y; |
saleiferis | 14:ee2175578993 | 123 | //moving window integration |
saleiferis | 14:ee2175578993 | 124 | movmean = 0.0; // reset for current sample |
saleiferis | 14:ee2175578993 | 125 | for(k=0; k<(int)WINDOW_SIZE/2; k++){ |
saleiferis | 14:ee2175578993 | 126 | //pc.printf("%d\n",(int)curr - (int)(WINDOW_SIZE)); |
saleiferis | 14:ee2175578993 | 127 | if ((int)curr - (int)(WINDOW_SIZE) > k){ //there are enough samples in squared[] |
saleiferis | 14:ee2175578993 | 128 | // movmean = movmean + squared[(curr-k-(int)WINDOW_SIZE/2)] + squared[curr+k-(int)WINDOW_SIZE/2]; |
saleiferis | 14:ee2175578993 | 129 | movmean = movmean + squared[curr-k]; |
saleiferis | 14:ee2175578993 | 130 | |
saleiferis | 14:ee2175578993 | 131 | }else{ |
saleiferis | 14:ee2175578993 | 132 | break; |
saleiferis | 14:ee2175578993 | 133 | } |
saleiferis | 14:ee2175578993 | 134 | } |
saleiferis | 14:ee2175578993 | 135 | movmean = movmean/(float)(k+1); |
saleiferis | 14:ee2175578993 | 136 | integral[curr] = movmean; |
saleiferis | 14:ee2175578993 | 137 | |
saleiferis | 15:b15b4b6c6da8 | 138 | pc.printf("%f %f\n",movmean/1000, y/50); |
saleiferis | 14:ee2175578993 | 139 | //pc.printf("%d\n",ppgRed16); |
saleiferis | 14:ee2175578993 | 140 | |
saleiferis | 14:ee2175578993 | 141 | hrServicePtr->updateHeartRate(ecgsigned16); //send ECG sample |
saleiferis | 14:ee2175578993 | 142 | // TODO: call pan_tompkins() |
saleiferis | 14:ee2175578993 | 143 | |
saleiferis | 10:28b8729cf5dc | 144 | } |
saleiferis | 14:ee2175578993 | 145 | |
saleiferis | 10:28b8729cf5dc | 146 | |
saleiferis | 10:28b8729cf5dc | 147 | |
saleiferis | 1:6e6f7e3cc1e1 | 148 | } |
saleiferis | 1:6e6f7e3cc1e1 | 149 | |
saleiferis | 1:6e6f7e3cc1e1 | 150 | void periodicCallback(void) |
saleiferis | 1:6e6f7e3cc1e1 | 151 | { |
saleiferis | 1:6e6f7e3cc1e1 | 152 | |
saleiferis | 1:6e6f7e3cc1e1 | 153 | if (BLE::Instance().getGapState().connected) { |
saleiferis | 1:6e6f7e3cc1e1 | 154 | eventQueue.call(updateSensorValue); |
saleiferis | 1:6e6f7e3cc1e1 | 155 | } |
saleiferis | 1:6e6f7e3cc1e1 | 156 | } |
saleiferis | 1:6e6f7e3cc1e1 | 157 | |
saleiferis | 1:6e6f7e3cc1e1 | 158 | void onBleInitError(BLE &ble, ble_error_t error) |
saleiferis | 1:6e6f7e3cc1e1 | 159 | { |
saleiferis | 1:6e6f7e3cc1e1 | 160 | (void)ble; |
saleiferis | 1:6e6f7e3cc1e1 | 161 | (void)error; |
saleiferis | 1:6e6f7e3cc1e1 | 162 | /* Initialization error handling should go here */ |
saleiferis | 1:6e6f7e3cc1e1 | 163 | } |
saleiferis | 1:6e6f7e3cc1e1 | 164 | |
saleiferis | 1:6e6f7e3cc1e1 | 165 | void bleInitComplete(BLE::InitializationCompleteCallbackContext *params) |
saleiferis | 1:6e6f7e3cc1e1 | 166 | { |
saleiferis | 1:6e6f7e3cc1e1 | 167 | BLE& ble = params->ble; |
saleiferis | 1:6e6f7e3cc1e1 | 168 | ble_error_t error = params->error; |
saleiferis | 1:6e6f7e3cc1e1 | 169 | |
saleiferis | 1:6e6f7e3cc1e1 | 170 | if (error != BLE_ERROR_NONE) { |
saleiferis | 1:6e6f7e3cc1e1 | 171 | onBleInitError(ble, error); |
saleiferis | 1:6e6f7e3cc1e1 | 172 | return; |
saleiferis | 1:6e6f7e3cc1e1 | 173 | } |
saleiferis | 1:6e6f7e3cc1e1 | 174 | |
saleiferis | 1:6e6f7e3cc1e1 | 175 | if (ble.getInstanceID() != BLE::DEFAULT_INSTANCE) { |
saleiferis | 1:6e6f7e3cc1e1 | 176 | return; |
saleiferis | 1:6e6f7e3cc1e1 | 177 | } |
saleiferis | 1:6e6f7e3cc1e1 | 178 | |
saleiferis | 1:6e6f7e3cc1e1 | 179 | ble.gap().onDisconnection(disconnectionCallback); |
saleiferis | 4:4233f5538abf | 180 | ble.gap().onConnection(connectionCallback); |
saleiferis | 1:6e6f7e3cc1e1 | 181 | |
saleiferis | 1:6e6f7e3cc1e1 | 182 | /* Setup primary service. */ |
saleiferis | 7:4debec043316 | 183 | //hrServicePtr = new HeartRateService(ble, hrmCounter, HeartRateService::LOCATION_FINGER); |
saleiferis | 7:4debec043316 | 184 | hrServicePtr = new ECGService(ble, hrmCounter); |
saleiferis | 5:30495ab95b86 | 185 | |
saleiferis | 4:4233f5538abf | 186 | pc.printf("Setup primary service ...\n"); |
saleiferis | 1:6e6f7e3cc1e1 | 187 | |
saleiferis | 1:6e6f7e3cc1e1 | 188 | /* Setup advertising. */ |
saleiferis | 1:6e6f7e3cc1e1 | 189 | ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE); |
saleiferis | 1:6e6f7e3cc1e1 | 190 | ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, (uint8_t *)uuid16_list, sizeof(uuid16_list)); |
saleiferis | 1:6e6f7e3cc1e1 | 191 | ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::GENERIC_HEART_RATE_SENSOR); |
saleiferis | 1:6e6f7e3cc1e1 | 192 | ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)DEVICE_NAME, sizeof(DEVICE_NAME)); |
saleiferis | 10:28b8729cf5dc | 193 | |
saleiferis | 1:6e6f7e3cc1e1 | 194 | ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED); |
saleiferis | 1:6e6f7e3cc1e1 | 195 | ble.gap().setAdvertisingInterval(1000); /* 1000ms */ |
saleiferis | 10:28b8729cf5dc | 196 | |
saleiferis | 1:6e6f7e3cc1e1 | 197 | ble.gap().startAdvertising(); |
saleiferis | 10:28b8729cf5dc | 198 | |
saleiferis | 4:4233f5538abf | 199 | pc.printf("Exiting bleInitComplete() ... \n"); |
saleiferis | 4:4233f5538abf | 200 | return; |
saleiferis | 4:4233f5538abf | 201 | } |
saleiferis | 4:4233f5538abf | 202 | |
saleiferis | 1:6e6f7e3cc1e1 | 203 | |
saleiferis | 1:6e6f7e3cc1e1 | 204 | void scheduleBleEventsProcessing(BLE::OnEventsToProcessCallbackContext* context) { |
saleiferis | 1:6e6f7e3cc1e1 | 205 | BLE &ble = BLE::Instance(); |
saleiferis | 1:6e6f7e3cc1e1 | 206 | eventQueue.call(Callback<void()>(&ble, &BLE::processEvents)); |
saleiferis | 1:6e6f7e3cc1e1 | 207 | } |