Maxim Integrated's IoT development kit.

Dependencies:   MAX30101 MAX30003 MAX113XX_Pixi MAX30205 max32630fthr USBDevice

max30101_app.cpp

Committer:
Mahir Ozturk
Date:
2018-07-19
Revision:
16:503f8308e2db
Parent:
13:fba77a5d0fa0

File content as of revision 16:503f8308e2db:

/*
 * 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;
		}

	}
}