MAX30100 Oximeter

Pulse oximeter and heart-rate sensor


Cited from Wikipedia-article on Pulse oximetry: "A blood-oxygen monitor displays the percentage of hemoglobin, the protein in blood that carries oxygen, that is loaded. Normal ranges are from 95 to 99 percent. For a patient breathing room air at or near sea level, an estimate of arterial pO2 can be made from the blood-oxygen monitor "saturation of peripheral oxygen" (SpO2) reading. A typical pulse oximeter uses a pair of small LEDs facing a photodiode through a translucent part of the patient's body, usually a fingertip or an earlobe. One LED is red, with wavelength of 660 nm, and the other is infrared with a wavelength of 940 nm. Absorption of light at these wavelengths differs significantly between blood loaded with oxygen and blood lacking oxygen. Oxygenated hemoglobin absorbs more infrared light and allows more red light to pass through. Deoxygenated hemoglobin allows more infrared light to pass through and absorbs more red light. The LEDs sequence through their cycle of one on, then the other, then both off about thirty times per second which allows the photodiode to respond to the red and infrared light separately and also adjust for the ambient light baseline."

The MAX30100 is a complete Pulse Oximeter and Heart-Rate sensor solution from Maxim Integrated with two-wire serial interface (I2C).

I bought two different breakout boards. The green board I purchased first had 3 I2C pull-up resistors tied to 1.8V. It didn't work. I removed the three surface mounted resistors and replaced them with external 4.7k resistors tied to 3.3V but the sensor still didn't answer on I2C. I ordered a different board then.

Do not buy this board!


The first version of the project used a Pro Micro clone. However, because of the small memory of the AVR chips, I had to use the smallest 128x32 pixel OLED display, which was difficult to read. In the end, I used a Seeeduino Xiao for the project together with a 128x64 pixel OLED display.

The sensor is very sensitive to the slightest finger movement (It would be better to attach the sensor to the finger). The readings seem also to be on the low side. I have a "real" Pulse Oximeter which measured 99%.






Android phone with Serial USB Terminal (Kai Morich)

Choose CDC as USB device

(USB communications device class)

(Phone with USB-C port)

Program


#include <Arduino.h>

#include <Wire.h>

#include "MAX30100_PulseOximeter.h"

#include <U8g2lib.h>


#define REPORTING_PERIOD_MS 1000


// U8G2_SSD1306_128X32_UNIVISION_F_HW_I2C u8g2(U8G2_R0);

// U8G2_SSD1306_128X64_NONAME_F_HW_I2C(rotation, [reset [, clock, data]]) [full framebuffer, size = 1024 bytes]

U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0);


// PulseOximeter is the higher level interface to the sensor

// it offers:

// * beat detection reporting

// * heart rate calculation

// * SpO2 (oxidation level) calculation

PulseOximeter pox;


bool beat=false;


uint32_t tsLastReport = 0;


void show_beat() //and update display

{

u8g2.setFont(u8g2_font_cursor_tf); // https://github.com/olikraus/u8g2/wiki/fntgrpx11#cursor

u8g2.setCursor(8,10);

if (beat==false) {

u8g2.print("_");

}

else

{

u8g2.print("^");

}

u8g2.sendBuffer();

}


// Callback (registered below) fired when a pulse is detected

void onBeatDetected()

{

show_beat();

beat = !beat;

}



void setup()

{

Serial.begin(9600); //this line is not really needed on the Xiao

u8g2.begin();


Serial.print("Initializing pulse oximeter..");


// Initialize the PulseOximeter instance

// Failures are generally due to an improper I2C wiring, missing power supply

// or wrong target chip

if (!pox.begin()) {

Serial.println("FAILED");

for(;;);

} else {

Serial.println("SUCCESS");

}


// The default current for the IR LED is 50mA and it could be changed

// by uncommenting the following line. Check MAX30100_Registers.h for all the

// available options.

// pox.setIRLedCurrent(MAX30100_LED_CURR_7_6MA);


// Register a callback for the beat detection

pox.setOnBeatDetectedCallback(onBeatDetected);

}


void loop()

{

// Make sure to call update as fast as possible

pox.update();


// Asynchronously dump heart rate and oxidation levels to the serial

// For both, a value of 0 means "invalid"

if (millis() - tsLastReport > REPORTING_PERIOD_MS) {

Serial.print("Heart rate: ");

Serial.print(pox.getHeartRate());

Serial.print(" bpm / SpO2: ");

Serial.print(pox.getSpO2());

Serial.println(" %");


u8g2.clearBuffer();

u8g2.setFont(u8g2_font_7x14B_tf); // https://github.com/olikraus/u8g2/wiki/fntgrpx11#7x14b

// u8g2.setFont(u8g2_font_9x18B_tf);

u8g2.setCursor(0,60);

u8g2.print("SpO2 ");

u8g2.setFont(u8g2_font_courB18_tf);

// u8g2.setCursor(65,12);

// u8g2.setCursor(0,30);

u8g2.setCursor(50,18);

u8g2.print(pox.getHeartRate(),0);

//u8g2.print(" Bpm");

// u8g2.setCursor(0,30);

// u8g2.setCursor(65,30);

u8g2.setCursor(50,60);

u8g2.print(pox.getSpO2());

u8g2.print(" %");

show_beat();

tsLastReport = millis();

}

}

Logging with python 3.7

Below, an example how to read raw data from the Arduino into python over serial. I use jupyter notebook, which is a powerful tool for interactively developing and presenting data science projects. Here, in this simple example, we just read the serial data and plot it. The signals are first zero, as no light is reflected back to the photo diode before the finger is placed on the sensor. Then, the signals rise fast as there is now both IR and red light reflected to the photo diode. Oxygenated hemoglobin absorbs more infrared light and allows more red light to pass through. The IR LED is set brighter in this example program (IR: 50 mA, Red: 27 mA). Corrected for the difference in brightness, less of the red than of the IR light is absorbed.

© 2021 notthemarsian