Counter shows more than single count for a pulse

I am using Arduino Nano, ATmega328p (Old bootloader) to program my 3-pin photodiode Pulse Sensor. I have fine-tuned the threshold to the value of 403 in my case by using the example code from pulsesensor.com. The code works fine, where it blinks once a pulse is detected. However, I have started facing problems when I want to get the BPM. I have used the code below to count the pulse for 10 seconds and will multiply by 6 at the end to get BPM. The problem I faced is there will be a two to three extra count for the pulse every time it exceeds the threshold, but what I want is just to take a single count every time it exceeds the threshold.

//Pulse sensor model = SEN-11574 brought from shopee (3pin - Vcc, Gnd, Signal)

//  Variables
int PulseSensorSignalPin = 0;        // Pulse Sensor SIGNAL WIRE (white colour wire in my case) connected to ANALOG PIN 0
int LED13 = 13;   //  The on-board Arduion LED (Arduino Nano Built-in Led in my case)

int currSignal;                // holds the incoming raw data. Signal value can range from 0-1023 (1024 values in total)
int prevSignal;
int Threshold = 403;            // Determine which Signal to "count as a beat", and which to ingore. (in my case is 402)
int BPM = 0;
int Pulse = 0;
bool countedPulse = false;     // Initially save as false as no pulse have been counted.
const unsigned long eventInterval = 10000;
unsigned long startTime = 0;      // unsigned - store variables neglecting the +- sign, long - larger bytes for storing variable


void setup() {
  Serial.begin(115200);         // Set's up Serial Communication at certain speed.
  pinMode(LED13, OUTPUT);        // pin that will blink to your heartbeat!
  digitalWrite(LED13, LOW);         // Set initial condition for LED - initially not blinking.
}

void loop() {
  
  startTime = millis();          // let startTime = millis() that will keep update (not sure work or not)

  while (millis() < startTime + 10000) {

    currSignal = analogRead (PulseSensorSignalPin);

    if (currSignal > Threshold && prevSignal < Threshold) {
      if (countedPulse == false) {
        Pulse++;
        Serial.print("Pulse = ");
        Serial.println(Pulse);
        digitalWrite(LED13, HIGH);
        delay(20);
        digitalWrite(LED13, LOW);
        countedPulse = true;
      }
    }
    
    else if (currSignal < Threshold && countedPulse == true) {
      countedPulse = false;
      digitalWrite(LED13, LOW);
    }
    prevSignal = currSignal;
  }
    BPM = Pulse * 6;             // measure number of pulse in 10s, multiply by 6 to get pulses per min
    Serial.println ();
    Serial.print ("BPM = ");
    Serial.println (BPM);
    Serial.println ();
    Pulse = 0;                   // return the pulse count to 0 after print BPM
  
}

There is a stupid way I have done, which I divided 2 for my BPM calculation to make the BPM looks legit since there is an extra 2 to 3 counts every time. But I hope there is a better way to solve the problem.

what is generating the pulse ? is it bouncing?

Side note:

this is not the right way to test for elapsed time

while (millis() < startTime + 10000) {

you should write

while (millis() - startTime < 10000) {

to handle rollover safely (in 50 days)

The code above is referred to the code from "counting state change in analog input" and "(8) At your fingertip: registering your heart beat with a heart pulse sensor and an Arduino equipped with a TFT display - Zonnestroompanelen in Nederland".

The pulse is just the count, once the signal is above the threshold I set, I personally take it as a pulse.
Yap, the signal is bouncing up and down when my finger is put on the pulse sensor.

I would write this as a small state machine, something like this (typed here fully untested)

//Pulse sensor model = SEN-11574 brought from shopee (3pin - Vcc, Gnd, Signal)

enum : byte {PULSE_OFF, PULSE_ON} pulseState = PULSE_OFF;
const byte ledPin = LED_BUILTIN;  // on board led

const byte pulsePin = A0;         // Pulse Sensor SIGNAL WIRE (white colour wire in my case) connected to ANALOG PIN 0
const int pulseThreshold = 403;   // Determine which Signal to "count as a beat", and which to ingore. (in my case is 402)
unsigned int pulseCount = 0;

const unsigned long eventInterval = 10000ul;
unsigned long startTime = 0;      // unsigned - store variables neglecting the +- sign, long - larger bytes for storing variable

void setup() {
  pinMode(ledPin, OUTPUT);        // it's LOW by default
  Serial.begin(115200);
  startTime = millis();           // let startTime = millis() that will keep update (not sure work or not)
}

void loop() {
  if (millis() - startTime < eventInterval) {
    int pulseValue = analogRead (pulsePin);

    switch (pulseState) {
      case PULSE_OFF: // we are waiting to go over the threshold
        if (pulseValue >= pulseThreshold) {
          pulseCount++;
          digitalWrite(ledPin, HIGH);
          pulseState = PULSE_ON;
        }
        break;

      case PULSE_ON: // we are waiting to return below the threshold
        if (pulseValue < pulseThreshold) {
          digitalWrite(ledPin, LOW);
          pulseState = PULSE_OFF;
        }
        break;
    }
  } else { // time's up
    Serial.print(F("BPM = ")); Serial.println (pulseCount * 6);
    // get ready for next run
    digitalWrite(ledPin, LOW);
    pulseState = PULSE_OFF;
    pulseCount = 0;
    startTime = millis();
  }
}

I have tested the code, but it seems that it is more worst. As BPM goes up to thousands. I have add Serial.print ("Pulse = "); and Serial.println (pulseCount); in ur code and realized that it takes an extra 8 to 10 counts every time the pulseValue is above the threshold.

I'd probably consider a high and a low threshold instead of just one, say 380 and 403 as an example but it also depends how quickly the signal rises and falls.

if you look at the code, I only increase pulseCount when the state is PULSE_OFF otherwise I wait for the pulseValue to go back below the pulseThreshold

so the count is right. It might be that your threshold is wrong or that you should use an hysteresis to ensure the signal went back well below pulseThreshold

ok noted.

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.