Measure Heart pulses/BPM And Oxygen saturation SpO² with MAX30102 and Arduino
Hello, and welcome to this tutorial where I’m testing another module to measure BPM, after the 1$ module dodgy tests, here we are with another one it’s the MAX30102, I’ll use it to measure my BPM and Pulse oximetry with the help of an Arduino UNO board, and also using an OLED display + Buzzer for a little BPM related project that you can find below.
The MAX30102 is an integrated pulse oximetry and heart-rate monitor biosensor module. It includes internal LEDs, photodetectors, optical elements also i²c compatible and with the help of the SparkFun library it will be easy to measure these parameters, and don’t forget that the module has a built-in temperature sensor and it’s actually very accurate and stable but it’s not meant to measure body temperature.
You can find it in different shapes but the important is the chip used, (I’m using the black one, WAVGAT):
Module main chip and different variants of the module.
Hardware:
For this tutorial, I used the module alone with the Arduino to test its functions, and for the project I add an OLED and a buzzer. In case you don’t know how to use the OLED screen or the buzzer check these tutorials:
Wiring:
The MAX30102 Can use the i²c ,so wire it with the Arduino board using the usual A4/A5 with SDA/SCL same with the OLED, the first is powered by 5v and the second by 3.3v, the buzzer is also using 5v/GND and it’s I/O wired with D3, if you’re using 2 pins buzzer: GND with GND and (+) (don’t forget a resistor) with D3.
Libraries:
If you’re using the module alone download only its library (The first one), the other two are for the OLED display used in the project.
- SparkFun MAX30102 library: Download here from Github or Download here.
- Adafruit OLED SSD1306 library: Download here from Github or Download here.
- Adafruit GFX Library: Download here or Download here.
Codes:
Code 1: Library example – HeartBeat_Plotter
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
/* Heart beat plotting! By: Nathan Seidle @ SparkFun Electronics Date: October 20th, 2016 Shows the user's heart beat on Arduino's serial plotter Instructions: 1) Load code onto Redboard 2) Attach sensor to your finger with a rubber band (see below) 3) Open Tools->'Serial Plotter' 4) Make sure the drop down is set to 115200 baud 5) Checkout the blips! 6) Feel the pulse on your neck and watch it mimic the blips It is best to attach the sensor to your finger using a rubber band or other tightening device. Humans are generally bad at applying constant pressure to a thing. When you press your finger against the sensor it varies enough to cause the blood in your finger to flow differently which causes the sensor readings to go wonky. Hardware Connections (Breakoutboard to Arduino): -5V = 5V (3.3V is allowed) -GND = GND -SDA = A4 (or SDA) -SCL = A5 (or SCL) -INT = Not connected The MAX30105 Breakout can handle 5V or 3.3V I2C logic. We recommend powering the board with 5V but it will also run at 3.3V. */ #include <Wire.h> #include "MAX30105.h" MAX30105 particleSensor; void setup() { Serial.begin(115200); Serial.println("Initializing..."); // Initialize sensor if (!particleSensor.begin(Wire, I2C_SPEED_FAST)) //Use default I2C port, 400kHz speed { Serial.println("MAX30105 was not found. Please check wiring/power. "); while (1); } //Setup to sense a nice looking saw tooth on the plotter byte ledBrightness = 0x1F; //Options: 0=Off to 255=50mA byte sampleAverage = 8; //Options: 1, 2, 4, 8, 16, 32 byte ledMode = 3; //Options: 1 = Red only, 2 = Red + IR, 3 = Red + IR + Green int sampleRate = 100; //Options: 50, 100, 200, 400, 800, 1000, 1600, 3200 int pulseWidth = 411; //Options: 69, 118, 215, 411 int adcRange = 4096; //Options: 2048, 4096, 8192, 16384 particleSensor.setup(ledBrightness, sampleAverage, ledMode, sampleRate, pulseWidth, adcRange); //Configure sensor with these settings //Arduino plotter auto-scales annoyingly. To get around this, pre-populate //the plotter with 500 of an average reading from the sensor //Take an average of IR readings at power up const byte avgAmount = 64; long baseValue = 0; for (byte x = 0 ; x < avgAmount ; x++) { baseValue += particleSensor.getIR(); //Read the IR value } baseValue /= avgAmount; //Pre-populate the plotter so that the Y scale is close to IR values for (int x = 0 ; x < 500 ; x++) Serial.println(baseValue); } void loop() { Serial.println(particleSensor.getIR()); //Send raw data to plotter } |
Code 2: Library example – SpO2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 |
/* Optical SP02 Detection (SPK Algorithm) using the MAX30105 Breakout By: Nathan Seidle @ SparkFun Electronics Date: October 19th, 2016 This demo shows heart rate and SPO2 levels. It is best to attach the sensor to your finger using a rubber band or other tightening device. Humans are generally bad at applying constant pressure to a thing. When you press your finger against the sensor it varies enough to cause the blood in your finger to flow differently which causes the sensor readings to go wonky. This example is based on MAXREFDES117 and RD117_LILYPAD.ino from Maxim. Their example was modified to work with the SparkFun MAX30105 library and to compile under Arduino 1.6.11 Please see license file for more info. Hardware Connections (Breakoutboard to Arduino): -5V = 5V (3.3V is allowed) -GND = GND -SDA = A4 (or SDA) -SCL = A5 (or SCL) -INT = Not connected The MAX30105 Breakout can handle 5V or 3.3V I2C logic. We recommend powering the board with 5V but it will also run at 3.3V. */ #include <Wire.h> #include "MAX30105.h" #include "spo2_algorithm.h" MAX30105 particleSensor; #define MAX_BRIGHTNESS 255 #if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__) //Arduino Uno doesn't have enough SRAM to store 100 samples of IR led data and red led data in 32-bit format //To solve this problem, 16-bit MSB of the sampled data will be truncated. Samples become 16-bit data. uint16_t irBuffer[100]; //infrared LED sensor data uint16_t redBuffer[100]; //red LED sensor data #else uint32_t irBuffer[100]; //infrared LED sensor data uint32_t redBuffer[100]; //red LED sensor data #endif int32_t bufferLength; //data length int32_t spo2; //SPO2 value int8_t validSPO2; //indicator to show if the SPO2 calculation is valid int32_t heartRate; //heart rate value int8_t validHeartRate; //indicator to show if the heart rate calculation is valid byte pulseLED = 11; //Must be on PWM pin byte readLED = 13; //Blinks with each data read void setup() { Serial.begin(115200); // initialize serial communication at 115200 bits per second: pinMode(pulseLED, OUTPUT); pinMode(readLED, OUTPUT); // Initialize sensor if (!particleSensor.begin(Wire, I2C_SPEED_FAST)) //Use default I2C port, 400kHz speed { Serial.println(F("MAX30105 was not found. Please check wiring/power.")); while (1); } Serial.println(F("Attach sensor to finger with rubber band. Press any key to start conversion")); while (Serial.available() == 0) ; //wait until user presses a key Serial.read(); byte ledBrightness = 60; //Options: 0=Off to 255=50mA byte sampleAverage = 4; //Options: 1, 2, 4, 8, 16, 32 byte ledMode = 2; //Options: 1 = Red only, 2 = Red + IR, 3 = Red + IR + Green byte sampleRate = 100; //Options: 50, 100, 200, 400, 800, 1000, 1600, 3200 int pulseWidth = 411; //Options: 69, 118, 215, 411 int adcRange = 4096; //Options: 2048, 4096, 8192, 16384 particleSensor.setup(ledBrightness, sampleAverage, ledMode, sampleRate, pulseWidth, adcRange); //Configure sensor with these settings } void loop() { bufferLength = 100; //buffer length of 100 stores 4 seconds of samples running at 25sps //read the first 100 samples, and determine the signal range for (byte i = 0 ; i < bufferLength ; i++) { while (particleSensor.available() == false) //do we have new data? particleSensor.check(); //Check the sensor for new data redBuffer[i] = particleSensor.getRed(); irBuffer[i] = particleSensor.getIR(); particleSensor.nextSample(); //We're finished with this sample so move to next sample Serial.print(F("red=")); Serial.print(redBuffer[i], DEC); Serial.print(F(", ir=")); Serial.println(irBuffer[i], DEC); } //calculate heart rate and SpO2 after first 100 samples (first 4 seconds of samples) maxim_heart_rate_and_oxygen_saturation(irBuffer, bufferLength, redBuffer, &spo2, &validSPO2, &heartRate, &validHeartRate); //Continuously taking samples from MAX30102. Heart rate and SpO2 are calculated every 1 second while (1) { //dumping the first 25 sets of samples in the memory and shift the last 75 sets of samples to the top for (byte i = 25; i < 100; i++) { redBuffer[i - 25] = redBuffer[i]; irBuffer[i - 25] = irBuffer[i]; } //take 25 sets of samples before calculating the heart rate. for (byte i = 75; i < 100; i++) { while (particleSensor.available() == false) //do we have new data? particleSensor.check(); //Check the sensor for new data digitalWrite(readLED, !digitalRead(readLED)); //Blink onboard LED with every data read redBuffer[i] = particleSensor.getRed(); irBuffer[i] = particleSensor.getIR(); particleSensor.nextSample(); //We're finished with this sample so move to next sample //send samples and calculation result to terminal program through UART Serial.print(F("red=")); Serial.print(redBuffer[i], DEC); Serial.print(F(", ir=")); Serial.print(irBuffer[i], DEC); Serial.print(F(", HR=")); Serial.print(heartRate, DEC); Serial.print(F(", HRvalid=")); Serial.print(validHeartRate, DEC); Serial.print(F(", SPO2=")); Serial.print(spo2, DEC); Serial.print(F(", SPO2Valid=")); Serial.println(validSPO2, DEC); } //After gathering 25 new samples recalculate HR and SP02 maxim_heart_rate_and_oxygen_saturation(irBuffer, bufferLength, redBuffer, &spo2, &validSPO2, &heartRate, &validHeartRate); } } |
Code 3: Library example – Temperature_Sense
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
/* MAX3010 Breakout: Read the onboard temperature sensor By: Nathan Seidle @ SparkFun Electronics Date: October 20th, 2016 This demo outputs the onboard temperature sensor. The temp sensor is accurate to +/-1 C but has an astonishing precision of 0.0625 C. Hardware Connections (Breakoutboard to Arduino): -5V = 5V (3.3V is allowed) -GND = GND -SDA = A4 (or SDA) -SCL = A5 (or SCL) -INT = Not connected The MAX30105 Breakout can handle 5V or 3.3V I2C logic. We recommend powering the board with 5V but it will also run at 3.3V. */ #include <Wire.h> #include "MAX30105.h" //Get it here: http://librarymanager/All#SparkFun_MAX30105 MAX30105 particleSensor; void setup() { Serial.begin(9600); Serial.println("Initializing..."); // Initialize sensor if (particleSensor.begin(Wire, I2C_SPEED_FAST) == false) //Use default I2C port, 400kHz speed { Serial.println("MAX30105 was not found. Please check wiring/power. "); while (1); } //The LEDs are very low power and won't affect the temp reading much but //you may want to turn off the LEDs to avoid any local heating particleSensor.setup(0); //Configure sensor. Turn off LEDs //particleSensor.setup(); //Configure sensor. Use 25mA for LED drive particleSensor.enableDIETEMPRDY(); //Enable the temp ready interrupt. This is required. } void loop() { float temperature = particleSensor.readTemperature(); Serial.print("temperatureC="); Serial.print(temperature, 4); float temperatureF = particleSensor.readTemperatureF(); //Because I am a bad global citizen Serial.print(" temperatureF="); Serial.print(temperatureF, 4); Serial.println(); } |
Code 4: Heart rate measuring and display on OLED + buzzer sound
The code I’ve used that works with the OLED and Buzzer, it’s a modified version of “HeartRate” code. Download the code here, or see below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
/* This code works with MAX30102 + 128x32 OLED i2c + Buzzer and Arduino UNO * It's displays the Average BPM on the screen, with an animation and a buzzer sound * everytime a heart pulse is detected * It's a modified version of the HeartRate library example * Refer to http://www.surtrtech.com for more details or SurtrTech YouTube channel */ #include <Adafruit_GFX.h> //OLED libraries #include <Adafruit_SSD1306.h> #include <Wire.h> #include "MAX30105.h" //MAX3010x library #include "heartRate.h" //Heart rate calculating algorithm MAX30105 particleSensor; const byte RATE_SIZE = 4; //Increase this for more averaging. 4 is good. byte rates[RATE_SIZE]; //Array of heart rates byte rateSpot = 0; long lastBeat = 0; //Time at which the last beat occurred float beatsPerMinute; int beatAvg; #define SCREEN_WIDTH 128 // OLED display width, in pixels #define SCREEN_HEIGHT 32 // OLED display height, in pixels #define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino 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, //Logo2 and Logo3 are two bmp pictures that display on the OLED if called 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 }; void setup() { display.begin(SSD1306_SWITCHCAPVCC, 0x3C); //Start the OLED display display.display(); delay(3000); // Initialize sensor particleSensor.begin(Wire, I2C_SPEED_FAST); //Use default I2C port, 400kHz speed particleSensor.setup(); //Configure sensor with default settings particleSensor.setPulseAmplitudeRed(0x0A); //Turn Red LED to low to indicate sensor is running } 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 //Also detecting a heartbeat if(irValue > 7000){ //If a finger is detected display.clearDisplay(); //Clear the display display.drawBitmap(5, 5, logo2_bmp, 24, 21, WHITE); //Draw the first bmp picture (little heart) display.setTextSize(2); //Near it display the average BPM you can display the BPM if you want display.setTextColor(WHITE); display.setCursor(50,0); display.println("BPM"); display.setCursor(50,18); display.println(beatAvg); display.display(); if (checkForBeat(irValue) == true) //If a heart beat is detected { display.clearDisplay(); //Clear the display display.drawBitmap(0, 0, logo3_bmp, 32, 32, WHITE); //Draw the second picture (bigger heart) display.setTextSize(2); //And still displays the average BPM display.setTextColor(WHITE); display.setCursor(50,0); display.println("BPM"); display.setCursor(50,18); display.println(beatAvg); display.display(); tone(3,1000); //And tone the buzzer for a 100ms you can reduce it it will be better delay(100); noTone(3); //Deactivate the buzzer to have the effect of a "bip" //We sensed a beat! long delta = millis() - lastBeat; //Measure duration between two beats lastBeat = millis(); beatsPerMinute = 60 / (delta / 1000.0); //Calculating the BPM if (beatsPerMinute < 255 && beatsPerMinute > 20) //To calculate the average we strore some values (4) then do some math to calculate the average { rates[rateSpot++] = (byte)beatsPerMinute; //Store this reading in the array rateSpot %= RATE_SIZE; //Wrap variable //Take average of readings beatAvg = 0; for (byte x = 0 ; x < RATE_SIZE ; x++) beatAvg += rates[x]; beatAvg /= RATE_SIZE; } } } if (irValue < 7000){ //If no finger is detected it inform the user and put the average BPM to 0 or it will be stored for the next measure beatAvg=0; display.clearDisplay(); display.setTextSize(1); display.setTextColor(WHITE); display.setCursor(30,5); display.println("Please Place "); display.setCursor(30,15); display.println("your finger "); display.display(); noTone(3); } } |
Test:
After wiring the components, upload the code.
Put your finger on the sensor, relax for a while until you hear the buzzer is synchronized with your heart beats, or you see that the heart pictures change following your heart beats.
Making a BPM picture:
Both hearts are BPM pictures and in the code you’ll see them as this format:
To make them you can follow Adafruit tutorial or continue here:
First thing to do is to make a 2 colors picture (Black with White background is better) and don’t forget to scale it (my OLED is 128×32 px) some are (128×64) so you should stay at this limits.
For the project I took two different heart pictures, scale them and most important !!
They must be “Monochrome Bitmap” you can save them as this format from you picture editing software or just “MS Paint”
Once you have the picture scaled and in the right format open the LCD Assistant (Download and unzip)
And to call it in the code, here’s how in my code
1 |
display.drawBitmap(5, 5, logo2_bmp, 24, 21, WHITE); |
Meaning
1 |
display.drawBitmap(Starting x pixel, Starting y pixel, pic name, pic width, pic height, color); |
And here you go make what you want.
I hope it works for and don’t forget to subscribe to the YouTube channel for more content.
Categories
Yassine View All
Automation and Electrical Engineer, Electronics amateur trying to share my little projects.
hello, I am recently working with nodemcu and max30102 and I had a problem. red light in max30102 won’t turn on. I’ve checked all the power things and I am sure nothing was wrong. the red light will turn on if I connect ird or rd pin to the ground. and also the program was stuck on initializing. I tried to change to Arduino Uno and it’s all same.
Did you check if the module is soldred right? Also try to run an i²c scanner to check if the module is responding or not.
hi how can i added 2 key for edit hour and minutes to this project??
HELLO VERY GOOD MORNING YOUR TUTORIAL WORKS WELL, DO YOU THINK YOU COULD HELP ME BY ADDING SPO2 THE OXYGENATION OF BLOOD TO THE PROJECT?
PLEASE
y crear una app para el celular
and create an app so you can see the spo2 bpm levels and a set point in case the levels are not correct
tambien pienso incluir el nivel de azucar en la sangre
please can you send me the libraries and the library that i am unable to download
Hi,
I have got the heart rate monitor working, however I am not seeing any results on the serial plotter because the code does not include that.
I would really appreciate if you could send me the code you used to run it successfully?
Many thanks.
Hello, please check the video you’ll see which one I used, I can’t remember, it was either a library example or something modified that I left a link to download it…
hi i have a problem with the code, it shows error compiling for board arduino uno . can anyone explain to me. how to solve?
Please make sure that you connected wiring properly with Arduino.
Ciao ,penso non serva chiederti informazioni o modifiche,in quanto hai solo copiato un esempio dalla libreria!!!
PLEASE SEND THIS CODE IN ENGLISH LANGUAGE IT IS VERY HELPFUL FOR ME ,THANK YOU