This is a project with an ESP32, MAX30102 heartrate monitor module, an SSD1306 OLED display was recently added. Link to ESP-IDF github repository, with instructions to get working, included below.


The MAX chip has a red led and an ir led along with photo detectors that measure the amount of red and ir light returning to the chip. When you put a capillary rich part of your body on (or near) the chip a significant amount of both red and ir light is returned. The MAX chip digitizes the red and ir measurements and places the results in a fifo where the 24 bit data is read out with the i2c interface at 100 samples per second. The predominate feature of this data is substantial low frequency variation when you move your finger even a little, however, on top of this is an about 0.5% variation occurring approximately every 1 second.

My understanding is that - it turns out that as you heart beats new, oxygenated, blood is swept into the capillarys and the amount of reflected light into the MAX chip changes. The color of blood changes as oxygenation changes, so, there is an established relationship between the heartbeat related ac component of the returned red and ir light and the oxygen concentration of the blood ( spo2% = 110-25(%redac/%irac) ).

My ESP32 is running two tasks. The first task runs about five times per second, it i2c reads how many samples are stored in MAX30102 fifo, it reads the samples, it passes the samples through 0.5-5Hz 2nd order butterworth band pass filter, does cycle peak and ac/dc magnitude extraction on data stream, calculates heartrate and spo2 concentrations (and averages over five samples), once every ~1 second updates oled display and saves raw data in tcp response string for other task. The other task hosts up index.html page with javascipt to read raw data from ESP32 - the script displays heartrate and spo2 numbers, uses plotly to graph raw data and provides an interface to pause javascript, switch between raw and filtered data and to change led intensities.

The Code and Hookup

Hookup is straight forward - vcc goes to 3.3v (NOT 5V), sda and scl pins defined main/heartrate.c:25 - around there.

Checked with two releases of isp-idf (as reported in esp-idf directory with git describe) v4.1-dev-2055 and v3.3-beta1-506. v4.1 gives a couple obsolete soft warnings during compile that I haven't tracked down yet. v3.3 requires extra include file added to main/heartrate.c (#include "esp_event_loop.h").

So all needs be done (hopefully - once you have esp-idf environment setup) is "git clone <link below>", "cd esp32-max30102-website" and "make flash monitor" (when menuconfig comes up setup correct correct tty port and the ~1Mbuad rate works good for me). That's what worked for me on a couple clean linux systems.

Never posted code before so not sure if I gave enough information, feel free to contact.

Note on Accuracy

The time bases in esp and max30102 agree very well, and both look good over a 10 minute stopwatch count (<.1%), so I'm pretty sure the heartrate is accurate (although it does vary from pulse to pulse ~10% or so - keeping a running average of five pulses seems to steady it out to ~1% about). I just plugged the measurements into the spO2 equation published by maxim integrated and got the result that I am usually reading is 99% +/-1 (unless the signal is too small) - I don't really know how to calibrate the spO2 - but I did do this dumb experiment where I was breathing into plastic bag and did get the spO2 to decrease - started getting pretty dizzy approaching 92% - hypoxic. Although having copd or a bad case of the flu can get you lower than that, anything under 90% or a 3% drop in short amount of time googled up very bad. Disclaimer - these are my impressions gained working project and have not done a detailed experiment.

Improvements that I would like to make include - mode where LED powers are automatically optimized and data logging for long term tracking.

Anybody tries this let me know how it goes.