[SOLVED] Creating a threshold when reading a heart rate

Hi all,

First time poster here but have hit a slight roadblock in a university project and am unsure how to continue. I have some coding experience in the past but this is my first time using arduino.

I have an optical heart rate sensor which returns a value and from this value you can see your heart rate (shown in the attached screenshot). For my project i'm mainly concerned with the peaks of the graph as I want to record the time intervals between heart rates however this is difficult as the value which is returned from the heart rate sensor varies greatly depending on how hard it is pressed to your skin.

I intended to use a threshold in the middle (the yellow line - Threshold) and then detect heartbeats whenever the wave crossed this line. In order to generate this threshold, I've used an average for the lowest and highest variables (green - LowTotal and red - HighTotal respectively) and the threshold is the mean of these two lines. However, these lines are not calculating as intended and are offset. The low average is a little above the bottom and the high average is way above at the top.

HighSample[TimersTriggered] = HighestSignal;
    LowSample[TimersTriggered] = LowestSignal;



    
  for (int i = 0; i <= ThresholdSamples - 1; i++){
    HighTotal += HighSample[i];
  }
  HighTotal /= ThresholdSamples;
  
    for (int i = 0; i <= ThresholdSamples - 1; i++) {
    LowTotal += LowSample[i];
  }
  LowTotal /= ThresholdSamples;

    Threshold = (HighTotal + LowTotal)/2;

I'm unsure as to how to rectify this and also unsure as to whether this method is the best solution for detecting the peaks in the wave.

As I said, this is my first time using arduino and my coding knowledge is a little rusty so any help would be greatly appreciated!

Full Code:

int LED13 = 13;   //  The on-board Arduion LED
int Signal;                // holds the incoming raw data. Signal value can range from 0-1024
int Threshold;            // Determine which Signal to "count as a beat", and which to ingore.
int N;
int TimersTriggered = 0;
int HighSample[10];
int LowSample[10];
int HighestSignal = 0;
int LowestSignal = 1000;
const int ThresholdSamples = 10;
int HighTotal;
int LowTotal;


const int RunningAverageCount = 16;
float RunningAverageBuffer[RunningAverageCount];
int NextRunningAverage;
float RunningAverageSignal = 0;


void setup() {
  
  //Threshold = 500;                // sets a starting threshold level
  pinMode(LED13,OUTPUT);         // pin that will blink to your heartbeat!
   Serial.begin(9600);         // Set's up Serial Communication at certain speed.

}


void loop() {
  
  Signal = analogRead(0);  
  
  RunningAverageBuffer[NextRunningAverage++] = Signal;
  
  if (NextRunningAverage >= RunningAverageCount)
  {
    NextRunningAverage = 0; 
  }
  RunningAverageSignal = 0;
  for(int i=0; i< RunningAverageCount; i++)
  {
    RunningAverageSignal += RunningAverageBuffer[i];
  }
  
  RunningAverageSignal /= RunningAverageCount;




if (RunningAverageSignal < LowestSignal){
  LowestSignal = RunningAverageSignal;
}
if (RunningAverageSignal > HighestSignal){
  HighestSignal = RunningAverageSignal;
}




  if(N == 20){
    HighSample[TimersTriggered] = HighestSignal;
    LowSample[TimersTriggered] = LowestSignal;



    
  for (int i = 0; i <= ThresholdSamples - 1; i++){
    HighTotal += HighSample[i];
  }
  HighTotal /= ThresholdSamples;
  
    for (int i = 0; i <= ThresholdSamples - 1; i++) {
    LowTotal += LowSample[i];
  }
  LowTotal /= ThresholdSamples;

    Threshold = (HighTotal + LowTotal)/2;
    
    
    if(TimersTriggered == 9){
      TimersTriggered = 0;
    }
    N = 0;
    HighestSignal = 0;
    LowestSignal = 1000;
    TimersTriggered++;
     }
  

   
  Serial.print(RunningAverageSignal);
  Serial.print(" ");

  Serial.print(HighTotal);
  Serial.print(" ");

  Serial.print(LowTotal);
  Serial.print(" ");

  Serial.println(Threshold);
 
   

    N += 1;
delay(10);


}

when I worked on speakerphones, we needed to recognize varying levels of speed in the presence of varying levels of background sounds. So both were measured and speech was determined when it was 6dB above background. You can do similar to recognize a pulse when the levels vary.

we used leaky integration with different rates depending on whether the current sample is greater or less than the current average. We want the speech measurement to have a fast attack and slower decay rate. We wanted background to have a very slow attack and fast decay.

leaky integration is
An += (sn - An-1)) * K // K < 1.

An is the current average, sn the current sample and K the rate. K can be either the attack rate, KA or the decay rate, KD
if (An < sn
K = KA
else
K = KD

A += (s - A) * K

there is a speech average, AS and a background or noise average, AN.

The speech attack and decay rates might be 1/8 and 1/32. The noise attack and decay rates might be 1/32 and 1/1024. (notice the powers of two that can be implemented with shifts).

by the way, my masters thesis was Abnormal Arrhythmia Detection.

Just tried out the leaky integration algorithm that you sent and it works perfectly for what I need! Thank you very much, been a big help for my project! :slight_smile: