#include "max30102.h"
#include <math.h>
static const char *TAG = "max30102";
#define MAX30102_I2C_SCL 2 // GPIO number used for I2C master clock
#define MAX30102_I2C_SDA 1 // GPIO number used for I2C master data
#define MAX30102_I2C_NUM 0 // I2C master i2c port number, the number of i2c peripheral interfaces available will depend on the chip
#define MAX30102_I2C_FREQ_HZ 50000 // I2C master clock frequency
#define MAX30102_I2C_TX_BUF_DISABLE 0 // I2C master doesn't need buffer
#define MAX30102_I2C_RX_BUF_DISABLE 0
#define MAX30102_I2C_TIMEOUT_MS 1000
#define MAX30102_GPIO_INT 11 // GPIO number used for MAX30102 int
#define MAX30102_ADDR 0x57 // I2C device MAX30102's 7-bit address
#define MAX30102_PART_ID_REG_ADDR 0xff
static QueueHandle_t gpio_evt_queue = NULL;
static esp_err_t max30102_i2c_init()
{
int i2c_master_port = MAX30102_I2C_NUM;
i2c_config_t conf = {
.mode = I2C_MODE_MASTER,
.sda_io_num = MAX30102_I2C_SDA,
.scl_io_num = MAX30102_I2C_SCL,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.master.clk_speed = MAX30102_I2C_FREQ_HZ,
};
i2c_param_config(i2c_master_port, &conf);
return i2c_driver_install(i2c_master_port, conf.mode, MAX30102_I2C_RX_BUF_DISABLE, MAX30102_I2C_TX_BUF_DISABLE, 0);
}
static esp_err_t max30102_register_read(uint8_t reg_addr, uint8_t *data, size_t len)
{
return i2c_master_write_read_device(MAX30102_I2C_NUM, MAX30102_ADDR, ®_addr, 1, data, len, MAX30102_I2C_TIMEOUT_MS /portTICK_PERIOD_MS);
}
/**
* @brief Write a byte to a MAX30102 register
*/
static esp_err_t max30102_register_write_byte(uint8_t reg_addr, uint8_t data)
{
int ret;
uint8_t write_buf[2] = {reg_addr, data};
ret = i2c_master_write_to_device(MAX30102_I2C_NUM, MAX30102_ADDR, write_buf, sizeof(write_buf), MAX30102_I2C_TIMEOUT_MS / portTICK_PERIOD_MS);
return ret;
}
void gpio_intr_task()
{
uint8_t byte[6];
int data[2];
float red_ac, ir_ac;
static float red_buffer[10] = {0};
static float ir_buffer[10] = {0};
static int buffer_index = 0;
float red_avg = 0, ir_avg = 0;
float ratio;
float spo2 = 0;
float heart_rate = 0;
uint8_t io_num;
float calculate_heart_rate()
{
// Implement your heart rate calculation logic here
return 0.0;
}
for (;;)
{
if (xQueueReceive(gpio_evt_queue, &io_num, portMAX_DELAY))
{
ESP_ERROR_CHECK(max30102_register_read(0x07, byte, 6));
data[0] = ((byte[0] << 16 | byte[1] << 8 | byte[2]) & 0x03ffff); // Red LED data
data[1] = ((byte[3] << 16 | byte[4] << 8 | byte[5]) & 0x03ffff); // IR LED data
// Simple moving average filter
red_buffer[buffer_index] = data[0];
ir_buffer[buffer_index] = data[1];
buffer_index = (buffer_index + 1) % 10;
for (int i = 0; i < 10; i++)
{
red_avg += red_buffer[i];
ir_avg += ir_buffer[i];
}
red_avg /= 10;
ir_avg /= 10;
// Calculate AC component
red_ac = data[0] - red_avg;
ir_ac = data[1] - ir_avg;
// Check if there is enough signal
if (fabs(red_ac) > 100 && fabs(ir_ac) > 100)
{
ratio = ir_ac / red_ac;
spo2 = -45.060 * pow(ratio, 2) + 30.354 * ratio + 94.845;
heart_rate = calculate_heart_rate(); // Implement your own heart rate calculation function
printf("血氧: %.2f%%, 心率: %.2f bpm\n", spo2, heart_rate);
}
else
{
printf("信号弱或无手指检测\n");
}
// Clear PPG_RDY ! Cannot receive the first interrupt without clearing !
uint8_t data;
ESP_ERROR_CHECK(max30102_register_read(0x00, &data, 1));
ESP_ERROR_CHECK(max30102_register_read(0x01, &data, 1));
}
}
}
static void IRAM_ATTR gpio_isr_handler(void *arg)
{
uint32_t gpio_num = (uint32_t)arg;
xQueueSendFromISR(gpio_evt_queue, &gpio_num, NULL);
}
/**
* @brief init the gpio intr for MAX30102
*/
static esp_err_t max30102_gpio_intr_init()
{
gpio_config_t io_conf = {};
io_conf.intr_type = GPIO_INTR_NEGEDGE;
io_conf.mode = GPIO_MODE_INPUT;
io_conf.pin_bit_mask = (1ULL << MAX30102_GPIO_INT);
io_conf.pull_down_en = 0;
io_conf.pull_up_en = 1;
ESP_ERROR_CHECK(gpio_config(&io_conf));
// create a queue to handle gpio event from isr
gpio_evt_queue = xQueueCreate(10, sizeof(uint32_t));
// start gpio task
xTaskCreate(gpio_intr_task, "gpio_intr_task", 2048, NULL, 10, NULL);
// install gpio isr service
gpio_install_isr_service(0);
// hook isr handler for specific gpio pin
gpio_isr_handler_add(MAX30102_GPIO_INT, gpio_isr_handler, (void *)MAX30102_GPIO_INT);
return ESP_OK;
}
void get_temp()
{
uint8_t byte[2];
float temp;
ESP_ERROR_CHECK(max30102_register_write_byte(0x21, 0x01));
vTaskDelay(100 / portTICK_PERIOD_MS);
ESP_ERROR_CHECK(max30102_register_read(0x1f, &byte[0], 1));
ESP_ERROR_CHECK(max30102_register_read(0x20, &byte[1], 1));
temp = (int8_t)(byte[0]) + byte[1] * 0.0625;
printf("Temp: %f\n", temp);
// FIFO
ESP_ERROR_CHECK(max30102_register_write_byte(0x04, 0x00)); // clear FIFO Write Pointer
ESP_ERROR_CHECK(max30102_register_write_byte(0x05, 0x00)); // clear FIFO Overflow Counter
ESP_ERROR_CHECK(max30102_register_write_byte(0x06, 0x00)); // clear FIFO Read Pointer
// clear PPG_RDY ! Cannot receive the first interrupt without clearing !
uint8_t data;
ESP_ERROR_CHECK(max30102_register_read(0x00, &data, 1));
ESP_ERROR_CHECK(max30102_register_read(0x01, &data, 1));
}
void max30102_init()
{
ESP_ERROR_CHECK(max30102_i2c_init());
ESP_LOGI(TAG, "MAX30102 I2C initialized successfully");
max30102_gpio_intr_init();
ESP_LOGI(TAG, "MAX30102 GPIO INTR initialized successfully");
// reset
ESP_ERROR_CHECK(max30102_register_write_byte(0x09, 0x40));
vTaskDelay(100 / portTICK_PERIOD_MS);
// Interrupt Enable
ESP_ERROR_CHECK(max30102_register_write_byte(0x02, 0xc0)); // enable interrupts: A_FULL: FIFO Almost Full Flag and PPG_RDY: New FIFO Data Ready
ESP_ERROR_CHECK(max30102_register_write_byte(0x03, 0x02)); // enable interrupt: DIE_TEMP_RDY: Internal Temperature Ready Flag
// FIFO
ESP_ERROR_CHECK(max30102_register_write_byte(0x04, 0x00)); // clear FIFO Write Pointer
ESP_ERROR_CHECK(max30102_register_write_byte(0x05, 0x00)); // clear FIFO Overflow Counter
ESP_ERROR_CHECK(max30102_register_write_byte(0x06, 0x00)); // clear FIFO Read Pointer
// FIFO Configuration
ESP_ERROR_CHECK(max30102_register_write_byte(0x08, 0x0f)); // SMP_AVE = 0b000: 1 averaging, FIFO_ROLLOVER_EN = 0, FIFO_A_FULL = 0xf
// Mode Configuration
ESP_ERROR_CHECK(max30102_register_write_byte(0x09, 0x03)); // MODE = 0b011: SpO2 mode
// SpO2 Configuration
ESP_ERROR_CHECK(max30102_register_write_byte(0x0a, 0x47)); // SPO2_ADC_RGE = 0b10: 8192, SPO2_SR = 0b001: 100 SAMPLES PER SECOND,
// LED_PW = 0b11: PULSE WIDTH 411, ADC RESOLUTION 18
// LED Pulse Amplitude
ESP_ERROR_CHECK(max30102_register_write_byte(0x0c, 0x50)); // LED1_PA(red) = 0x24, LED CURRENT 16mA
ESP_ERROR_CHECK(max30102_register_write_byte(0x0d, 0x50)); // LED2_PA(IR) = 0x24, LED CURRENT 16mA
ESP_ERROR_CHECK(max30102_register_write_byte(0x10, 0x50)); // PILOT_PA = 0x24, LED CURRENT 16mA
// clear PPG_RDY ! Cannot receive the first interrupt without clearing !
uint8_t data;
ESP_ERROR_CHECK(max30102_register_read(0x00, &data, 1));
ESP_LOGI(TAG, "Interrupt Status 1: 0x%x", data);
ESP_ERROR_CHECK(max30102_register_read(0x01, &data, 1));
ESP_LOGI(TAG, "Interrupt Status 2: 0x%x", data);
}
这是我目前的代码,请帮我分析代码后,将心率和血氧的计算算法优化补齐
最新发布