Code Monkey home page Code Monkey logo

max30102_by_rf's Introduction

MAX30102_by_RF

Arduino C code for MAX30102 pulse oximetry sensor (MAXIM Integrated, Inc.)

This project originated from a public domain reference design MAXREFDES117# published by Maxim Integrated, Inc., at https://www.maximintegrated.com/en/design/reference-design-center/system-board/6300.html/tb_tab0 for the System Board 6300 containing MAX30102 sensor for pulse oximetry. I adopted it to work with Adafruit Feather M0 Adalogger (https://www.adafruit.com/product/2796). First, I copied the firmware code for Arduino platform from that page, then I heavily modified it making the following changes:

  1. All the original I2C calls in max30102.cpp that relied on platform-dependent SoftI2C library have been replaced by their equivalents from the generic Wire library. Consequently, SoftI2CMaster.h header file was no longer needed and has been removed.
  2. Unnecessary Adafruit_NeoPixel.h and Adafruit_NeoPixel.cpp files have been removed.
  3. A software bug in maxim_heart_rate_and_oxygen_saturation() function in algorithm.cpp has been fixed: after applying 4-point moving average, the an_x[] still contained original last three points (an_x[97], an_x[98], an_x[99]). Subsequent code still referred to the full an_x[] array (0-99) instead of its averaged portion (0-96).
  4. In algorithm.h the "uint8_t uch_spo2_table[184]" has been replaced by "float uch_spo2_table[184]", where the original integers have been recalculated into floating point numbers with 6 decimal digits precision.
  5. A completely new algorithm of signal processing and resulting heart rate and SpO2 calculation has been designed and placed in new files algorithm_by_RF.h and algorithm_by_RF.cpp. The algorithm has been described and published in the following Instructable: https://www.instructables.com/id/Pulse-Oximeter-With-Much-Improved-Precision/.
  6. The RD117_ARDUINO.ino file has been rewritten from the ground up. Code for saving oximetry data to an SD card has been added, algorithm_by_RF has been made default, while Maxim's algorithm has been made optional. Integer variables were replaced by floating point variables, where applicable.

The following two crucial constants are defined in algorithm_by_RF.h:

const float min_autocorrelation_ratio; const float min_pearson_correlation;

Values of these constants determine the severity of quality filter applied to raw signals emitted by MAX30102. Generally, the higher their values, the more strict the filter will be. Details can be found in this project's Instructable.

The RD117_ARDUINO.ino contains several defines that will enable/disable debug printing, testing of the original MAXIM algorithm, saving raw data, and most importantly whether or not you use Adafruit Feather M0 Adalogger as your MCU. Disable the latter option if you want to use an alternative microcontroller. But I have to give you a fair warning: Feather M0's features an ATSAMD21G18 ARM Cortex M0 processor, clocked at 48 MHz and with a whopping 256K of FLASH (8x more than the Atmega328 or 32u4) and 32K of RAM (16x as much). As such, it can handle this code without a drop of sweat. Lesser MCUs may have serious problems with it, especially in terms of sufficient memory.

HOW TO REPORT BUGS

Since I am not a psychic, all inquiries containing some form of vague "your code does not work" and no useful information at all will invariably be referred to this section of the README file. I am sorry, but I have honestly tried being helpful to quite a number of people contacting me either through GitHub or Instructables mail - and in each case I had to waste entire days of e-mail exchanges until I had at least a minimum of useful information and data. Hence, I will welcome a software bug report, but I will not be able to help you with the following issues:

  1. Underpowered MCU (see previous paragraphs).
  2. Misunderstanding of oximetry, especially motion artifacts. Don't expect a useful signal when the sensor is moving. Don't expect a useful signal from the very first batch, it may take some time - say, a minute - for the whole system to get going.
  3. Faulty hardware, such as faulty wiring in some cheap clones (see https://reedpaper.wordpress.com/2018/08/22/pulse-oximeter-max30100-max30102-how-to-fix-wrong-board/ as well as https://www.youtube.com/watch?v=ZqdmA4NAqb0 for details) or swapped RED and IR channels (see Rob Cazzaro's post: #13 (comment)) Rob's post is a prime example of an excellent bug report, by the way!
  4. Incorrect connections.

At minimum, in order to accept a bug report I need you to:

A. Obtain at least one batch of the RAW DATA coming from the MAX30102 sensor, both red and IR channels, preferably as two columns of 100 numbers. Turn #DEBUG or #SAVE_RAW_DATA directive on in RD117_ARDUINO.ino to get raw data in the Serial Monitor.

B. Plot the data on your end making sure that the resulting graph matches examples of good signals in this project's Instructable or the ExpectedGoodQualitySignals.png image above. If not, then don't expect a bad signal to produce correct results! Sorry, since I live a very busy life, I cannot accept bug reports without plots.

C. If the signal is of good quality, but the code still refuses to cooperate, then send the data to me, but please don't use Instructables mail for this purpose (data always comes truncated)! Preferably, open a new issue in this GitHub repository and paste the data there.

Thank you.

max30102_by_rf's People

Contributors

0xmihir avatar aromring avatar fexadom avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

max30102_by_rf's Issues

nrf52832&Max30102

Screen Shot 2020-04-09 at 2 25 51 PM

Hi, i have a custom project using the nrf5283 SoC from Nordic.

I have used the maxim algorithm to test the data i am receiving but the data seems to be incorrect.
I have attached a batch of data...from analyzing the data could you possibly tell me where to find my fault. I have also attached the two functions, the reading of fifo and the heart rate algorithm.
Screen Shot 2020-04-09 at 2 24 37 PM

Any help will be much appreciated.
[nrf52_max30102.pdf]
(https://github.com/aromring/MAX30102_by_RF/files/4456066/nrf52_max30102.pdf)

@aromring

BUG and Improvement suggestions

Very good program but I should say 1) move initializing I2C before reset the MAX30102 because you haven't start I2C but you communicated with it. 2) Set clock to 400 k 3) make it custom pin config like this:
Wire.begin(SDA_Pin,SCL_Pin);
Wire.setClock(400000L);
maxim_max30102_reset(); //resets the MAX30102

Suggestions:
MAX30101 is superior to the MAX30102 in term of accuracy and lower noise, so I would be grateful if you consider minor changes like these into you code:
max30102.h:
#define REG_LED3_PA 0x0E // I ADDED
//Multi-LED Mode configuration (pg 22)
static const uint8_t MAX30102_SLOT1_MASK = 0xF8;
static const uint8_t MAX30102_SLOT2_MASK = 0x8F;
static const uint8_t MAX30102_SLOT3_MASK = 0xF8;
static const uint8_t MAX30102_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;
uint8_t getRevisionID();
void setPulseAmplitudeGreen(uint8_t amplitude);
void bitMask(uint8_t reg, uint8_t mask, uint8_t thing);

max30102.cpp:
in bool maxim_max30102_init():
// I ADDED
if(!maxim_max30102_write_reg(REG_LED3_PA,0x24)) // Choose value for ~ 7mA for LED3 = GREEN in MAX30101
return false;
bitMask(REG_MULTI_LED_CTRL2, 0xF8,0x03);//MAX30105_SLOT3_MASK=0xF8
/////
// I Added
//
// Device ID and Revision
//
uint8_t readPartID() {
uint8_t ChipID;
maxim_max30102_read_reg(REG_PART_ID , &ChipID);
return ChipID;
}

uint8_t getRevisionID() {
uint8_t ChipRev;
maxim_max30102_read_reg(REG_REV_ID , &ChipRev);
return ChipRev;
}
void setPulseAmplitudeGreen(uint8_t amplitude) {
maxim_max30102_write_reg(REG_LED3_PA, amplitude);
}
//Given a register, read it, mask it, and then set the thing
void bitMask(uint8_t reg, uint8_t mask, uint8_t thing)
{
uint8_t originalContents =0;
// Grab current register context
maxim_max30102_read_reg(reg,&originalContents);
// Zero-out the portions of the register we're interested in
originalContents = originalContents & mask;
// Change contents
maxim_max30102_write_reg(reg, originalContents | thing);
}
//Report the most recent Green value
uint32_t getGreen(void)
{
do somthing
}
Add related changes in the RD117_Arduino.ino

Many thanks

Heart Rate Stuck at 93

I reviewed and early post about the Heart Rate stuck at 83. Not sure how it was resolved but my 30102 sensor is Report 93. The SPO2 looks good just the Heart Rate is off

Time[s] SpO2 HR Clock Ratio Corr
0 5.98 93 0:0:0 0.56 1.00
0 26.58 93 0:0:0 0.75 1.00
0 17.62 93 0:0:0 0.85 1.00
1 26.58 93 0:0:1 0.75 1.00
6 99.26 93 0:0:6 0.81 0.91
6 99.86 93 0:0:6 0.73 0.80
6 93.11 93 0:0:6 0.60 0.96
7 95.67 93 0:0:7 0.77 0.93
8 96.52 93 0:0:8 0.65 0.93
9 98.58 93 0:0:9 0.71 0.98
10 99.03 93 0:0:10 0.76 0.95
10 96.22 93 0:0:10 0.65 0.93
11 95.39 93 0:0:11 0.82 0.95
11 94.23 93 0:0:11 0.83 0.95
12 90.65 93 0:0:12 0.76 0.95
12 92.81 93 0:0:12 0.95 0.96
13 99.54 93 0:0:13 0.88 0.83
13 97.56 93 0:0:13 0.91 0.94
14 99.90 93 0:0:14 0.65 0.98
15 99.08 93 0:0:15 0.58 0.90
15 98.73 93 0:0:15 0.79 0.99
15 90.72 93 0:0:15 0.81 0.93
16 98.96 93 0:0:16 0.84 0.99
17 83.07 93 0:0:17 0.99 0.99
18 94.98 93 0:0:18 0.64 0.99
18 95.53 93 0:0:18 0.77 1.00
20 95.09 93 0:0:20 0.58 0.94
20 97.04 93 0:0:20 0.65 1.00
21 92.96 93 0:0:21 0.94 1.00
21 95.86 93 0:0:21 0.75 1.00
21 97.29 93 0:0:21 0.77 1.00
22 98.09 93 0:0:22 0.78 1.00
22 92.17 93 0:0:22 0.86 0.98
23 94.82 93 0:0:23 0.74 1.00
23 96.86 93 0:0:23 0.75 1.00
24 98.21 93 0:0:24 0.77 1.00
24 99.28 93 0:0:24 0.76 1.00
26 99.42 93 0:0:26 0.70 0.85
26 98.07 93 0:0:26 0.93 0.86
27 93.19 93 0:0:27 0.85 0.98
29 99.91 93 0:0:29 0.54 0.94
31 99.74 93 0:0:31 0.63 0.93
33 99.49 93 0:0:33 0.95 0.87
33 99.19 93 0:0:33 0.67 1.00
34 97.84 93 0:0:34 0.89 0.95
34 99.88 93 0:0:34 0.89 0.91
35 98.91 93 0:0:35 0.58 0.92
37 99.02 93 0:0:37 0.72 0.91
38 98.38 93 0:0:38 1.00 0.98
38 96.92 93 0:0:38 0.54 0.97
40 98.48 93 0:0:40 1.02 0.95
41 99.39 93 0:0:41 0.99 0.94
42 98.65 93 0:0:42 0.92 0.97
43 98.91 93 0:0:43 0.55 0.97
43 98.69 93 0:0:43 0.59 0.90
43 99.39 93 0:0:43 0.73 0.88
44 98.80 93 0:0:44 0.65 0.87
44 99.51 93 0:0:44 0.81 0.90
45 98.38 93 0:0:45 0.95 1.00
46 99.21 93 0:0:46 0.91 0.99
47 99.30 93 0:0:47 0.97 0.99
48 95.59 93 0:0:48 0.85 0.99
49 83.87 93 0:0:49 0.85 1.00
49 83.68 93 0:0:49 0.75 1.00
49 84.82 93 0:0:49 0.84 1.00
50 98.40 93 0:0:50 0.84 0.99
50 84.16 93 0:0:50 0.68 0.99
51 79.73 93 0:0:51 0.73 1.00
51 76.69 93 0:0:51 0.66 0.99
52 77.12 93 0:0:52 0.53 0.99
52 85.53 93 0:0:52 0.53 0.99
52 90.90 93 0:0:52 0.54 0.94
53 70.04 93 0:0:53 0.84 0.87
53 97.72 93 0:0:53 1.01 0.91
57 95.19 93 0:0:57 0.75 0.86
70 87.25 93 0:1:10 0.68 0.88
72 99.96 93 0:1:12 0.89 0.90
73 99.95 93 0:1:13 0.75 0.84
102 97.36 93 0:1:42 0.87 0.95
102 89.99 93 0:1:42 0.67 1.00
102 86.68 93 0:1:42 0.63 0.99
103 70.56 93 0:1:43 0.50 0.94
104 86.80 93 0:1:44 0.79 0.88
105 89.36 93 0:1:45 0.77 0.82
107 96.46 93 0:1:47 0.65 0.90
118 92.20 93 0:1:58 0.72 0.82
119 87.88 93 0:1:59 0.65 0.84
150 88.46 93 0:2:30 0.56 0.84
185 90.86 93 0:3:5 0.91 0.98
186 96.07 93 0:3:6 0.53 0.93
186 82.33 93 0:3:6 0.82 0.95
186 96.22 93 0:3:6 0.54 0.83
187 99.40 93 0:3:7 0.90 0.86
208 34.77 93 0:3:28 0.58 0.88
218 94.83 93 0:3:38 0.67 0.90
218 95.05 93 0:3:38 0.78 0.88
219 61.99 93 0:3:39 0.86 0.83
226 95.69 93 0:3:46 0.55 0.85
237 92.92 93 0:3:57 0.92 0.97
237 90.59 93 0:3:57 0.82 0.99
238 96.78 93 0:3:58 0.83 0.88
239 75.07 93 0:3:59 0.81 0.94
240 62.97 93 0:4:0 0.88 0.90
241 79.74 93 0:4:1 0.76 0.95
242 96.11 93 0:4:2 0.87 0.93
243 95.96 93 0:4:3 0.73 0.92
251 84.73 93 0:4:11 0.68 0.91
252 91.68 93 0:4:12 0.54 0.80
253 95.51 93 0:4:13 0.80 0.89

Not work with higher sampling frequency

I test your code with 25 sampling frequency
#define FS 25
But when i change sampling frequency to 50 or 100 it not work always show invalid:
#define FS 100

Sensor not turning on

I copied over the max30102 cpp and h file form your project into the original project that i download from maxim website and removed the softi2cmaster library.
but each time i run the code it gets stuck in this line
while(digitalRead(10)==1);
i can never pass this line and the sensor doesn't even turn as it doesn't light up red
any idea what can i do?
i'm trying to use the code the ESP8266 12E (nodeMCU 1.0 board) but i'm trying it on the arduino UNO first to make sure it is working.
Thanks

Need to change 50 Sampling Frequency / 1 Sampling Time

Hi, i need to change the sampling Frequency to 50 / 1 Sampling Time .

#define ST 4 // Sampling time in s. WARNING: if you change ST, then you MUST recalcuate the sum_X2 parameter below!
#define FS 25 // Sampling frequency in Hz. WARNING: if you change FS, then you MUST recalcuate the sum_X2 parameter below!
// Sum of squares of ST*FS numbers from -mean_X (see below) to +mean_X incremented be one. For example, given ST=4 and FS=25,
// the sum consists of 100 terms: (-49.5)^2 + (-48.5)^2 + (-47.5)^2 + ... + (47.5)^2 + (48.5)^2 + (49.5)^2
// The sum is symmetrc, so you can evaluate it by multiplying its positive half by 2. It is precalcuated here for enhanced
// performance.

Already you mentioned the procedure to change bu t i could not able to work out , its hanging if touch rf algorithm .

could you please help me to share code for 50 sampling frequency in 4 second Sampling time or 50 sampling frequency in 1 second Sampling time

File names and case sensitivity

I just took your code and repurposed it in a Linux/Visual Studio Code environment. It is quite a simple process as follows:

  1. The *.ino file is renamed to main.cpp
  2. The header files are moved to the "include" directory.
  3. In Linux files names are case-sensitive (unlike Windows). So references to <arduino.h> must be rewritten as <Arduino.h>

You mention pin 10 as an interrupt pin without using it as an interrupt at all, rather as just GPIO. This may account for sampling problems.
I shall try and adjust the code to work with true interrupts.

new code bug

When I used your new code, I found that the heart rate still showed half of the actual result in the occasional test. This kind of probability is very low, but it still exists, so I can't record the data to analyze, so I wonder if it's because the setting of relative autocorrelation 0.5 is too large? According to your algorithm, as long as the initial detected heart rate is half of the actual value, the subsequent detected heart rate should be only half of the actual value.

50hz,3s

There are more than 150 data here because I also copied the data that was not passed through the algorithm

my hr=120,bur show 60

raw ir data
110491 110550 110537 110512 110505 110535 110597 110704 110778 110778 110811 110883 110936 110932 110978 111074 111153 111138 110949 110722 110630 110649 110766 110838 110882 110972 110997 110939 110887 110832 110814 110851 110932 111034 111078 111086 111162 111226 111225 111233 111322 111379 111218 110965 110840 110881 110926 110956 110976 111018 111064 111106 111148 111183 111247 111305 111374 111405 111456 111522 111501 111484 111508 111515 111527 111559 111410 111145 111017 111052 111169 111275 111315 111361 111347 111289 111258 111302 111320 111351 111432 111496 111556 111586 111626 111620 111616 111667 111712 111659 111444 111250 111179 111264 111359 111387 111454 111531 111509 111474 111491 111499 111535 111584 111619 111658 111703 111750 111782 111793 111812 111863 111913 111936 111892 111730 111479 111350 111348 111396 111545 111665 111684 111666 111657 111638 111626 111675 111715 111750 111810 111869 111895 111917 111930 111916 111922 111971 112003 111844 111621 111532 111549 111590 111680 111795 111864 111851 111802 111782 111792 111832 111874 111912 111987 112035 112022 112013 112099 112153 112146 112133 112252 112451 112317 111959 111680 111715 111803 111824 111875 111979 111999 111953 111888 111906 111953 111988 112019 112068 112134 112149 112136 112123 112159 112174 112196 112246 112207 111938 111659 111602 111705 111831 111931 112041 112074 112059 112029 112021 112036 112116 112215 112292 112329 112377 112454 112500 112488 112493 112510 112508 112526 112575 112613 112674 112570 112257 112051 112061 112138 112200 112304 112426 112440 112378 hr:60 spo2:98

112338 112302 112313 112381 112478 112549 112592 112630 112669 112693 112743 112735 112727 112646 112417 112221 112202 112300 112420 112494 112563 112592 112567 112531 112507 112531 112585 112657 112700 112737 112791 112825 112804 112810 112793 112784 112797 112861 112861 112605 112373 112330 112378 112459 112538 112623 112761 112777 112715 112683 112673 112698 112751 112885 112993 113016 112989 112982 112979 113018 113092 113146 113139 112861 112546 112422 112451 112546 112655 112755 112837 112846 112804 112755 112740 112762 112816 112936 113016 113054 113068 113085 113135 113183 113206 113092 112811 112618 112568 112635 112765 112868 112967 113027 113034 112981 112928 112932 112957 113029 113126 113217 113227 113223 113261 113344 113370 113331 113072 112775 112655 112665 112775 112887 113003 113081 113099 113072 112997 112982 113074 113163 113232 113251 113294 113346 113386 113412 113434 113473 113341 113048 112898 112895 112956 113031 113141 113251 113280 113214 113145 113156 113194 113253 113368 113469 113465 113467 113505 113549 hr:65 spo2:99

Calibration issue -> Potential issue with maxim_max30102_read_fifo

Hi Aromring,

Thank you very much for providing such a wonderful (and well documented resource).

I have used your code successfully, and it works great. My issue comes when I try saving my data.

I am using a Seeeduino Xiao BLE Sense, which has a nrf32840 chip that clocks in at 60 Mhz (with 256k of flash, like the M0). I have made a PCB for this project that interfaces the Seeed board with an on board IC SPI flash. I build up a buffer of 4096 bytes of data, including IMU readings, sensor readings, and time stamps. 4096 is the size of one page on my flash memory.

I have created a way to calibrate the LED amplitude to try and maximize the ADC readings without oversaturating the photodiode, it is a very simple proportional feedback loop. If you are curious about how I do this, I have forked your code and have a minimal viable code to show the issue (comments should be pretty clear but please reach out). There is a macro header you can comment and uncomment to see the issue.

My calibration works great, and the readings I get from the sensor are beautiful, when I do not write to the flash memory. When I do write to the flash memory, which introduces a 125 ms delay each time the buffer is dumped, the calibration process goes unstable. I have a number of MAX30102 boards and they all run into the same issue.

I'm including 1 file that has two sheets, one showing the problem with calibration, and one where I do not "write to flash".

Test_Data.xlsx

The reason I thought you may care about this issue, is I think it could potentially be a problem with the max30102.cpp file in the function maxim_max30102_read_fifo. The datasheet suggests that the FIFO_WR_PTR, OVF_COUNTER, and FIFO_RD_PTR be cleared when starting a new SpO2 calculation. (See paragraph three of below excerpt from datasheet).

image

I may be mistaken, but I'm not sure this occurs in the call to calculate the spo2 and HR values (in either your, or maxims original code), please do correct me if I'm wrong. I'm still new to manipulating registers, so I do not fully understand this clearing of the FIFO.

Thank you for any advice on this issue, whether it be in the clearing of the FIFO or any other issues you see in the way I am using your code.

As a bonus, you may, (or may hate) checking out my code. I've included some encoding I do to save memory when writing to my flash memory, as well as python files for reading that encoded data from a txt file straight into excel, I think it's pretty neat.

EDIT: I am using the MAXREFDES117 from Maxim. (not the knockoff).

Share reference raw data from MAX30102 module

Hi,

Could you share sample raw data from the module before any processing has been applied please?
This could be .CSV containing MAX30102 output numbers, or .WAV or any other format, which is convenient for you. Having correctly pre-collected data as a reference and comparing them with our data would help to understand if everything OK with schematics and software.

Thanks!
Alexander

Can't get SpO2 to work

First of all, thanks so much for the work you did and for making it available.

I'm having a problem making the code work properly and I'm hoping you can point me in the right direction.

I'm using an ESP32, which is many times faster than the M0 processor you are using. And I have quite a lot of ESP32 experience, so I'm sure I2C works properly. I built a separate ESP32/MAX30102 project and that worked perfectly, so I checked the code for any potential problem and I think that the data reading from the sensor is working properly. I also have 2 different MAX30102 boards, and there is no real difference in the results between them

I'm including 2 files, one a data capture in txt format, the other I imported the same data as CSV into Excel and plotted the graph for the first 100 samples. To my eyes, that looks like an almost perfect graph, so I was expecting the code to work. The only potential issue is the the amplitude of the IR signal is much lower than the RED one

MAX30102.xlsx
MAX30102.txt

You will notice from the CSV that I'm printing both the results of your algorithm and the Maxim results and, if anything, the Maxim ones are worse, so I'm struggling to understand where the problem is. Your algorithm does a great job of reporting the heart rate (double checked with a commercial pulse oximeter), but does not report any valid SpO2 result (flag is 0, result is -999.00, expected when the flag is 0)

I tried changing the LED intensity (registers 0x0C and 0x0D), but it doesn't make a real difference, outside of "shifting the curves up" when I do it

Thanks in advance for any idea/pointer to troubleshoot my problem

min_autocorrelation_ratio = 0.5???

The calculated heart rate obtained by your algorithm is half of the actual heart rate, and the first local maximum of its relative autocorrelation is 0.29.

RAW IR DATA. 50Hz 3s
129650 129663 129675 129702 129688 129667 129666 129677 129648 129575 129485 129453 129461 129485 129506 129542 129565 129562 129583 129611 129624 129614 129602 129588 129614 129652 129672 129677 129679 129707 129710 129689 129708 129750 129763 129749 129751 129764 129780 129792 129782 129775 129782 129797 129782 129719 129640 129598 129596 129613 129615 129641 129657 129683 129696 129694 129703 129702 129693 129682 129668 129704 129719 129737 129735 129735 129752 129754 129756 129729 129746 129748 129759 129751 129752 129757 129750 129730 129723 129656 129583 129529 129511 129539 129537 129546 129574 129604 129624 129665 129690 129682 129685 129698 129712 129728 129743 129771 129781 129804 129785 129799 129805 129823 129839 129851 129864 129897 129902 129899 129881 129913 129920 129931 129927 129920 129927 129936 129941 129923 129885 129809 129766 129713 129730 129740 129763 129791 129819 129813 129817 129834 129842 129816 129810 129810 129806 129825 129846 129869 129909 129904 129905 129900 129917 129951 129973 129941 129959 129977

The question about Relative autocorrelation

According to your description, the relative autocorrelation graph under a good signal should look like this
Uploading 捕获32.GIF…

But when I actually used it, I found that the relative autocorrelation graph of the signals that passed the Pearson correlation coefficient was like this

3

Is it necessary to add some filtering means such as moving average filtering

raw data
DATA.xlsx

Unable to print SPO2 and HR via serial connection

I am integrating MAX30102 with Arduino without Adafruit's Feather M0 Adalogger. Although I changed the output to Serial port (at baud rate 115200), the SPO2 and HR numbers are zero. Below is the example of the output from Serial pot. I had attached the code as well.

Start calculate heart rate
--RF--
529 0.00 0 0:8:49

Start calculate heart rate
--RF--
530 0.00 0 0:8:50

RD117_ARDUINO_new1.zip

Accurate HR and inaccurate spo2 with inaccurate low spo2 test sample

Hey there!. Firstly thanks for a lovely algo! Secondly sorry if I didn't follow the troubleshooting guide as that is not the case here. Everything working like a charm, Also I know this does not have any liability but we could just make it more accurate/reliable.

Using Arduino Mega, Chinese clone MH-ET 30102
for normal individuals the spo2 and heartrate are accurate.

However, I tested the values with a person whose spo2 was 89-92, and the values on the algo shot 97-98.
the other Maxim algo is absolute garbage for both MAX30100 30102 where the values hit 98-100 even when the spo2 was around 92. One possibility is that people have not tested these algos/ devices on patients with low saturation. However, I feel when my O2 was 99.6 ish and it did drop to 97 ish for a patient with low O2 around 90 ish, you/we could tweak around something to get more accuracy in that region. I am happy to go back to the COVID area and test on another patient if you have any changes.

My observation so far(for RF algo)-

Healthy patient O2 reported - 99.8 ish Actual O2 from a medical monitor- 98 ish
Low O2 patient O2 reported - 97 ish Actual O2 from a medical monitor- 91 ish

Any thoughts?

using esp8266-12e

I try to get the code working with esp8266-12e. I assume the hardware is ok... tested with esp32 and followed #13 to determine the leds are swapped...but other than that seems to work correctly, at least if you have warm hands.

issues to build, the ones mentioned in #19 plus the need to have (all happens on a linux, plarform io)

void millis_to_hours(uint32_t ms, char* hr_str);
#ifdef USE_ADALOGGER
// blink three times if isOK is true, otherwise blink continuously
void blinkLED(const byte led, bool isOK)
#endif

before setup()

now success finishes here enabling debug I endup with the same numbers as shown below. looks to me like detection of presence does not work... any help appreciated.
int is connected to gpio12 that is d6, sda on d2, scl on d1

--RF--
40      80.14   125     0:0:40
------
40      80.14   125     0:0:40  1.00    1.00
0       196351  196351
1       196351  196351
2       196351  196351
3       196351  196351
4       196351  196351
5       196351  196351
6       196351  196351
7       196351  196351
8       196351  196351
9       196351  196351
10      196351  196351
11      196351  196351
12      196351  196351
13      196351  196351
14      196351  196351
15      196351  196351
16      196351  196351
17      196351  196351
18      196351  196351
19      196351  196351
20      196351  196351
21      196351  196351
22      196351  196351
23      196351  196351
24      196351  196351
25      196351  196351
26      196351  196351
27      196351  196351
28      196351  196351
29      196351  196351
30      196351  196351
31      196351  196351
32      196351  196351
33      196351  196351
34      196351  196351
35      196351  196351
36      196351  196351
37      196351  196351
38      196351  196351
39      196351  196351
40      196351  196351
41      196351  196351
42      196351  196351
43      196351  196351
44      196351  196351
45      196351  196351
46      196351  196351
47      196351  196351
48      196351  196351
49      196351  196351
50      196351  196351
51      196351  196351
52      196351  196351
53      196351  196351
54      196351  196351
55      196351  196351
56      196351  196351
57      196351  196351
58      196351  196351
59      196351  196351
60      196351  196351
61      196351  196351
62      196351  196351
63      196351  196351
64      196351  196351
65      196351  196351
66      196351  196351
67      196351  196351
68      196351  196351
69      196351  196351
70      196351  196351
71      196351  196351
72      196351  196351
73      196351  196351
74      196351  196351
75      196351  196351
76      196351  196351
77      196351  196351
78      196351  196351
79      196351  196351
80      196351  196351
81      196351  196351
82      196351  196351
83      196351  196351
84      196351  196351
85      196351  196351
86      196351  196351
87      196351  196351
88      196351  196351
89      196351  196351
90      196351  196351
91      196351  196351
92      196351  196351
93      196351  196351
94      196351  196351
95      196351  196351
96      196351  196351
97      196351  196351
98      196351  196351
99      196351  196351
--RF--
40      80.14   125     0:0:40
------
40      80.14   125     0:0:40  1.00    1.00

Are we confident enough in the data coming from the board?

Greetings -

I was trying to find your email so that I could ask this directly, but I cannot find it so I thought I'd open an issue. I'm not reporting a bug for your software, but mostly curious about an observation I've had when using the board, and also when observing the data plots (yours included).

Namely this: the flat line for heart rate and SPO2 seem just plain wrong. Heart rate varies quite a bit during sleep, in fact, there should be a "saddle" effect that occurs when you're sleeping properly. Also, during REM sleep, breathing rate and heart rate also increase. In the data plot you're showing (https://www.instructables.com/id/Pulse-Oximeter-With-Much-Improved-Precision/), it's a straight line with what seems to be noise about it. I understand it might be how it looks due to scale, but it doesn't look like the type of heart patterns I've come to know as indicative of sleep cycles, etc.

I have a belief I might have sleep apnea, so I bought one of these boards. I used the "noisy" version of the software provided by the manufacturer but noticed my SPO2 was constant throughout the entire night at nearly 97. This was suspect for me.

Do we know for sure the data coming off these boards are even right? Both oxygen and heart rate should vary more than what I see when observing my data plots as well as that of others. I thus have a great deal of suspicion about how well these boards even function. Do you have any reason to believe at least the SPO2 signal is anything more than a constant number with random noise?

My data plot:

image

Error while compiling

i am getting this error when i compile

   45 | #define FS 25     // Sampling frequency in Hz. WARNING: if you change FS, then you MUST recalcuate the sum_X2 parameter below!
      |            ^~
/home/csr/snap/arduino/70/.arduino15/packages/esp8266/hardware/esp8266/3.0.2/cores/esp8266/FS.h:34:7: note: in expansion of macro 'FS'
   34 | class FS;
      |       ^~
/home/csr/snap/arduino/current/Arduino/libraries/MAX30102_by_RF-master/algorithm_by_RF.h:45:12: error: expected unqualified-id before numeric constant
   45 | #define FS 25     // Sampling frequency in Hz. WARNING: if you change FS, then you MUST recalcuate the sum_X2 parameter below!
      |            ^~
/home/csr/snap/arduino/70/.arduino15/packages/esp8266/hardware/esp8266/3.0.2/cores/esp8266/FS.h:34:7: note: in expansion of macro 'FS'
   34 | class FS;
      |       ^~
/home/csr/snap/arduino/current/Arduino/libraries/MAX30102_by_RF-master/algorithm_by_RF.h:45:12: error: expected identifier before numeric constant
   45 | #define FS 25     // Sampling frequency in Hz. WARNING: if you change FS, then you MUST recalcuate the sum_X2 parameter below!
      |            ^~
/home/csr/snap/arduino/70/.arduino15/packages/esp8266/hardware/esp8266/3.0.2/cores/esp8266/FS.h:55:41: note: in expansion of macro 'FS'
   55 |     File(FileImplPtr p = FileImplPtr(), FS *baseFS = nullptr) : _p(p), _fakeDir(nullptr), _baseFS(baseFS) { }
      |                                         ^~
/home/csr/snap/arduino/current/Arduino/libraries/MAX30102_by_RF-master/algorithm_by_RF.h:45:12: error: expected ',' or '...' before numeric constant
   45 | #define FS 25     // Sampling frequency in Hz. WARNING: if you change FS, then you MUST recalcuate the sum_X2 parameter below!
      |            ^~
/home/csr/snap/arduino/70/.arduino15/packages/esp8266/hardware/esp8266/3.0.2/cores/esp8266/FS.h:55:41: note: in expansion of macro 'FS'
   55 |     File(FileImplPtr p = FileImplPtr(), FS *baseFS = nullptr) : _p(p), _fakeDir(nullptr), _baseFS(baseFS) { }
      |                                         ^~
/home/csr/snap/arduino/current/Arduino/libraries/MAX30102_by_RF-master/algorithm_by_RF.h:45:12: error: expected unqualified-id before numeric constant
   45 | #define FS 25     // Sampling frequency in Hz. WARNING: if you change FS, then you MUST recalcuate the sum_X2 parameter below!
      |            ^~
/home/csr/snap/arduino/70/.arduino15/packages/esp8266/hardware/esp8266/3.0.2/cores/esp8266/FS.h:127:5: note: in expansion of macro 'FS'
  127 |     FS                  *_baseFS;
      |     ^~
/home/csr/snap/arduino/current/Arduino/libraries/MAX30102_by_RF-master/algorithm_by_RF.h:45:12: error: default argument missing for parameter 2 of 'fs::File::File(fs::FileImplPtr, int)'
   45 | #define FS 25     // Sampling frequency in Hz. WARNING: if you change FS, then you MUST recalcuate the sum_X2 parameter below!
      |            ^~
/home/csr/snap/arduino/70/.arduino15/packages/esp8266/hardware/esp8266/3.0.2/cores/esp8266/FS.h:55:41: note: in expansion of macro 'FS'
   55 |     File(FileImplPtr p = FileImplPtr(), FS *baseFS = nullptr) : _p(p), _fakeDir(nullptr), _baseFS(baseFS) { }
      |                                         ^~
In file included from /home/csr/snap/arduino/70/.arduino15/packages/esp8266/hardware/esp8266/3.0.2/libraries/ESP8266WiFi/src/CertStoreBearSSL.h:26,
                 from /home/csr/snap/arduino/70/.arduino15/packages/esp8266/hardware/esp8266/3.0.2/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.h:30,
                 from /home/csr/snap/arduino/70/.arduino15/packages/esp8266/hardware/esp8266/3.0.2/libraries/ESP8266WiFi/src/WiFiClientSecure.h:23,
                 from /home/csr/snap/arduino/70/.arduino15/packages/esp8266/hardware/esp8266/3.0.2/libraries/ESP8266WiFi/src/WiFiServerSecure.h:20,
                 from /home/csr/snap/arduino/70/.arduino15/packages/esp8266/hardware/esp8266/3.0.2/libraries/ESP8266WiFi/src/ESP8266WiFi.h:41,
                 from /home/csr/Documents/smart watch/max30102_test/max30102_test.ino:7:
/home/csr/snap/arduino/70/.arduino15/packages/esp8266/hardware/esp8266/3.0.2/cores/esp8266/FS.h:55:22: note: ...following parameter 1 which has a default argument
   55 |     File(FileImplPtr p = FileImplPtr(), FS *baseFS = nullptr) : _p(p), _fakeDir(nullptr), _baseFS(baseFS) { }
      |          ~~~~~~~~~~~~^~~~~~~~~~~~~~~~~
/home/csr/snap/arduino/70/.arduino15/packages/esp8266/hardware/esp8266/3.0.2/cores/esp8266/FS.h: In constructor 'fs::File::File(fs::FileImplPtr, int)':
/home/csr/snap/arduino/70/.arduino15/packages/esp8266/hardware/esp8266/3.0.2/cores/esp8266/FS.h:55:91: error: class 'fs::File' does not have any field named '_baseFS'
   55 |     File(FileImplPtr p = FileImplPtr(), FS *baseFS = nullptr) : _p(p), _fakeDir(nullptr), _baseFS(baseFS) { }
      |                                                                                           ^~~~~~~
/home/csr/snap/arduino/70/.arduino15/packages/esp8266/hardware/esp8266/3.0.2/cores/esp8266/FS.h:55:99: error: 'baseFS' was not declared in this scope
   55 |     File(FileImplPtr p = FileImplPtr(), FS *baseFS = nullptr) : _p(p), _fakeDir(nullptr), _baseFS(baseFS) { }
      |                                                                                                   ^~~~~~
In file included from /home/csr/Documents/smart watch/max30102_test/max30102_test.ino:4:
/home/csr/snap/arduino/70/.arduino15/packages/esp8266/hardware/esp8266/3.0.2/cores/esp8266/FS.h: At global scope:
/home/csr/snap/arduino/current/Arduino/libraries/MAX30102_by_RF-master/algorithm_by_RF.h:45:12: error: expected identifier before numeric constant
   45 | #define FS 25     // Sampling frequency in Hz. WARNING: if you change FS, then you MUST recalcuate the sum_X2 parameter below!
      |            ^~
/home/csr/snap/arduino/70/.arduino15/packages/esp8266/hardware/esp8266/3.0.2/cores/esp8266/FS.h:132:41: note: in expansion of macro 'FS'
  132 |     Dir(DirImplPtr impl = DirImplPtr(), FS *baseFS = nullptr): _impl(impl), _baseFS(baseFS) { }
      |                                         ^~
/home/csr/snap/arduino/current/Arduino/libraries/MAX30102_by_RF-master/algorithm_by_RF.h:45:12: error: expected ',' or '...' before numeric constant
   45 | #define FS 25     // Sampling frequency in Hz. WARNING: if you change FS, then you MUST recalcuate the sum_X2 parameter below!
      |            ^~
/home/csr/snap/arduino/70/.arduino15/packages/esp8266/hardware/esp8266/3.0.2/cores/esp8266/FS.h:132:41: note: in expansion of macro 'FS'
  132 |     Dir(DirImplPtr impl = DirImplPtr(), FS *baseFS = nullptr): _impl(impl), _baseFS(baseFS) { }
      |                                         ^~
/home/csr/snap/arduino/current/Arduino/libraries/MAX30102_by_RF-master/algorithm_by_RF.h:45:12: error: expected unqualified-id before numeric constant
   45 | #define FS 25     // Sampling frequency in Hz. WARNING: if you change FS, then you MUST recalcuate the sum_X2 parameter below!
      |            ^~
/home/csr/snap/arduino/70/.arduino15/packages/esp8266/hardware/esp8266/3.0.2/cores/esp8266/FS.h:150:5: note: in expansion of macro 'FS'
  150 |     FS       *_baseFS;
      |     ^~
/home/csr/snap/arduino/current/Arduino/libraries/MAX30102_by_RF-master/algorithm_by_RF.h:45:12: error: default argument missing for parameter 2 of 'fs::Dir::Dir(fs::DirImplPtr, int)'
   45 | #define FS 25     // Sampling frequency in Hz. WARNING: if you change FS, then you MUST recalcuate the sum_X2 parameter below!
      |            ^~
/home/csr/snap/arduino/70/.arduino15/packages/esp8266/hardware/esp8266/3.0.2/cores/esp8266/FS.h:132:41: note: in expansion of macro 'FS'
  132 |     Dir(DirImplPtr impl = DirImplPtr(), FS *baseFS = nullptr): _impl(impl), _baseFS(baseFS) { }
      |                                         ^~
In file included from /home/csr/snap/arduino/70/.arduino15/packages/esp8266/hardware/esp8266/3.0.2/libraries/ESP8266WiFi/src/CertStoreBearSSL.h:26,
                 from /home/csr/snap/arduino/70/.arduino15/packages/esp8266/hardware/esp8266/3.0.2/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.h:30,
                 from /home/csr/snap/arduino/70/.arduino15/packages/esp8266/hardware/esp8266/3.0.2/libraries/ESP8266WiFi/src/WiFiClientSecure.h:23,
                 from /home/csr/snap/arduino/70/.arduino15/packages/esp8266/hardware/esp8266/3.0.2/libraries/ESP8266WiFi/src/WiFiServerSecure.h:20,
                 from /home/csr/snap/arduino/70/.arduino15/packages/esp8266/hardware/esp8266/3.0.2/libraries/ESP8266WiFi/src/ESP8266WiFi.h:41,
                 from /home/csr/Documents/smart watch/max30102_test/max30102_test.ino:7:
/home/csr/snap/arduino/70/.arduino15/packages/esp8266/hardware/esp8266/3.0.2/cores/esp8266/FS.h:132:20: note: ...following parameter 1 which has a default argument
  132 |     Dir(DirImplPtr impl = DirImplPtr(), FS *baseFS = nullptr): _impl(impl), _baseFS(baseFS) { }
      |         ~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~
/home/csr/snap/arduino/70/.arduino15/packages/esp8266/hardware/esp8266/3.0.2/cores/esp8266/FS.h: In constructor 'fs::Dir::Dir(fs::DirImplPtr, int)':
/home/csr/snap/arduino/70/.arduino15/packages/esp8266/hardware/esp8266/3.0.2/cores/esp8266/FS.h:132:77: error: class 'fs::Dir' does not have any field named '_baseFS'
  132 |     Dir(DirImplPtr impl = DirImplPtr(), FS *baseFS = nullptr): _impl(impl), _baseFS(baseFS) { }
      |                                                                             ^~~~~~~
/home/csr/snap/arduino/70/.arduino15/packages/esp8266/hardware/esp8266/3.0.2/cores/esp8266/FS.h:132:85: error: 'baseFS' was not declared in this scope
  132 |     Dir(DirImplPtr impl = DirImplPtr(), FS *baseFS = nullptr): _impl(impl), _baseFS(baseFS) { }
      |                                                                                     ^~~~~~
In file included from /home/csr/Documents/smart watch/max30102_test/max30102_test.ino:4:
/home/csr/snap/arduino/70/.arduino15/packages/esp8266/hardware/esp8266/3.0.2/cores/esp8266/FS.h: At global scope:
/home/csr/snap/arduino/current/Arduino/libraries/MAX30102_by_RF-master/algorithm_by_RF.h:45:12: error: expected identifier before numeric constant
   45 | #define FS 25     // Sampling frequency in Hz. WARNING: if you change FS, then you MUST recalcuate the sum_X2 parameter below!
      |            ^~
/home/csr/snap/arduino/70/.arduino15/packages/esp8266/hardware/esp8266/3.0.2/cores/esp8266/FS.h:201:7: note: in expansion of macro 'FS'
  201 | class FS
      |       ^~
/home/csr/snap/arduino/current/Arduino/libraries/MAX30102_by_RF-master/algorithm_by_RF.h:45:12: error: expected unqualified-id before numeric constant
   45 | #define FS 25     // Sampling frequency in Hz. WARNING: if you change FS, then you MUST recalcuate the sum_X2 parameter below!
      |            ^~
/home/csr/snap/arduino/70/.arduino15/packages/esp8266/hardware/esp8266/3.0.2/cores/esp8266/FS.h:201:7: note: in expansion of macro 'FS'
  201 | class FS
      |       ^~
/home/csr/snap/arduino/current/Arduino/libraries/MAX30102_by_RF-master/algorithm_by_RF.h:45:12: error: expected unqualified-id before numeric constant
   45 | #define FS 25     // Sampling frequency in Hz. WARNING: if you change FS, then you MUST recalcuate the sum_X2 parameter below!
      |            ^~
/home/csr/snap/arduino/70/.arduino15/packages/esp8266/hardware/esp8266/3.0.2/cores/esp8266/FS.h:262:11: note: in expansion of macro 'FS'
  262 | using fs::FS;
      |           ^~
In file included from /home/csr/snap/arduino/70/.arduino15/packages/esp8266/hardware/esp8266/3.0.2/libraries/ESP8266WiFi/src/CertStoreBearSSL.h:26,
                 from /home/csr/snap/arduino/70/.arduino15/packages/esp8266/hardware/esp8266/3.0.2/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.h:30,
                 from /home/csr/snap/arduino/70/.arduino15/packages/esp8266/hardware/esp8266/3.0.2/libraries/ESP8266WiFi/src/WiFiClientSecure.h:23,
                 from /home/csr/snap/arduino/70/.arduino15/packages/esp8266/hardware/esp8266/3.0.2/libraries/ESP8266WiFi/src/WiFiServerSecure.h:20,
                 from /home/csr/snap/arduino/70/.arduino15/packages/esp8266/hardware/esp8266/3.0.2/libraries/ESP8266WiFi/src/ESP8266WiFi.h:41,
                 from /home/csr/Documents/smart watch/max30102_test/max30102_test.ino:7:
/home/csr/snap/arduino/70/.arduino15/packages/esp8266/hardware/esp8266/3.0.2/cores/esp8266/FS.h:262:11: error: expected ';' before numeric constant
  262 | using fs::FS;
      |           ^
      |           ;
In file included from /home/csr/Documents/smart watch/max30102_test/max30102_test.ino:4:
/home/csr/snap/arduino/current/Arduino/libraries/MAX30102_by_RF-master/algorithm_by_RF.h:45:12: error: expected unqualified-id before numeric constant
   45 | #define FS 25     // Sampling frequency in Hz. WARNING: if you change FS, then you MUST recalcuate the sum_X2 parameter below!
      |            ^~
/home/csr/snap/arduino/70/.arduino15/packages/esp8266/hardware/esp8266/3.0.2/cores/esp8266/FS.h:262:11: note: in expansion of macro 'FS'
  262 | using fs::FS;
      |           ^~
/home/csr/snap/arduino/current/Arduino/libraries/MAX30102_by_RF-master/algorithm_by_RF.h:45:12: error: expected unqualified-id before numeric constant
   45 | #define FS 25     // Sampling frequency in Hz. WARNING: if you change FS, then you MUST recalcuate the sum_X2 parameter below!
      |            ^~
/home/csr/snap/arduino/70/.arduino15/packages/esp8266/hardware/esp8266/3.0.2/cores/esp8266/FS.h:275:12: note: in expansion of macro 'FS'
  275 | extern fs::FS SPIFFS __attribute__((deprecated("SPIFFS has been deprecated. Please consider moving to LittleFS or other filesystems.")));
      |            ^~
/home/csr/snap/arduino/current/Arduino/libraries/MAX30102_by_RF-master/algorithm_by_RF.h:45:12: error: expected identifier before numeric constant
   45 | #define FS 25     // Sampling frequency in Hz. WARNING: if you change FS, then you MUST recalcuate the sum_X2 parameter below!
      |            ^~
/home/csr/snap/arduino/70/.arduino15/packages/esp8266/hardware/esp8266/3.0.2/libraries/ESP8266WiFi/src/CertStoreBearSSL.h:48:27: note: in expansion of macro 'FS'
   48 |     int initCertStore(fs::FS &fs, const char *indexFileName, const char *dataFileName);
      |                           ^~
/home/csr/snap/arduino/current/Arduino/libraries/MAX30102_by_RF-master/algorithm_by_RF.h:45:12: error: expected ',' or '...' before numeric constant
   45 | #define FS 25     // Sampling frequency in Hz. WARNING: if you change FS, then you MUST recalcuate the sum_X2 parameter below!
      |            ^~
/home/csr/snap/arduino/70/.arduino15/packages/esp8266/hardware/esp8266/3.0.2/libraries/ESP8266WiFi/src/CertStoreBearSSL.h:48:27: note: in expansion of macro 'FS'
   48 |     int initCertStore(fs::FS &fs, const char *indexFileName, const char *dataFileName);
      |                           ^~
/home/csr/snap/arduino/current/Arduino/libraries/MAX30102_by_RF-master/algorithm_by_RF.h:45:12: error: expected unqualified-id before numeric constant
   45 | #define FS 25     // Sampling frequency in Hz. WARNING: if you change FS, then you MUST recalcuate the sum_X2 parameter below!
      |            ^~
/home/csr/snap/arduino/70/.arduino15/packages/esp8266/hardware/esp8266/3.0.2/libraries/ESP8266WiFi/src/CertStoreBearSSL.h:54:9: note: in expansion of macro 'FS'
   54 |     fs::FS *_fs = nullptr;
      |         ^~
exit status 1

Heart-rate is always the same (83 Bpm)

Hi!

I have used this library with Arduino Nano, and it looks like SpO2 is calculated right but heart-rate value is always 83 and does not changes. Can you help me to understand what is going wrong and how to deal with it, please?
image

I am using the sketch below (there is no any changes, I've just commented some features (DEBUG, ADALOGGER, SAVE_ROW_DATA)

/********************************************************
*
* Project: MAXREFDES117#
* Filename: RD117_ARDUINO.ino
* Description: This module contains the Main application for the MAXREFDES117 example program.
*
* Revision History:
*\n 1-18-2016 Rev 01.00 GL Initial release.
*\n 12-22-2017 Rev 02.00 Significantlly modified by Robert Fraczkiewicz
*\n 08-22-2018 Rev 02.01 Added conditional compilation of the code related to ADALOGGER SD card operations
*
* --------------------------------------------------------------------
*
* This code follows the following naming conventions:
*
* char              ch_pmod_value
* char (array)      s_pmod_s_string[16]
* float             f_pmod_value
* int32_t           n_pmod_value
* int32_t (array)   an_pmod_value[16]
* int16_t           w_pmod_value
* int16_t (array)   aw_pmod_value[16]
* uint16_t          uw_pmod_value
* uint16_t (array)  auw_pmod_value[16]
* uint8_t           uch_pmod_value
* uint8_t (array)   auch_pmod_buffer[16]
* uint32_t          un_pmod_value
* int32_t *         pn_pmod_value
*
* ------------------------------------------------------------------------- */

#include "Arduino.h"
#include <Wire.h>
#include <SPI.h>
#include "algorithm_by_RF.h"
#include "max30102.h"

//#define DEBUG // Uncomment for debug output to the Serial stream
//#define USE_ADALOGGER // Comment out if you don't have ADALOGGER itself but your MCU still can handle this code
//#define TEST_MAXIM_ALGORITHM // Uncomment if you want to include results returned by the original MAXIM algorithm
//#define SAVE_RAW_DATA // Uncomment if you want raw data coming out of the sensor saved to SD card. Red signal first, IR second.

#ifdef USE_ADALOGGER
  #include <SD.h>
#endif

#ifdef TEST_MAXIM_ALGORITHM
  #include "algorithm.h" 
#endif

// Interrupt pin
const byte oxiInt = 10; // pin connected to MAX30102 INT

// ADALOGGER pins
#ifdef USE_ADALOGGER
  File dataFile;
  const byte chipSelect = 4;
  const byte cardDetect = 7;
  const byte batteryPin = 9;
  const byte ledPin = 13; // Red LED on ADALOGGER
  const byte sdIndicatorPin = 8; // Green LED on ADALOGGER
  bool cardOK;
#endif

uint32_t elapsedTime,timeStart;

uint32_t aun_ir_buffer[BUFFER_SIZE]; //infrared LED sensor data
uint32_t aun_red_buffer[BUFFER_SIZE];  //red LED sensor data
float old_n_spo2;  // Previous SPO2 value
uint8_t uch_dummy,k;

void setup() {

  pinMode(oxiInt, INPUT);  //pin D10 connects to the interrupt output pin of the MAX30102

#ifdef USE_ADALOGGER
  pinMode(cardDetect,INPUT_PULLUP);
  pinMode(batteryPin,INPUT);
  pinMode(ledPin,OUTPUT);
  digitalWrite(ledPin,LOW);
  pinMode(sdIndicatorPin,OUTPUT);
  digitalWrite(sdIndicatorPin,LOW);
#endif

  Wire.begin();

#if defined(DEBUG) || !defined(USE_ADALOGGER)
  // initialize serial communication at 115200 bits per second:
  Serial.begin(115200);
#endif

  maxim_max30102_reset(); //resets the MAX30102
  delay(1000);

  maxim_max30102_read_reg(REG_INTR_STATUS_1,&uch_dummy);  //Reads/clears the interrupt status register
  maxim_max30102_init();  //initialize the MAX30102
  old_n_spo2=0.0;

#ifdef USE_ADALOGGER
    // Measure battery voltage
  float measuredvbat = analogRead(batteryPin);
  measuredvbat *= 2;    // we divided by 2, so multiply back
  measuredvbat *= 3.3;  // Multiply by 3.3V, our reference voltage
  measuredvbat /= 1024; // convert to voltage

  char my_status[20];
  if(HIGH==digitalRead(cardDetect)) {
    // we'll use the initialization code from the utility libraries
    // since we're just testing if the card is working!
    if(!SD.begin(chipSelect)) {
      cardOK=false;
      strncpy(my_status,"CardInit!",9);
    } else cardOK=true;
  } else {
    cardOK=false;
    strncpy(my_status,"NoSDCard!",9);
  }

  if(cardOK) {
    long count=0;
    char fname[20];
    do {
//      if(useClock && now.month()<13 && now.day()<32) {
//        sprintf(fname,"%d-%d_%d.txt",now.month(),now.day(),++count);
//      } else {
        sprintf(fname,"data_%d.txt",++count);
//      }
    } while(SD.exists(fname));
    dataFile = SD.open(fname, FILE_WRITE);
    strncpy(my_status,fname,19);
  }
  
#ifdef DEBUG
  while(Serial.available()==0)  //wait until user presses a key
  {
    Serial.print(F("Vbatt=\t"));
    Serial.println(measuredvbat);
    Serial.println(my_status);
    Serial.println(dataFile,HEX);
    Serial.println(F("Press any key to start conversion"));
    delay(1000);
  }
  uch_dummy=Serial.read();
#endif

  blinkLED(ledPin,cardOK);

  k=0;
  dataFile.println(F("Vbatt=\t"));
  dataFile.println(measuredvbat);
  dataFile.println(my_status);
#ifdef TEST_MAXIM_ALGORITHM
  dataFile.print(F("Time[s]\tSpO2\tHR\tSpO2_MX\tHR_MX\tClock\tRatio\tCorr"));
#else // TEST_MAXIM_ALGORITHM
  dataFile.print(F("Time[s]\tSpO2\tHR\tClock\tRatio\tCorr"));
#endif // TEST_MAXIM_ALGORITHM
#ifdef SAVE_RAW_DATA
  int32_t i;
  // These are headers for the red signal
  for(i=0;i<BUFFER_SIZE;++i) {
    dataFile.print("\t");
    dataFile.print(i);
  }
  // These are headers for the infrared signal
  for(i=0;i<BUFFER_SIZE;++i) {
    dataFile.print("\t");
    dataFile.print(i);
  }
#endif // SAVE_RAW_DATA
  dataFile.println("");

#else // USE_ADALOGGER

  while(Serial.available()==0)  //wait until user presses a key
  {
    Serial.println(F("Press any key to start conversion"));
    delay(1000);
  }
  uch_dummy=Serial.read();
#ifdef TEST_MAXIM_ALGORITHM
  Serial.print(F("Time[s]\tSpO2\tHR\tSpO2_MX\tHR_MX\tClock\tRatio\tCorr"));
#else // TEST_MAXIM_ALGORITHM
  Serial.print(F("Time[s]\tSpO2\tHR\tClock\tRatio\tCorr"));
#endif // TEST_MAXIM_ALGORITHM
#ifdef SAVE_RAW_DATA
  int32_t i;
  // These are headers for the red signal
  for(i=0;i<BUFFER_SIZE;++i) {
    Serial.print("\t");
    Serial.print(i);
  }
  // These are headers for the infrared signal
  for(i=0;i<BUFFER_SIZE;++i) {
    Serial.print("\t");
    Serial.print(i);
  }
#endif // SAVE_RAW_DATA
  Serial.println("");
  
#endif // USE_ADALOGGER
  
  timeStart=millis();
}

//Continuously taking samples from MAX30102.  Heart rate and SpO2 are calculated every ST seconds
void loop() {
  float n_spo2,ratio,correl;  //SPO2 value
  int8_t ch_spo2_valid;  //indicator to show if the SPO2 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
  int32_t i;
  char hr_str[10];
     
  //buffer length of BUFFER_SIZE stores ST seconds of samples running at FS sps
  //read BUFFER_SIZE samples, and determine the signal range
  for(i=0;i<BUFFER_SIZE;i++)
  {
    while(digitalRead(oxiInt)==1);  //wait until the interrupt pin asserts
    maxim_max30102_read_fifo((aun_red_buffer+i), (aun_ir_buffer+i));  //read from MAX30102 FIFO
#ifdef DEBUG
    Serial.print(i, DEC);
    Serial.print(F("\t"));
    Serial.print(aun_red_buffer[i], DEC);
    Serial.print(F("\t"));
    Serial.print(aun_ir_buffer[i], DEC);    
    Serial.println("");
#endif // DEBUG
  }

  //calculate heart rate and SpO2 after BUFFER_SIZE samples (ST seconds of samples) using Robert's method
  rf_heart_rate_and_oxygen_saturation(aun_ir_buffer, BUFFER_SIZE, aun_red_buffer, &n_spo2, &ch_spo2_valid, &n_heart_rate, &ch_hr_valid, &ratio, &correl); 
  elapsedTime=millis()-timeStart;
  millis_to_hours(elapsedTime,hr_str); // Time in hh:mm:ss format
  elapsedTime/=1000; // Time in seconds

#ifdef DEBUG
  Serial.println("--RF--");
  Serial.print(elapsedTime);
  Serial.print("\t");
  Serial.print(n_spo2);
  Serial.print("\t");
  Serial.print(n_heart_rate, DEC);
  Serial.print("\t");
  Serial.println(hr_str);
  Serial.println("------");
#endif // DEBUG

#ifdef TEST_MAXIM_ALGORITHM
  //calculate heart rate and SpO2 after BUFFER_SIZE samples (ST seconds of samples) using MAXIM's method
  float n_spo2_maxim;  //SPO2 value
  int8_t ch_spo2_valid_maxim;  //indicator to show if the SPO2 calculation is valid
  int32_t n_heart_rate_maxim; //heart rate value
  int8_t  ch_hr_valid_maxim;  //indicator to show if the heart rate calculation is valid
  maxim_heart_rate_and_oxygen_saturation(aun_ir_buffer, BUFFER_SIZE, aun_red_buffer, &n_spo2_maxim, &ch_spo2_valid_maxim, &n_heart_rate_maxim, &ch_hr_valid_maxim); 
#ifdef DEBUG
  Serial.println("--MX--");
  Serial.print(elapsedTime);
  Serial.print("\t");
  Serial.print(n_spo2_maxim);
  Serial.print("\t");
  Serial.print(n_heart_rate_maxim, DEC);
  Serial.print("\t");
  Serial.println(hr_str);
  Serial.println("------");
#endif // DEBUG
#endif // TEST_MAXIM_ALGORITHM

  //save samples and calculation result to SD card
#ifdef TEST_MAXIM_ALGORITHM
  if(ch_hr_valid && ch_spo2_valid || ch_hr_valid_maxim && ch_spo2_valid_maxim) {
#else   // TEST_MAXIM_ALGORITHM
  if(ch_hr_valid && ch_spo2_valid) { 
#endif // TEST_MAXIM_ALGORITHM
#ifdef USE_ADALOGGER
    ++k;
    dataFile.print(elapsedTime);
    dataFile.print("\t");
    dataFile.print(n_spo2);
    dataFile.print("\t");
    dataFile.print(n_heart_rate, DEC);
    dataFile.print("\t");
#ifdef TEST_MAXIM_ALGORITHM
    dataFile.print(n_spo2_maxim);
    dataFile.print("\t");
    dataFile.print(n_heart_rate_maxim, DEC);
    dataFile.print("\t");
#endif // TEST_MAXIM_ALGORITHM
    dataFile.print(hr_str);
    dataFile.print("\t");
    dataFile.print(ratio);
    dataFile.print("\t");
    dataFile.print(correl);
#ifdef SAVE_RAW_DATA
    // Save raw data for unusual O2 levels
    for(i=0;i<BUFFER_SIZE;++i)
    {
      dataFile.print(F("\t"));
      dataFile.print(aun_red_buffer[i], DEC);
    }
    for(i=0;i<BUFFER_SIZE;++i)
    {
      dataFile.print(F("\t"));
      dataFile.print(aun_ir_buffer[i], DEC);    
    }
#endif // SAVE_RAW_DATA
    dataFile.println("");
     // Blink green LED to indicate save event
    digitalWrite(sdIndicatorPin,HIGH);
    delay(10);
    digitalWrite(sdIndicatorPin,LOW);
    // FLush SD buffer every 10 points
    if(k>=10) {
      dataFile.flush();
      k=0;
    }
#else // USE_ADALOGGER
    Serial.print(elapsedTime);
    Serial.print("\t");
    Serial.print(n_spo2);
    Serial.print("\t");
    Serial.print(n_heart_rate, DEC);
    Serial.print("\t");
#ifdef TEST_MAXIM_ALGORITHM
    Serial.print(n_spo2_maxim);
    Serial.print("\t");
    Serial.print(n_heart_rate_maxim, DEC);
    Serial.print("\t");
#endif //TEST_MAXIM_ALGORITHM
    Serial.print(hr_str);
    Serial.print("\t");
    Serial.print(ratio);
    Serial.print("\t");
    Serial.print(correl);
#ifdef SAVE_RAW_DATA
    // Save raw data for unusual O2 levels
    for(i=0;i<BUFFER_SIZE;++i)
    {
      Serial.print(F("\t"));
      Serial.print(aun_red_buffer[i], DEC);
    }
    for(i=0;i<BUFFER_SIZE;++i)
    {
      Serial.print(F("\t"));
      Serial.print(aun_ir_buffer[i], DEC);    
    }
#endif // SAVE_RAW_DATA
    Serial.println("");
#endif // USE_ADALOGGER
    old_n_spo2=n_spo2;
  }
}

void millis_to_hours(uint32_t ms, char* hr_str)
{
  char istr[6];
  uint32_t secs,mins,hrs;
  secs=ms/1000; // time in seconds
  mins=secs/60; // time in minutes
  secs-=60*mins; // leftover seconds
  hrs=mins/60; // time in hours
  mins-=60*hrs; // leftover minutes
  itoa(hrs,hr_str,10);
  strcat(hr_str,":");
  itoa(mins,istr,10);
  strcat(hr_str,istr);
  strcat(hr_str,":");
  itoa(secs,istr,10);
  strcat(hr_str,istr);
}

#ifdef USE_ADALOGGER
// blink three times if isOK is true, otherwise blink continuously
void blinkLED(const byte led, bool isOK)
{
  byte i;
  if(isOK) {
    for(i=0;i<3;++i) {
      digitalWrite(led,HIGH);
      delay(200);
      digitalWrite(led,LOW);
      delay(200);
    }
  } else {
    while(1) {
      for(i=0;i<2;++i) {
        digitalWrite(led,HIGH);
        delay(50);
        digitalWrite(led,LOW);
        delay(50);
      }
      delay(500);
    }
  }
}
#endif

MAX30102 FIFO returns IR data first (i.e. LED1 = IR & LED2 = Red)

Right now I’m working on MAX30102 with the MH-ET LIVE breakout board for it and I’ve noticed that when you drive the module in the heart rate mode (i.e. 0x02 mode configuration) the IR led becomes active. The datasheet states that in this mode only the red led should be working. Furthermore, the data read from the FIFO is IR data first and then the red data. Curiously, in the MAX30100 this exact behavior is expected. (I’m referring to its datasheet and haven’t confirmed this physically.) I’m getting part id 0x15 from the module I’m working with so it is indeed MAX30102 (or 101 or 105, but these are all the same in this regard) and not a MAX30101. I possibly can’t think of anything that could cause this behavior except that the info on the datasheet for MAX30102 is wrong and surprisingly everybody is using this module wrong, including the guys at Maxim Integrated! Obviously reading raw IR and Red data instead of each other results in the wrong calculation of SpO2 levels.

So have anyone noticed this behavior in MAX30102?

P.S.: There is a chance that I might have a faulty revision of the module. My part returns Revision ID: 0x03.

Float buffer allocation uses unnecessary memory

The first part of the rf_heart_rate_and_oxygen_saturation code allocates two float arrays to perform calculations on. The default buffer size of 100 results in 800 bytes (2 arrays * 4 bytes * 100 elements) of memory usage, making many libraries that require some dynamic memory impossible to use in conjunction with this one.

I know that it's the best convention to leave parameters unmodified, but when working on underpowered microcontrollers like the Arduino Uno, performing all calculations on a single float buffer makes this library use a lot less memory. I have already modified the code to work this way, but I wanted to hear your thoughts before opening a PR.

Wrong Readings

Hello there I read your comment about changing the values if your heart rate is above 60bpm, my problem is that the reading goes down to 51 and up again to 100/107, if I use the regular library the reading is too erratic it can go down to 20 and up to 200, I'm not sure what can be done or how to fix it:
Here's with your algorithm:
Press any key to start conversion
Time[s] SpO2 HR Clock Ratio Corr
1 90.69 100 0:0:1 0.52 0.86
1 82.68 100 0:0:1 0.56 1.00
3 66.89 100 0:0:3 0.51 0.99
4 42.65 51 0:0:4 0.80 0.99
6 99.58 107 0:0:6 0.55 0.87
8 46.33 100 0:0:8 0.52 1.00
9 58.07 100 0:0:9 0.55 0.99
9 60.20 51 0:0:9 0.65 0.95
11 66.92 100 0:0:11 0.51 1.00
12 65.64 100 0:0:12 0.52 1.00
12 56.33 51 0:0:12 0.64 1.00
15 69.23 51 0:0:15 0.74 1.00
19 76.69 51 0:0:19 0.72 0.96
20 53.66 51 0:0:20 0.91 1.00
21 78.59 51 0:0:21 0.69 1.00
21 80.72 51 0:0:21 0.83 1.00
22 58.30 51 0:0:22 0.76 1.00
22 67.87 51 0:0:22 0.83 1.00
23 67.11 51 0:0:23 0.76 0.99
24 51.70 100 0:0:24 0.51 0.95
25 27.25 51 0:0:25 0.69 0.81
26 99.86 51 0:0:26 0.99 0.92
29 11.86 51 0:0:29 1.01 1.00
29 40.34 51 0:0:29 0.69 0.86
31 58.53 51 0:0:31 0.74 0.88
31 65.91 51 0:0:31 0.91 0.93
32 17.21 100 0:0:32 0.50 1.00
35 79.89 51 0:0:35 0.83 1.00

Here's with the standard algorithm from Maxim

Press any key to start conversion
Time[s] SpO2 HR SpO2_MX HR_MX Clock Ratio Corr
1 -999.00 -999 29.19 107 0:0:1 0.43 0.99
2 -999.00 -999 14.99 107 0:0:2 0.33 0.93
2 -999.00 -999 33.64 107 0:0:2 0.26 0.98
2 -999.00 -999 79.54 51 0:0:2 -0.27 0.93
8 -999.00 -999 75.07 68 0:0:8 -0.14 0.79
8 -999.00 -999 91.89 107 0:0:8 0.09 0.70
9 59.89 107 -999.00 -999 0:0:9 0.51 0.91
9 -999.00 -999 63.20 136 0:0:9 0.51 0.48
9 -999.00 -999 3.49 60 0:0:9 0.07 0.88
11 -999.00 -999 44.12 20 0:0:11 0.47 0.89
11 57.63 51 -999.00 115 0:0:11 0.53 0.84
11 87.51 51 99.95 83 0:0:11 0.77 0.84
15 -999.00 -999 78.31 107 0:0:15 0.12 0.93
15 -999.00 -999 51.86 62 0:0:15 0.17 0.94
17 -999.00 -999 72.32 107 0:0:17 -0.22 0.97
18 7.87 107 -999.00 107 0:0:18 0.50 0.93
18 -999.00 -999 50.92 115 0:0:18 0.50 0.76
21 -999.00 -999 40.03 53 0:0:21 -0.28 0.79
24 -999.00 -999 77.68 32 0:0:24 0.39 0.75
25 -999.00 -999 98.85 125 0:0:25 0.39 0.43
29 -999.00 -999 91.89 60 0:0:29 -0.31 0.88
31 93.04 107 -999.00 -999 0:0:31 0.55 0.95
33 -999.00 -999 33.64 32 0:0:33 0.33 0.89
33 -999.00 -999 94.34 57 0:0:33 0.00 0.83
33 -999.00 -999 81.32 115 0:0:33 0.37 0.94
34 -999.00 -999 94.01 115 0:0:34 0.37 0.77
35 -999.00 -999 29.19 83 0:0:35 -0.05 1.00
35 -999.00 -999 42.10 115 0:0:35 -0.06 0.94
36 -999.00 -999 41.07 83 0:0:36 -0.05 1.00
36 52.17 107 44.12 107 0:0:36 0.53 0.91
36 47.22 107 38.99 83 0:0:36 0.54 0.92
36 -999.00 -999 24.60 107 0:0:36 0.47 0.85
38 -999.00 -999 28.06 57 0:0:38 0.02 0.98
38 -999.00 -999 99.48 107 0:0:38 -0.07 0.91
39 -999.00 -999 87.62 78 0:0:39 0.40 0.94
39 -999.00 -999 77.04 107 0:0:39 -0.07 0.92
40 -999.00 -999 65.60 107 0:0:40 -0.07 1.00

What's happenning :(

My connections are the same but I'm using an ESP32 Board and I'm using the GPIO15 as the INT pin

Thanks

Can't get past maxim_max30102_init(); on ESP32-C3

No idea what the issue is, but I saw some other people got it working on a regular ESP32, so I thought I'd share my troubleshooting trying to get this sketch to work on an ESP32-C3.

I know the MAX board works because I have tested it with the Sparkfun library and it works fine there.

But in this sketch, nothing happens whatsoever - the LEDs do not light up, and not even a single word gets printed to the Serial monitor. Even if DEBUG is defined/uncommented.

Even if I add "Serial.println("hellothere!!!");" after Serial.begin, it never prints.

...until I comment out the line "maxim_max30102_init();".

In fact I can strip down the code to almost nothing:

#include "algorithm_by_RF.h"
#include "max30102.h"

void setup() {
  Serial.begin(115200);
  Serial.println("hellothere!!!");

  //maxim_max30102_init();  //initialize the MAX30102

void loop() {
  Serial.println("hellothere!!!");
}

If that init line is commented, the serial prints "hellothere" just fine. But if I uncomment the maxim_max30102_init line, the serial never prints anything.

I have no idea how this line being present is blocking the Serial print from showing up since the Serial print line is before the init line.

I have dug into the cpp file and played around with things like I2C speeds, comparing it to the working Sparkfun "begin" function, but I could not get that init function to work. I have no idea why, or what is different about the C3 that is making it not work.

The C3 supposedly supports all the way up to 400kbps I2C. But again I tried changing it to 100000 and it still didn't work, so I don't think the I2C speed is the issue.

if (!sensor.begin())

I am likely missing something very basic - so sorry, I'm still quite new at this. I've downloaded the repo & opened the MAX30100_Tester sketch, but when I try to compile I get an error on line 34:
if (!sensor.begin())
could not convert 'sensor.MAX30100::begin(pw1600, i50, sr100)' from 'void' to 'bool'

I've otherwise gotten Feather M0 code to compile on this board. I'm trying to find a way to modify Jiri Praus's Beating Heart Sculpture (which uses a MAX30100) so that it can work with a Feather. I have the LEDs lighting, but haven't yet been able to get the MAX30100 to work with the Feather M0. I was hoping your code would help me at least confirm the sensor was working, but I can't seem to get this running. Again - apologies for newbie gaffe I've likely committed. Thanks for sharing your code. There's not much out there on Feather M0 & MAX30100.

IR and Red signals do not show any heart rate info

Hi, I've ported your code to Python to work on a Raspberry Pi and it works great on some sample data I found in another repository but when I run it the IR and Red signals are relatively flat with no apparently heart rate signal. These signals do change when I move my finger around so I know the sensor is working but any changes due to heart rate are not present. Either I have no pulse or I haven't configured the MAX30102 correctly. Can you suggest what parameters in the sensor's config may need to modified to get a valid heart rate?
Thanks!

New code bug

When I used your new code, I found that the heart rate still showed half of the actual result in the occasional test. This kind of probability is very low, but it still exists, so I can't record the data to analyze, so I wonder if it's because the setting of relative autocorrelation 0.5 is too large? According to your algorithm, as long as the initial detected heart rate is half of the actual value, the subsequent detected heart rate should be only half of the actual value.

Max30102 is not powering up

I have checked my connections a hundred times and I have checked the code too but i can't figure out the reason my sensor is not powering up (I even changed the sensor). Till now I have diagnosed that upon reading any of the registers all the values it is showing me is "0xFF" only (irrespective of me setting any values to the registers or not). According to youtube one has to desolder the surface mount resistors and connect external resistors,, I have done this too. My microcontroller is PIC18F4525.

Can anybody advice me how to tackle this situation, please?

some abnormal data

Use your latest code
My parameters 50hz 3s
raw data.xlsx

I have some questions about the following code

if(LOWEST_PERIOD==n_last_peak_interval)
rf_initialize_periodicity_search(an_x, BUFFER_SIZE, &n_last_peak_interval, HIGHEST_PERIOD, min_autocorrelation_ratio, f_ir_sumsq);
/*
At this time n_last_peak_interval !=0,and then if(n_last_peak_interval!=0)
rf_signal_periodicity(an_x, BUFFER_SIZE, &n_last_peak_interval, LOWEST_PERIOD, HIGHEST_PERIOD, min_autocorrelation_ratio, f_ir_sumsq, ratio);
Calculated twice??????????
*/

// Find signal periodicity
if(*correl>=min_pearson_correlation) {
// At the beginning of oximetry run the exact range of heart rate is unknown. This may lead to wrong rate if the next call does not find the first
// peak of the autocorrelation function. E.g., second peak would yield only 50% of the true rate.
if(LOWEST_PERIOD==n_last_peak_interval)
rf_initialize_periodicity_search(an_x, BUFFER_SIZE, &n_last_peak_interval, HIGHEST_PERIOD, min_autocorrelation_ratio, f_ir_sumsq);
// RF, If correlation os good, then find average periodicity of the IR signal. If aperiodic, return periodicity of 0
if(n_last_peak_interval!=0)
rf_signal_periodicity(an_x, BUFFER_SIZE, &n_last_peak_interval, LOWEST_PERIOD, HIGHEST_PERIOD, min_autocorrelation_ratio, f_ir_sumsq, ratio);
} else n_last_peak_interval=0;

HR always -999 or invalid while wearing sensor on wrist.

I have checked/verified listed below thing before raising issue.

  • Sensor is original not swapped (Tested).
  • When I put sensor on finger/thumb it works fine and give accurate readings. I also compared with amaze fit and looks fine. But, when i put sensor on my wrist using my watch case assembly it's always read invalid HR.
  • I have attached reading sample as .xlsx file (on wrist).
  • Can you please check and verify everything is expected or not

Thanks,
Have a good day.

assembly
Sensor_On_Wrist_V1.xlsx
Sensor_On_Finger.xlsx

RED/IR sensor value looks strange

Good morning, sir. Currently, I am implementing your code using Atmega4809.(at I2C 100kHz, BAUD RATE = 28800)
However, my data value is very different from the data value you uploaded on the homepage. I saw the issue posted by the previous people, but no one seems to be in the same situation as me. I test whether the RED LED and IR LED are reversed, but my device was normal. Below is my data value that I received through UART communication. What could have gone wrong? I'll be waiting for the reply. Thank you, teacher.

0: red = 232060, ir = 242086
1: red = 231443, ir = 241347
2: red = 232013, ir = 242024
3: red = 232013, ir = 242024
4: red = 231429, ir = 241330
5: red = 231978, ir = 241986
6: red = 231978, ir = 241986
7: red = 231978, ir = 241986
8: red = 231412, ir = 241316
9: red = 231997, ir = 241989
10: red = 231997, ir = 241989
11: red = 231376, ir = 241246
12: red = 232078, ir = 242055
13: red = 232078, ir = 242055
14: red = 232078, ir = 242055
15: red = 232078, ir = 242055
16: red = 232062, ir = 242047
17: red = 232062, ir = 242047
18: red = 231245, ir = 241130
19: red = 232080, ir = 242092
20: red = 232080, ir = 242092
21: red = 231257, ir = 241134
22: red = 232012, ir = 242014
23: red = 232012, ir = 242014
24: red = 232012, ir = 242014
25: red = 231276, ir = 241173
26: red = 232006, ir = 241941
27: red = 232006, ir = 241941
28: red = 231301, ir = 241180
29: red = 231932, ir = 241870
30: red = 231932, ir = 241870
31: red = 231392, ir = 241290
32: red = 231783, ir = 241756
33: red = 231783, ir = 241756
34: red = 231783, ir = 241756
35: red = 231373, ir = 241270
36: red = 231733, ir = 241553
37: red = 231733, ir = 241553
38: red = 231312, ir = 241161
39: red = 231518, ir = 241353
40: red = 231518, ir = 241353
41: red = 231518, ir = 241353
42: red = 231308, ir = 241198
43: red = 231322, ir = 241179
44: red = 231322, ir = 241179
45: red = 231304, ir = 241203
46: red = 231308, ir = 241085
47: red = 231308, ir = 241085
48: red = 231360, ir = 241197
49: red = 231200, ir = 240970
50: red = 231200, ir = 240970
51: red = 231200, ir = 240970
52: red = 231330, ir = 241248
53: red = 231106, ir = 240902
54: red = 231106, ir = 240902
55: red = 231300, ir = 241211
56: red = 231217, ir = 241027
57: red = 231217, ir = 241027
58: red = 231217, ir = 241027
59: red = 231305, ir = 241219
60: red = 231284, ir = 241110
61: red = 231284, ir = 241110
62: red = 231275, ir = 241180
63: red = 231313, ir = 241169
64: red = 231313, ir = 241169
65: red = 231285, ir = 241166
66: red = 231323, ir = 241173
67: red = 231323, ir = 241173
68: red = 231323, ir = 241173
69: red = 231278, ir = 241215
70: red = 231287, ir = 241147
71: red = 231287, ir = 241147
72: red = 231259, ir = 241189
73: red = 231352, ir = 241172
74: red = 231352, ir = 241172
75: red = 231352, ir = 241172
76: red = 231331, ir = 241227
77: red = 231331, ir = 241227
78: red = 231331, ir = 241227
79: red = 231195, ir = 241122
80: red = 231356, ir = 241236
81: red = 231356, ir = 241236
82: red = 231180, ir = 241124
83: red = 231427, ir = 241244
84: red = 231427, ir = 241244
85: red = 231427, ir = 241244
86: red = 231194, ir = 241102
87: red = 231415, ir = 241336
88: red = 231415, ir = 241336
89: red = 231141, ir = 240973
90: red = 231421, ir = 241283
91: red = 231421, ir = 241283
92: red = 231063, ir = 240899
93: red = 231422, ir = 241313
94: red = 231422, ir = 241313
95: red = 231422, ir = 241313
96: red = 231068, ir = 240892
97: red = 231407, ir = 241317
98: red = 231407, ir = 241317
99: red = 231026, ir = 240774

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.