Code modification for measuring Heart Pulse Rate with MAX30102 pulse sensor

I am trying to read the Beats per minute through my Arduino based set up and a MAX30102 pulse sensor (datasheet).

The strength of the pulse is much weaker than usual.

Below is the visualization with signal and noise, which shows difference between rat pulse and human pulse.

Actual:

Theoretical:

The above visualizations are achieved through the below code.

   /*
  MAX30102- Heart Rate Pulse Detection-Module
  Home

  Based on Arduino Library Example   
*/
#include <Wire.h>
#include "MAX30105.h"

MAX30105 particleSensor;

void setup()
{
  Serial.begin(19200);
  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
}

And below is a measure of the BPM measurements of rat and human compared.

Rat BPM:

Human BPM:

Below is the code used to achieve the above.

    /*
  Optical Heart Rate Detection (PBA Algorithm) using the MAX30105 Breakout
  By: Nathan Seidle @ SparkFun Electronics
  Date: October 2nd, 2016
  https://github.com/sparkfun/MAX30105_Breakout

  This is a demo to show the reading of heart rate or beats per minute (BPM) using
  a Penpheral Beat Amplitude (PBA) algorithm.

  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"

#include "heartRate.h"

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;

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);
  }
  Serial.println("Place your index finger on the sensor with steady pressure.");

  particleSensor.setup(); //Configure sensor with default settings
  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();

  if (checkForBeat(irValue) == true)
  {
    //We sensed a beat!
    long delta = millis() - lastBeat;
    lastBeat = millis();

    beatsPerMinute = 60 / (delta / 1000.0);

    if (beatsPerMinute < 255 && beatsPerMinute > 20)
    {
      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;
    }
      }

  Serial.print("IR=");
  Serial.print(irValue);
  Serial.print(", BPM=");
  Serial.print(beatsPerMinute);
  Serial.print(", Avg BPM=");
  Serial.print(beatAvg);

  if (irValue < 50000)
    Serial.print(" No finger?");

  Serial.println();
}

Obviously the measurement of the real BPM is faulted as a healthy subject could never have a BPM like that.

I would like to know how to modify the above code to measure the BPM and to eliminate the noise (In order of priority).

I guess you know enough about rats that you know how to interpret the signal plot. You have used black and green color to mark some signal parts but you didn't tell us yet what it means. If all black is noise it should be already filtered out. The problem is, the curve we see doesn't show a much higher frequency than the human image, although we see only a small window. Are you sure that the sensor does work for the rat? Did you apply it correctly? I would expect a similar picture as from a human if the sensor is applied correctly and the measuring principle works for rats too. As you have much more noise and much higher amplitude in the noise but not the periodic spikes as for the human I must assume that the measuring isn't done correctly.

About whether I know the sensor works for rats, I think it should, nothing stops it from not working as the principal is the same (medical principal).

About not much higher frequency seen for a rat, I have noticed that too and that bothers me too but I would like to see the microcontroller computed figures before declaring that the tiny saw tooths I am seeing are not pulse. (My rat is reasonably healthy, so the frequency of saw tooths should be in healthy range).

I have attached the sensor well, the location should not be a problem. I do not expect a very similar image to a human's as the pulse of rat is much more jagged in general.

About the occurrence of noise in rat data and absence of the same in human data, its because the human data is taken on a finger and a human behaves well and keeps finger stable. The Rat's data is taken on its tail and even the tiniest of twitch in the tail creates noise.

The basic principle works but as the method is an optical check the parameters may be completely different. The parameters set by the library are chosen for a human test object. I guess these parameters were chosen after a lot of tests where the best working set was included into the library.
You have to do theses tests yourself to get a parameter set for rats that work.

Exactly, I wanted to know which parameters I needed to alter to make it check frequency of such low amplitudes. Could you help identify? I'm not software educated.

Just a guess, because I did not look at it, all your parameters will be found in "heartRate.h". Study that code and see what parameters they are using.

1 Like

Low amplitude? Your output doesn't look like it's just a lower amplitude. The periodic saw tooth is just not the same but that's filtered out anyway. But as the rat output doesn't have that saw tooth signal I guess that the hardware parameters are wrong. The parameters to the setup() method are mostly default values with the exception of the sampleAverage and sampleRate values. Interestingly you've chosen a lower sample rate for an animal that has a higher heart rate.

BTW, I'm rather surprised that you call that sensor "particleSensor", it doesn't measure particles at all.

Thanks, will study your reply in detail and try to check the code and entire set up. And that name 'particlesensor', I too don't know why the manufacturers call it that, maybe because its also supposed to measure blood oxygen levels.

ok, will check

I checked heartrate.h, it doesn't seem to have anything in it.

#if (ARDUINO >= 100)
 #include "Arduino.h"
#else
 #include "WProgram.h"
#endif

bool checkForBeat(int32_t sample);
int16_t averageDCEstimator(int32_t *p, uint16_t x);
int16_t lowPassFIRFilter(int16_t din);
int32_t mul16(int16_t x, int16_t y);

can you guide what I should be looking for?

Hi,
Are you going to run two separate threads on the same subject?
https://forum.arduino.cc/t/how-to-amplify-signal-received-from-i2c-interface/1058080/5

Tom.. :smiley: :+1: :coffee: :coffee: :coffee: :australia:

My priority is to calculate heart rate and see its fluctuation in regards to my experiment which involves the heart rate of the animal to vary and fall and even disappear under different test conditions.

I'm using a MAX30102 sensor which gives out signal through a I2C interface. If it was an analog signal I could've easily amplified it. Any way to do it in this scenario?

Hi, @anon90086852

Why do you want to amplify the I2C signal, it will not amplify the values that the I2C is communicating with the controller?

Can you please explain the situation, and what do you want to amplify, the value being measured by the 30102 or the actual I2C communication signal.

Thanks.. Tom.. :smiley: :+1: :coffee: :australia:
PS. The sensor sends pulse and oxy levels, why would you want to amplify those values?

So my problem is (as I had mentioned in another previous question) that I am trying to pick a rat's heart pulse from this sensor, placing it on its tail as is the process. The human pulse this sensor picks pretty neatly and calculates the heart rate, but the rat pulse is too low in amplitude and too high in frequency for the algorithms of the sample code to interpret (which I downloaded from the internet). I would like to do two things, one increase its amplitude, two remove the vase signal which takes the I2C adc values to above 60000 (I think its a 17bit ADC), so my code can calculate it easily and it can be easily visualized on a plotter as well.

/*
 Optical Heart Rate Detection (PBA Algorithm) using the MAX30105 Breakout
 By: Nathan Seidle @ SparkFun Electronics
 Date: October 2nd, 2016
 https://github.com/sparkfun/MAX30105_Breakout

 This is a demo to show the reading of heart rate or beats per minute (BPM) using
 a Penpheral Beat Amplitude (PBA) algorithm.

 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"

#include "heartRate.h"

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;

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);
 }
 Serial.println("Place your index finger on the sensor with steady pressure.");

 particleSensor.setup(); //Configure sensor with default settings
 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();

 if (checkForBeat(irValue) == true)
 {
   //We sensed a beat!
   long delta = millis() - lastBeat;
   lastBeat = millis();

   beatsPerMinute = 60 / (delta / 1000.0);

   if (beatsPerMinute < 255 && beatsPerMinute > 20)
   {
     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;
   }
 }

 Serial.print("IR=");
 Serial.print(irValue);
 Serial.print(", BPM=");
 Serial.print(beatsPerMinute);
 Serial.print(", Avg BPM=");
 Serial.print(beatAvg);

 if (irValue < 50000)
   Serial.print(" No finger?");

 Serial.println();
}

Hi,
Thanks for the heads up, not all of us read all the threads.

It would be good if you posted your existing working code that you have, rather than getting us to keep referring tp your other post.

Tom... :smiley: :+1: :coffee: :australia:

Hi, sure. Below is my code which I use for reading heartrate, and it is followed by code which I use to visualize the data...

/*
  Optical Heart Rate Detection (PBA Algorithm) using the MAX30105 Breakout
  By: Nathan Seidle @ SparkFun Electronics
  Date: October 2nd, 2016
  https://github.com/sparkfun/MAX30105_Breakout

  This is a demo to show the reading of heart rate or beats per minute (BPM) using
  a Penpheral Beat Amplitude (PBA) algorithm.

  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"

#include "heartRate.h"

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;

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);
  }
  Serial.println("Place your index finger on the sensor with steady pressure.");

  particleSensor.setup(); //Configure sensor with default settings
  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();

  if (checkForBeat(irValue) == true)
  {
    //We sensed a beat!
    long delta = millis() - lastBeat;
    lastBeat = millis();

    beatsPerMinute = 60 / (delta / 1000.0);

    if (beatsPerMinute < 255 && beatsPerMinute > 20)
    {
      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;
    }
  }

  Serial.print("IR=");
  Serial.print(irValue);
  Serial.print(", BPM=");
  Serial.print(beatsPerMinute);
  Serial.print(", Avg BPM=");
  Serial.print(beatAvg);

  if (irValue < 50000)
    Serial.print(" No finger?");

  Serial.println();
}

/*
MAX30102- Heart Rate Pulse Detection-Module
Home

Based on Arduino Library Example
*/
#include <Wire.h>
#include "MAX30105.h"

MAX30105 particleSensor;

void setup()
{
Serial.begin(19200);
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
}

Someone suggested I should make some modifications in heartrate.h, I checked that library but there isn't much in it, can you suggest would could be needing modification to read a low amplitude pulse frequency?


#if (ARDUINO >= 100)
 #include "Arduino.h"
#else
 #include "WProgram.h"
#endif

bool checkForBeat(int32_t sample);
int16_t averageDCEstimator(int32_t *p, uint16_t x);
int16_t lowPassFIRFilter(int16_t din);
int32_t mul16(int16_t x, int16_t y);

@anon90086852,

Your two topics on the same or similar subject have been merged.

I appreciate your questions are about different aspects of your project but they are both about the same project. Generally the answer to one question provides useful context for the next, so we encourage you to run one topic for all your questions about your project.

Could you take a few moments to Learn How To Use The Forum

It will help you get the best out of the forum in the future.

Thank you.

Thanks for merging these two. I had started a different question because the first didn't yield a solution, so I thought maybe a different question for different approach could sort things out.

1 Like

You are expecting too much for the forum. We do not give solutions to research projects, only offer suggestions to try.