SpO2 algorithm used in MAX30101 onboard MAX32620HSP. Algorithm is not perfect but it works
Dependencies: mbed MAX14720 USBDevice
main.cpp@0:af4d13000e95, 2019-03-28 (annotated)
- Committer:
- douqan93
- Date:
- Thu Mar 28 08:07:54 2019 +0000
- Revision:
- 0:af4d13000e95
This program for MAX32620HSP's onboard SpO2 sensor (MAX30101). Algorithm is not perfect but it works.
Who changed what in which revision?
| User | Revision | Line number | New contents of line |
|---|---|---|---|
| douqan93 | 0:af4d13000e95 | 1 | #include "mbed.h" |
| douqan93 | 0:af4d13000e95 | 2 | #include "MAX14720.h" |
| douqan93 | 0:af4d13000e95 | 3 | #include "MAX30102.h" |
| douqan93 | 0:af4d13000e95 | 4 | #include "USBSerial.h" |
| douqan93 | 0:af4d13000e95 | 5 | #include "System.h" |
| douqan93 | 0:af4d13000e95 | 6 | #include "algorithm.h" |
| douqan93 | 0:af4d13000e95 | 7 | |
| douqan93 | 0:af4d13000e95 | 8 | /// define the HVOUT Boost Voltage default for the MAX14720 PMIC |
| douqan93 | 0:af4d13000e95 | 9 | #define HVOUT_VOLTAGE 4500 // set to 4500 mV |
| douqan93 | 0:af4d13000e95 | 10 | |
| douqan93 | 0:af4d13000e95 | 11 | #define MAX14720_I2C_SLAVE_ADDR (0x54) |
| douqan93 | 0:af4d13000e95 | 12 | |
| douqan93 | 0:af4d13000e95 | 13 | #define MAX_BRIGHTNESS 255 |
| douqan93 | 0:af4d13000e95 | 14 | |
| douqan93 | 0:af4d13000e95 | 15 | uint32_t aun_ir_buffer[500]; //IR LED sensor data |
| douqan93 | 0:af4d13000e95 | 16 | int32_t n_ir_buffer_length; //data length |
| douqan93 | 0:af4d13000e95 | 17 | uint32_t aun_red_buffer[500]; //Red LED sensor data |
| douqan93 | 0:af4d13000e95 | 18 | int32_t n_sp02; //SPO2 value |
| douqan93 | 0:af4d13000e95 | 19 | int8_t ch_spo2_valid; //indicator to show if the SP02 calculation is valid |
| douqan93 | 0:af4d13000e95 | 20 | int32_t n_heart_rate; //heart rate value |
| douqan93 | 0:af4d13000e95 | 21 | int8_t ch_hr_valid; //indicator to show if the heart rate calculation is valid |
| douqan93 | 0:af4d13000e95 | 22 | uint8_t uch_dummy; |
| douqan93 | 0:af4d13000e95 | 23 | |
| douqan93 | 0:af4d13000e95 | 24 | //Serial pc(USBTX, USBRX); //initializes the serial port |
| douqan93 | 0:af4d13000e95 | 25 | /// Define with Maxim VID and a Maxim assigned PID, set to version 0x0001 and non-blocking |
| douqan93 | 0:af4d13000e95 | 26 | |
| douqan93 | 0:af4d13000e95 | 27 | USBSerial usbSerial(0x0b6a, 0x0122, 0x0001, false); |
| douqan93 | 0:af4d13000e95 | 28 | |
| douqan93 | 0:af4d13000e95 | 29 | /// I2C Master 2 |
| douqan93 | 0:af4d13000e95 | 30 | I2C i2c2(I2C2_SDA, I2C2_SCL); // used by MAX14720, MAX30101, LIS2DH |
| douqan93 | 0:af4d13000e95 | 31 | /// SPI Master 0 with SPI0_SS for use with MAX30001 |
| douqan93 | 0:af4d13000e95 | 32 | |
| douqan93 | 0:af4d13000e95 | 33 | MAX14720 max14720(&i2c2, MAX14720_I2C_SLAVE_ADDR); |
| douqan93 | 0:af4d13000e95 | 34 | DigitalOut led(LED1); |
| douqan93 | 0:af4d13000e95 | 35 | DigitalIn INT(P4_0); |
| douqan93 | 0:af4d13000e95 | 36 | int main(){ |
| douqan93 | 0:af4d13000e95 | 37 | //--------------------------------------------------- |
| douqan93 | 0:af4d13000e95 | 38 | // hold results for returning functions |
| douqan93 | 0:af4d13000e95 | 39 | int result; |
| douqan93 | 0:af4d13000e95 | 40 | // initialize HVOUT on the MAX14720 PMIC |
| douqan93 | 0:af4d13000e95 | 41 | result = max14720.init(); |
| douqan93 | 0:af4d13000e95 | 42 | if (result == MAX14720_ERROR){ |
| douqan93 | 0:af4d13000e95 | 43 | printf("Error initializing MAX14720"); |
| douqan93 | 0:af4d13000e95 | 44 | } |
| douqan93 | 0:af4d13000e95 | 45 | max14720.boostEn = MAX14720::BOOST_ENABLED; |
| douqan93 | 0:af4d13000e95 | 46 | max14720.boostSetVoltage(HVOUT_VOLTAGE); |
| douqan93 | 0:af4d13000e95 | 47 | //--------------------------------------------------- |
| douqan93 | 0:af4d13000e95 | 48 | uint32_t un_min, un_max, un_prev_data; //variables to calculate the on-board LED brightness that reflects the heartbeats |
| douqan93 | 0:af4d13000e95 | 49 | int i; |
| douqan93 | 0:af4d13000e95 | 50 | int32_t n_brightness; |
| douqan93 | 0:af4d13000e95 | 51 | float f_temp; |
| douqan93 | 0:af4d13000e95 | 52 | long unBlockedValue = 0; |
| douqan93 | 0:af4d13000e95 | 53 | |
| douqan93 | 0:af4d13000e95 | 54 | maxim_max30102_reset(); //resets the MAX30102 |
| douqan93 | 0:af4d13000e95 | 55 | wait(1); |
| douqan93 | 0:af4d13000e95 | 56 | maxim_max30102_init(); //initializes the MAX30102 |
| douqan93 | 0:af4d13000e95 | 57 | |
| douqan93 | 0:af4d13000e95 | 58 | n_brightness=0; |
| douqan93 | 0:af4d13000e95 | 59 | un_min=0x3FFFF; |
| douqan93 | 0:af4d13000e95 | 60 | un_max=0; |
| douqan93 | 0:af4d13000e95 | 61 | |
| douqan93 | 0:af4d13000e95 | 62 | n_ir_buffer_length=500; //buffer length of 100 stores 5 seconds of samples running at 100sps |
| douqan93 | 0:af4d13000e95 | 63 | |
| douqan93 | 0:af4d13000e95 | 64 | //read the first 500 samples, and determine the signal range |
| douqan93 | 0:af4d13000e95 | 65 | for(i=0;i<n_ir_buffer_length;i++) |
| douqan93 | 0:af4d13000e95 | 66 | { |
| douqan93 | 0:af4d13000e95 | 67 | while(INT.read()==1); //wait until the interrupt pin asserts |
| douqan93 | 0:af4d13000e95 | 68 | |
| douqan93 | 0:af4d13000e95 | 69 | maxim_max30102_read_fifo((aun_red_buffer+i), (aun_ir_buffer+i)); //read from MAX30102 FIFO |
| douqan93 | 0:af4d13000e95 | 70 | |
| douqan93 | 0:af4d13000e95 | 71 | if(un_min>aun_red_buffer[i]) |
| douqan93 | 0:af4d13000e95 | 72 | un_min=aun_red_buffer[i]; //update signal min |
| douqan93 | 0:af4d13000e95 | 73 | if(un_max<aun_red_buffer[i]) |
| douqan93 | 0:af4d13000e95 | 74 | un_max=aun_red_buffer[i]; //update signal max |
| douqan93 | 0:af4d13000e95 | 75 | /*usbSerial.printf("red="); |
| douqan93 | 0:af4d13000e95 | 76 | usbSerial.printf("%i", aun_red_buffer[i]); |
| douqan93 | 0:af4d13000e95 | 77 | usbSerial.printf(", ir="); |
| douqan93 | 0:af4d13000e95 | 78 | usbSerial.printf("%i\n\r", aun_ir_buffer[i]);*/ |
| douqan93 | 0:af4d13000e95 | 79 | } |
| douqan93 | 0:af4d13000e95 | 80 | un_prev_data=aun_red_buffer[i]; |
| douqan93 | 0:af4d13000e95 | 81 | |
| douqan93 | 0:af4d13000e95 | 82 | |
| douqan93 | 0:af4d13000e95 | 83 | //calculate heart rate and SpO2 after first 500 samples (first 5 seconds of samples) |
| douqan93 | 0:af4d13000e95 | 84 | 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); |
| douqan93 | 0:af4d13000e95 | 85 | |
| douqan93 | 0:af4d13000e95 | 86 | //Continuously taking samples from MAX30102. Heart rate and SpO2 are calculated every 1 second |
| douqan93 | 0:af4d13000e95 | 87 | |
| douqan93 | 0:af4d13000e95 | 88 | |
| douqan93 | 0:af4d13000e95 | 89 | while (1) { |
| douqan93 | 0:af4d13000e95 | 90 | i=0; |
| douqan93 | 0:af4d13000e95 | 91 | un_min=0x3FFFF; |
| douqan93 | 0:af4d13000e95 | 92 | un_max=0; |
| douqan93 | 0:af4d13000e95 | 93 | |
| douqan93 | 0:af4d13000e95 | 94 | //dumping the first 100 sets of samples in the memory and shift the last 400 sets of samples to the top |
| douqan93 | 0:af4d13000e95 | 95 | for(i=200;i<500;i++) |
| douqan93 | 0:af4d13000e95 | 96 | { |
| douqan93 | 0:af4d13000e95 | 97 | aun_red_buffer[i-200]=aun_red_buffer[i]; |
| douqan93 | 0:af4d13000e95 | 98 | aun_ir_buffer[i-200]=aun_ir_buffer[i]; |
| douqan93 | 0:af4d13000e95 | 99 | |
| douqan93 | 0:af4d13000e95 | 100 | //update the signal min and max |
| douqan93 | 0:af4d13000e95 | 101 | if(un_min>aun_red_buffer[i]) |
| douqan93 | 0:af4d13000e95 | 102 | un_min=aun_red_buffer[i]; |
| douqan93 | 0:af4d13000e95 | 103 | if(un_max<aun_red_buffer[i]) |
| douqan93 | 0:af4d13000e95 | 104 | un_max=aun_red_buffer[i]; |
| douqan93 | 0:af4d13000e95 | 105 | } |
| douqan93 | 0:af4d13000e95 | 106 | |
| douqan93 | 0:af4d13000e95 | 107 | //take 100 sets of samples before calculating the heart rate. |
| douqan93 | 0:af4d13000e95 | 108 | for(i=300;i<500;i++) |
| douqan93 | 0:af4d13000e95 | 109 | { |
| douqan93 | 0:af4d13000e95 | 110 | un_prev_data=aun_red_buffer[i-1]; |
| douqan93 | 0:af4d13000e95 | 111 | while(INT.read()==1); |
| douqan93 | 0:af4d13000e95 | 112 | maxim_max30102_read_fifo((aun_red_buffer+i), (aun_ir_buffer+i)); |
| douqan93 | 0:af4d13000e95 | 113 | |
| douqan93 | 0:af4d13000e95 | 114 | if(aun_red_buffer[i]>un_prev_data) |
| douqan93 | 0:af4d13000e95 | 115 | { |
| douqan93 | 0:af4d13000e95 | 116 | f_temp=aun_red_buffer[i]-un_prev_data; |
| douqan93 | 0:af4d13000e95 | 117 | f_temp/=(un_max-un_min); |
| douqan93 | 0:af4d13000e95 | 118 | f_temp*=MAX_BRIGHTNESS; |
| douqan93 | 0:af4d13000e95 | 119 | n_brightness-=(int)f_temp; |
| douqan93 | 0:af4d13000e95 | 120 | if(n_brightness<0) |
| douqan93 | 0:af4d13000e95 | 121 | n_brightness=0; |
| douqan93 | 0:af4d13000e95 | 122 | } |
| douqan93 | 0:af4d13000e95 | 123 | else |
| douqan93 | 0:af4d13000e95 | 124 | { |
| douqan93 | 0:af4d13000e95 | 125 | f_temp=un_prev_data-aun_red_buffer[i]; |
| douqan93 | 0:af4d13000e95 | 126 | f_temp/=(un_max-un_min); |
| douqan93 | 0:af4d13000e95 | 127 | f_temp*=MAX_BRIGHTNESS; |
| douqan93 | 0:af4d13000e95 | 128 | n_brightness+=(int)f_temp; |
| douqan93 | 0:af4d13000e95 | 129 | if(n_brightness>MAX_BRIGHTNESS) |
| douqan93 | 0:af4d13000e95 | 130 | n_brightness=MAX_BRIGHTNESS; |
| douqan93 | 0:af4d13000e95 | 131 | } |
| douqan93 | 0:af4d13000e95 | 132 | //send samples and calculation result to terminal program through UART |
| douqan93 | 0:af4d13000e95 | 133 | /*usbSerial.printf("red="); |
| douqan93 | 0:af4d13000e95 | 134 | usbSerial.printf("%i", aun_red_buffer[i]); |
| douqan93 | 0:af4d13000e95 | 135 | usbSerial.printf(", ir="); |
| douqan93 | 0:af4d13000e95 | 136 | usbSerial.printf("%i", aun_ir_buffer[i]);*/ |
| douqan93 | 0:af4d13000e95 | 137 | usbSerial.printf("HR=%i\t", n_heart_rate); |
| douqan93 | 0:af4d13000e95 | 138 | //usbSerial.printf("HRvalid=%i, ", ch_hr_valid); |
| douqan93 | 0:af4d13000e95 | 139 | usbSerial.printf("SpO2=%i\n\r", n_sp02); |
| douqan93 | 0:af4d13000e95 | 140 | //usbSerial.printf("SPO2Valid=%i\n\r", ch_spo2_valid); |
| douqan93 | 0:af4d13000e95 | 141 | } |
| douqan93 | 0:af4d13000e95 | 142 | 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); |
| douqan93 | 0:af4d13000e95 | 143 | } |
| douqan93 | 0:af4d13000e95 | 144 | } |