This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

getting sop2 value from MAX30105 sensor

Hi ,
i try to translate the arduino library  using this URL    github.com/.../src

to manipulate the spo2 oxygen concentrator value using the max30105 sensor 
I try to configure this sensor by invoking the max30105.h library but the problem is when I compile the max30105.c code it fails without specifying the exact error :

here you will find my code max30105.c :

/***************************************************
This is a library written for the Maxim MAX30105 Optical Smoke Detector
It should also work with the MAX30102. However, the MAX30102 does not have a Green LED.
These sensors use I2C to communicate, as well as a single (optional)
interrupt line that is not currently supported in this driver.
Written by Peter Jansen and Nathan Seidle (SparkFun)
BSD license, all text above must be included in any redistribution.
*****************************************************/

#include "MAX30105.h"
#include "nrf_delay.h"
#include "i2c.h"
#include "boards.h"
#include "nrfx_twim.h "
#include "nrf_drv_twi.h"
#include "app_util_platform.h"
#include "app_error.h"
#include "nrf_log.h"
#include "nrf_log_ctrl.h"
// Status Registers
static const uint8_t MAX30105_INTSTAT1 = 0x00;
static const uint8_t MAX30105_INTSTAT2 = 0x01;
static const uint8_t MAX30105_INTENABLE1 = 0x02;
static const uint8_t MAX30105_INTENABLE2 = 0x03;

// FIFO Registers
static const uint8_t MAX30105_FIFOWRITEPTR = 0x04;
static const uint8_t MAX30105_FIFOOVERFLOW = 0x05;
static const uint8_t MAX30105_FIFOREADPTR = 0x06;
static const uint8_t MAX30105_FIFODATA = 0x07;

// Configuration Registers
static const uint8_t MAX30105_FIFOCONFIG = 0x08;
static const uint8_t MAX30105_MODECONFIG = 0x09;
static const uint8_t MAX30105_PARTICLECONFIG = 0x0A; // Note, sometimes listed as "SPO2" config in datasheet (pg. 11)
static const uint8_t MAX30105_LED1_PULSEAMP = 0x0C;
static const uint8_t MAX30105_LED2_PULSEAMP = 0x0D;
static const uint8_t MAX30105_LED3_PULSEAMP = 0x0E;
static const uint8_t MAX30105_LED_PROX_AMP = 0x10;
static const uint8_t MAX30105_MULTILEDCONFIG1 = 0x11;
static const uint8_t MAX30105_MULTILEDCONFIG2 = 0x12;

// Die Temperature Registers
static const uint8_t MAX30105_DIETEMPINT = 0x1F;
static const uint8_t MAX30105_DIETEMPFRAC = 0x20;
static const uint8_t MAX30105_DIETEMPCONFIG = 0x21;

// Proximity Function Registers
static const uint8_t MAX30105_PROXINTTHRESH = 0x30;

// Part ID Registers
static const uint8_t MAX30105_REVISIONID = 0xFE;
static const uint8_t MAX30105_PARTID = 0xFF; // Should always be 0x15. Identical to MAX30102.

// MAX30105 Commands
// Interrupt configuration (pg 13, 14)
static const uint8_t MAX30105_INT_A_FULL_MASK = (uint8_t)~0x80;
static const uint8_t MAX30105_INT_A_FULL_ENABLE = 0x80;
static const uint8_t MAX30105_INT_A_FULL_DISABLE = 0x00;

static const uint8_t MAX30105_INT_DATA_RDY_MASK = (uint8_t)~0x80;
static const uint8_t MAX30105_INT_DATA_RDY_ENABLE = 0x40;
static const uint8_t MAX30105_INT_DATA_RDY_DISABLE = 0x00;

static const uint8_t MAX30105_INT_ALC_OVF_MASK = (uint8_t)~0x80;
static const uint8_t MAX30105_INT_ALC_OVF_ENABLE = 0x20;
static const uint8_t MAX30105_INT_ALC_OVF_DISABLE = 0x00;

static const uint8_t MAX30105_INT_PROX_INT_MASK = (uint8_t)~0x80;
static const uint8_t MAX30105_INT_PROX_INT_ENABLE = 0x10;
static const uint8_t MAX30105_INT_PROX_INT_DISABLE = 0x00;

static const uint8_t MAX30105_INT_DIE_TEMP_RDY_MASK = (uint8_t)~0x80;
static const uint8_t MAX30105_INT_DIE_TEMP_RDY_ENABLE = 0x02;
static const uint8_t MAX30105_INT_DIE_TEMP_RDY_DISABLE = 0x00;

static const uint8_t MAX30105_SAMPLEAVG_MASK = (uint8_t)~0x80;
static const uint8_t MAX30105_SAMPLEAVG_1 = 0x00;
static const uint8_t MAX30105_SAMPLEAVG_2 = 0x20;
static const uint8_t MAX30105_SAMPLEAVG_4 = 0x40;
static const uint8_t MAX30105_SAMPLEAVG_8 = 0x60;
static const uint8_t MAX30105_SAMPLEAVG_16 = 0x80;
static const uint8_t MAX30105_SAMPLEAVG_32 = 0xA0;

static const uint8_t MAX30105_ROLLOVER_MASK = 0xEF;
static const uint8_t MAX30105_ROLLOVER_ENABLE = 0x10;
static const uint8_t MAX30105_ROLLOVER_DISABLE = 0x00;

static const uint8_t MAX30105_A_FULL_MASK = 0xF0;

// Mode configuration commands (page 19)
static const uint8_t MAX30105_SHUTDOWN_MASK = 0x7F;
static const uint8_t MAX30105_SHUTDOWN = 0x80;
static const uint8_t MAX30105_WAKEUP = 0x00;

static const uint8_t MAX30105_RESET_MASK = 0xBF;
static const uint8_t MAX30105_RESET = 0x40;

static const uint8_t MAX30105_MODE_MASK = 0xF8;
static const uint8_t MAX30105_MODE_REDONLY = 0x02;
static const uint8_t MAX30105_MODE_REDIRONLY = 0x03;
static const uint8_t MAX30105_MODE_MULTILED = 0x07;

