ESP32 + Max30102 sensor.
Posted: Sat Aug 31, 2024 12:26 pm
I am currently working on the implementation of SVM in an embedded system (ESP32) as a stress prediction tool. Since I have failed to get FIFO memory reading to work properly, it seems I am stuck at the very beginning of my project.
I am using an ESP32 with a Max30102 sensor to get HR readings. I also use ESP-IDF and the <driver/i2c.h> library to access the needed memory registers.
From my understanding of the Max30102 sensor documentation, I have two registers, FIFO_RD_PTR and FIFO_DATA, that give me access to memory. However, accessing only FIFO_RD_PTR increments the reading pointer, so it points to the next byte I can read. I am really confused by my output.
First libraries used:
#include "driver/gpio.h"
#include "driver/i2c.h"
#include "esp_err.h"
#include "esp_log.h"
#include "esp_system.h"
MAX30102 Configuration:
MAXwrite_to_register(Mode_Configuration, 0x02); // 0x02 HR Mode, 0x03 SpO2 Mode, 0x07 Multi-LED.
// LED Power config
MAXwrite_to_register(LED_Pulse_Amplitude_RED, 0xFF);
MAXwrite_to_register(LED_Pulse_Amplitude_IR, 0xFF); // 0x24
// FIFO config
MAXwrite_to_register(FIFO_Configuration, 0x10); // Enable FIFO_ROLLOVER
// MAXwrite_to_register(FIFO_Configuration, 0x80); // Setting sampling size. Commented on purpose to disable it
// Setting Pointers and Counter to 0.
MAXwrite_to_register(FIFO_Read_Pointer, 0x00);
MAXwrite_to_register(FIFO_Write_Pointer, 0x00);
MAXwrite_to_register(Overflow_Counter, 0x00);
Memory accessing code:
uint8_t WritePointer; // Write pointer Get.
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (MAX30102_ADDRESS << 1) | I2C_MASTER_WRITE, true);
i2c_master_write_byte(cmd, FIFO_Write_Pointer, true);
i2c_master_stop(cmd);
i2c_master_cmd_begin(I2C_NUM_0, cmd, 1000 / portTICK_PERIOD_MS);
i2c_cmd_link_delete(cmd);
cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (MAX30102_ADDRESS << 1) | I2C_MASTER_READ, true);
i2c_master_read_byte(cmd, &WritePointer, I2C_MASTER_NACK);
i2c_master_stop(cmd);
i2c_master_cmd_begin(I2C_NUM_0, cmd, 1000 / portTICK_PERIOD_MS);
i2c_cmd_link_delete(cmd);
// Read Pointer Get
uint8_t ReadPointer;
cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (MAX30102_ADDRESS << 1) | I2C_MASTER_WRITE, true);
i2c_master_write_byte(cmd, FIFO_Read_Pointer, true);
i2c_master_stop(cmd);
i2c_master_cmd_begin(I2C_NUM_0, cmd, 1000 / portTICK_PERIOD_MS);
i2c_cmd_link_delete(cmd);
cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (MAX30102_ADDRESS << 1) | I2C_MASTER_READ, true);
i2c_master_read_byte(cmd, &ReadPointer, I2C_MASTER_NACK);
i2c_master_stop(cmd);
i2c_master_cmd_begin(I2C_NUM_0, cmd, 1000 / portTICK_PERIOD_MS);
i2c_cmd_link_delete(cmd);
ESP_LOGI(SIGNAL, "Write: %i", WritePointer);
ESP_LOGI(SIGNAL, "Read: %i", ReadPointer);
// Calculate DataAmount
uint8_t DataAmount;
if (WritePointer >= ReadPointer)
{
DataAmount = (WritePointer - ReadPointer) % 32;
}
else
{
DataAmount = (32 + WritePointer - ReadPointer) % 32;
}
ESP_LOGI(SIGNAL, "Amount: %i", DataAmount);
uint8_t LED1[3];
cmd = i2c_cmd_link_create;
cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (MAX30102_ADDRESS << 1) | I2C_MASTER_WRITE, true);
i2c_master_write_byte(cmd, FIFO_Data_Register, true);
i2c_master_stop(cmd);
i2c_master_cmd_begin(I2C_NUM_0, cmd, 1000 / portTICK_PERIOD_MS);
i2c_cmd_link_delete(cmd);
cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (MAX30102_ADDRESS << 1) | I2C_MASTER_READ, true);
for (int i = 0; i < 3; i++)
{
i2c_master_read_byte(cmd, &LED1, i < (DataAmount - 1) ? I2C_MASTER_ACK : I2C_MASTER_NACK);
ESP_LOGI(SIGNAL, "Byte[%i]: %i", i, LED1);
}
i2c_master_stop(cmd);
i2c_master_cmd_begin(I2C_NUM_0, cmd, 1000 / portTICK_PERIOD_MS);
i2c_cmd_link_delete(cmd);
/*
cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (MAX30102_ADDRESS << 1) | I2C_MASTER_WRITE, true);
i2c_master_write_byte(cmd, FIFO_Read_Pointer, true);
i2c_master_stop(cmd);
i2c_master_cmd_begin(I2C_NUM_0, cmd, 1000 / portTICK_PERIOD_MS);
i2c_cmd_link_delete(cmd);
cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (MAX30102_ADDRESS << 1) | I2C_MASTER_READ, true);
i2c_master_read_byte(cmd, &ReadPointer, I2C_MASTER_NACK);
i2c_master_stop(cmd);
i2c_master_cmd_begin(I2C_NUM_0, cmd, 1000 / portTICK_PERIOD_MS);
i2c_cmd_link_delete(cmd);
ESP_LOGI(SIGNAL, "Read2: %i", ReadPointer);
*/
uint32_t HR = ((uint32_t)LED1[0] << 10) | ((uint32_t)LED1[1] << 2) | (LED1[2] >> 6);
return HR;
I understand that I can obtain one sample by "merging" 3 bytes of data into one sample since one sample from Max30102 consists of 3 bytes.
From the following function I get following example output:
I (24676) SIGNAL: Write: 5
I (24686) SIGNAL: Read: 27
I (24686) SIGNAL: Amount: 10
I (24686) SIGNAL: Byte[0]: 64
I (24696) SIGNAL: Byte[1]: 63
I (24696) SIGNAL: Byte[2]: 95
I (24696) SIGNAL: Data is: 95
I (24706) SIGNAL: Write: 6
I (24706) SIGNAL: Read: 28
I (24706) SIGNAL: Amount: 10
I (24716) SIGNAL: Byte[0]: 64
I (24716) SIGNAL: Byte[1]: 63
I (24726) SIGNAL: Byte[2]: 95
I (24726) SIGNAL: Data is: 97
I (24726) SIGNAL: Write: 7
I (24736) SIGNAL: Read: 29
I (24736) SIGNAL: Amount: 10
I (24736) SIGNAL: Byte[0]: 64
I (24746) SIGNAL: Byte[1]: 63
I (24746) SIGNAL: Byte[2]: 97
I (24746) SIGNAL: Data is: 96
No matter what I do, it seems that neither of these registers actually increments because the data in the first two bytes stays constant.
When I put my finger on the sensor, the data increases to:
I (22686) SIGNAL: Data is: 65535.
This result also does not seem correct because it remains constant. In other projects, I have seen that the sample is a 6-digit number, not a 5-digit one.
I am really confused. What am I missing here? I would greatly appreciate any advice or critique regarding my understanding of my code.
Thank you!
I am using an ESP32 with a Max30102 sensor to get HR readings. I also use ESP-IDF and the <driver/i2c.h> library to access the needed memory registers.
From my understanding of the Max30102 sensor documentation, I have two registers, FIFO_RD_PTR and FIFO_DATA, that give me access to memory. However, accessing only FIFO_RD_PTR increments the reading pointer, so it points to the next byte I can read. I am really confused by my output.
First libraries used:
#include "driver/gpio.h"
#include "driver/i2c.h"
#include "esp_err.h"
#include "esp_log.h"
#include "esp_system.h"
MAX30102 Configuration:
MAXwrite_to_register(Mode_Configuration, 0x02); // 0x02 HR Mode, 0x03 SpO2 Mode, 0x07 Multi-LED.
// LED Power config
MAXwrite_to_register(LED_Pulse_Amplitude_RED, 0xFF);
MAXwrite_to_register(LED_Pulse_Amplitude_IR, 0xFF); // 0x24
// FIFO config
MAXwrite_to_register(FIFO_Configuration, 0x10); // Enable FIFO_ROLLOVER
// MAXwrite_to_register(FIFO_Configuration, 0x80); // Setting sampling size. Commented on purpose to disable it
// Setting Pointers and Counter to 0.
MAXwrite_to_register(FIFO_Read_Pointer, 0x00);
MAXwrite_to_register(FIFO_Write_Pointer, 0x00);
MAXwrite_to_register(Overflow_Counter, 0x00);
Memory accessing code:
uint8_t WritePointer; // Write pointer Get.
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (MAX30102_ADDRESS << 1) | I2C_MASTER_WRITE, true);
i2c_master_write_byte(cmd, FIFO_Write_Pointer, true);
i2c_master_stop(cmd);
i2c_master_cmd_begin(I2C_NUM_0, cmd, 1000 / portTICK_PERIOD_MS);
i2c_cmd_link_delete(cmd);
cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (MAX30102_ADDRESS << 1) | I2C_MASTER_READ, true);
i2c_master_read_byte(cmd, &WritePointer, I2C_MASTER_NACK);
i2c_master_stop(cmd);
i2c_master_cmd_begin(I2C_NUM_0, cmd, 1000 / portTICK_PERIOD_MS);
i2c_cmd_link_delete(cmd);
// Read Pointer Get
uint8_t ReadPointer;
cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (MAX30102_ADDRESS << 1) | I2C_MASTER_WRITE, true);
i2c_master_write_byte(cmd, FIFO_Read_Pointer, true);
i2c_master_stop(cmd);
i2c_master_cmd_begin(I2C_NUM_0, cmd, 1000 / portTICK_PERIOD_MS);
i2c_cmd_link_delete(cmd);
cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (MAX30102_ADDRESS << 1) | I2C_MASTER_READ, true);
i2c_master_read_byte(cmd, &ReadPointer, I2C_MASTER_NACK);
i2c_master_stop(cmd);
i2c_master_cmd_begin(I2C_NUM_0, cmd, 1000 / portTICK_PERIOD_MS);
i2c_cmd_link_delete(cmd);
ESP_LOGI(SIGNAL, "Write: %i", WritePointer);
ESP_LOGI(SIGNAL, "Read: %i", ReadPointer);
// Calculate DataAmount
uint8_t DataAmount;
if (WritePointer >= ReadPointer)
{
DataAmount = (WritePointer - ReadPointer) % 32;
}
else
{
DataAmount = (32 + WritePointer - ReadPointer) % 32;
}
ESP_LOGI(SIGNAL, "Amount: %i", DataAmount);
uint8_t LED1[3];
cmd = i2c_cmd_link_create;
cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (MAX30102_ADDRESS << 1) | I2C_MASTER_WRITE, true);
i2c_master_write_byte(cmd, FIFO_Data_Register, true);
i2c_master_stop(cmd);
i2c_master_cmd_begin(I2C_NUM_0, cmd, 1000 / portTICK_PERIOD_MS);
i2c_cmd_link_delete(cmd);
cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (MAX30102_ADDRESS << 1) | I2C_MASTER_READ, true);
for (int i = 0; i < 3; i++)
{
i2c_master_read_byte(cmd, &LED1, i < (DataAmount - 1) ? I2C_MASTER_ACK : I2C_MASTER_NACK);
ESP_LOGI(SIGNAL, "Byte[%i]: %i", i, LED1);
}
i2c_master_stop(cmd);
i2c_master_cmd_begin(I2C_NUM_0, cmd, 1000 / portTICK_PERIOD_MS);
i2c_cmd_link_delete(cmd);
/*
cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (MAX30102_ADDRESS << 1) | I2C_MASTER_WRITE, true);
i2c_master_write_byte(cmd, FIFO_Read_Pointer, true);
i2c_master_stop(cmd);
i2c_master_cmd_begin(I2C_NUM_0, cmd, 1000 / portTICK_PERIOD_MS);
i2c_cmd_link_delete(cmd);
cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (MAX30102_ADDRESS << 1) | I2C_MASTER_READ, true);
i2c_master_read_byte(cmd, &ReadPointer, I2C_MASTER_NACK);
i2c_master_stop(cmd);
i2c_master_cmd_begin(I2C_NUM_0, cmd, 1000 / portTICK_PERIOD_MS);
i2c_cmd_link_delete(cmd);
ESP_LOGI(SIGNAL, "Read2: %i", ReadPointer);
*/
uint32_t HR = ((uint32_t)LED1[0] << 10) | ((uint32_t)LED1[1] << 2) | (LED1[2] >> 6);
return HR;
I understand that I can obtain one sample by "merging" 3 bytes of data into one sample since one sample from Max30102 consists of 3 bytes.
From the following function I get following example output:
I (24676) SIGNAL: Write: 5
I (24686) SIGNAL: Read: 27
I (24686) SIGNAL: Amount: 10
I (24686) SIGNAL: Byte[0]: 64
I (24696) SIGNAL: Byte[1]: 63
I (24696) SIGNAL: Byte[2]: 95
I (24696) SIGNAL: Data is: 95
I (24706) SIGNAL: Write: 6
I (24706) SIGNAL: Read: 28
I (24706) SIGNAL: Amount: 10
I (24716) SIGNAL: Byte[0]: 64
I (24716) SIGNAL: Byte[1]: 63
I (24726) SIGNAL: Byte[2]: 95
I (24726) SIGNAL: Data is: 97
I (24726) SIGNAL: Write: 7
I (24736) SIGNAL: Read: 29
I (24736) SIGNAL: Amount: 10
I (24736) SIGNAL: Byte[0]: 64
I (24746) SIGNAL: Byte[1]: 63
I (24746) SIGNAL: Byte[2]: 97
I (24746) SIGNAL: Data is: 96
No matter what I do, it seems that neither of these registers actually increments because the data in the first two bytes stays constant.
When I put my finger on the sensor, the data increases to:
I (22686) SIGNAL: Data is: 65535.
This result also does not seem correct because it remains constant. In other projects, I have seen that the sample is a 6-digit number, not a 5-digit one.
I am really confused. What am I missing here? I would greatly appreciate any advice or critique regarding my understanding of my code.
Thank you!