Maxim Integrated's IoT development kit.

Dependencies:   MAX30101 MAX30003 MAX113XX_Pixi MAX30205 max32630fthr USBDevice

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers max30101_app.cpp Source File

max30101_app.cpp

00001 /*
00002  * max30101_app.cpp
00003  *
00004  *  Created on: Jun 20, 2018
00005  *      Author: Mahir.Ozturk
00006  */
00007 #include <mbed.h>
00008 #include "max30101_app.h"
00009 #include "MAX30101.h"
00010 #include "max30101_algo.h"
00011 
00012 #define MAX30101_IRQ_ASSERTED_ID    1
00013 
00014 #define MAX30101_BUFFER_LEN         500
00015 
00016 static Thread *thread = 0;
00017 
00018 //variable for the algorithm
00019 uint16_t sampleRate = 100;
00020 uint16_t compSpO2 = 1;
00021 int16_t ir_ac_comp = 0;
00022 int16_t red_ac_comp = 0;
00023 int16_t green_ac_comp = 0;
00024 int16_t ir_ac_mag = 0;
00025 int16_t red_ac_mag = 0;
00026 int16_t green_ac_mag = 0;
00027 uint16_t HRbpm2 = 0;
00028 uint16_t SpO2B = 0;
00029 uint16_t DRdy = 0;
00030 
00031 //declare large variables outside of main
00032 uint32_t redData[MAX30101_BUFFER_LEN];//set array to max fifo size
00033 uint32_t irData[MAX30101_BUFFER_LEN];//set array to max fifo size
00034 uint32_t greenData[MAX30101_BUFFER_LEN];//set array to max fifo size
00035 
00036 bool max30101_config(MAX30101 &op_sensor)
00037 {
00038     //Reset Device
00039     MAX30101::ModeConfiguration_u modeConfig;
00040     modeConfig.all = 0;
00041     modeConfig.bits.reset = 1;
00042     modeConfig.bits.mode = MAX30101::MultiLedMode;     // Sets SPO2 Mode
00043     int32_t rc = op_sensor.setModeConfiguration(modeConfig);
00044 
00045     //enable MAX30101 interrupts
00046     MAX30101::InterruptBitField_u ints;
00047     if (rc == 0) {
00048         ints.all = 0;
00049         ints.bits.a_full = 1;       // Enable FIFO almost full interrupt
00050         ints.bits.ppg_rdy =1;       //Enables an interrupt when a new sample is ready
00051         rc = op_sensor.enableInterrupts(ints);
00052     }
00053 
00054     //configure FIFO
00055     MAX30101::FIFO_Configuration_u fifoConfig;
00056     if (rc == 0) {
00057         fifoConfig.all = 0;
00058         fifoConfig.bits.fifo_a_full = 10;                            // Max level of 17 samples
00059         fifoConfig.bits.sample_average = MAX30101::AveragedSamples_0;// Average 0 samples
00060         rc = op_sensor.setFIFOConfiguration(fifoConfig);
00061     }
00062 
00063     MAX30101::SpO2Configuration_u spo2Config;
00064     if (rc == 0) {
00065         spo2Config.all = 0;                                 // clears register
00066         spo2Config.bits.spo2_adc_range = 1;                 //sets resolution to 4096 nAfs
00067         spo2Config.bits.spo2_sr = MAX30101::SR_100_Hz;     // SpO2 SR = 100Hz
00068         spo2Config.bits.led_pw = MAX30101::PW_3;            // 18-bit ADC resolution ~400us
00069         rc = op_sensor.setSpO2Configuration(spo2Config);
00070     }
00071 
00072     //Set time slots for LEDS
00073     MAX30101::ModeControlReg_u multiLED;
00074     if (rc == 0) {
00075         //sets timing for control register 1
00076         multiLED.bits.lo_slot=1;
00077         multiLED.bits.hi_slot=2;
00078         rc = op_sensor.setMultiLEDModeControl(MAX30101::ModeControlReg1, multiLED);
00079         if (rc == 0) {
00080             multiLED.bits.lo_slot=3;
00081             multiLED.bits.hi_slot=0;
00082             rc = op_sensor.setMultiLEDModeControl(MAX30101::ModeControlReg2, multiLED);
00083         }
00084     }
00085 
00086     //Set LED drive currents
00087     if (rc == 0) {
00088         // Heart Rate only, 1 LED channel, Pulse amp. = ~7mA
00089         rc = op_sensor.setLEDPulseAmplitude(MAX30101::LED1_PA, 0x24);
00090         //To include SPO2, 2 LED channel, Pulse amp. ~7mA
00091         if (rc == 0) {
00092             rc = op_sensor.setLEDPulseAmplitude(MAX30101::LED2_PA, 0x24);
00093         }
00094         if (rc == 0) {
00095             rc = op_sensor.setLEDPulseAmplitude(MAX30101::LED3_PA, 0x24);
00096         }
00097     }
00098 
00099     //Set operating mode
00100     modeConfig.all = 0;
00101     if (rc == 0) {
00102         modeConfig.bits.mode = MAX30101::MultiLedMode;     // Sets multiLED mode
00103         rc = op_sensor.setModeConfiguration(modeConfig);
00104     }
00105 
00106     return rc;
00107 }
00108 
00109 void max30101wing_pmic_config(I2C & i2c_bus, DigitalOut & pmic_en)
00110 {
00111     const uint8_t PMIC_ADRS = 0x54;
00112     const uint8_t BBB_EXTRA_ADRS = 0x1C;
00113     const uint8_t BOOST_VOLTAGE = 0x05;
00114 
00115     char data_buff[] = {BBB_EXTRA_ADRS, 0x40};    //BBBExtra register address
00116     //and data to enable passive
00117     //pull down.
00118     i2c_bus.write(PMIC_ADRS, data_buff,2);        //write to BBBExtra register
00119 
00120     data_buff[0] = BOOST_VOLTAGE;
00121     data_buff[1] = 0x08;                          //Boost voltage configuration
00122     //register followed by data
00123     //to set voltage to 4.5V 1f
00124     pmic_en = 0;                                  //disables VLED 08
00125     i2c_bus.write(PMIC_ADRS, data_buff,2);        //write to BBBExtra register
00126     pmic_en = 1;                                  //enables VLED
00127 }
00128 
00129 /* Op Sensor FIFO nearly full callback */
00130 void max30101_intr_callback()
00131 {
00132     if (thread != 0) {
00133         thread->signal_set(MAX30101_IRQ_ASSERTED_ID);
00134     }
00135 }
00136 
00137 void max30101_reader_task(struct max30101_reader_task_args *args)
00138 {
00139     InterruptIn op_sensor_int(args->pinIntr);       // Config P3_2 as int. in for
00140     op_sensor_int.fall(max30101_intr_callback);     // FIFO ready interrupt
00141 
00142     DigitalOut VLED_EN(args->pinVLED,0);            //Enable for VLEDs
00143     max30101wing_pmic_config(args->i2cBus, VLED_EN);
00144 
00145     MAX30101 op_sensor(args->i2cBus);               // Create new MAX30101 on i2cBus
00146     int rc = max30101_config(op_sensor);            // Config sensor, return 0 on success
00147 
00148     MAX30101::InterruptBitField_u ints;             // Read interrupt status to clear
00149     rc = op_sensor.getInterruptStatus(ints);        // power on interrupt
00150 
00151     thread = args->self;
00152 
00153     uint8_t fifoData[MAX30101::MAX_FIFO_BYTES];
00154     uint16_t idx, readBytes;
00155     uint16_t HRTemp;
00156     uint16_t spo2Temp;
00157 
00158     uint16_t lastValidHR = 0;
00159     uint16_t lastValidSPO2 = 0;
00160 
00161     int r=0; //counter for redData position
00162     int ir=0; //counter for irData position
00163     int g =0; //counter for greenData position
00164     int c=0; //counter to print values
00165 
00166     int consecCalcFailCnt = 0;
00167 
00168     printf("Starting MAX30101 HeartRate / SPO2 Demo Application...\r\n");
00169     printf("Please wait a few seconds while data is being collected.\r\n");
00170 
00171     Timer bleNotifyTimer;
00172 
00173     bleNotifyTimer.start();
00174 
00175     while (1) {
00176         if (rc == 0) {
00177             /* Check if op_sensor interrupt asserted */
00178             thread->signal_wait(MAX30101_IRQ_ASSERTED_ID);
00179 
00180             /* Read interrupt status to clear interrupt */
00181             rc = op_sensor.getInterruptStatus(ints);
00182 
00183             /* Confirms proper read prior to executing */
00184             if (rc == 0) {
00185                 // Read FIFO
00186                 rc = op_sensor.readFIFO(MAX30101::ThreeLedChannels, fifoData, readBytes);
00187 
00188                 if (rc == 0) {
00189                     /* Convert read bytes into samples */
00190                     for (idx = 0; idx < readBytes; idx+=9) {
00191                         if (r >= 500 || ir >= 500 || g >= 500) {
00192                             printf("Overflow! r=%d ir=%d g=%d\r\n", r, ir, g);
00193                             break;
00194                         }
00195 
00196                         if (readBytes >= (idx + 8)) {
00197                             redData[r++] = ((fifoData[idx] << 16) | (fifoData[idx + 1] << 8) | (fifoData[idx + 2])) & 0x03FFFF;
00198                             irData[ir++] = ((fifoData[idx + 3] << 16) | (fifoData[idx + 4] << 8) | (fifoData[idx + 5])) & 0x03FFFF;
00199                             greenData[g++] = ((fifoData[idx + 6] << 16) | (fifoData[idx + 7] << 8) | (fifoData[idx + 8])) & 0x03FFFF;
00200                         }
00201                     }
00202 
00203                     if ((r >= MAX30101_BUFFER_LEN) && (ir >= MAX30101_BUFFER_LEN) && (g >= MAX30101_BUFFER_LEN)) {/* checks to make sure there are 500 */
00204                         /* samples in data buffers */
00205 
00206                         /* runs the heart rate and SpO2 algorithm */
00207                         for (c = 0, HRTemp = 0; c < r; c++) {
00208                             HRSpO2Func(irData[c], redData[c],greenData[c], c,sampleRate, compSpO2,
00209                                        &ir_ac_comp,&red_ac_comp, &green_ac_comp, &ir_ac_mag,&red_ac_mag,
00210                                        &green_ac_mag, &HRbpm2,&SpO2B,&DRdy);
00211                             if (DRdy) {
00212                                 HRTemp = HRbpm2;
00213                                 spo2Temp = SpO2B;
00214                             }
00215                         }
00216 
00217                         /* If the above algorithm returns a valid heart rate on the last sample, it is printed */
00218                         if (DRdy == 1) {
00219                             printf("Heart Rate = %i\r\n",HRbpm2);
00220                             printf("SPO2 = %i\r\n",SpO2B);
00221                             lastValidHR = HRbpm2;
00222                             lastValidSPO2 = SpO2B;
00223                             consecCalcFailCnt = 0;
00224                         } else if (HRTemp != 0) { /* if a valid heart was calculated at all, it is printed */
00225                             printf("Heart Rate = %i\r\n",HRTemp);
00226                             printf("SPO2 = %i\r\n",spo2Temp);
00227                             lastValidHR = HRTemp;
00228                             lastValidSPO2 = spo2Temp;
00229                             consecCalcFailCnt = 0;
00230                         } else {
00231                             consecCalcFailCnt++;
00232                             if (consecCalcFailCnt >= 10) {
00233                                 printf("Calculation failed...waiting for more samples...\r\n");
00234                                 printf("Please keep your finger on the MAX30101 sensor with minimal movement.\r\n");
00235                                 consecCalcFailCnt = 0;
00236                             }
00237                         }
00238 
00239                         /* dump the first hundred samples after calculation */
00240                         for (c = 100; c < 500; c++) {
00241                             redData[c - 100] = redData[c];
00242                             irData[c - 100] = irData[c];
00243                             greenData[c - 100] = greenData[c];
00244                         }
00245 
00246                         /* reset counters */
00247                         r = 400;
00248                         ir = 400;
00249                         g = 400;
00250                     }
00251 
00252                     if (bleNotifyTimer.read_ms() >= (args->notify_period_sec * 1000)) {
00253                         bleGattAttrWrite(args->gattHeartRate, (uint8_t *)&lastValidHR, sizeof(lastValidHR));
00254                         bleGattAttrWrite(args->gattSPO2, (uint8_t *)&lastValidSPO2, sizeof(lastValidSPO2));
00255                         bleNotifyTimer.reset();
00256                     }
00257                 }
00258             }
00259         } else { // If rc != 0, a communication error has occurred
00260 
00261             printf("Something went wrong, "
00262                       "check the I2C bus or power connections... \r\n");
00263 
00264             return;
00265         }
00266 
00267     }
00268 }
00269 
00270