/*
* Ear-ly Detection of Respiratory Viral Illnesses
* Advanced Wearables
*/
#include <zephyr.h>
#include <device.h>
#include <drivers/gpio.h>
#include <sys/util.h>
#include <sys/printk.h>
#include <inttypes.h>
#include <drivers/i2c.h>
#include <math.h>
#define MLX90632_ADDRESS 0x3A
#define MLX90632_PRODUCT_CODE 0x2409
#define MLX90632_EE_P_R 0x240C
#define MLX90632_EE_P_G 0x240E
#define MLX90632_EE_P_T 0x2410
#define MLX90632_EE_P_O 0x2412
#define MLX90632_EE_Ea 0x2424
#define MLX90632_EE_Bb 0x2426
#define MLX90632_EE_Fa 0x2428
#define MLX90632_EE_Fb 0x242A
#define MLX90632_EE_Ga 0x242C
#define MLX90632_EE_Ka 0x242F
#define MLX90632_EE_Ha 0x2481
#define MLX90632_EE_Hb 0x2482
#define MLX90632_EE_Gb 0x242E
#define MLX90632_REG_CONTROL 0x3001
#define MLX90632_REG_STATUS 0x3FFF
#define MLX90632_RAM_4 0x4003
#define MLX90632_RAM_5 0x4004
#define MLX90632_RAM_6 0x4005
#define MLX90632_RAM_7 0x4006
#define MLX90632_RAM_8 0x4007
#define MLX90632_RAM_9 0x4008
#define MLX90632_MODE_SLEEP 0x01
#define MLX90632_MODE_CONTINUOUS 0x03
double MLX90632_P_R = 0;
double MLX90632_P_G = 0;
double MLX90632_P_T = 0;
double MLX90632_P_O = 0;
double MLX90632_Ea = 0;
double MLX90632_Eb = 0;
double MLX90632_Fa = 0;
double MLX90632_Fb = 0;
double MLX90632_Ga = 0;
double MLX90632_Gb = 0;
double MLX90632_Ka = 0;
double MLX90632_Ha = 0;
double MLX90632_Hb = 0;
double TOdut = 25.0; //Assume 25C for first iteration
double TO0 = 25.0; //object temp from previous calculation
double TA0 = 25.0; //ambient temp from previous calculation
double objectT = 0;
#define MAX30102_ADDRESS 0x57
#define MAX30102_INT_STATUS_1 0x00
#define MAX30102_INT_STATUS_2 0x01
#define MAX30102_INT_ENABLE_1 0x02
#define MAX30102_INT_ENABLE_2 0x03
#define MAX30102_FIFO_WRITE_PTR 0x04
#define MAX30102_OVERFLOW_COUNT 0x05
#define MAX30102_FIFO_READ_PTR 0x06
#define MAX30102_REG_FIFO_DATA 0x07
#define MAX30102_FIFO_CONFIG 0x08
#define MAX30102_MODE_CONFIG 0x09
#define MAX30102_SPO2_CONFIG 0x0A
#define MAX30102_LED1_PULSE_AMP 0x0C
#define MAX30102_LED2_PULSE_AMP 0x0D
#define MAX30102_PARTIDR 0xFF
#define MAX30102_RESET 0x40
#define MAX_BRIGHTNESS 255
int32_t n_ir_buffer_length; //data length
uint32_t aun_ir_buffer[500]; //IR LED sensor data
uint32_t aun_red_buffer[500]; //Red LED sensor data
int32_t n_sp02; //SPO2 value
int8_t ch_spo2_valid; //indicator to show if the SP02 calculation is valid
int32_t n_heart_rate; //heart rate value
int8_t ch_hr_valid; //indicator to show if the heart rate calculation is valid
#define FS 100
#define BUFFER_SIZE (FS* 5)
#define MA4_SIZE 4 // DO NOT CHANGE
#define HAMMING_SIZE 5 // DO NOT CHANGE
#define min(x,y) ((x) < (y) ? (x) : (y))
const uint16_t auw_hamm[31]={ 41, 276, 512, 276, 41 }; //Hamm= long16(512* hamming(5)');
//uch_spo2_table is computed as -45.060*ratioAverage* ratioAverage + 30.354 *ratioAverage + 94.845 ;
const uint8_t uch_spo2_table[184]={ 95, 95, 95, 96, 96, 96, 97, 97, 97, 97, 97, 98, 98, 98, 98, 98, 99, 99, 99, 99,
99, 99, 99, 99, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100,
100, 100, 100, 100, 99, 99, 99, 99, 99, 99, 99, 99, 98, 98, 98, 98, 98, 98, 97, 97,
97, 97, 96, 96, 96, 96, 95, 95, 95, 94, 94, 94, 93, 93, 93, 92, 92, 92, 91, 91,
90, 90, 89, 89, 89, 88, 88, 87, 87, 86, 86, 85, 85, 84, 84, 83, 82, 82, 81, 81,
80, 80, 79, 78, 78, 77, 76, 76, 75, 74, 74, 73, 72, 72, 71, 70, 69, 69, 68, 67,
66, 66, 65, 64, 63, 62, 62, 61, 60, 59, 58, 57, 56, 56, 55, 54, 53, 52, 51, 50,
49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 31, 30, 29,
28, 27, 26, 25, 23, 22, 21, 20, 19, 17, 16, 15, 14, 12, 11, 10, 9, 7, 6, 5,
3, 2, 1 } ;
static int32_t an_dx[ BUFFER_SIZE - MA4_SIZE]; // delta
static int32_t an_x[ BUFFER_SIZE]; //ir
static int32_t an_y[ BUFFER_SIZE]; //red
void maxim_heart_rate_and_oxygen_saturation(uint32_t *pun_ir_buffer, int32_t n_ir_buffer_length, uint32_t *pun_red_buffer, int32_t *pn_spo2, int8_t *pch_spo2_valid, int32_t *pn_heart_rate, int8_t *pch_hr_valid);
uint16_t cyclePosition;
uint16_t sixRAM;
uint16_t nineRAM;
uint16_t lowerRAM = 0;
uint16_t upperRAM = 0;
double VRta = 0;
double AMB = 0;
double sensorTemp = 0;
float S = 0;
double VRto = 0;
double Sto = 0;
double TAdut = 0;
double ambientTempK = 0;
double objectTempCalc = 0;
double objectTemp = 0;
#define SLEEP_TIME_MS 2000
/*
* Get button configuration from the devicetree sw0 alias.
*
* At least a GPIO device and pin number must be provided. The 'flags'
* cell is optional.
*/
#define SW0_NODE DT_ALIAS(sw0)
#if DT_NODE_HAS_STATUS(SW0_NODE, okay)
#define SW0_GPIO_LABEL DT_GPIO_LABEL(SW0_NODE, gpios)
#define SW0_GPIO_PIN DT_GPIO_PIN(SW0_NODE, gpios)
#define SW0_GPIO_FLAGS (GPIO_INPUT | DT_GPIO_FLAGS(SW0_NODE, gpios))
#else
#error "Unsupported board: sw0 devicetree alias is not defined"
#define SW0_GPIO_LABEL ""
#define SW0_GPIO_PIN 0
#define SW0_GPIO_FLAGS 0
#endif
#define SW3_NODE DT_ALIAS(sw3)
#define PORT3 DT_GPIO_LABEL(SW3_NODE, gpios)
#if DT_NODE_HAS_STATUS(SW3_NODE, okay)
#define SW3_GPIO_LABEL DT_GPIO_LABEL(SW3_NODE, gpios)
#define SW3_GPIO_PIN DT_GPIO_PIN(SW3_NODE, gpios)
#define SW3_GPIO_FLAGS (GPIO_INPUT | DT_GPIO_FLAGS(SW3_NODE, gpios))
#else
#error "Unsupported board: sw0 devicetree alias is not defined"
#define SW3_GPIO_LABEL ""
#define SW3_GPIO_PIN 0
#define SW3_GPIO_FLAGS 0
#endif
/* LED helpers, which use the led0 devicetree alias if it's available. */
static const struct device *initialize_led(void);
static void match_led_to_button(const struct device *button,
const struct device *led);
static struct gpio_callback button_cb_data;
static int init_I2C(const struct device *i2c_dev)
{
uint32_t dev_config = I2C_SPEED_SET(I2C_SPEED_STANDARD) | I2C_MODE_MASTER;
int ret;
ret = i2c_configure(i2c_dev, dev_config);
if(ret) printk("I2C: Error (%d)\n", ret);
else printk("I2C: Ok (%d)\n", ret);
return ret;
}
static int write_bytes(const struct device *i2c_dev, uint8_t *buf, uint32_t num_bytes, uint16_t addrDevice)
{
struct i2c_msg msg;
msg.buf = buf;
msg.len = num_bytes;
msg.flags = I2C_MSG_WRITE | I2C_MSG_STOP;
return i2c_transfer(i2c_dev, &msg, 1, addrDevice);
}
static int read_bytes(const struct device *i2c_dev, uint16_t addr, uint8_t *data, uint32_t num_bytes)
{
uint8_t wr_addr[2];
struct i2c_msg msgs[2];
wr_addr[0] = (addr >> 8) & 0xFF;
wr_addr[1] = addr & 0xFF;
/* Setup I2C messages */
/* Send the address to read from */
msgs[0].buf = wr_addr;
msgs[0].len = 2U;
msgs[0].flags = I2C_MSG_WRITE;
/* Read from device. STOP after this. */
msgs[1].buf = data;
msgs[1].len = num_bytes;
msgs[1].flags = I2C_MSG_READ | I2C_MSG_STOP;
return i2c_transfer(i2c_dev, &msgs[0], 2, MLX90632_ADDRESS);
}
static int check_MLX90632(const struct device *i2c_dev) // Read Product Code
{
uint8_t ProductCode[2] = {0x0, 0x0};
int ret;
ret = read_bytes(i2c_dev, MLX90632_PRODUCT_CODE, ProductCode, 2);
if(ret) printk("MLX90632: Error - not present!\n");
else
{
if ((ProductCode[0] == 0x00) && (ProductCode[1] == 0x21)) printk("MLX90632: present! PRODUCT CODE: (%d) (%d)\n", ProductCode[0], ProductCode[1]); // MSByte ProductCode[0] - LSByte: ProductCode[1]
else
{
printk("MLX90632: Error! PRODUCT CODE: (%d) (%d)\n", ProductCode[0], ProductCode[1]);
ret = -99;
}
}
return ret;
}
static int init_MLX90632(const struct device *i2c_dev) // This function resets the MAX30102
{
int32_t tempValue32;
int16_t tempValue16;
uint16_t lowerByte;
uint16_t upperByte;
uint8_t buf4[4] = {0x0, 0x0, 0x0, 0x0};
int ret;
uint8_t mode = 0x03;
uint8_t buf[2] = {0x0, 0x0};
uint16_t reg = 0;
ret = read_bytes(i2c_dev, MLX90632_REG_CONTROL, buf, 2);
reg = ((buf[0] << 8) | buf[1]);
reg &= ~(0x0003 << 1);
reg &= ~(0x06);
reg |= (mode << 1);
buf4[0] = 0x30;
buf4[1] = 0x01;
buf4[2] = (reg >> 8) & 0xFF;
buf4[3] = reg & 0xFF;
ret = write_bytes(i2c_dev, buf4, 4, MLX90632_ADDRESS);
// MLX90632_EE_P_R
ret = read_bytes(i2c_dev, MLX90632_EE_P_R, buf4, 4);
lowerByte = ((buf4[0] << 8) | buf4[1]);
upperByte = ((buf4[2] << 8) | buf4[3]);
tempValue32 = upperByte << 16 | lowerByte;
MLX90632_P_R = (double)tempValue32 * pow(2, -8);
// MLX90632_EE_P_G
ret = read_bytes(i2c_dev, MLX90632_EE_P_G, buf4, 4);
lowerByte = ((buf4[0] << 8) | buf4[1]);
upperByte = ((buf4[2] << 8) | buf4[3]);
tempValue32 = upperByte << 16 | lowerByte;
MLX90632_P_G = (double)tempValue32 * pow(2, -20);
// MLX90632_EE_P_T
ret = read_bytes(i2c_dev, MLX90632_EE_P_T, buf4, 4);
lowerByte = ((buf4[0] << 8) | buf4[1]);
upperByte = ((buf4[2] << 8) | buf4[3]);
tempValue32 = upperByte << 16 | lowerByte;
MLX90632_P_T = (double)tempValue32 * pow(2, -44);
// MLX90632_EE_P_O
ret = read_bytes(i2c_dev, MLX90632_EE_P_O, buf4, 4);
lowerByte = ((buf4[0] << 8) | buf4[1]);
upperByte = ((buf4[2] << 8) | buf4[3]);
tempValue32 = upperByte << 16 | lowerByte;
MLX90632_P_O = (double)tempValue32 * pow(2, -8);
// MLX90632_EE_Ea
ret = read_bytes(i2c_dev, MLX90632_EE_Ea, buf4, 4);
lowerByte = ((buf4[0] << 8) | buf4[1]);
upperByte = ((buf4[2] << 8) | buf4[3]);
tempValue32 = upperByte << 16 | lowerByte;
MLX90632_Ea = (double)tempValue32 * pow(2, -16);
// MLX90632_EE_Bb
ret = read_bytes(i2c_dev, MLX90632_EE_Bb, buf4, 4);
lowerByte = ((buf4[0] << 8) | buf4[1]);
upperByte = ((buf4[2] << 8) | buf4[3]);
tempValue32 = upperByte << 16 | lowerByte;
MLX90632_Eb = (double)tempValue32 * pow(2, -8);
// MLX90632_EE_Fa
ret = read_bytes(i2c_dev, MLX90632_EE_Fa, buf4, 4);
lowerByte = ((buf4[0] << 8) | buf4[1]);
upperByte = ((buf4[2] << 8) | buf4[3]);
tempValue32 = upperByte << 16 | lowerByte;
MLX90632_Fa = (double)tempValue32 * pow(2, -46);
// MLX90632_EE_Fb
ret = read_bytes(i2c_dev, MLX90632_EE_Fb, buf4, 4);
lowerByte = ((buf4[0] << 8) | buf4[1]);
upperByte = ((buf4[2] << 8) | buf4[3]);
tempValue32 = upperByte << 16 | lowerByte;
MLX90632_Fb = (double)tempValue32 * pow(2, -36);
// MLX90632_EE_Ga
ret = read_bytes(i2c_dev, MLX90632_EE_Ga, buf4, 4);
lowerByte = ((buf4[0] << 8) | buf4[1]);
upperByte = ((buf4[2] << 8) | buf4[3]);
tempValue32 = upperByte << 16 | lowerByte;
MLX90632_Ga = (double)tempValue32 * pow(2, -36);
// MLX90632_EE_Gb
ret = read_bytes(i2c_dev, MLX90632_EE_Gb, buf4, 2);
tempValue16 = (((uint16_t)buf4[0] << 8) | buf4[1]);
MLX90632_Gb = (double)tempValue16 * pow(2, -10);
// MLX90632_EE_Ka
ret = read_bytes(i2c_dev, MLX90632_EE_Ka, buf4, 2);
tempValue16 = (((uint16_t)buf4[0] << 8) | buf4[1]);
MLX90632_Ka = (double)tempValue16 * pow(2, -10);
// MLX90632_EE_Ha
ret = read_bytes(i2c_dev, MLX90632_EE_Ha, buf4, 2);
tempValue16 = (((uint16_t)buf4[0] << 8) | buf4[1]);
MLX90632_Ha = (double)tempValue16 * pow(2, -14);
// MLX90632_EE_Hb
ret = read_bytes(i2c_dev, MLX90632_EE_Hb, buf4, 2);
tempValue16 = (((uint16_t)buf4[0] << 8) | buf4[1]);
MLX90632_Hb = (double)tempValue16 * pow(2, -14);
return 0;
}
bool dataAvailable_MLX90632(const struct device *i2c_dev) //Returns true if new data is available
{
int ret;
uint8_t buf[2] = {0x00, 0x00};
uint16_t deviceStatus;
ret = read_bytes(i2c_dev, MLX90632_REG_STATUS, buf, 2);
deviceStatus = ((buf[0] << 8) | buf[1]);
if (deviceStatus & 0xFFFF) return(true);
return (false);
}
double getObjectTemp(const struct device *i2c_dev)
{
uint16_t mode;
uint8_t buf[2] = {0x0, 0x0};
uint8_t buf4[4] = {0x0, 0x0, 0x0, 0x0};
uint16_t reg = 0;
int ret;
ret = read_bytes(i2c_dev, MLX90632_REG_CONTROL, buf, 2);
mode = ((buf[0] << 8) | buf[1]);
mode = (mode >> 1) & 0x0003;
if(mode != MLX90632_MODE_CONTINUOUS)
{
printk("Errore - Sets the Start of Conversion (SOC) bit\n");
ret = read_bytes(i2c_dev, MLX90632_REG_CONTROL, buf, 2);
if(ret) printk("MLX90632_REG_CONTROL: R Error!\n");
else printk("MLX90632_REG_CONTROL: R (%d) (%d)\n", buf[0], buf[1]);
reg = ((buf[0] << 8) | buf[1]);
reg |= (1 << 3); //Set the bit
buf4[0] = 0x30;
buf4[1] = 0x01;
buf4[2] = (reg >> 8) & 0xFF;
buf4[3] = reg & 0xFF;
ret = write_bytes(i2c_dev, buf4, 4, MLX90632_ADDRESS);
if(ret) printk("MLX90632_REG_CONTROL: W Error! %d\n",ret);
else printk("MLX90632_REG_CONTROL: W (%d) (%d)\n", buf[0], buf[1]);
}
ret = read_bytes(i2c_dev, MLX90632_REG_STATUS, buf, 2);
reg = ((buf[0] << 8) | buf[1]);
reg &= 0xFFFE;
buf4[0] = 0x3F;
buf4[1] = 0xFF;
buf4[2] = (reg >> 8) & 0xFF;
buf4[3] = reg & 0xFF;
ret = write_bytes(i2c_dev, buf4, 4, MLX90632_ADDRESS);
//Check when new_data = 1
uint16_t counter = 0;
while (dataAvailable_MLX90632(i2c_dev) == false)
{
k_msleep(1);
counter++;
if (counter == 750)
{
printk("Data available timeout\n");
return 0;
}
}
//Get MLX90632_RAM_6 and RAM_9
ret = read_bytes(i2c_dev, MLX90632_RAM_6, buf, 2);
sixRAM = ((buf[0] << 8) | buf[1]);
ret = read_bytes(i2c_dev, MLX90632_RAM_9, buf, 2);
nineRAM = ((buf[0] << 8) | buf[1]);
VRta = nineRAM + MLX90632_Gb * (sixRAM / 12.0);
AMB = (sixRAM / 12.0) / VRta * pow(2, 19);
sensorTemp = MLX90632_P_O + (AMB - MLX90632_P_R) / MLX90632_P_G + MLX90632_P_T * pow((AMB - MLX90632_P_R), 2);
//Read cycle_pos to get measurement pointer
ret = read_bytes(i2c_dev, MLX90632_REG_STATUS, buf, 2);
reg = ((buf[0] << 8) | buf[1]);
cyclePosition = (reg >> 2) & 0x1F;
//If cycle_pos = 1
//Calculate TA and TO based on RAM_4, RAM_5, RAM_6, RAM_9
if (cyclePosition == 1)
{
ret = read_bytes(i2c_dev, MLX90632_RAM_4, buf, 2);
lowerRAM = ((buf[0] << 8) | buf[1]);
ret = read_bytes(i2c_dev, MLX90632_RAM_5, buf, 2);
upperRAM = ((buf[0] << 8) | buf[1]);
}
//If cycle_pos = 2
//Calculate TA and TO based on RAM_7, RAM_8, RAM_6, RAM_9
else if (cyclePosition == 2)
{
ret = read_bytes(i2c_dev, MLX90632_RAM_7, buf, 2);
lowerRAM = ((buf[0] << 8) | buf[1]);
ret = read_bytes(i2c_dev, MLX90632_RAM_8, buf, 2);
upperRAM = ((buf[0] << 8) | buf[1]);
}
else
{
ret = read_bytes(i2c_dev, MLX90632_RAM_4, buf, 2);
lowerRAM = ((buf[0] << 8) | buf[1]);
ret = read_bytes(i2c_dev, MLX90632_RAM_5, buf, 2);
upperRAM = ((buf[0] << 8) | buf[1]);
}
//Object temp requires 3 iterations
for (uint8_t i = 0 ; i < 3 ; i++)
{
VRta = nineRAM + MLX90632_Gb * (sixRAM / 12.0);
AMB = (sixRAM / 12.0) / VRta * pow(2, 19);
sensorTemp = MLX90632_P_O + (AMB - MLX90632_P_R) / MLX90632_P_G + MLX90632_P_T * pow((AMB - MLX90632_P_R), 2);
S = (float)(lowerRAM + upperRAM) / 2.0;
VRto = nineRAM + MLX90632_Ka * (sixRAM / 12.0);
Sto = (S / 12.0) / (VRto * (double)pow(2, 19));
TAdut = ((AMB - MLX90632_Eb) / MLX90632_Ea) + 25.0;
ambientTempK = TAdut + 273.15;
objectTempCalc = Sto / (1 * MLX90632_Fa * MLX90632_Ha * (1 + MLX90632_Ga * (TOdut - TO0) + MLX90632_Fb * (TAdut - TA0)));
objectTemp = objectTempCalc + pow(ambientTempK, 4);
objectTemp = pow(objectTemp, 0.25); //Take 4th root
objectTemp = objectTemp - 273.15 - MLX90632_Hb;
TO0 = objectTemp;
}
return TO0;
}
// ---------- MAX30102 -----------
static int check_MAX30102(const struct device *i2c_dev)
{
uint8_t partid[1] = {0x0};
int ret;
ret = i2c_burst_read(i2c_dev, MAX30102_ADDRESS, MAX30102_PARTIDR, partid, 1); // Read part ID
if(ret) printk("MAX30102: Error - not present!\n");
else
{
if (partid[0] == 0x15) printk("MAX30102: present! Part ID: %dd\n", partid[0]);
else
{
printk("MAX30102: Error! Part ID: %dd\n", partid[0]);
ret = -99;
}
}
return ret;
}
static int reset_MAX30102(const struct device *i2c_dev) // This function resets the MAX30102
{
uint8_t sendbuf[1] = {MAX30102_RESET};
int ret;
ret = i2c_reg_write_byte(i2c_dev, MAX30102_ADDRESS, MAX30102_MODE_CONFIG, sendbuf[0]);
if(ret) printk("MAX30102: Reset Error!\n");
else printk("MAX30102: Reset ok!\n");
return ret;
}
static int clearInt_MAX30102(const struct device *i2c_dev) // Reads/clears the interrupt status register
{
uint8_t IntStatus1[1] = {0x0};
int ret;
ret = i2c_burst_read(i2c_dev, MAX30102_ADDRESS, MAX30102_INT_STATUS_1, IntStatus1, 1);
if(ret) printk("MAX30102: Interrupt Status 1: Error!\n");
else printk("MAX30102: Interrupt Status 1: Ok (%d)\n", IntStatus1[0]);
return ret;
}
static int init_MAX30102(const struct device *i2c_dev) // This function resets the MAX30102
{
uint8_t buf[1] = {0x00};
int ret, ret1 = 0;
buf[0] = 0xC0;
ret = i2c_reg_write_byte(i2c_dev, MAX30102_ADDRESS, MAX30102_INT_ENABLE_1, buf[0]); // INTR setting
if(ret)
{
ret1 = -99;
printk("MAX30102: INTR setting: Error!\n");
}
else printk("MAX30102: INTR setting: ok!\n");
buf[0] = 0x00;
ret = i2c_reg_write_byte(i2c_dev, MAX30102_ADDRESS, MAX30102_INT_ENABLE_2, buf[0]); // INTR setting
if(ret)
{
ret1 = -99;
printk("MAX30102: INTR setting: Error!\n");
}
else printk("MAX30102: INTR setting: ok!\n");
buf[0] = 0x00;
ret = i2c_reg_write_byte(i2c_dev, MAX30102_ADDRESS, MAX30102_FIFO_WRITE_PTR, buf[0]); // FIFO_WR_PTR[4:0]
if(ret)
{
ret1 = -99;
printk("MAX30102: FIFO_WR_PTR: Error!\n");
}
else printk("MAX30102: FIFO_WR_PTR: ok!\n");
buf[0] = 0x00;
ret = i2c_reg_write_byte(i2c_dev, MAX30102_ADDRESS, MAX30102_OVERFLOW_COUNT, buf[0]); // OVF_COUNTER[4:0]
if(ret)
{
ret1 = -99;
printk("MAX30102: FIFO_WR_PTR: Error!\n");
}
else printk("MAX30102: FIFO_WR_PTR: ok!\n");
buf[0] = 0x00;
ret = i2c_reg_write_byte(i2c_dev, MAX30102_ADDRESS, MAX30102_FIFO_READ_PTR, buf[0]); // FIFO_RD_PTR[4:0]
if(ret)
{
ret1 = -99;
printk("MAX30102: FIFO_RD_PTR: Error!\n");
}
else printk("MAX30102: FIFO_RD_PTR: ok!\n");
buf[0] = 0x0F;
ret = i2c_reg_write_byte(i2c_dev, MAX30102_ADDRESS, MAX30102_FIFO_CONFIG, buf[0]); // sample avg = 1, fifo rollover=false, fifo almost full = 17
if(ret)
{
ret1 = -99;
printk("MAX30102: FIFO CONFIG: Error!\n");
}
else printk("MAX30102: FIFO CONFIG: ok!\n");
buf[0] = 0x03;
ret = i2c_reg_write_byte(i2c_dev, MAX30102_ADDRESS, MAX30102_MODE_CONFIG, buf[0]);// 0x02 for Red only, 0x03 for SpO2 mode 0x07 multimode LED
if(ret)
{
ret1 = -99;
printk("MAX30102: MODE CONFIG: Error!\n");
}
else printk("MAX30102: MODE CONFIG: ok!\n");
buf[0] = 0x27;
ret = i2c_reg_write_byte(i2c_dev, MAX30102_ADDRESS, MAX30102_SPO2_CONFIG, buf[0]);// SPO2_ADC range = 4096nA, SPO2 sample rate (100 Hz), LED pulseWidth (400uS)
if(ret)
{
ret1 = -99;
printk("MAX30102: SPO2 ADC: Error!\n");
}
else printk("MAX30102: SPO2 ADC: ok!\n");
buf[0] = 0x24;
ret = i2c_reg_write_byte(i2c_dev, MAX30102_ADDRESS, MAX30102_LED1_PULSE_AMP, buf[0]); // Choose value for ~ 7mA for LED1
if(ret)
{
ret1 = -99;
printk("MAX30102: LED1: Error!\n");
}
else printk("MAX30102: LED1: ok!\n");
buf[0] = 0x24;
ret = i2c_reg_write_byte(i2c_dev, MAX30102_ADDRESS, MAX30102_LED2_PULSE_AMP, buf[0]); // Choose value for ~ 7mA for LED2
if(ret)
{
ret1 = -99;
printk("MAX30102: LED2: Error!\n");
}
else printk("MAX30102: LED2: ok!\n");
return ret1;
}
bool maxim_max30102_read_reg(const struct device *i2c_dev, uint8_t uch_addr, uint8_t *puch_data)
{
int ret;
uint8_t buf[1] = {0x00};
ret = i2c_burst_read(i2c_dev, MAX30102_ADDRESS, uch_addr, buf, 1);
if(ret)
{
printk("MAX30102: Error!\n");
return false;
}
else
{
*puch_data = buf[0];
return true;
}
}
bool maxim_max30102_read_fifo(const struct device *i2c_dev, uint32_t *pun_red_led, uint32_t *pun_ir_led)
{
int ret;
uint32_t un_temp;
unsigned char uch_temp;
*pun_red_led=0;
*pun_ir_led=0;
char ach_i2c_data[6];
//read and clear status register
maxim_max30102_read_reg(i2c_dev, MAX30102_INT_STATUS_1, &uch_temp);
maxim_max30102_read_reg(i2c_dev, MAX30102_INT_STATUS_2, &uch_temp);
ret = i2c_burst_read(i2c_dev, MAX30102_ADDRESS, MAX30102_REG_FIFO_DATA, ach_i2c_data, 6);
if(ret) printk("MAX30102: maxim_max30102_read_fifo Error!\n");
un_temp=(unsigned char) ach_i2c_data[0];
un_temp<<=16;
*pun_red_led+=un_temp;
un_temp=(unsigned char) ach_i2c_data[1];
un_temp<<=8;
*pun_red_led+=un_temp;
un_temp=(unsigned char) ach_i2c_data[2];
*pun_red_led+=un_temp;
un_temp=(unsigned char) ach_i2c_data[3];
un_temp<<=16;
*pun_ir_led+=un_temp;
un_temp=(unsigned char) ach_i2c_data[4];
un_temp<<=8;
*pun_ir_led+=un_temp;
un_temp=(unsigned char) ach_i2c_data[5];
*pun_ir_led+=un_temp;
*pun_red_led&=0x03FFFF;
*pun_ir_led&=0x03FFFF;
return true;
}
void button_pressed(const struct device *dev, struct gpio_callback *cb,
uint32_t pins)
{
printk("Button pressed at %" PRIu32 "\n", k_cycle_get_32());
}
void main(void)
{
const struct device *button;
const struct device *led;
int ret;
uint8_t data[16];
// Button and LED Setup
button = device_get_binding(SW0_GPIO_LABEL);
if (button == NULL) {
printk("Error: didn't find %s device\n", SW0_GPIO_LABEL);
return;
}
ret = gpio_pin_configure(button, SW0_GPIO_PIN, SW0_GPIO_FLAGS);
if (ret != 0) {
printk("Error %d: failed to configure %s pin %d\n",
ret, SW0_GPIO_LABEL, SW0_GPIO_PIN);
return;
}
ret = gpio_pin_interrupt_configure(button,
SW0_GPIO_PIN,
GPIO_INT_EDGE_TO_ACTIVE);
if (ret != 0) {
printk("Error %d: failed to configure interrupt on %s pin %d\n",
ret, SW0_GPIO_LABEL, SW0_GPIO_PIN);
return;
}
gpio_init_callback(&button_cb_data, button_pressed, BIT(SW0_GPIO_PIN));
gpio_add_callback(button, &button_cb_data);
printk("Set up button at %s pin %d\n", SW0_GPIO_LABEL, SW0_GPIO_PIN);
led = initialize_led();
printk("Press the button\n");
// -------------------------------------
uint32_t un_min, un_max, un_prev_data; //variables to calculate the on-board LED brightness that reflects the heartbeats
int i;
int32_t n_brightness;
float f_temp;
const struct device *IntMAX30102;
IntMAX30102 = device_get_binding(SW3_GPIO_LABEL);
if (IntMAX30102 == NULL)
{
printk("IntMAX30102 Error: didn't find %s device\n", SW3_GPIO_LABEL);
return;
}
ret = gpio_pin_configure(IntMAX30102, SW3_GPIO_PIN, SW3_GPIO_FLAGS);
if (ret != 0)
{
printk("Error %d: failed to configure %s pin %d\n", ret, SW3_GPIO_LABEL, SW3_GPIO_PIN);
return;
}
k_msleep(1000);
const struct device *i2c_dev;
i2c_dev = device_get_binding("I2C_1");
if (i2c_dev == NULL)
{
printk("Error: didn't find I2C_1 device\n");
return;
}
init_I2C(i2c_dev); // I2C initialize
// *************** MLX90632 *******************
check_MLX90632(i2c_dev); // Read Product Code - MLX90632
init_MLX90632(i2c_dev); // Initialize the MLX90632
objectT = getObjectTemp(i2c_dev); // get Object Temp
// *************** MAX30102 *******************
// Initialize MAX30102 Settings
check_MAX30102(i2c_dev); // Read part ID
reset_MAX30102(i2c_dev); // This function resets the MAX30102
clearInt_MAX30102(i2c_dev); // Reads/clears the interrupt status register
init_MAX30102(i2c_dev); // Initialize the MAX30102
n_brightness=0;
un_min=0x3FFFF;
un_max=0;
n_ir_buffer_length=500;
// MAX30102 - read the first 500 samples, and determine the signal range
for(i=0; i<n_ir_buffer_length; i++)
{
while(gpio_pin_get(device_get_binding(PORT3), SW3_GPIO_PIN) == 0); // wait until the interrupt pin asserts
maxim_max30102_read_fifo(i2c_dev, (aun_red_buffer+i), (aun_ir_buffer+i)); // read from MAX30102 FIFO
if(un_min>aun_red_buffer[i]) un_min=aun_red_buffer[i]; //update signal min
if(un_max<aun_red_buffer[i]) un_max=aun_red_buffer[i]; //update signal max
printk("red=%i, ir=%i\n", aun_red_buffer[i], aun_ir_buffer[i]);
}
un_prev_data=aun_red_buffer[i];
//calculate heart rate and SpO2 after first 500 samples (first 5 seconds of samples)
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);
//Continuously taking samples from MAX30102. Heart rate and SpO2 are calculated every 1 second
while(1)
{
i=0;
un_min=0x3FFFF;
un_max=0;
//dumping the first 100 sets of samples in the memory and shift the last 400 sets of samples to the top
for(i=100;i<500;i++)
{
aun_red_buffer[i-100]=aun_red_buffer[i];
aun_ir_buffer[i-100]=aun_ir_buffer[i];
//update the signal min and max
if(un_min>aun_red_buffer[i])
un_min=aun_red_buffer[i];
if(un_max<aun_red_buffer[i])
un_max=aun_red_buffer[i];
}
//take 100 sets of samples before calculating the heart rate.
for(i=400;i<500;i++)
{
un_prev_data=aun_red_buffer[i-1];
while(gpio_pin_get(device_get_binding(PORT3), SW3_GPIO_PIN) == 0); // wait until the interrupt pin asserts
maxim_max30102_read_fifo(i2c_dev, (aun_red_buffer+i), (aun_ir_buffer+i));
if(aun_red_buffer[i]>un_prev_data)
{
f_temp=aun_red_buffer[i]-un_prev_data;
f_temp/=(un_max-un_min);
f_temp*=MAX_BRIGHTNESS;
n_brightness-=(int)f_temp;
if(n_brightness<0)
n_brightness=0;
}
else
{
f_temp=un_prev_data-aun_red_buffer[i];
f_temp/=(un_max-un_min);
f_temp*=MAX_BRIGHTNESS;
n_brightness+=(int)f_temp;
if(n_brightness>MAX_BRIGHTNESS)
n_brightness=MAX_BRIGHTNESS;
}
//send samples and calculation result to terminal program through UART
printk("red=%i, ir=%i, HR=%i, HRvalid=%i, SpO2=%i, SPO2Valid=%i \n", aun_red_buffer[i], aun_ir_buffer[i], n_heart_rate, ch_hr_valid, n_sp02, ch_spo2_valid);
k_msleep(10);
}
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);
objectT = getObjectTemp(i2c_dev); // get Object Temp
uint16_t printobjectT = (int)(objectT*100);
printk("Temp_ear: %d (value/100 = [degrees Celsius])\n", printobjectT);
match_led_to_button(button, led);
}
}
/*
* The led0 devicetree alias is optional. If present, we'll use it
* to turn on the LED whenever the button is pressed.
*/
#define LED0_NODE DT_ALIAS(led0)
#if DT_NODE_HAS_STATUS(LED0_NODE, okay) && DT_NODE_HAS_PROP(LED0_NODE, gpios)
#define LED0_GPIO_LABEL DT_GPIO_LABEL(LED0_NODE, gpios)
#define LED0_GPIO_PIN DT_GPIO_PIN(LED0_NODE, gpios)
#define LED0_GPIO_FLAGS (GPIO_OUTPUT | DT_GPIO_FLAGS(LED0_NODE, gpios))
#endif
#ifdef LED0_GPIO_LABEL
static const struct device *initialize_led(void)
{
const struct device *led;
int ret;
led = device_get_binding(LED0_GPIO_LABEL);
if (led == NULL) {
printk("Didn't find LED device %s\n", LED0_GPIO_LABEL);
return NULL;
}
ret = gpio_pin_configure(led, LED0_GPIO_PIN, LED0_GPIO_FLAGS);
if (ret != 0) {
printk("Error %d: failed to configure LED device %s pin %d\n",
ret, LED0_GPIO_LABEL, LED0_GPIO_PIN);
return NULL;
}
printk("Set up LED at %s pin %d\n", LED0_GPIO_LABEL, LED0_GPIO_PIN);
return led;
}
static void match_led_to_button(const struct device *button,
const struct device *led)
{
bool val;
val = gpio_pin_get(button, SW0_GPIO_PIN);
gpio_pin_set(led, LED0_GPIO_PIN, val);
if(val == 1) k_msleep(SLEEP_TIME_MS);
}
#else /* !defined(LED0_GPIO_LABEL) */
static const struct device *initialize_led(void)
{
printk("No LED device was defined\n");
return NULL;
}
static void match_led_to_button(const struct device *button,
const struct device *led)
{
return;
}
#endif /* LED0_GPIO_LABEL */
// ********** Algorithm - maxim_heart_rate_and_oxygen_saturation **********************
void maxim_sort_ascend(int32_t *pn_x,int32_t n_size)
/**
* \brief Sort array
* \par Details
* Sort array in ascending order (insertion sort algorithm)
*
* \retval None
*/
{
int32_t i, j, n_temp;
for (i = 1; i < n_size; i++) {
n_temp = pn_x[i];
for (j = i; j > 0 && n_temp < pn_x[j-1]; j--)
pn_x[j] = pn_x[j-1];
pn_x[j] = n_temp;
}
}
void maxim_sort_indices_descend(int32_t *pn_x, int32_t *pn_indx, int32_t n_size)
/**
* \brief Sort indices
* \par Details
* Sort indices according to descending order (insertion sort algorithm)
*
* \retval None
*/
{
int32_t i, j, n_temp;
for (i = 1; i < n_size; i++) {
n_temp = pn_indx[i];
for (j = i; j > 0 && pn_x[n_temp] > pn_x[pn_indx[j-1]]; j--)
pn_indx[j] = pn_indx[j-1];
pn_indx[j] = n_temp;
}
}
void maxim_remove_close_peaks(int32_t *pn_locs, int32_t *pn_npks, int32_t *pn_x, int32_t n_min_distance)
/**
* \brief Remove peaks
* \par Details
* Remove peaks separated by less than MIN_DISTANCE
*
* \retval None
*/
{
int32_t i, j, n_old_npks, n_dist;
/* Order peaks from large to small */
maxim_sort_indices_descend( pn_x, pn_locs, *pn_npks );
for ( i = -1; i < *pn_npks; i++ ){
n_old_npks = *pn_npks;
*pn_npks = i+1;
for ( j = i+1; j < n_old_npks; j++ ){
n_dist = pn_locs[j] - ( i == -1 ? -1 : pn_locs[i] ); // lag-zero peak of autocorr is at index -1
if ( n_dist > n_min_distance || n_dist < -n_min_distance )
pn_locs[(*pn_npks)++] = pn_locs[j];
}
}
// Resort indices longo ascending order
maxim_sort_ascend( pn_locs, *pn_npks );
}
void maxim_peaks_above_min_height(int32_t *pn_locs, int32_t *pn_npks, int32_t *pn_x, int32_t n_size, int32_t n_min_height)
/**
* \brief Find peaks above n_min_height
* \par Details
* Find all peaks above MIN_HEIGHT
*
* \retval None
*/
{
int32_t i = 1, n_width;
*pn_npks = 0;
while (i < n_size-1){
if (pn_x[i] > n_min_height && pn_x[i] > pn_x[i-1]){ // find left edge of potential peaks
n_width = 1;
while (i+n_width < n_size && pn_x[i] == pn_x[i+n_width]) // find flat peaks
n_width++;
...
This file has been truncated, please download it to see its full contents.
Comments