Pages: 1 [2] 3   Go Down
Author Topic: Calculating heartbeat frequency using PPG method - Help needed!  (Read 2051 times)
0 Members and 1 Guest are viewing this topic.
New Jersey
Offline Offline
Faraday Member
**
Karma: 67
Posts: 3677
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I should have been more clear: don't bring back all the prevcheck stuff you had. Just put this as the last line in loop:
Code:
check = prevcheck;
Logged

Offline Offline
Jr. Member
**
Karma: 0
Posts: 58
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Thanks Wildbill, getting closer!

The LEDs flicker now and the printed lines are like:

Code:
5965
2852
3460
1240
5687
4362
3984
7007
6946
6566
6641
2015

The lines are printed about every 6 seconds, so that works.

I'm guessing the processor is running through the loop several times faster than i want it - is this anything to do with the 9600 baud rate?

Thanks
Logged

New Jersey
Offline Offline
Faraday Member
**
Karma: 67
Posts: 3677
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

oops. Should have been
Code:
prevcheck = check;
Logged

Offline Offline
Jr. Member
**
Karma: 0
Posts: 58
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Now the LED flashes correctly - with the heartbeat, but there is nothing printed. I've also tried going back, but still nothing is printed. Here's the code now:
Code:
int LED = 13; //LED to blink when pulsing
int din = 2; // digital pin 2 to read 0V or 5V
int check; // checks current status of digital pin 2
int prevcheck;// previous check status
int pulsecount = 0; //number of pulses (highs) seen on the digital pin
int BPM = 0; //beats perminute = counter value * 10
unsigned long firstpulse =0; //time of first pulse
unsigned long timecount = 0;//cumulative time of total pulses
unsigned long latestpulse=0;
unsigned long interval = 6000; //setting 6 seconds as max read time

void setup()

{
  pinMode(LED, OUTPUT);
  pinMode(din,INPUT);
  Serial.begin(9600);
}

void loop()

{
  check = digitalRead(din);
 
  //if (check != prevcheck ) // comparing current state to previous state
  {
    if (check == LOW)
    {
      digitalWrite(LED, LOW);
      //prevcheck == LOW;
    }
   
    if (check == HIGH)
    {
      digitalWrite(LED, HIGH);
      //prevcheck == HIGH; //storing current state of input pin
      check == LOW; //reset the check

      if (pulsecount == 0 ) // if this is the first count
     
      {
        firstpulse = millis(); // recording time of first pulse
        pulsecount = pulsecount +1; //increment the counter
      }
     
      if (pulsecount >=1 )
      {
        pulsecount = pulsecount + 1; // increment pulse counter when detected
        //timecount = millis() + firstpulse + timecount; //sumation of all pulse periods
        latestpulse = millis(); // recording latest pulse time
      }
     

    }
  }
       
        if (latestpulse - firstpulse > interval) //checking if 6 seconds has elapsed
      {
        BPM = pulsecount;
        Serial.println(BPM); //display the number of counts over 6 seconds
        pulsecount = 0; // counter restarted
        firstpulse = 0; // restart for time of new first-pulse read
        latestpulse = 0; // restart for new time readings
        //delay(100);
      }
      prevcheck = check;
    }
 
  Thanks
 
 
 


Logged

New Jersey
Offline Offline
Faraday Member
**
Karma: 67
Posts: 3677
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

ok. try this:
Code:
void loop()
{
check = digitalRead(din);
if(check != prevcheck)
  {
  if (check == LOW)
    {
    digitalWrite(LED, LOW);
    }

  if (check == HIGH)
    {
    digitalWrite(LED, HIGH);

    if (pulsecount == 0 ) // if this is the first count
      {
      firstpulse = millis(); // recording time of first pulse
      pulsecount = pulsecount +1; //increment the counter
      }

    if (pulsecount >=1 )
      {
      pulsecount = pulsecount + 1; // increment pulse counter when detected
      latestpulse = millis(); // recording latest pulse time
      }
    }
  }

if (latestpulse - firstpulse > interval) //checking if 6 seconds has elapsed
  {
  BPM = pulsecount;
  Serial.println(BPM); //display the number of counts over 6 seconds
  pulsecount = 0; // counter restarted
  firstpulse = 0; // restart for time of new first-pulse read
  latestpulse = 0; // restart for new time readings
  }
prevcheck = check;
}   
Logged

Offline Offline
Jr. Member
**
Karma: 0
Posts: 58
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Thanks Wildbill, closest so far!

Though the concept works, there seems to be extra reads, not sure where from, making the BPM a bit unrealistic:

Code:
9
16
20
20
26
9
11
7
12
16
14
13
14
18

how did you get
Code:
if(check != prevcheck)
to run with it? Did i have extra brackets?

Thanks
Logged

New Jersey
Offline Offline
Faraday Member
**
Karma: 67
Posts: 3677
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Most likely need to debounce. This is a usage where delay is an acceptable solution. Throw in a delay(20) or so after the digitalread.

Quote
Did i have extra brackets?
Yes. I assume you left them in from when you had the check against prevcheck.
Logged

Offline Offline
Jr. Member
**
Karma: 0
Posts: 58
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Excellent work Wildbill! here are some of the read values (with 50ms delay):

Code:
12
8
8
9
9
8
9
8
9
9
8
8
10
8
10

6

I will try and confirm the pulses with some scope measurements.

Do you think this is the most simple and reliable way of recording BPM?

Thanks again!
« Last Edit: March 06, 2014, 05:45:23 pm by willing2learn » Logged

New Jersey
Offline Offline
Faraday Member
**
Karma: 67
Posts: 3677
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

You're welcome. However:
Quote
Do you think this is the most simple and reliable way of recording BPM?
You took a turn there out of my area of expertise  smiley-wink

Logged

Offline Offline
Jr. Member
**
Karma: 0
Posts: 58
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

 smiley @ Wildbill, sorry i mean't with the arduino. Is this the best way to determine pulse counts? Could arrays / for loops be used? I'm considering using case statements. The BPM is not always consistent so far.

Thanks
Logged

New Jersey
Offline Offline
Faraday Member
**
Karma: 67
Posts: 3677
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

The code looks fine to me - I don't see any advantage to using different constructs. The area I'd look at is how well your sensor is working - there seems to be quite wide variation in what it reads - or perhaps you got excited to see it finally working  smiley-wink You might be able to mask that with a longer sensing period, but it looks like you need to compare what you're getting with a manual check of your pulse.
Logged

Offline Offline
Jr. Member
**
Karma: 0
Posts: 58
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi, Yes i've been very excited but up to 300 BPM readings would be concerning  smiley-lol

I was able to take some scope measurements - setup:
2V per box, 1 second per box, graph width of 10s - with this i was measuring a square wave (5V peak) and on average 7 pulses.

But the arduino kept displaying values from 100 upwards on an attached LCD - i increased the time interval to 10s. Here's the current code:

Code:
int LED = 13; //LED to blink when pulsing
int din = 2; // digital pin 2 to read 0V or 5V
int check; // checks current status of digital pin 2
int prevcheck;// previous check status
int pulsecount = 0; //number of pulses (highs) seen on the digital pin
int BPM = 0; //beats perminute = counter value * 10
unsigned long firstpulse =0; //time of first pulse
unsigned long timecount = 0;//cumulative time of total pulses
unsigned long latestpulse=0;
unsigned long interval = 10000; //setting 6 seconds as max read time
#include <LiquidCrystal.h> //include LCD

LiquidCrystal lcd(12, 11, 6, 5, 4, 3);

void setup()

{
  pinMode(LED, OUTPUT);
  pinMode(din,INPUT);
  lcd.begin(16, 2);// set up the LCD's number of columns and rows:
  lcd.print("AV. HEART RATE:");// Print a message to the LCD.
  Serial.begin(9600);
}

void loop()
{
check = digitalRead(din);
delay(100);
if(check != prevcheck)
  {
  if (check == LOW)
    {
    digitalWrite(LED, LOW);
    }

  if (check == HIGH)
    {
    digitalWrite(LED, HIGH);

    if (pulsecount == 0 ) // if this is the first count
      {
      firstpulse = millis(); // recording time of first pulse
      pulsecount = pulsecount +1; //increment the counter
      }

    if (pulsecount >=1 )
      {
      pulsecount = pulsecount + 1; // increment pulse counter when detected
      latestpulse = millis(); // recording latest pulse time
      }
    }
  }

//-----DISPLAY------///

if (latestpulse - firstpulse > interval) //checking if 6 seconds has elapsed
  {
  BPM = pulsecount*10;
  Serial.println(BPM); //display the number of counts over 6 seconds
  pulsecount = 0; // counter restarted
  firstpulse = 0; // restart for time of new first-pulse read
  latestpulse = 0; // restart for new time readings
  lcd.setCursor(0, 1); //setting LCD cursor to 1st column, second row
  lcd.print(BPM); // print the HR on the LCD as well
  lcd.setCursor(5,1); // setting cursor in 5th column, row 2
  lcd.print("BPM"); // printing text next to BPM
  }
prevcheck = check;
}  

Thanks
Logged

New Jersey
Offline Offline
Faraday Member
**
Karma: 67
Posts: 3677
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

The LCD code is dodgy. You may be leaving digits on the screen from prior readings. Either clear the LCD or print spaces after the number. Frankly, I'd concentrate on the results from the serial port for now until you're convinced that the sensing is working.
Logged

UK
Offline Offline
Shannon Member
****
Karma: 223
Posts: 12631
-
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I think you're trying to take too many steps at the same time.

Start by writing your sketch so that it detects each pulse, using the logic demonstrated in the state change example.

Then add some code that detects when the elapsed time reaches six seconds. I suggest this timeout should be independent of the pulse detection/timing logic since you presumably don't want it to hang if no pulse is detected.

Once that is working, leave that logic alone and just focus on the code that is run each time a pulse is detected. It needs to determine whether this is the first pulse or not and either save the first pulse time, or save the last pulse time and increment the pulse count. You could use the non-zero first pulse time to determine whether this is the first pulse or not.

Once that's working, add some code that executes after the six second timeout has elapsed that does your arithmetic to calculate the bps.

At each step, make sure the code you're adding / changing does what you want before you move on to add the next bit of functionality. You're only looking at about a dozen lines of code and none of it is complicated, but you need to develop it methodically or you will just end up going round in circles.
Logged

I only provide help via the forum - please do not contact me for private consultancy.

Offline Offline
Jr. Member
**
Karma: 0
Posts: 58
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Woops! Thanks for the replies guys

I had :

Code:
unsigned long interval = 10000; // pulse reading time
BPM = pulsecount*10; // multiply by (6) to get BPMinute

When changed:

Code:
unsigned long interval = 10000; // pulse reading time
BPM = pulsecount*6; // multiply by (6) to get BPMinute

Here are some of my readings:

Code:
84
90
84
102
90
90
78
90

Thanks
Logged

Pages: 1 [2] 3   Go Up
Jump to: