Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save cyrusmeh/6d6954a5f519d88af43eb3f8d4ffbe45 to your computer and use it in GitHub Desktop.

Select an option

Save cyrusmeh/6d6954a5f519d88af43eb3f8d4ffbe45 to your computer and use it in GitHub Desktop.
In this comprehensive tutorial, we’ll show you how to interface the MAX30102 Pulse Oximeter Sensor with an Arduino to measure two crucial health parameters: Heart Rate (BPM) and Blood Oxygen Concentration (SpO2). The MAX30102 sensor is perfect for creating wearable health monitoring devices, combining both heart rate and SpO2 measurement capabil…
#include <Adafruit_GFX.h> //OLED libraries
#include <Adafruit_SSD1306.h> //OLED libraries
#include "MAX30105.h" //MAX3010x library
#include "heartRate.h" //Heart rate calculating algorithm
////////////////////////////
//#include "ESP32Servo.h"
/////////////////////////////
MAX30105 particleSensor;
const byte RATE_SIZE = 4; //
byte rates[RATE_SIZE]; //
byte rateSpot = 0;
long lastBeat = 0; //Time at which the last beat occurred
float beatsPerMinute;
int beatAvg;
double avered = 0;
double aveir = 0;
double sumirrms = 0;
double sumredrms = 0;
double SpO2 = 0;
double ESpO2 = 60.0;//
double FSpO2 = 0.7; //filter factor for estimated SpO2
double frate = 0.95; //low pass filter for IR/red LED value to eliminate AC component
int i = 0;
int Num = 30;
#define FINGER_ON 7000
#define MINIMUM_SPO2 60.0
#define SCREEN_WIDTH 128 //OLED
#define SCREEN_HEIGHT 64 //OLED
#define OLED_RESET -1 //Reset pin
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); //Declaring the display name (display)
static const unsigned char PROGMEM logo2_bmp[] =
{ 0x03, 0xC0, 0xF0, 0x06, 0x71, 0x8C, 0x0C, 0x1B, 0x06, 0x18, 0x0E, 0x02, 0x10, 0x0C, 0x03, 0x10,
0x04, 0x01, 0x10, 0x04, 0x01, 0x10, 0x40, 0x01, 0x10, 0x40, 0x01, 0x10, 0xC0, 0x03, 0x08, 0x88,
0x02, 0x08, 0xB8, 0x04, 0xFF, 0x37, 0x08, 0x01, 0x30, 0x18, 0x01, 0x90, 0x30, 0x00, 0xC0, 0x60,
0x00, 0x60, 0xC0, 0x00, 0x31, 0x80, 0x00, 0x1B, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x04, 0x00,
};
static const unsigned char PROGMEM logo3_bmp[] =
{ 0x01, 0xF0, 0x0F, 0x80, 0x06, 0x1C, 0x38, 0x60, 0x18, 0x06, 0x60, 0x18, 0x10, 0x01, 0x80, 0x08,
0x20, 0x01, 0x80, 0x04, 0x40, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0x02, 0xC0, 0x00, 0x08, 0x03,
0x80, 0x00, 0x08, 0x01, 0x80, 0x00, 0x18, 0x01, 0x80, 0x00, 0x1C, 0x01, 0x80, 0x00, 0x14, 0x00,
0x80, 0x00, 0x14, 0x00, 0x80, 0x00, 0x14, 0x00, 0x40, 0x10, 0x12, 0x00, 0x40, 0x10, 0x12, 0x00,
0x7E, 0x1F, 0x23, 0xFE, 0x03, 0x31, 0xA0, 0x04, 0x01, 0xA0, 0xA0, 0x0C, 0x00, 0xA0, 0xA0, 0x08,
0x00, 0x60, 0xE0, 0x10, 0x00, 0x20, 0x60, 0x20, 0x06, 0x00, 0x40, 0x60, 0x03, 0x00, 0x40, 0xC0,
0x01, 0x80, 0x01, 0x80, 0x00, 0xC0, 0x03, 0x00, 0x00, 0x60, 0x06, 0x00, 0x00, 0x30, 0x0C, 0x00,
0x00, 0x08, 0x10, 0x00, 0x00, 0x06, 0x60, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00
};
static const unsigned char PROGMEM O2_bmp[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x3f, 0xc3, 0xf8, 0x00, 0xff, 0xf3, 0xfc,
0x03, 0xff, 0xff, 0xfe, 0x07, 0xff, 0xff, 0xfe, 0x0f, 0xff, 0xff, 0xfe, 0x0f, 0xff, 0xff, 0x7e,
0x1f, 0x80, 0xff, 0xfc, 0x1f, 0x00, 0x7f, 0xb8, 0x3e, 0x3e, 0x3f, 0xb0, 0x3e, 0x3f, 0x3f, 0xc0,
0x3e, 0x3f, 0x1f, 0xc0, 0x3e, 0x3f, 0x1f, 0xc0, 0x3e, 0x3f, 0x1f, 0xc0, 0x3e, 0x3e, 0x2f, 0xc0,
0x3e, 0x3f, 0x0f, 0x80, 0x1f, 0x1c, 0x2f, 0x80, 0x1f, 0x80, 0xcf, 0x80, 0x1f, 0xe3, 0x9f, 0x00,
0x0f, 0xff, 0x3f, 0x00, 0x07, 0xfe, 0xfe, 0x00, 0x0b, 0xfe, 0x0c, 0x00, 0x1d, 0xff, 0xf8, 0x00,
0x1e, 0xff, 0xe0, 0x00, 0x1f, 0xff, 0x00, 0x00, 0x1f, 0xf0, 0x00, 0x00, 0x1f, 0xe0, 0x00, 0x00,
0x0f, 0xe0, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
int Tonepin = 4;
void setup() {
Serial.begin(115200);
Serial.println("System Start");
display.begin(SSD1306_SWITCHCAPVCC, 0x3C); //Start the OLED display
display.display();
delay(3000);
if (!particleSensor.begin(Wire, I2C_SPEED_FAST)) //Use default I2C port, 400kHz speed
{
Serial.println("找不到MAX30102");
while (1);
}
byte ledBrightness = 0x7F;
byte sampleAverage = 4; //Options: 1, 2, 4, 8, 16, 32
byte ledMode = 2; //Options: 1 = Red only, 2 = Red + IR
int sampleRate = 800; //Options: 50, 100, 200, 400, 800, 1000, 1600, 3200
int pulseWidth = 215; //Options: 69, 118, 215, 411
int adcRange = 16384; //Options: 2048, 4096, 8192, 16384
// Set up the wanted parameters
particleSensor.setup(ledBrightness, sampleAverage, ledMode, sampleRate, pulseWidth, adcRange); //Configure sensor with these settings
particleSensor.enableDIETEMPRDY();
particleSensor.setPulseAmplitudeRed(0x0A); //Turn Red LED to low to indicate sensor is running
particleSensor.setPulseAmplitudeGreen(0); //Turn off Green LED
}
void loop() {
long irValue = particleSensor.getIR(); //Reading the IR value it will permit us to know if there's a finger on the sensor or not
if (irValue > FINGER_ON ) {
if (checkForBeat(irValue) == true) {
display.clearDisplay();
display.drawBitmap(0, 0, logo3_bmp, 32, 32, WHITE);
display.setTextSize(2);
display.setTextColor(WHITE);
display.setCursor(42, 10);
//////////////////////////////////////////////////////
display.print(beatAvg); display.println(" BPM");
///////////////////////////////////////////////////
display.drawBitmap(0, 35, O2_bmp, 32, 32, WHITE);
display.setCursor(42, 40);
if (beatAvg > 30) display.print(String(ESpO2) + "%");
else display.print("---- %" );
display.display();
////////////////////////////////////////////////////
tone(Tonepin, 1000);
delay(10);
noTone(Tonepin);
Serial.print("Bpm="); Serial.println(beatAvg);
////////////////////////////////////////////////////
long delta = millis() - lastBeat;
lastBeat = millis();
beatsPerMinute = 60 / (delta / 1000.0);
if (beatsPerMinute < 255 && beatsPerMinute > 20) {
rates[rateSpot++] = (byte)beatsPerMinute;
rateSpot %= RATE_SIZE;
beatAvg = 0;
for (byte x = 0 ; x < RATE_SIZE ; x++) beatAvg += rates[x];
beatAvg /= RATE_SIZE;
}
}
uint32_t ir, red ;
double fred, fir;
particleSensor.check(); //Check the sensor, read up to 3 samples
if (particleSensor.available()) {
i++;
ir = particleSensor.getFIFOIR();
red = particleSensor.getFIFORed();
///////////////////////////////////////////////////////////////////////////////////
Serial.println("red=" + String(red) + ",IR=" + String(ir) + ",i=" + String(i));
///////////////////////////////////////////////////////////////////////////////////
fir = (double)ir;
fred = (double)red;
aveir = aveir * frate + (double)ir * (1.0 - frate); //average IR level by low pass filter
avered = avered * frate + (double)red * (1.0 - frate);//average red level by low pass filter
sumirrms += (fir - aveir) * (fir - aveir);//square sum of alternate component of IR level
sumredrms += (fred - avered) * (fred - avered); //square sum of alternate component of red level
if ((i % Num) == 0) {
double R = (sqrt(sumirrms) / aveir) / (sqrt(sumredrms) / avered);
SpO2 = -23.3 * (R - 0.4) + 120;
ESpO2 = FSpO2 * ESpO2 + (1.0 - FSpO2) * SpO2;//low pass filter
if (ESpO2 <= MINIMUM_SPO2) ESpO2 = MINIMUM_SPO2; //indicator for finger detached
if (ESpO2 > 100) ESpO2 = 99.9;
////////////////////////////////////////////////
Serial.print(",SPO2="); Serial.println(ESpO2);
////////////////////////////////////////////////
sumredrms = 0.0; sumirrms = 0.0; SpO2 = 0;
i = 0;
}
particleSensor.nextSample(); //We're finished with this sample so move to next sample
}
Serial.print("Bpm:" + String(beatAvg));
if (beatAvg > 30) Serial.println(",SPO2:" + String(ESpO2));
else Serial.println(",SPO2:" + String(ESpO2));
display.clearDisplay();
display.drawBitmap(5, 5, logo2_bmp, 24, 21, WHITE);
display.setTextSize(2);
display.setTextColor(WHITE);
display.setCursor(42, 10);
display.print(beatAvg); display.println(" BPM");
display.drawBitmap(0, 35, O2_bmp, 32, 32, WHITE);
display.setCursor(42, 40);
if (beatAvg > 30) display.print(String(ESpO2) + "%");
else display.print("---- %" );
display.display();
}
else {
for (byte rx = 0 ; rx < RATE_SIZE ; rx++) rates[rx] = 0;
beatAvg = 0; rateSpot = 0; lastBeat = 0;
avered = 0; aveir = 0; sumirrms = 0; sumredrms = 0;
SpO2 = 0; ESpO2 = 90.0;
display.clearDisplay();
display.setTextSize(2);
display.setTextColor(WHITE);
display.setCursor(30, 5);
display.println("Finger");
display.setCursor(30, 35);
display.println("Please");
display.display();
////////////////////////
noTone(Tonepin);
///////////////////////
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment