Pages: [1]   Go Down
Author Topic: Heart Rate Monitor  (Read 4771 times)
0 Members and 1 Guest are viewing this topic.
0
Offline Offline
Newbie
*
Karma: 0
Posts: 8
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi everyone,
I'm doing my first project with Arduino, I am doing a heart rate monitor on 7 segments display and I'd like some tips about the code I have.
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 8
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

First, I am receiving the signal from a Polar chest belt with the Rick Moll's circuit (http://rick.mollprojects.com/hrm/)

** I will soon replace it with the Polar Heart Rate module from Sparkfun: http://www.sparkfun.com/commerce/product_info.php?products_id=8660.

I  put the signal on Port 2 of Arduino to use an interrupt in my code (which is at the bottom of this topic).

I, then, use port 3 to port 6 to send the BCD code to the the 7 segments and port 7 and 8 to choose which segment I use.
Here is my first version of the schematic:
http://3.bp.blogspot.com/_E0659_W7LV4/SbmWuarmQwI/AAAAAAAAPd4/i4JX3dA1UTQ/s1600-h/HRM_0000.jpg

Here is my code, I would like to know a better way to calculate the heart rate. Now I am doing an average of some value than i do it again and again. The beat per minute showing on my watch is varying slower than what I have on the 7-segments and changes in the beat per minute are less important. How could I improve this? Thank you for your help.

Code:
volatile int moyennefrequence, D3 = 3, D4 = 4, D5 =5, D6 = 6, D7 = 7, D8 = 8;
volatile int state = 0, lastTime, thisTime;

void setup()
{
  pinMode(D3, OUTPUT);                                   //P3 to P8 as output
  pinMode(D4, OUTPUT);
  pinMode(D5, OUTPUT);
  pinMode(D6, OUTPUT);
  pinMode(D7, OUTPUT);
  pinMode(D8, OUTPUT);
  attachInterrupt(0, pulse, CHANGE);                     //call pulse function when an interrupt is detected on Port 2;  
}

void loop()
{
  static int i = 0, m, fc[5], waitTime, sommefrequence = 0, frequence;
  long DEAD_TIME     = 2000;
  
  if (state == 2) {                                    //if 2nd pulse detected
    frequence = (60000 / (thisTime-lastTime));         //calculate bpm using time between the two pulses
      if ((frequence >= 40) && (frequence <= 220)) {   //if bpm is between 40 and 220
        fc[i] = frequence;                             //add bpm at the position "i" in the array
        i++;                                           //change array position
    }
    }
  
  waitTime = millis()-lastTime;                        //Time since no pulses were detected
  if (waitTime > DEAD_TIME) {                          //if too long
    moyennefrequence = 0;                              //bpm = 0;
  }
  if (i == 4) {                                        //if array is full
    for(m = 0; m < 5; m++) {
    sommefrequence = fc[m] + sommefrequence;           //addition of the bpm in the array
    }
  moyennefrequence = (sommefrequence / 4);             //calculate average
  i = 0;                                               //reset array position
  sommefrequence = 0;                                  //reset addition result
 }
  ssegments();                                         //call ssegments function                                  
}

void pulse()
{
  if (state == 2) {                                   //if second pulse is detected
    lastTime = thisTime;                              //the time of the first pulse is put in the lastTime variable
    state = 1;                                        //reset pulse counter at 1
  }
  if (state < 2) {                                    //if less than 2 pulse has been detected
    thisTime = millis();                              //put the current time in thisTime variable
    state = state++;                                  //increment pulse counter
  }
}


void ssegments() {  
  int unite, dizaine, centaine;
  unite = (moyennefrequence % 10);                  //put the first number of the decimal value of bpm in unite variable
  dizaine = (((moyennefrequence % 100) - unite)/10);//put the second number of the decimal value of bpm in dizaine variable
  centaine = (((moyennefrequence % 1000) - dizaine - unite)/100);//put the third number of the decimal value of bpm in centaine variable
  
  
  digitalWrite(D7, LOW);  //address of first number
  digitalWrite(D8, HIGH);
  chiffre(unite);         //send unite to funtion chiffre to be displayed
  delay(1);
  digitalWrite(D7, HIGH);//address of second number
  digitalWrite(D8, LOW);
  chiffre(dizaine);      //send dizaine to funtion chiffre to be displayed
  delay(1);
  digitalWrite(D7, HIGH);//address of third number
  digitalWrite(D8, HIGH);
  chiffre(centaine);     //send centaine to funtion chiffre to be displayed
  delay(1);
  
}

void chiffre(int i) {  //send bcd value of decimal number to ports D3 to D6
  switch(i){
    case 1:
      digitalWrite(D6, HIGH);
      digitalWrite(D3, LOW);
      digitalWrite(D4, LOW);
      digitalWrite(D5, LOW);
      break;
    case 2:
      digitalWrite(D6, LOW);
      digitalWrite(D3, HIGH);
      digitalWrite(D4, LOW);
      digitalWrite(D5, LOW);
      break;
    case 3:
      digitalWrite(D6, HIGH);
      digitalWrite(D3, HIGH);
      digitalWrite(D4, LOW);
      digitalWrite(D5, LOW);
      break;
    case 4:
      digitalWrite(D6, LOW);
      digitalWrite(D3, LOW);
      digitalWrite(D4, HIGH);
      digitalWrite(D5, LOW);
      break;
    case 5:
      digitalWrite(D6, HIGH);
      digitalWrite(D3, LOW);
      digitalWrite(D4, HIGH);
      digitalWrite(D5, LOW);
      break;
    case 6:
      digitalWrite(D6, LOW);
      digitalWrite(D3, HIGH);
      digitalWrite(D4, HIGH);
      digitalWrite(D5, LOW);
      break;
    case 7:
      digitalWrite(D6, HIGH);
      digitalWrite(D3, HIGH);
      digitalWrite(D4, HIGH);
      digitalWrite(D5, LOW);
      break;
    case 8:
      digitalWrite(D6, LOW);
      digitalWrite(D3, LOW);
      digitalWrite(D4, LOW);
      digitalWrite(D5, HIGH);
      break;
    case 9:
      digitalWrite(D6, HIGH);
      digitalWrite(D3, LOW);
      digitalWrite(D4, LOW);
      digitalWrite(D5, HIGH);
      break;
    case 0:
      digitalWrite(D6, LOW);
      digitalWrite(D3, LOW);
      digitalWrite(D4, LOW);
      digitalWrite(D5, LOW);
      break;
   }
}



Sorry about my english, I usually speak french.  smiley-wink
« Last Edit: March 12, 2009, 06:41:14 pm by fpoisson » Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 8
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I have change the code a little bit. Instead of detecting the interrupt on changes, I detect it on rising front, this is more accurate because I calculate the time between two rising fronts instead of one falling front and one rising front.

Although, the arduino is not seeming to always see it, so the heart rate takes times to refresh. I will try to see on the scope tomorrow if my signal is taking all the amplitude from 5V to 0V when there is a pulse. I will probably play with the peak detector capacitor. I think I will enjoy the polar module from sparkfun...

Here is my new code:
Code:
volatile int fc, D3 = 3, D4 = 4, D5 =5, D6 = 6, D7 = 7, D8 = 8, state = 0, lastTime, thisTime;

void setup()
{
  pinMode(D3, OUTPUT);                                   //P3 to P8 as output
  pinMode(D4, OUTPUT);
  pinMode(D5, OUTPUT);
  pinMode(D6, OUTPUT);
  pinMode(D7, OUTPUT);
  pinMode(D8, OUTPUT);
  attachInterrupt(0, pulse, RISING);                     //call pulse function when an interrupt is detected on Port 2;  
}

void loop()
{
  int frequence;
  if (state == 2) {
    frequence = (60000 / (thisTime-lastTime));         //calculate bpm using time between the two pulses
      if ((frequence >= 40) && (frequence <= 220)) {   //if bpm is between 40 and 220
        fc = frequence;
      }
  }
  ssegments();                                         //call ssegments function                                  
}

void pulse()
{
  switch(state) {
    case 0:
    case 1:
      thisTime = millis();
      state++;
      break;
    case 2:
      lastTime = thisTime;
      state = 0;
      break;
  }
}


void ssegments() {  
  int unite, dizaine, centaine;
  unite = (fc % 10);                  //put the first number of the decimal value of bpm in unite variable
  dizaine = (((fc % 100) - unite)/10);//put the second number of the decimal value of bpm in dizaine variable
  centaine = (((fc % 1000) - dizaine - unite)/100);//put the third number of the decimal value of bpm in centaine variable
  
  
  digitalWrite(D7, LOW);  //address of first number
  digitalWrite(D8, HIGH);
  chiffre(unite);         //send unite to funtion chiffre to be displayed
  delay(1);
  digitalWrite(D7, HIGH);//address of second number
  digitalWrite(D8, LOW);
  chiffre(dizaine);      //send dizaine to funtion chiffre to be displayed
  delay(1);
  digitalWrite(D7, HIGH);//address of third number
  digitalWrite(D8, HIGH);
  chiffre(centaine);     //send centaine to funtion chiffre to be displayed
  delay(1);
  
}

