Introduction: Pulse Oximeter With ESP32

About: Alireza Mousavian Electrical Engineer

These days, the COVID19 causes people to pay more attention to their health.One of the most important parameter effected by the corona virus is the level of blood oxygen.Thus, it would be nice if you made a device at home to monitor your blood oxygen and check your healthy!

With MAX30100 pulse oximeter module you can measure your heart beat and the blood oxygen percentage.

In this tutorial, we use ESP32 DEVKIT v1 but you can use ESP8266 or any Arduino board like Arduino UNO.

The display we used is OLED 128*64 SSD1306. It communicates with the MCU through I2C, thus the connection is so simple as it is shown in the article.

Supplies

ESP32 or any other MCU that works with Arduino IDE such as ESP32, ESP8266 and ARDUINO Boards

MAX30100 pulse oximeter module

OLED SSD1306 display

BreadBoard

jumper

Step 1: MAX30100

How the module works?It sends a red light and an infrared light through your fingertip, then it measure the reflected lights with its integrated ADC and does some processing on the signals.The more oxygen the blood contains, the more infrared it absorbs and also more oxygen in the blood would let pass more red light through it. Similarly, When heart pumps blood, module would sense the increment of oxygen caused by pumped blood.

This module sends data to the microcontroller through I2C connection.

There are two version of MAX30100 in the store:

the green one and the purple one shown in the picture.

The purple one is ready to use, but the green one have an issue that should be solved before using.

Briefly, the I2C of this module has the HIGH level of 1.8V while the Arduino considers at least 2V as HIGH. Consequently, they are not able to communicate correctly.

In the schematic of MAX30100 could be seen that the SDA and SCL are pulled-up to 1.8V.

Therefore, if we disconnect the resistors from 1.8V and connect them to 3.3V on-board regulator, the problem will be solved!


What is the solution?


First cut the track that is marked with the red line in pic with a sharp razor.

Second, connect the two blue marked point (in the picture) soldering.

If you did these two steps correctly, the module would be ready to use.

Step 2: OLED SSD1306 0.96" DISPLAY

This display is almost the most popular oled display that is used in electronic projects with 128*64 pixels. It is controlled by the powerful ssd1306 driver that uses I2C protocol to communicate.

The VCC power for this monitor is 3.3V and it dose not need separate power for background light.


Step 3: ESP32

Briefly, ESP32 is a series of powerfull low-cost microcontrollers with integrated WiFi and Bluetooth.

It could be programmed through Arduino IDE and its on-board micro USB port.



Step 4: Schematic

According to ESP32 pinout, the SCL pin is D22 and the SDA is D21.

As you can see in the picture, the connections are:

ESP32 D22 ----> MAX30100 SCL

ESP32 D21 ----> MAX30100 SDA

ESP32 GND ----> MAX30100 GND

ESP32 3.3V -----> MAX30100 VIN

OLED VDD -----> ESP32 3.3V

OLED GND -----> ESP32 GND

OLED SCL -----> ESP32 D22

OLED SDA ----> ESP D21

Step 5: Preparing Arduino IDE and Add Necessary Libraries

Add ESP32 Boards To Arduino IDE


To program ESP32 with Arduino IDE, first we should add esp32 boards to the app. To do so, follow the steps:

(if you already have added ESP32 boards to your IDE, you can skip these steps.)

1) Open Arduino IDE and go to File > Preferences > Additional Boards Manager URLs and paste this link:

https://dl.espressif.com/dl/package_esp32_index.json

and click OK.


2) In IDE go to Tools > Board > Board Manager

Type ESP32 in search field and select install for ESP32 by Espressif Systems


3) Now the ESP32 is added to your IDE and you can select them as the target device to upload your program.

To do this:

Go to Tools > Board > ESP32 Arduino > ESP32 Dev Module (for this tutorial)

If you did this correctly, in the right lower corner, the type of the board is written (ESP32 Dev Module) like in the picture.


Add Necessary Libraries To Arduino IDE


To use MAX30100 add MAX30100_PulseOximeter.h and for the OLED display add Adafruit_SSD1306.h and Adafruit_GFX.h

To do this:

Go to Tools > Manage Libraries

search MAX30100 and install MAX30100lib like in the picture

search and install Adafruit_SSD1306.h

search and install Adafruit_GFX.h




Step 6: MAX30100 and OLED Libraries Explanation

OLED libraries:


With Adafruit_SSD1306.h library and Adafruit_GFX.h library together we can control the oled display.

the commands that we will use in this project:

To create an instant of ssd1306 which is defined in the library: (the name "OLED" can be whatever you like, but it will be used in the rest of the code)

it should be out of setup section or loop section.

Adafruit_SSD1306 OLED = Adafruit_SSD1306(128, 64, &Wire);

(128 and 64 are the width and the length of display )

To initialize the screen:

it should be in setup section.

OLED.begin(SSD1306_SWITCHCAPVCC, 0x3C);

0x3c is the default I2C address of the display.

To put cursor in the desired location:

OLED.setCursor(row pixel,column pixel);

For example to put the cursor in the middle position:

