How do i read the frequency for a heart rate monitor?

I am doing a heart rate monitor and I need to read the beats per minute. What is the easiest way to calculate this assuming I have an analog input signal with 5 volt peaks? The signal contains other smaller peaks at around 1-2 volts which I don't want to read.

What is the easiest way to calculate this assuming I have an analog input signal with 5 volt peaks?

Why does no one ever come in here and ask "What is the proper way to do this". Everyone wants the easiest/cheapest/least effort method.

If you have an analog signal, you read values using analogRead. That function returns a value between 0 and 1023. It isn't rocket science to figure out how to use an if statement to only increment the counter when the value is greater than some threshold.

Frankly, if you can't figure out something this simple, you have no business doing anything as complex as a heart rate monitor.

Yes, however I figured that it would count multiple values for each peak. I was wondering if putting an if statement and then doing a delay would only count the peak once or if it was better to try to convert it into a digital signal and use an attach interrupt function?

Yes, however I figured that it would count multiple values for each peak.

Presuming that your signal goes up, then down, then back up, it should not be that hard to figure out when the signal dropped below the threshold, too. Only increment the counter once when the signal is above the threshold.

bool aboveThreshold = false;
const int heartPin = 0;
int threshold = 500;
int counter = 0;

void loop()
{
  int val = analogRead(heartPin);
  if(val > threshold && !aboveThreshold)
  {
     counter++;
     aboveThreshold = true;
  }

  if(val < threshold)
    aboveThreshold = false;
}

suggest you add hysterisis as the level is increasing it has to pass levelHI to get counted as a HIGH as the level decreases it has to pass levelLO to be regarded as a LOW then measure the time between successive HIGH and convert to a rate

hope that makes sense no I won't write the code :)

Thanks for the help. I need to calculate the BPM every 5 seconds. Is this a proper way of doing it?

bool aboveThreshold = false;
const int heartPin = 0;
int threshold = 500;
int counter = 0;
int bpm = 0;
offset = millis();

void loop()
{
int val = analogRead(heartPin);
if (millis() - offset >= 0 && < 5000 )
if(val > threshold && !aboveThreshold)
{
counter++;
aboveThreshold = true;
}

if(val < threshold)
{
aboveThreshold = false;
}
elseif{
bpm = counter * 12;
counter = 0;
offset = millis();
}

}

  if (millis() - offset >= 0 && < 5000 )

So, subtract offset from millis() (now). Compare the delta to 0. Then compare the true/false result to 5000. Not a reasonable comparison in my book.

unsigned long delta = millis() - offset;
if(delta >= 0 && delta < 5000)

In any case, I’d change the logic:

if(millis() - offset >= 5000)
{
   // Compute bpm
}
// Do the rest of the stuff (it's not an else condition)

It works well reading a sine wave but it can only read up to around 138 BPM when I increase the frequency. I believe it is because of my delay. How do I rid of the delay but still get the screen not to update as much? I know it’s because I have lcd.begin outside of the setup, but that allows it to erase characters that don’t update. For instance if my heart rate is first 66 and then the next time it computes it to be 0, it will display 06 instead of 0.

Also on my laptop I keep getting this error: “avr-g++: CreateProcess: No such file or directory” when I try to compile.
Thanks for the help :slight_smile:

#include <LiquidCrystal.h>
LiquidCrystal lcd(7, 8, 9, 10, 11, 12);
  bool aboveThreshold = false;
  int threshold = 500;
  int counter = 0;
  int bpm = 50;
  long offset = 0;
  int heartPin = A0;  // Analog input on A0
  int val = 0;
  const int ledPin =  13;   // LED on Pin 13
  int ledState = LOW; 
  
void setup()
{
  pinMode(ledPin, OUTPUT); 

}

void loop()
{
  unsigned long delta = millis() - offset;
   int val = analogRead(heartPin);
  if(delta >= 0 && delta < 10000) 
   {

    if(val > threshold && !aboveThreshold)
    {
       counter++;
       aboveThreshold = true;
    ledState = HIGH;
    digitalWrite(ledPin, ledState); // Turn on LED

    
    }

    if(val < threshold)
     {
       aboveThreshold = false;
    ledState = LOW;
    digitalWrite(ledPin, ledState); // Turn off LED
      }
      
   }
   if(delta >= 10000)
  {
   bpm = counter * 6;   // Compute bpm
   counter = 0;
   offset = millis();  // Reset Counter
  }
  
  lcd.begin(16, 2);       // Display Constants
  lcd.setCursor(5, 0);  
  lcd.print("Input");
  lcd.setCursor(0, 1);
  lcd.print("HeartRate");
  lcd.setCursor(13, 1);
  lcd.print("BPM");
  lcd.setCursor(12, 0);
  lcd.print(val);
  lcd.setCursor(10, 1);
  lcd.print(bpm);
  lcd.setCursor(0, 0);
  lcd.print(counter, DEC);
  delay(100);
}

I believe it is because of my delay.

Yes. And the time it takes to write to the LCD.

How do I rid of the delay but still get the screen not to update as much?

I'd tell you to look at the blink without delay example, but you've been there/done that, but apparently it didn't take.

// Add as global
unsigned long lastUpdate = 0;
unsigned long updateInterval = 500;

// Put in loop
if(millis() - lastUpdate > updateInterval)
{
   // Put your LCD code here, except 
   // lcd.begin() which belongs in setup
   // and get rid of the delay() call
}

I know it's because I have lcd.begin outside of the setup, but that allows it to erase characters that don't update.

  lcd.print(bpm);
  lcd.print("   "); // Wipe out trailing characters...

For instance if my heart rate is first 66 and then the next time it computes it to be 0, it will display 06 instead of 0.

If it computes 0, your in cardiac arrest. Is the display important then?