// Particle sensing configuration commands (pgs 19-20)
static const uint8_t MAX30105_ADCRANGE_MASK = 0x9F;
static const uint8_t MAX30105_ADCRANGE_2048 = 0x00;
static const uint8_t MAX30105_ADCRANGE_4096 = 0x20;
static const uint8_t MAX30105_ADCRANGE_8192 = 0x40;
static const uint8_t MAX30105_ADCRANGE_16384 = 0x60;

static const uint8_t MAX30105_SAMPLERATE_MASK = 0xE3;
static const uint8_t MAX30105_SAMPLERATE_50 = 0x00;
static const uint8_t MAX30105_SAMPLERATE_100 = 0x04;
static const uint8_t MAX30105_SAMPLERATE_200 = 0x08;
static const uint8_t MAX30105_SAMPLERATE_400 = 0x0C;
static const uint8_t MAX30105_SAMPLERATE_800 = 0x10;
static const uint8_t MAX30105_SAMPLERATE_1000 = 0x14;
static const uint8_t MAX30105_SAMPLERATE_1600 = 0x18;
static const uint8_t MAX30105_SAMPLERATE_3200 = 0x1C;

static const uint8_t MAX30105_PULSEWIDTH_MASK = 0xFC;
static const uint8_t MAX30105_PULSEWIDTH_69 = 0x00;
static const uint8_t MAX30105_PULSEWIDTH_118 = 0x01;
static const uint8_t MAX30105_PULSEWIDTH_215 = 0x02;
static const uint8_t MAX30105_PULSEWIDTH_411 = 0x03;

//Multi-LED Mode configuration (pg 22)
static const uint8_t MAX30105_SLOT1_MASK = 0xF8;
static const uint8_t MAX30105_SLOT2_MASK = 0x8F;
static const uint8_t MAX30105_SLOT3_MASK = 0xF8;
static const uint8_t MAX30105_SLOT4_MASK = 0x8F;

static const uint8_t SLOT_NONE = 0x00;
static const uint8_t SLOT_RED_LED = 0x01;
static const uint8_t SLOT_IR_LED = 0x02;
static const uint8_t SLOT_GREEN_LED = 0x03;
static const uint8_t SLOT_NONE_PILOT = 0x04;
static const uint8_t SLOT_RED_PILOT = 0x05;
static const uint8_t SLOT_IR_PILOT = 0x06;
static const uint8_t SLOT_GREEN_PILOT = 0x07;

static const uint8_t MAX_30105_EXPECTEDPARTID = 0x15;
ret_code_t err_code;
uint8_t _i2cPort;
/* TWI instance ID. */
#if TWI0_ENABLED
#define TWI_INSTANCE_ID 0
#elif TWI1_ENABLED
#define TWI_INSTANCE_ID 1
#endif
//#define NRF_DRV_TWI_INSTANCE_(id)
/* Number of possible TWI addresses. */
#define TWI_ADDRESSES 127
/* TWI instance. */
static const nrf_drv_twi_t m_twi = NRF_DRV_TWI_INSTANCE(TWI_INSTANCE_ID);

/**
* @brief TWI initialization.
*/
void twi_init (void)
{
ret_code_t err_code;

const nrf_drv_twi_config_t twi_config = {
.scl = ARDUINO_SCL_PIN,
.sda = ARDUINO_SDA_PIN,
.frequency = NRF_DRV_TWI_FREQ_100K,
.interrupt_priority = APP_IRQ_PRIORITY_HIGH,
.clear_bus_init = false
};

err_code = nrf_drv_twi_init(&m_twi, &twi_config, NULL, NULL);
APP_ERROR_CHECK(err_code);

nrf_drv_twi_enable(&m_twi);
}


//The MAX30105 stores up to 32 samples on the IC
//This is additional local storage to the microcontroller
//const int 4 = 4; //Each long is 4 bytes so limit this to fit on your micro
struct Record
{
uint32_t red[4];
uint32_t IR[4];
uint32_t green[4];
uint32_t head;
uint32_t tail;
} sense; //This is our circular buffer of readings from the sensor


bool MAX30105_begin(uint32_t wirePort, uint32_t i2cSpeed, uint8_t i2caddr) {

_i2cPort = &wirePort; //Grab which port the user wants us to use

//_i2cPort->begin();
//_i2cPort.setClock(i2cSpeed);

_i2caddr = i2caddr;

// Step 1: Initial Communciation and Verificati on
// Check that a MAX30105 is connected
if (!readPartID() == MAX_30105_EXPECTEDPARTID) {
// Error -- Part ID read from MAX30105 does not match expected part ID.
// This may mean there is a physical connectivity problem (broken wire, unpowered, etc).
return false;
}

// Populate revision ID
readRevisionID();

return true;
}

//
// Configuration
//


//Begin Interrupt configuration
uint8_t MAX30105_getINT1() {
err_code = nrf_drv_twi_tx(&m_twi,_i2caddr, MAX30105_INTSTAT1, 1, false);
nrf_delay_ms(10);
err_code = nrf_drv_twi_rx(&m_twi,_i2caddr, MAX30105_INTSTAT1,1);
nrf_delay_ms(10);
return err_code;
}
uint8_t MAX30105_getINT2() {
err_code = nrf_drv_twi_tx(&m_twi,_i2caddr, MAX30105_INTSTAT2, 1, false);
nrf_delay_ms(10);
err_code = nrf_drv_twi_rx(&m_twi,_i2caddr, MAX30105_INTSTAT2,1);
nrf_delay_ms(10);
return err_code;
}

void MAX30105_enableAFULL() {
bitMask(MAX30105_INTENABLE1, MAX30105_INT_A_FULL_MASK, MAX30105_INT_A_FULL_ENABLE);
}
void MAX30105_disableAFULL() {
bitMask(MAX30105_INTENABLE1, MAX30105_INT_A_FULL_MASK, MAX30105_INT_A_FULL_DISABLE);
}

void MAX30105_enableDATARDY() {
bitMask(MAX30105_INTENABLE1, MAX30105_INT_DATA_RDY_MASK, MAX30105_INT_DATA_RDY_ENABLE);
}
void MAX30105_disableDATARDY() {
bitMask(MAX30105_INTENABLE1, MAX30105_INT_DATA_RDY_MASK, MAX30105_INT_DATA_RDY_DISABLE);
}