OLED.setCursor(32,64);

To turn on an single pixel:

(for example in (10,10) position)

OLED.drawPixel(10, 10, SSD1306_WHITE);     //  WITHE is the color and it depends on the oled display that you have.

They are available in blue and yellow color too.

To print something on screen:

OLED.print();

To print something on screen with new line after it:

OLED.println();

To change the font size:

OLED.setTextSize(2);     it can be 1 or 2

To clear the buffer:

OLED.clearDisplay();

(It turns out that everything you want to print on the screen, it goes first in the buffer, then you should update the display to make it actually display things)

To update the screen:

OLED.display();


MAX30100 library:


create an object of pulseoximeter to use in the code with desired name(like max_sensor):

it should be out of setup section or loop section.

PulseOximeter max_sensor;

The below code returns 1 if the sensor is working:

max_sensor.begin()

To tell the module to measure:

max_sensor.update();

To get the heartrate:

max_sensor.getHeartRate();

To get the blood oxygen level:

max_sensor.getSpO2();


There are other things that can be done with these libraries but we wouldn't use them in this project.

Step 7: Code Step by Step

In this section, I explain the code step by step :

We also use serial to print something to know the sensor is working.

First of all we should include libraries:

#include <Wire.h>                                 //     Arduino I2C library
#include <Adafruit_GFX.h>                         //     needed for OLED display
#include <Adafruit_SSD1306.h>                     //     needed for OLED display
#include "MAX30100_PulseOximeter.h"               //     MAX30100 library

The max30100 reporting time period can be defined in the code:

For example, 100 means that every 100ms, the number of heart rate and oxygen level would be updated.

#define REPORTING_PERIOD_MS    100

Create an object of the class PulseOximeter that is defined in the library, in order to use in the code.

And do the same for the display.But the constructor for display library is a little different.I named it OLED and you should pass the size of display in to this code as well.ours is 128*64 pixels

I named it max_sensor, but it can be whatever you like.

PulseOximeter max_sensor;
Adafruit_SSD1306 OLED = Adafruit_SSD1306(128, 64, &Wire);

Create a variable to record the time between to reads of MAX30100:

uint32_t lastReportTime = 0;

Define onBeatDetected function:

void onBeatDetected()
{
   Serial.println("Beat!");
}


Setup section:


According to previous step you know these:

 OLED.begin(SSD1306_SWITCHCAPVCC, 0x3C);
 Serial.begin(115200);
 OLED.display();
 OLED.clearDisplay();
 OLED.setTextSize(2);
 OLED.setTextColor(SSD1306_WHITE);
 OLED.setCursor(0,0);
 OLED.display();
 delay(1000);


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

check if the sensor is detected and its working.Also print failed to serial if it is not working:

 if (!max_sensor.begin()) {
       Serial.println("FAILED");
       for(;;);
   } else {
       Serial.println("SUCCESS");
   }

run onBeatDetected function if module detects beat:

max_sensor.setOnBeatDetectedCallback(onBeatDetected);


loop section:


Tell MAX module to update data:

max_sensor.update();

Check if the max30100 reporting time period is passed or not:

if (millis() - lastReportTime > REPORTING_PERIOD_MS) 
{

You can guess the following code:)

       OLED.clearDisplay();
       OLED.setCursor(0,0);
       OLED.print("H:");
       OLED.print(max_sensor.getHeartRate());
       OLED.println("bpm");
       OLED.print("SpO2:");
       OLED.print(max_sensor.getSpO2());
       OLED.println("%");

Record time:

lastReportTime = millis();
} 

Finally display things on screen:

OLED.display();

Step 8: Complete Code

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include "MAX30100_PulseOximeter.h"
#define REPORTING_PERIOD_MS    100
PulseOximeter max_sensor;
uint32_t lastReportTime = 0;
Adafruit_SSD1306 OLED = Adafruit_SSD1306(128, 32, &Wire);

void onBeatDetected()
{
   Serial.println("Beat!");
}



void setup() {
 // put your setup code here, to run once:
 OLED.begin(SSD1306_SWITCHCAPVCC, 0x3C);
 Serial.begin(115200);
 OLED.display();
 OLED.clearDisplay();
 OLED.setTextSize(2);
 OLED.setTextColor(SSD1306_WHITE);
 OLED.setCursor(0,0);
 OLED.display();
 delay(1000);


   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 (!max_sensor.begin()) {
       Serial.println("FAILED");
       for(;;);
   } else {
       Serial.println("SUCCESS");
   }
       max_sensor.setOnBeatDetectedCallback(onBeatDetected);
}

void loop() {
 // put your main code here, to run repeatedly:

max_sensor.update();


   if (millis() - lastReportTime > REPORTING_PERIOD_MS) {

       OLED.clearDisplay();
       OLED.setCursor(0,0);
       OLED.print("H:");
       OLED.print(max_sensor.getHeartRate());
       OLED.println("bpm");
       OLED.print("SpO2:");
       OLED.print(max_sensor.getSpO2());
       OLED.println("%");
       lastReportTime = millis();
   }
   OLED.display();
}