Maxim Integrated's IoT development kit.
Dependencies: MAX30101 MAX30003 MAX113XX_Pixi MAX30205 max32630fthr USBDevice
max30101_app.cpp
- Committer:
- Mahir Ozturk
- Date:
- 2018-06-28
- Revision:
- 13:fba77a5d0fa0
File content as of revision 13:fba77a5d0fa0:
/*
* max30101_app.cpp
*
* Created on: Jun 20, 2018
* Author: Mahir.Ozturk
*/
#include <mbed.h>
#include "max30101_app.h"
#include "MAX30101.h"
#include "max30101_algo.h"
#define MAX30101_IRQ_ASSERTED_ID 1
#define MAX30101_BUFFER_LEN 500
static Thread *thread = 0;
//variable for the algorithm
uint16_t sampleRate = 100;
uint16_t compSpO2 = 1;
int16_t ir_ac_comp = 0;
int16_t red_ac_comp = 0;
int16_t green_ac_comp = 0;
int16_t ir_ac_mag = 0;
int16_t red_ac_mag = 0;
int16_t green_ac_mag = 0;
uint16_t HRbpm2 = 0;
uint16_t SpO2B = 0;
uint16_t DRdy = 0;
//declare large variables outside of main
uint32_t redData[MAX30101_BUFFER_LEN];//set array to max fifo size
uint32_t irData[MAX30101_BUFFER_LEN];//set array to max fifo size
uint32_t greenData[MAX30101_BUFFER_LEN];//set array to max fifo size
bool max30101_config(MAX30101 &op_sensor)
{
//Reset Device
MAX30101::ModeConfiguration_u modeConfig;
modeConfig.all = 0;
modeConfig.bits.reset = 1;
modeConfig.bits.mode = MAX30101::MultiLedMode; // Sets SPO2 Mode
int32_t rc = op_sensor.setModeConfiguration(modeConfig);
//enable MAX30101 interrupts
MAX30101::InterruptBitField_u ints;
if (rc == 0) {
ints.all = 0;
ints.bits.a_full = 1; // Enable FIFO almost full interrupt
ints.bits.ppg_rdy =1; //Enables an interrupt when a new sample is ready
rc = op_sensor.enableInterrupts(ints);
}
//configure FIFO
MAX30101::FIFO_Configuration_u fifoConfig;
if (rc == 0) {
fifoConfig.all = 0;
fifoConfig.bits.fifo_a_full = 10; // Max level of 17 samples
fifoConfig.bits.sample_average = MAX30101::AveragedSamples_0;// Average 0 samples
rc = op_sensor.setFIFOConfiguration(fifoConfig);
}
MAX30101::SpO2Configuration_u spo2Config;
if (rc == 0) {
spo2Config.all = 0; // clears register
spo2Config.bits.spo2_adc_range = 1; //sets resolution to 4096 nAfs
spo2Config.bits.spo2_sr = MAX30101::SR_100_Hz; // SpO2 SR = 100Hz
spo2Config.bits.led_pw = MAX30101::PW_3; // 18-bit ADC resolution ~400us
rc = op_sensor.setSpO2Configuration(spo2Config);
}
//Set time slots for LEDS
MAX30101::ModeControlReg_u multiLED;
if (rc == 0) {
//sets timing for control register 1
multiLED.bits.lo_slot=1;
multiLED.bits.hi_slot=2;
rc = op_sensor.setMultiLEDModeControl(MAX30101::ModeControlReg1, multiLED);
if (rc == 0) {
multiLED.bits.lo_slot=3;
multiLED.bits.hi_slot=0;
rc = op_sensor.setMultiLEDModeControl(MAX30101::ModeControlReg2, multiLED);
}
}
//Set LED drive currents
if (rc == 0) {
// Heart Rate only, 1 LED channel, Pulse amp. = ~7mA
rc = op_sensor.setLEDPulseAmplitude(MAX30101::LED1_PA, 0x24);
//To include SPO2, 2 LED channel, Pulse amp. ~7mA
if (rc == 0) {
rc = op_sensor.setLEDPulseAmplitude(MAX30101::LED2_PA, 0x24);
}
if (rc == 0) {
rc = op_sensor.setLEDPulseAmplitude(MAX30101::LED3_PA, 0x24);
}
}
//Set operating mode
modeConfig.all = 0;
if (rc == 0) {
modeConfig.bits.mode = MAX30101::MultiLedMode; // Sets multiLED mode
rc = op_sensor.setModeConfiguration(modeConfig);
}
return rc;
}
void max30101wing_pmic_config(I2C & i2c_bus, DigitalOut & pmic_en)
{
const uint8_t PMIC_ADRS = 0x54;
const uint8_t BBB_EXTRA_ADRS = 0x1C;
const uint8_t BOOST_VOLTAGE = 0x05;
char data_buff[] = {BBB_EXTRA_ADRS, 0x40}; //BBBExtra register address
//and data to enable passive
//pull down.
i2c_bus.write(PMIC_ADRS, data_buff,2); //write to BBBExtra register
data_buff[0] = BOOST_VOLTAGE;
data_buff[1] = 0x08; //Boost voltage configuration
//register followed by data
//to set voltage to 4.5V 1f
pmic_en = 0; //disables VLED 08
i2c_bus.write(PMIC_ADRS, data_buff,2); //write to BBBExtra register
pmic_en = 1; //enables VLED
}
/* Op Sensor FIFO nearly full callback */
void max30101_intr_callback()
{
if (thread != 0) {
thread->signal_set(MAX30101_IRQ_ASSERTED_ID);
}
}
void max30101_reader_task(struct max30101_reader_task_args *args)
{
InterruptIn op_sensor_int(args->pinIntr); // Config P3_2 as int. in for
op_sensor_int.fall(max30101_intr_callback); // FIFO ready interrupt
DigitalOut VLED_EN(args->pinVLED,0); //Enable for VLEDs
max30101wing_pmic_config(args->i2cBus, VLED_EN);
MAX30101 op_sensor(args->i2cBus); // Create new MAX30101 on i2cBus
int rc = max30101_config(op_sensor); // Config sensor, return 0 on success
MAX30101::InterruptBitField_u ints; // Read interrupt status to clear
rc = op_sensor.getInterruptStatus(ints); // power on interrupt
thread = args->self;
uint8_t fifoData[MAX30101::MAX_FIFO_BYTES];
uint16_t idx, readBytes;
uint16_t HRTemp;
uint16_t spo2Temp;
uint16_t lastValidHR = 0;
uint16_t lastValidSPO2 = 0;
int r=0; //counter for redData position
int ir=0; //counter for irData position
int g =0; //counter for greenData position
int c=0; //counter to print values
int consecCalcFailCnt = 0;
printf("Starting MAX30101 HeartRate / SPO2 Demo Application...\r\n");
printf("Please wait a few seconds while data is being collected.\r\n");
Timer bleNotifyTimer;
bleNotifyTimer.start();
while (1) {
if (rc == 0) {
/* Check if op_sensor interrupt asserted */
thread->signal_wait(MAX30101_IRQ_ASSERTED_ID);
/* Read interrupt status to clear interrupt */
rc = op_sensor.getInterruptStatus(ints);
/* Confirms proper read prior to executing */
if (rc == 0) {
// Read FIFO
rc = op_sensor.readFIFO(MAX30101::ThreeLedChannels, fifoData, readBytes);
if (rc == 0) {
/* Convert read bytes into samples */
for (idx = 0; idx < readBytes; idx+=9) {
if (r >= 500 || ir >= 500 || g >= 500) {
printf("Overflow! r=%d ir=%d g=%d\r\n", r, ir, g);
break;
}
if (readBytes >= (idx + 8)) {
redData[r++] = ((fifoData[idx] << 16) | (fifoData[idx + 1] << 8) | (fifoData[idx + 2])) & 0x03FFFF;
irData[ir++] = ((fifoData[idx + 3] << 16) | (fifoData[idx + 4] << 8) | (fifoData[idx + 5])) & 0x03FFFF;
greenData[g++] = ((fifoData[idx + 6] << 16) | (fifoData[idx + 7] << 8) | (fifoData[idx + 8])) & 0x03FFFF;
}
}
if ((r >= MAX30101_BUFFER_LEN) && (ir >= MAX30101_BUFFER_LEN) && (g >= MAX30101_BUFFER_LEN)) {/* checks to make sure there are 500 */
/* samples in data buffers */
/* runs the heart rate and SpO2 algorithm */
for (c = 0, HRTemp = 0; c < r; c++) {
HRSpO2Func(irData[c], redData[c],greenData[c], c,sampleRate, compSpO2,
&ir_ac_comp,&red_ac_comp, &green_ac_comp, &ir_ac_mag,&red_ac_mag,
&green_ac_mag, &HRbpm2,&SpO2B,&DRdy);
if (DRdy) {
HRTemp = HRbpm2;
spo2Temp = SpO2B;
}
}
/* If the above algorithm returns a valid heart rate on the last sample, it is printed */
if (DRdy == 1) {
printf("Heart Rate = %i\r\n",HRbpm2);
printf("SPO2 = %i\r\n",SpO2B);
lastValidHR = HRbpm2;
lastValidSPO2 = SpO2B;
consecCalcFailCnt = 0;
} else if (HRTemp != 0) { /* if a valid heart was calculated at all, it is printed */
printf("Heart Rate = %i\r\n",HRTemp);
printf("SPO2 = %i\r\n",spo2Temp);
lastValidHR = HRTemp;
lastValidSPO2 = spo2Temp;
consecCalcFailCnt = 0;
} else {
consecCalcFailCnt++;
if (consecCalcFailCnt >= 10) {
printf("Calculation failed...waiting for more samples...\r\n");
printf("Please keep your finger on the MAX30101 sensor with minimal movement.\r\n");
consecCalcFailCnt = 0;
}
}
/* dump the first hundred samples after calculation */
for (c = 100; c < 500; c++) {
redData[c - 100] = redData[c];
irData[c - 100] = irData[c];
greenData[c - 100] = greenData[c];
}
/* reset counters */
r = 400;
ir = 400;
g = 400;
}
if (bleNotifyTimer.read_ms() >= (args->notify_period_sec * 1000)) {
bleGattAttrWrite(args->gattHeartRate, (uint8_t *)&lastValidHR, sizeof(lastValidHR));
bleGattAttrWrite(args->gattSPO2, (uint8_t *)&lastValidSPO2, sizeof(lastValidSPO2));
bleNotifyTimer.reset();
}
}
}
} else { // If rc != 0, a communication error has occurred
printf("Something went wrong, "
"check the I2C bus or power connections... \r\n");
return;
}
}
}