void MAX30105_enableALCOVF() {
bitMask(MAX30105_INTENABLE1, MAX30105_INT_ALC_OVF_MASK, MAX30105_INT_ALC_OVF_ENABLE);
}
void MAX30105_disableALCOVF() {
bitMask(MAX30105_INTENABLE1, MAX30105_INT_ALC_OVF_MASK, MAX30105_INT_ALC_OVF_DISABLE);
}

void MAX30105_enablePROXINT() {
bitMask(MAX30105_INTENABLE1, MAX30105_INT_PROX_INT_MASK, MAX30105_INT_PROX_INT_ENABLE);
}
void MAX30105_disablePROXINT() {
bitMask(MAX30105_INTENABLE1, MAX30105_INT_PROX_INT_MASK, MAX30105_INT_PROX_INT_DISABLE);
}

void MAX30105_enableDIETEMPRDY() {
bitMask(MAX30105_INTENABLE2, MAX30105_INT_DIE_TEMP_RDY_MASK, MAX30105_INT_DIE_TEMP_RDY_ENABLE);
}
void MAX30105_disableDIETEMPRDY() {
bitMask(MAX30105_INTENABLE2, MAX30105_INT_DIE_TEMP_RDY_MASK, MAX30105_INT_DIE_TEMP_RDY_DISABLE);
}

//End Interrupt configuration

void MAX30105_softReset() {
bitMask(MAX30105_MODECONFIG, MAX30105_RESET_MASK, MAX30105_RESET);

// Poll for bit to clear, reset is then complete
// Timeout after 100ms
uint8_t response;
unsigned long startTime = millis();
while (millis() - startTime < 100)
{
response = nrf_drv_twi_tx(&m_twi,_i2caddr, MAX30105_MODECONFIG, 1, false);
nrf_delay_ms(10);
response = nrf_drv_twi_rx(&m_twi,_i2caddr, MAX30105_MODECONFIG, 1);
nrf_delay_ms(10);
if ((response & MAX30105_RESET) == 0) break; //We're done!
nrf_delay_ms(1); //Let's not over burden the I2C bus
}
}

void MAX30105_shutDown() {
// Put IC into low power mode (datasheet pg. 19)
// During shutdown the IC will continue to respond to I2C commands but will
// not update with or take new readings (such as temperature)
bitMask(MAX30105_MODECONFIG, MAX30105_SHUTDOWN_MASK, MAX30105_SHUTDOWN);
}

void MAX30105_wakeUp() {
// Pull IC out of low power mode (datasheet pg. 19)
bitMask(MAX30105_MODECONFIG, MAX30105_SHUTDOWN_MASK, MAX30105_WAKEUP);
}

void MAX30105_setLEDMode(uint8_t mode) {
// Set which LEDs are used for sampling -- Red only, RED+IR only, or custom.
// See datasheet, page 19
bitMask(MAX30105_MODECONFIG, MAX30105_MODE_MASK, mode);
}

void MAX30105_setADCRange(uint8_t adcRange) {
// adcRange: one of MAX30105_ADCRANGE_2048, _4096, _8192, _16384
bitMask(MAX30105_PARTICLECONFIG, MAX30105_ADCRANGE_MASK, adcRange);
}

void MAX30105_setSampleRate(uint8_t sampleRate) {
// sampleRate: one of MAX30105_SAMPLERATE_50, _100, _200, _400, _800, _1000, _1600, _3200
bitMask(MAX30105_PARTICLECONFIG, MAX30105_SAMPLERATE_MASK, sampleRate);
}

void MAX30105_setPulseWidth(uint8_t pulseWidth) {
// pulseWidth: one of MAX30105_PULSEWIDTH_69, _188, _215, _411
bitMask(MAX30105_PARTICLECONFIG, MAX30105_PULSEWIDTH_MASK, pulseWidth);
}

// NOTE: Amplitude values: 0x00 = 0mA, 0x7F = 25.4mA, 0xFF = 50mA (typical)
// See datasheet, page 21
void MAX30105_setPulseAmplitudeRed(uint8_t amplitude) {
uint8_t LEDPOWERconfig_Reg[2]={MAX30105_LED1_PULSEAMP, amplitude} ;
err_code = nrf_drv_twi_tx(&m_twi,_i2caddr,&LEDPOWERconfig_Reg, sizeof(LEDPOWERconfig_Reg), false);
nrf_delay_ms(10);
if (err_code == NRF_SUCCESS)
{
NRF_LOG_INFO(" written to RED LED AMPLITUDE 0x%x", MAX30105_LED1_PULSEAMP);
}
}

void MAX30105_setPulseAmplitudeIR(uint8_t amplitude) {
uint8_t LEDPOWERconfig_Reg[2]={MAX30105_LED2_PULSEAMP, amplitude} ;
err_code = nrf_drv_twi_tx(&m_twi,_i2caddr,&LEDPOWERconfig_Reg, sizeof(LEDPOWERconfig_Reg), false);
nrf_delay_ms(10);
if (err_code == NRF_SUCCESS)
{
NRF_LOG_INFO(" written to IR LED AMPLITUDE 0x%x", MAX30105_LED2_PULSEAMP);
}

void MAX30105_setPulseAmplitudeGreen(uint8_t amplitude) {
uint8_t LEDPOWERconfig_Reg[2]={MAX30105_LED3_PULSEAMP, amplitude} ;
err_code = nrf_drv_twi_tx(&m_twi,_i2caddr,&LEDPOWERconfig_Reg, sizeof(LEDPOWERconfig_Reg), false);
nrf_delay_ms(10);
if (err_code == NRF_SUCCESS)
{
NRF_LOG_INFO(" written to Green LED AMPLITUDE 0x%x", MAX30105_LED3_PULSEAMP);
}
}

void MAX30105_setPulseAmplitudeProximity(uint8_t amplitude) {
uint8_t LEDPOWERconfig_Reg[2]={MAX30105_LED_PROX_AMP, amplitude} ;
err_code = nrf_drv_twi_tx(&m_twi,_i2caddr,&LEDPOWERconfig_Reg, sizeof(LEDPOWERconfig_Reg), false);
nrf_delay_ms(10);
if (err_code == NRF_SUCCESS)
{
NRF_LOG_INFO(" written to Pulse AMPLITUDE proximity 0x%x", MAX30105_LED_PROX_AMP);
}
}


void MAX30105_setProximityThreshold(uint8_t threshMSB) {
// Set the IR ADC count that will trigger the beginning of particle-sensing mode.
// The threshMSB signifies only the 8 most significant-bits of the ADC count.
// See datasheet, page 24.
uint8_t LEDPOWERconfig_Reg[2]={MAX30105_PROXINTTHRESH, threshMSB} ;
err_code = nrf_drv_twi_tx( &m_twi,_i2caddr,&LEDPOWERconfig_Reg, sizeof(LEDPOWERconfig_Reg), false);
nrf_delay_ms(10);
if (err_code == NRF_SUCCESS)
{
NRF_LOG_INFO(" written to set proximity thershold 0x%x", MAX30105_PROXINTTHRESH);
}
}
}

//Given a slot number assign a thing to it
//Devices are SLOT_RED_LED or SLOT_RED_PILOT (proximity)
//Assigning a SLOT_RED_LED will pulse LED
//Assigning a SLOT_RED_PILOT will ??
void MAX30105_enableSlot(uint8_t slotNumber, uint8_t device) {

uint8_t originalContents;

switch (slotNumber) {
case (1):
bitMask(MAX30105_MULTILEDCONFIG1, MAX30105_SLOT1_MASK, device);
break;
case (2):
bitMask(MAX30105_MULTILEDCONFIG1, MAX30105_SLOT2_MASK, device << 4);
break;
case (3):
bitMask(MAX30105_MULTILEDCONFIG2, MAX30105_SLOT3_MASK, device);
break;
case (4):
bitMask(MAX30105_MULTILEDCONFIG2, MAX30105_SLOT4_MASK, device << 4);
break;
default:
//Shouldn't be here!
break;
}
}

//Clears all slot assignments
void MAX30105_disableSlots() {
uint8_t LEDPOWERconfig_Reg[2]={MAX30105_MULTILEDCONFIG1, 0} ;
err_code = nrf_drv_twi_tx(&m_twi, _i2caddr,&LEDPOWERconfig_Reg, sizeof(LEDPOWERconfig_Reg), false);
nrf_delay_ms(10);
if (err_code == NRF_SUCCESS)
{
NRF_LOG_INFO(" written succefull", MAX30105_MULTILEDCONFIG1);
}

uint8_t LEDPOWERconfig_Reg1[2]={MAX30105_MULTILEDCONFIG2, 0} ;
err_code = nrf_drv_twi_tx(&m_twi,_i2caddr,&LEDPOWERconfig_Reg1, sizeof(LEDPOWERconfig_Reg1), false);
nrf_delay_ms(10);
if (err_code == NRF_SUCCESS)
{
NRF_LOG_INFO(" written succefull", MAX30105_MULTILEDCONFIG2);
}
}

//
// FIFO Configuration
//

//Set sample average (Table 3, Page 18)
void MAX30105_setFIFOAverage(uint8_t numberOfSamples) {
bitMask(MAX30105_FIFOCONFIG, MAX30105_SAMPLEAVG_MASK, numberOfSamples);
}

//Resets all points to start in a known state
//Page 15 recommends clearing FIFO before beginning a read
void MAX30105_clearFIFO() {
uint8_t LEDPOWERconfig_Reg[2]={ MAX30105_FIFOWRITEPTR, 0} ;
err_code = nrf_drv_twi_tx(&m_twi,_i2caddr,&LEDPOWERconfig_Reg, sizeof(LEDPOWERconfig_Reg), false);
nrf_delay_ms(10);
if (err_code == NRF_SUCCESS)
{
NRF_LOG_INFO(" written succefull", MAX30105_FIFOWRITEPTR);
}
uint8_t LEDPOWERconfig_Reg1[2]={ MAX30105_FIFOOVERFLOW, 0} ;
err_code = nrf_drv_twi_tx(&m_twi,_i2caddr,&LEDPOWERconfig_Reg1, sizeof(LEDPOWERconfig_Reg1), false);
nrf_delay_ms(10);
if (err_code == NRF_SUCCESS)
{
NRF_LOG_INFO(" written succefull", MAX30105_FIFOOVERFLOW);
}
uint8_t LEDPOWERconfig_Reg2[2]={ MAX30105_FIFOREADPTR, 0} ;
err_code = nrf_drv_twi_tx(&m_twi,_i2caddr,&LEDPOWERconfig_Reg2, sizeof(LEDPOWERconfig_Reg2), false);
nrf_delay_ms(10);
if (err_code == NRF_SUCCESS)
{
NRF_LOG_INFO(" written succefull", MAX30105_FIFOREADPTR);
}
}

//Enable roll over if FIFO over flows
void MAX30105_enableFIFORollover() {
bitMask(MAX30105_FIFOCONFIG, MAX30105_ROLLOVER_MASK, MAX30105_ROLLOVER_ENABLE);
}

//Disable roll over if FIFO over flows
void MAX30105_disableFIFORollover() {
bitMask(MAX30105_FIFOCONFIG, MAX30105_ROLLOVER_MASK, MAX30105_ROLLOVER_DISABLE);
}

//Set number of samples to trigger the almost full interrupt (Page 18)
//Power on default is 32 samples
//Note it is reverse: 0x00 is 32 samples, 0x0F is 17 samples
void MAX30105_setFIFOAlmostFull(uint8_t numberOfSamples) {
bitMask(MAX30105_FIFOCONFIG, MAX30105_A_FULL_MASK, numberOfSamples);
}

//Read the FIFO Write Pointer
uint8_t MAX30105_getMAX30105_getWritePointer() {
err_code = nrf_drv_twi_tx(&m_twi,_i2caddr,&MAX30105_FIFOWRITEPTR, 1, false);
nrf_delay_ms(10);
err_code = nrf_drv_twi_rx(&m_twi,_i2caddr, &MAX30105_FIFOWRITEPTR,1);
nrf_delay_ms(10);
return err_code;
}

//Read the FIFO Read Pointer
uint8_t MAX30105_getMAX30105_getReadPointer() {
err_code = nrf_drv_twi_tx(&m_twi,_i2caddr,&MAX30105_FIFOREADPTR, 1, false);
nrf_delay_ms(10);
err_code = nrf_drv_twi_rx(&m_twi,_i2caddr, &MAX30105_FIFOREADPTR,1);
nrf_delay_ms(10);
return err_code;
}


// Die Temperature
// Returns temp in C
float MAX30105_readTemperature() {
uint8_t tempInt ;
uint8_t tempFrac;
// Step 1: Config die temperature register to take 1 temperature sample
uint8_t LEDPOWERconfig_Reg[2]={ MAX30105_DIETEMPCONFIG, 0x01} ;
err_code = nrf_drv_twi_tx( &m_twi,_i2caddr,&LEDPOWERconfig_Reg, sizeof(LEDPOWERconfig_Reg), false);
nrf_delay_ms(10);
if (err_code == NRF_SUCCESS)
{
NRF_LOG_INFO(" written succefull", MAX30105_DIETEMPCONFIG);
}

// Poll for bit to clear, reading is then complete
// Timeout after 100ms
unsigned long startTime = millis();
uint8_t response;
while (millis() - startTime < 100)
{
response = nrf_drv_twi_tx(&m_twi,_i2caddr, MAX30105_DIETEMPCONFIG, 1, false);
nrf_delay_ms(10);
response = nrf_drv_twi_rx(&m_twi,_i2caddr, MAX30105_DIETEMPCONFIG, 1);
nrf_delay_ms(10);
if ((response & 0x01) == 0) break; //We're done!
nrf_delay_ms(1); //Let's not over burden the I2C bus
}
//TODO How do we want to fail? With what type of error?
//? if(millis() - startTime >= 100) return(-999.0);

// Step 2: Read die temperature register (integer)
tempInt = nrf_drv_twi_tx(&m_twi,_i2caddr,&MAX30105_DIETEMPINT, 1, false);
nrf_delay_ms(10);
tempInt = nrf_drv_twi_rx(&m_twi,_i2caddr, &MAX30105_DIETEMPINT,1);
nrf_delay_ms(10);
tempFrac = nrf_drv_twi_tx(&m_twi, _i2caddr,&MAX30105_DIETEMPFRAC, 1, false);
nrf_delay_ms(10);
tempFrac = nrf_drv_twi_rx(&m_twi,_i2caddr, &MAX30105_DIETEMPFRAC,1);
nrf_delay_ms(10);
// Step 3: Calculate temperature (datasheet pg. 23)
return (float)tempInt + ((float)tempFrac * 0.0625);
}

// Returns die temp in F
float MAX30105_readTemperatureF() {
//float temp = MAX30105_readTemperature(MAX30105_H_);
float temp = MAX30105_readTemperature(MAX30105_H_);

if (temp != -999.0) temp = temp * 1.8 + 32.0;

return (temp);
}

// Set the PROX_INT_THRESHold
void MAX30105_setPROXINTTHRESH(uint8_t val) {
uint8_t LEDPOWERconfig_Reg[2]={ MAX30105_PROXINTTHRESH, val} ;
err_code = nrf_drv_twi_tx(&m_twi,_i2caddr,&LEDPOWERconfig_Reg, sizeof(LEDPOWERconfig_Reg), false);
nrf_delay_ms(10);
if (err_code == NRF_SUCCESS)
{
NRF_LOG_INFO(" written succefull", MAX30105_PROXINTTHRESH);
}

}


//
// Device ID and Revision
//
uint8_t MAX30105_readPartID() {
err_code = nrf_drv_twi_tx(&m_twi,_i2caddr, &MAX30105_PARTID, 1, false);
nrf_delay_ms(10);
err_code = nrf_drv_twi_rx(&m_twi,_i2caddr, &MAX30105_PARTID,1);
nrf_delay_ms(10);
return err_code;
}

void MAX30105_readRevisionID() {
revisionID = nrf_drv_twi_tx(&m_twi,_i2caddr, &MAX30105_REVISIONID, 1, false);
nrf_delay_ms(10);
revisionID = nrf_drv_twi_rx(&m_twi,_i2caddr, &MAX30105_REVISIONID,1);
nrf_delay_ms(10);

}

uint8_t MAX30105_getRevisionID() {
return revisionID;
}


//Setup the sensor
//The MAX30105 has many settings. By default we select:
// Sample Average = 4
// Mode = MultiLED
// ADC Range = 16384 (62.5pA per LSB)
// Sample rate = 50
//Use the default setup if you are just getting started with the MAX30105 sensor
void setup( uint8_t powerLevel, uint8_t sampleAverage,uint8_t ledMode , uint16_t sampleRate , uint16_t pulseWidth , uint32_t adcRange )
{
softReset(); //Reset all configuration, threshold, and data registers to POR values

//FIFO Configuration
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
//The chip will average multiple samples of same type together if you wish
if (sampleAverage == 1) setFIFOAverage(MAX30105_H_ MAX30105_SAMPLEAVG_1); //No averaging per FIFO record
else if (sampleAverage == 2) setFIFOAverage(MAX30105_H_ MAX30105_SAMPLEAVG_2);
else if (sampleAverage == 4) setFIFOAverage(MAX30105_H_ MAX30105_SAMPLEAVG_4);
else if (sampleAverage == 8) setFIFOAverage(MAX30105_H_ MAX30105_SAMPLEAVG_8);
else if (sampleAverage == 16) setFIFOAverage(MAX30105_H_ MAX30105_SAMPLEAVG_16);
else if (sampleAverage == 32) setFIFOAverage(MAX30105_H_ MAX30105_SAMPLEAVG_32);
else setFIFOAverage(MAX30105_H_ MAX30105_SAMPLEAVG_4);

//setFIFOAlmostFull(2); //Set to 30 samples to trigger an 'Almost Full' interrupt
enableFIFORollover(MAX30105_H_); //Allow FIFO to wrap/roll over
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

//Mode Configuration
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

if (ledMode == 3) setLEDMode(MAX30105_H_ MAX30105_MODE_MULTILED); //Watch all three LED channels
else if (ledMode == 2) setLEDMode(MAX30105_H_ MAX30105_MODE_REDIRONLY); //Red and IR
else setLEDMode(MAX30105_H_ MAX30105_MODE_REDONLY); //Red only
uint16_t activeLEDs = ledMode;
MAX30105_H_ activeLEDs = ledMode; //Used to control how many bytes to read from FIFO buffer
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

//Particle Sensing Configuration
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
if(adcRange < 4096) setADCRange(MAX30105_H_ MAX30105_ADCRANGE_2048); //7.81pA per LSB
else if(adcRange < 8192) setADCRange(MAX30105_H_ MAX30105_ADCRANGE_4096); //15.63pA per LSB
else if(adcRange < 16384) setADCRange(MAX30105_H_ MAX30105_ADCRANGE_8192); //31.25pA per LSB
else if(adcRange == 16384) setADCRange(MAX30105_H_ MAX30105_ADCRANGE_16384); //62.5pA per LSB
else setADCRange(MAX30105_H_ MAX30105_ADCRANGE_2048);

if (sampleRate < 100) MAX30105_setSampleRate(MAX30105_H_ MAX30105_SAMPLERATE_50); //Take 50 samples per second
else if (sampleRate < 200) MAX30105_setSampleRate(MAX30105_H_ MAX30105_SAMPLERATE_100);
else if (sampleRate < 400) MAX30105_setSampleRate(MAX30105_H_ MAX30105_SAMPLERATE_200);
else if (sampleRate < 800) MAX30105_setSampleRate(MAX30105_H_ MAX30105_SAMPLERATE_400);
else if (sampleRate < 1000) MAX30105_setSampleRate(MAX30105_H_ MAX30105_SAMPLERATE_800);
else if (sampleRate < 1600) MAX30105_setSampleRate(MAX30105_H_ MAX30105_SAMPLERATE_1000);
else if (sampleRate < 3200) MAX30105_setSampleRate(MAX30105_H_ MAX30105_SAMPLERATE_1600);
else if (sampleRate == 3200) MAX30105_setSampleRate(MAX30105_H_ MAX30105_SAMPLERATE_3200);
else MAX30105_setSampleRate(MAX30105_H_ MAX30105_SAMPLERATE_50);

//The longer the pulse width the longer range of detection you'll have
//At 69us and 0.4mA it's about 2 inches
//At 411us and 0.4mA it's about 6 inches
if (pulseWidth < 118) MAX30105_setPulseWidth(MAX30105_H_ MAX30105_PULSEWIDTH_69); //Page 26, Gets us 15 bit resolution
else if (pulseWidth < 215) MAX30105_setPulseWidth(MAX30105_H_ MAX30105_PULSEWIDTH_118); //16 bit resolution
else if (pulseWidth < 411) MAX30105_setPulseWidth(MAX30105_H_ MAX30105_PULSEWIDTH_215); //17 bit resolution
else if (pulseWidth == 411) MAX30105_setPulseWidth(MAX30105_H_ MAX30105_PULSEWIDTH_411); //18 bit resolution
else MAX30105_setPulseWidth(MAX30105_H_ MAX30105_PULSEWIDTH_69);
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

//LED Pulse Amplitude Configuration
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
//Default is 0x1F which gets us 6.4mA
//powerLevel = 0x02, 0.4mA - Presence detection of ~4 inch
//powerLevel = 0x1F, 6.4mA - Presence detection of ~8 inch
//powerLevel = 0x7F, 25.4mA - Presence detection of ~8 inch
//powerLevel = 0xFF, 50.0mA - Presence detection of ~12 inch

setPulseAmplitudeRed(MAX30105_H_ powerLevel);
setPulseAmplitudeIR(MAX30105_H_ powerLevel);
setPulseAmplitudeGreen(MAX30105_H_ powerLevel);
setPulseAmplitudeProximity(MAX30105_H_ powerLevel);
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

//Multi-LED Mode Configuration, Enable the reading of the three LEDs
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
enableSlot(1, SLOT_RED_LED);
if (ledMode > 1) enableSlot(2, SLOT_IR_LED);
if (ledMode > 2) enableSlot(3, SLOT_GREEN_LED);
//enableSlot(1, SLOT_RED_PILOT);
//enableSlot(2, SLOT_IR_PILOT);
//enableSlot(3, SLOT_GREEN_PILOT);
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

clearFIFO(MAX30105_H_); //Reset the FIFO before we begin checking the sensor
}

//
// Data Collection
//*77777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777*/

//Tell caller how many samples are available
uint8_t MAX30105_available()
{
uint8_t numberOfSamples = sense.head - sense.tail;
if (numberOfSamples < 0) numberOfSamples += 4;

return (numberOfSamples);
}

//Report the most recent red value
uint32_t MAX30105_getRed()
{
//Check the sensor for new data for 250ms
if(safeCheck(MAX30105_H_ 250))
return (sense.red[sense.head]);
else
return(0); //Sensor failed to find new data
}

//Report the most recent IR value
uint32_t MAX30105_getIR()
{
//Check the sensor for new data for 250ms
if(safeCheck(MAX30105_H_ 250))
return (sense.IR[sense.head]);
else
return(0); //Sensor failed to find new data
}

//Report the most recent Green value
uint32_t MAX30105_getGreen()
{
//Check the sensor for new data for 250ms
if(safeCheck(MAX30105_H_ 250))
return (sense.green[sense.head]);
else
return(0); //Sensor failed to find new data
}

//Report the next Red value in the FIFO
uint32_t MAX30105_getFIFORed()
{
return (sense.red[sense.tail]);
}

//Report the next IR value in the FIFO
uint32_t MAX30105_getFIFOIR()
{
return (sense.IR[sense.tail]);
}

//Report the next Green value in the FIFO
uint32_t MAX30105_getFIFOGreen()
{
return (sense.green[sense.tail]);
}

//Advance the tail
void MAX30105_nextSample()
{
if(available(MAX30105_H_)) //Only advance the tail if new data is available
{
sense.tail++;
sense.tail %= 4; //Wrap condition
}
}

//Polls the sensor for new data
//Call regularly
//If new data is available, it updates the head and tail in the main struct
//Returns number of new samples obtained
uint16_t MAX30105_check()
{
//Read register FIDO_DATA in (3-byte * number of active LED) chunks
//Until FIFO_RD_PTR = FIFO_WR_PTR

uint8_t MAX30105_getMAX30105_getReadPointer(){
err_code = nrf_drv_twi_tx(&m_twi,_i2caddr,&MAX30105_FIFOREADPTR, 1, false);
nrf_delay_ms(10);
err_code = nrf_drv_twi_rx(&m_twi,_i2caddr, &MAX30105_FIFOREADPTR,1);
nrf_delay_ms(10);
return err_code;
}

uint8_t MAX30105_getMAX30105_getWritePointer() {
err_code = nrf_drv_twi_tx(&m_twi,_i2caddr,&MAX30105_FIFOWRITEPTR, 1, false);
nrf_delay_ms(10);
err_code = nrf_drv_twi_rx(&m_twi,_i2caddr, &MAX30105_FIFOWRITEPTR,1);
nrf_delay_ms(10);
return err_code;
}


int numberOfSamples = 0;

//Do we have new data?
if (MAX30105_getReadPointer() != MAX30105_getWritePointer())
{
//Calculate the number of readings we need to get from sensor
numberOfSamples = MAX30105_getWritePointer - MAX30105_getReadPointer;
if (numberOfSamples < 0) numberOfSamples += 32; //Wrap condition

//We now have the number of readings, now calc bytes to read
//For this example we are just doing Red and IR (3 bytes each)
uint16_t activeLEDs;
int bytesLeftToRead = numberOfSamples * activeLEDs * 3;

//Get ready to read a burst of data from the FIFO register
ret_code_t err_code;
uint8_t reg_write[1] = {MAX30105_FIFODATA};
err_code = nrf_drv_twi_tx(&m_twi,MAX30105_ADDRESS, reg_write, sizeof(reg_write), true);
APP_ERROR_CHECK(err_code);


//Wire.requestFrom() is limited to I2C_I2C_BUFFER_LENGTH which is 32 on the Uno
//We may need to read as many as 288 bytes so we read in blocks no larger than 32
//I2C_I2C_BUFFER_LENGTH should work with other platforms with larger requestFrom buffers
while (bytesLeftToRead > 0)
{
int toGet = bytesLeftToRead;
if (toGet > I2C_BUFFER_LENGTH)
{
//If toGet is 32 this is bad because we read 6 bytes (Red+IR * 3 = 6) at a time
//32 % 6 = 2 left over. We don't want to request 32 bytes, we want to request 30.
//32 % 9 (Red+IR+GREEN) = 5 left over. We want to request 27.

toGet = I2C_BUFFER_LENGTH - (I2C_BUFFER_LENGTH % (activeLEDs * 3)); //Trim toGet to be a multiple of the samples we need to read
}

bytesLeftToRead -= toGet;

//Request toGet number of bytes from sensor
ret_code_t err_code1;
uint8_t m_sample[sizeof(toGet)];
while (toGet > 0)
{
sense.head++; //Advance the head of the storage struct
sense.head %= 4; //Wrap condition

uint8_t temp[sizeof(uint32_t)]; //Array of 4 uint8_ts that we will convert into long
uint32_t tempLong;

//Burst read three bytes - RED
err_code1 = nrf_drv_twi_rx(&m_twi,MAX30105_ADDRESS, temp, 3);
APP_ERROR_CHECK(err_code1);
tempLong = ((uint32_t)temp[0] << 16) | ((uint32_t)temp[1]<<8) | temp[2];

//Convert array to long
memcpy(&tempLong, temp, sizeof(tempLong));

tempLong &= 0x3FFFF; //Zero out all but 18 bits

sense.red[sense.head] = tempLong; //Store this reading into the sense array

if (activeLEDs > 1)
{
//Burst read three more bytes - IR
err_code1 = nrf_drv_twi_rx(&m_twi, MAX30105_ADDRESS, temp, 3);
APP_ERROR_CHECK(err_code1);
tempLong = ((uint32_t)temp[0] << 16) | ((uint32_t)temp[1]<<8) | temp[2];


//Convert array to long
memcpy(&tempLong, temp, sizeof(tempLong));

tempLong &= 0x3FFFF; //Zero out all but 18 bits

sense.IR[sense.head] = tempLong;
}

if (activeLEDs > 2)
{
//Burst read three more bytes - Green
err_code1 = nrf_drv_twi_rx(&m_twi, MAX30105_ADDRESS, temp, 3);
APP_ERROR_CHECK(err_code1);
tempLong = ((uint32_t)temp[0] << 16) | ((uint32_t)temp[1]<<8) | temp[2];

//Convert array to long
memcpy(&tempLong, temp, sizeof(tempLong));

tempLong &= 0x3FFFF; //Zero out all but 18 bits

sense.green[sense.head] = tempLong;
}

toGet -= activeLEDs * 3;
}

} //End while (bytesLeftToRead > 0)

} //End readPtr != writePtr

return (numberOfSamples); //Let the world know how much new data we found
}

//Check for new data but give up after a certain amount of time
//Returns true if new data was found
//Returns false if new data was not found
bool MAX30105_safeCheck(uint8_t maxTimeToCheck)
{
uint32_t markTime = millis();

while(1)
{
if(millis() - markTime > maxTimeToCheck) return(false);

if(check(MAX30105_H_) == true) //We found new data!
return(true);

nrf_delay_ms(1);
}
}

//Given a register, read it, mask it, and then set the thing
void MAX30105_bitMask(uint8_t reg, uint8_t mask, uint8_t thing)
{ uint8_t originalContents;
// Grab current register context
originalContents = nrf_drv_twi_tx(&m_twi, _i2caddr,&reg, 1, false);
nrf_delay_ms(10);
originalContents = nrf_drv_twi_rx(&m_twi, _i2caddr, &reg,1);
nrf_delay_ms(10);

// Zero-out the portions of the register we're interested in
originalContents = originalContents & mask;

// Change contents
uint8_t LEDPOWERconfig_Reg[2]={reg,originalContents | thing} ;
err_code = nrf_drv_twi_tx( &m_twi,_i2caddr,&LEDPOWERconfig_Reg, sizeof(LEDPOWERconfig_Reg), false);
nrf_delay_ms(10);
if (err_code == NRF_SUCCESS)
{
NRF_LOG_INFO(" written into register", reg);
}

}

//
// Low-level I2C Communication
void MAX30105_I2C_Communication(uint8_t reg, uint8_t address,uint8_t value)
{
err_code = nrf_drv_twi_tx(&m_twi,address,&reg, 1, false);
nrf_delay_ms(10);
err_code = nrf_drv_twi_rx(&m_twi,address, &reg,1);
nrf_delay_ms(10);
ret_code_t err_code;
int bytesLeftToRead;
int toGet = bytesLeftToRead;
uint8_t reg_write[1] = {reg};
uint8_t m_sample[sizeof(toGet)];
err_code = nrf_drv_twi_tx(&m_twi, address, reg_write, sizeof(reg_write), false);
err_code =nrf_drv_twi_rx(&m_twi, address, &m_sample, sizeof(m_sample));
APP_ERROR_CHECK(err_code);

{
//_i2cPort->beginTransmission(address);
//_i2cPort->write(reg);
//_i2cPort->endTransmission(false);
//_i2cPort->requestFrom(address, 1); // Request 1 byte

//int tries = 0;
//while (!_i2cPort available())
//{
// nrf_delay_ms(1);
//if (tries++ > 200) break;
//}
//if (tries == 200) return (0); //Fail
//err_code = nrf_drv_twi_rx(&m_twi, MAX30105_ADDRESS,_i2cPort, 3);
//return err_code;
}
void MAX30105_writeRegister8(uint8_t address, uint8_t reg, uint8_t value) {
uint8_t LEDPOWERconfig_Reg[2]={ reg, value} ;
err_code = nrf_drv_twi_tx(&m_twi,address,&LEDPOWERconfig_Reg, sizeof(LEDPOWERconfig_Reg), false);
nrf_delay_ms(10);
if (err_code == NRF_SUCCESS)
{
NRF_LOG_INFO(" written into register", reg);
}
{
ret_code_t err_code;
uint8_t reg_write1[1] = {reg};
err_code = nrf_drv_twi_tx(&m_twi, address, reg_write1, sizeof(reg_write1), true);
uint8_t reg_write2[1] = {value};
err_code = nrf_drv_twi_tx(&m_twi, address, reg_write2, sizeof(reg_write2), true);
APP_ERROR_CHECK(err_code);

// _i2cPort->beginTransmission(address);
// _i2cPort->write(reg);
//_i2cPort->write(value);
// _i2cPort->endTransmission();
}
}}
/*********************************************************/
void SP02_init(void){
uint32_t irBuffer[100]; //infrared LED sensor data
uint32_t redBuffer[100]; //red LED sensor data

int32_t bufferLength; //data length
int32_t spo2; //SPO2 value
int8_t validSPO2; //indicator to show if the SPO2 calculation is valid
int32_t heartRate; //heart rate value
int8_t validHeartRate; //indicator to show if the heart rate calculation is valid


NRF_LOG_INFO(0, "\nstart the max30105_SP02 program\n");
MAX30105_init( MAX30105_ADDRESS);
uint8_t ledBrightness = 60; //Options: 0=Off to 255=50mA
uint8_t sampleAverage = 4; //Options: 1, 2, 4, 8, 16, 32
uint8_t ledMode = 2; //Options: 1 = Red only, 2 = Red + IR, 3 = Red + IR + Green
uint8_t sampleRate = 100; //Options: 50, 100, 200, 400, 800, 1000, 1600, 3200
uint32_t pulseWidth = 411; //Options: 69, 118, 215, 411
uint32_t adcRange = 4096; //Options: 2048, 4096, 8192, 16384

setup(ledBrightness, sampleAverage, ledMode, sampleRate, pulseWidth, adcRange); //Configure sensor with these settings
bufferLength = 100; //buffer length of 100 stores 4 seconds of samples running at 25sps

//read the first 100 samples, and determine the signal range
for (uint8_t i = 0 ; i < bufferLength ; i++)
{
while (MAX30105_available(MAX30105_ADDRESS) == false) //do we have new data?
MAX30105_check(MAX30105_H_); //Check the sensor for new data

redBuffer[i] = MAX30105_getRed(MAX30105_ADDRESS);
irBuffer[i] = MAX30105_getIR(MAX30105_ADDRESS);
MAX30105_nextSample(MAX30105_ADDRESS); //We're finished with this sample so move to next sample
//SEGGER_RTT_printf(0, "\n red=%d ir=%d \n", redBuffer[i] , irBuffer[i]);
nrf_delay_ms(2);
//calculate heart rate and SpO2 after first 100 samples (first 4 seconds of samples)
maxim_heart_rate_and_oxygen_saturation(irBuffer, bufferLength, redBuffer, &spo2, &validSPO2, &heartRate, &validHeartRate);

}


}

void max30105_SP02(int32_t *heartRate , int32_t *spo2){
uint32_t irBuffer[100]; //infrared LED sensor data
uint32_t redBuffer[100]; //red LED sensor data
int32_t bufferLength=4; //data length
int8_t validSPO2; //indicator to show if the SPO2 calculation is valid
int8_t validHeartRate; //indicator to show if the heart rate calculation is valid



//dumping the first 25 sets of samples in the memory and shift the last 75 sets of samples to the top
for (uint8_t i = 25; i < 100; i++)
{
redBuffer[i - 25] = redBuffer[i];
irBuffer[i - 25] = irBuffer[i];
}

//take 25 sets of samples before calculating the heart rate.
for (uint8_t i = 75; i < 100; i++)
{
while (MAX30105_available(MAX30105_ADDRESS) == false) //do we have new data?
MAX30105_check(MAX30105_ADDRESS); //Check the sensor for new data

//digitalWrite(readLED, !digitalRead(readLED)); //Blink onboard LED with every data read

redBuffer[i] = MAX30105_getRed(MAX30105_ADDRESS);
irBuffer[i] = MAX30105_getIR(MAX30105_ADDRESS);
MAX30105_nextSample(MAX30105_ADDRESS); //We're finished with this sample so move to next sample

//send samples and calculation result to terminal program through UART
//SEGGER_RTT_printf(0, "\n red=%d ir=%d HR=%d HRvalid=%d SPO2=%d SPO2Valid=%d \n", redBuffer[i] , irBuffer[i] , heartRate ,validHeartRate , spo2 , validSPO2);

}


maxim_heart_rate_and_oxygen_saturation(irBuffer, bufferLength, redBuffer, &spo2, &validSPO2, &heartRate, &validHeartRate);

}

please help me solve this problem 

Related