void chiffre(int i) {  //send bcd value of decimal number to ports D3 to D6
  switch(i){
    case 1:
      digitalWrite(D6, HIGH);
      digitalWrite(D3, LOW);
      digitalWrite(D4, LOW);
      digitalWrite(D5, LOW);
      break;
    case 2:
      digitalWrite(D6, LOW);
      digitalWrite(D3, HIGH);
      digitalWrite(D4, LOW);
      digitalWrite(D5, LOW);
      break;
    case 3:
      digitalWrite(D6, HIGH);
      digitalWrite(D3, HIGH);
      digitalWrite(D4, LOW);
      digitalWrite(D5, LOW);
      break;
    case 4:
      digitalWrite(D6, LOW);
      digitalWrite(D3, LOW);
      digitalWrite(D4, HIGH);
      digitalWrite(D5, LOW);
      break;
    case 5:
      digitalWrite(D6, HIGH);
      digitalWrite(D3, LOW);
      digitalWrite(D4, HIGH);
      digitalWrite(D5, LOW);
      break;
    case 6:
      digitalWrite(D6, LOW);
      digitalWrite(D3, HIGH);
      digitalWrite(D4, HIGH);
      digitalWrite(D5, LOW);
      break;
    case 7:
      digitalWrite(D6, HIGH);
      digitalWrite(D3, HIGH);
      digitalWrite(D4, HIGH);
      digitalWrite(D5, LOW);
      break;
    case 8:
      digitalWrite(D6, LOW);
      digitalWrite(D3, LOW);
      digitalWrite(D4, LOW);
      digitalWrite(D5, HIGH);
      break;
    case 9:
      digitalWrite(D6, HIGH);
      digitalWrite(D3, LOW);
      digitalWrite(D4, LOW);
      digitalWrite(D5, HIGH);
      break;
    case 0:
      digitalWrite(D6, LOW);
      digitalWrite(D3, LOW);
      digitalWrite(D4, LOW);
      digitalWrite(D5, LOW);
      break;
   }
}

« Last Edit: March 12, 2009, 10:20:34 pm by fpoisson » Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 18
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Very nice!
Logged

London
Offline Offline
Tesla Member
***
Karma: 10
Posts: 6255
Have fun!
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Francis, I have not run this code but here are some ideas for simplifying your sketch

Code:
int  D3 = 3, D4 = 4, D5 =5, D6 = 6, D7 = 7, D8 = 8;
volatile unsigned long beats = 0, lastTime, period;


void setup()
{
  pinMode(D3, OUTPUT);                                   //P3 to P8 as output
  pinMode(D4, OUTPUT);
  pinMode(D5, OUTPUT);
  pinMode(D6, OUTPUT);
  pinMode(D7, OUTPUT);
  pinMode(D8, OUTPUT);
  attachInterrupt(0, pulse, RISING);                     //call pulse function when an interrupt is detected on Port 2;  
}

void loop()
{
  static int fc;
  int frequence;
  if (beats > 1) {
    frequence = (60000 / period);         //calculate bpm using time between the two pulses
    if ((frequence >= 40) && (frequence <= 220)) {   //if bpm is between 40 and 220
      fc = frequence;
    }
  }
  ssegments(fc);                                         //call ssegments function                                  
}

void pulse()
{
  if (beats > 0){
    period =  millis() - lastTime;
    beats++;
  }
  lastTime = millis();
}


void ssegments(int fc) {  
// rest of code is unchanged
}
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 8
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

When do you reset beats to 0?
Logged

London
Offline Offline
Tesla Member
***
Karma: 10
Posts: 6255
Have fun!
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

beats maintins a count of the number of heartbeats, keeps counting for as along as your heart is beating.  the code only wants to know that at least two beats have occured so it can caluclate the period since the last beat, so you could replace it with a flag if you wanted to.

period is the variable holding the duration of the last heartbeat. The sketch should really disable interrupts when this value is used.

as I said, the code above is off the top of my head, but I hope it gives you some ideas.
« Last Edit: March 13, 2009, 05:23:44 pm by mem » Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 8
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Thank you for this nice idea! I've try it but it does not seem to work, although some things were not working properly, I made somage changes to make it theorically working but it doesn't, here it is:

Code:
int  D3 = 3, D4 = 4, D5 =5, D6 = 6, D7 = 7, D8 = 8;
volatile unsigned long beats = 0, lastTime, period;


void setup()
{
  pinMode(D3, OUTPUT);                                   //P3 to P8 as output
  pinMode(D4, OUTPUT);
  pinMode(D5, OUTPUT);
  pinMode(D6, OUTPUT);
  pinMode(D7, OUTPUT);
  pinMode(D8, OUTPUT);
  attachInterrupt(0, pulse, FALLING);                     //call pulse function when an interrupt is detected on Port 2;
  Serial.begin(9600);
}

void loop()
{
  static int fc;
  int frequence;
  if (beats > 1) {
    Serial.println(period, DEC);
    frequence = (60000 / period);         //calculate bpm using time between the two pulses
    if ((frequence >= 40) && (frequence <= 220)) {   //if bpm is between 40 and 220
      fc = frequence;
    }
  }
  ssegment(fc);                                         //call ssegments function
}

void pulse()
{
  if (beats > 0){
    period =  millis() - lastTime;
    beats = 0;
  }
  lastTime = millis();
  beats++;
  }
}

void ssegment(int fc) {  

Logged

London
Offline Offline
Tesla Member
***
Karma: 10
Posts: 6255
Have fun!
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Why are you resetting beats to 0 in the interrupt handler.  Does the heartbeat monitor generate two pulses per heartbeat?

edit:
I looked at some information on the hardware you are using, it seems it sends an analog output. I am not sure if you will get reliable triggering of the interrupt from this. I noticed some sample code that monitors the output using the equivalent of analogRead and you could do something like that, or you could condition the signal using a Schmitt trigger or similar. But the Sparkfun part looks like it outputs a 1ms pulse so it may not be worth the trouble trying to get the analog signal working. But if you do want to, you can read more about Schmitt triggers here: http://en.wikipedia.org/wiki/Schmitt_trigger
« Last Edit: March 13, 2009, 07:45:25 pm by mem » Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 8
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

that's right, I should not reset it to 0, I just understand.

The code seems to work now when I detect the interrupt on "CHANGE" instead of "FALLING" but this would probably change if I used a schmitt trigger or the polar module from sparkfun that I've just ordered.

Code:
int  D3 = 3, D4 = 4, D5 =5, D6 = 6, D7 = 7, D8 = 8;
volatile unsigned long beats = 0, lastTime, period;


void setup()
{
  pinMode(D3, OUTPUT);                                   //P3 to P8 as output
  pinMode(D4, OUTPUT);
  pinMode(D5, OUTPUT);
  pinMode(D6, OUTPUT);
  pinMode(D7, OUTPUT);
  pinMode(D8, OUTPUT);
  attachInterrupt(0, pulse, FALLING);                     //call pulse function when an interrupt is detected on Port 2;
  Serial.begin(9600);
}

void loop()
{
  static int fc;
  int frequence;
  if (beats > 1) {
    Serial.println(period, DEC);
    frequence = (60000 / period);         //calculate bpm using time between the two pulses
    if ((frequence >= 40) && (frequence <= 220)) {   //if bpm is between 40 and 220
      fc = frequence;
    }
  }
  ssegment(fc);                                         //call ssegments function
}

void pulse()
{
  if (beats > 0){
    period =  millis() - lastTime;
  }
  lastTime = millis();
  beats++;
}

void ssegment(int fc) {

Logged

London
Offline Offline
Tesla Member
***
Karma: 10
Posts: 6255
Have fun!
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I would think that “CHANGE” would give different values every other pulse unless the on time of the pulse are the same as the off times.

You may want to see if you get better readings if you only use the odd or even beats.
In loop you could do something like this to have a look:


Code:
  if( beats % 2 == 0 )  // only display if even
// if( beats % 2 != 0 ) // only display if odd   <- comment above and uncomment this to see odd beats
       ssegments(fc);                 //call ssegments function      
« Last Edit: March 13, 2009, 08:16:59 pm by mem » Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 8
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

No changes on reading. That's not that bad, it is showing the value I have on my watch +/- 10 but sometimes there's a little peak at 140 for example, probably due to environment magnetic noises. The bandpass I am using right now is only of first order with a high gain, so it is sensisite to the environment. The module will probably get better results.

Do you think it would be better to count the time between 3 beats for example and do the average? I would probably get a more stable  output. I don't need the value that is showing to resfresh every beat.
« Last Edit: March 13, 2009, 08:28:12 pm by fpoisson » Logged

London
Offline Offline
Tesla Member
***
Karma: 10
Posts: 6255
Have fun!
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Yes, I would smooth the data over at least four beats. There is a tutorial  on smoothing if you need it here: http://www.arduino.cc/en/Tutorial/Smoothing
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 8
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I have finally receive the polar heart rate module and I have updated my code. With the module, it is working with coded and non coded belt transmitter. You can follow my project on my blog: (http://22wire.blogspot.com)
Logged

Pages: [1]   Go Up
Jump to: