Heart rate code working mikroc pic -- and question

Thread Starter

Hassan Abu al rub

Joined Feb 10, 2019
12
i find this code for heart rate monitor and its working 100%
but i have two question

1- this code read puls rate from pin AN0 analoge -- but when remove my finger why its not give me zero?? result will be same
how to add to the code to give me zero 0 when no puls in


2- another question why he program mcu to be as counter puls from timer 0 and other interrupt pin
why he put puls to analog pin


complete code file in attachment

C:
//    >>> Pulse Sensor purple wire goes to Analog Pin 0 <<<
//Pulse Sensor sample aquisition and processing happens in the background via Timer0 interrupt. 2mS sample rate.
//The following variables are automatically updated:
//Signal :    int that holds the analog signal data straight from the sensor. updated every 2mS.
//IBI  :      int that holds the time interval between beats. 2mS resolution.
//BPM  :      int that holds the heart rate value, derived every beat, from averaging previous 10 IBI values.
//QS  :       boolean that is made true whenever Pulse is found and BPM is updated. User must reset.
//Pulse :     boolean that is true when a heartbeat is sensed then false .
//THIS PROGRAM WAS TESTED ON A PIC18F452 RUNNING AT 32MHz HS_PLLx4
/*************************************************************************/
sbit LCD_RS at RD2_bit;
sbit LCD_EN at RD3_bit;
sbit LCD_D4 at RD4_bit;
sbit LCD_D5 at RD5_bit;
sbit LCD_D6 at RD6_bit;
sbit LCD_D7 at RD7_bit;

sbit LCD_RS_Direction at TRISD2_bit;
sbit LCD_EN_Direction at TRISD3_bit;
sbit LCD_D4_Direction at TRISD4_bit;
sbit LCD_D5_Direction at TRISD5_bit;
sbit LCD_D6_Direction at TRISD6_bit;
sbit LCD_D7_Direction at TRISD7_bit;

enum {FALSE = 0, TRUE = 1};
  char intro_msg[] = "ELECTRONIC HEART RATE MONITOR";
  char temp_str[8], disp_result;

int rate[10];                    // array to hold last ten IBI values
unsigned long sampleCounter = 0;          // used to determine pulse timing
unsigned long lastBeatTime = 0;           // used to find IBI
int Peak =512;                      // used to find peak in pulse wave, seeded
int Trough = 512;                     // used to find trough in pulse wave, seeded
int thresh = 512;                // used to find instant moment of heart beat, seeded
int amp = 100;                   // used to hold amplitude of pulse waveform, seeded
bit firstBeat;        // used to seed rate array so we startup with reasonable BPM
bit secondBeat;      // used to seed rate array so we startup with reasonable BPM
int pulsePin = 0;                 // Pulse Sensor purple wire connected to analog pin 0
int blinkPin = 13;                // pin to blink led at each beat

int  runningTotal = 0;                  // clear the runningTotal variable
// these variables are volatile because they are used during the interrupt service routine!
unsigned int BPM;                   // used to hold the pulse rate
unsigned int Signal;                // holds the incoming raw data
unsigned int IBI = 600;             // holds the time between beats, must be seeded!
bit Pulse ;     // true when pulse wave is high, false when it's low
bit QS;
int  N_cnt, P_cnt;
int i = 0;
void InitTimer0(){  //timer0 is set to Interrupt every 2ms. PIC18F452 is running at 32MHz
    T0CON         = 0xC5;
    TMR0L         = 0x06;
    GIE_bit         = 1;
    TMR0IE_bit         = 1;
  }

void Interrupt(){
     GIE_bit = 0;
  if (TMR0IF_bit){              //every 2ms

    //READ HEART RATE FROM LCD
   Signal = ADC_Get_Sample(0);
    sampleCounter += 2;
    N_cnt = sampleCounter - lastBeatTime;
     if(Signal < thresh && N_cnt > (IBI/5)*3){
      if (Signal < Trough){
        Trough = Signal;
      }
    }
    if(Signal > thresh && Signal > P_cnt){
        P_cnt = Signal;
      }
   
       //  NOW IT'S TIME TO LOOK FOR THE HEART BEAT
  // signal surges up in value every time there is a pulse
  if (N_cnt > 250){                                   // avoid high frequency noise
    if ( (Signal > thresh) && (Pulse == FALSE) && (N_cnt > (IBI/5)*3) ){
      Pulse = TRUE;                               // set the Pulse flag when we think there is a pulse
      IBI = sampleCounter - lastBeatTime;         // measure time between beats in mS
      lastBeatTime = sampleCounter;               // keep track of time for next pulse

      if(secondBeat){                        // if this is the second beat, if secondBeat == TRUE
        secondBeat = FALSE;                  // clear secondBeat flag
        for(i=0; i<=9; i++){             // seed the running total to get a realisitic BPM at startup
          rate[I] = IBI;
        }
      }

      if(firstBeat){                         // if it's the first time we found a beat, if firstBeat == TRUE
        firstBeat = FALSE;                   // clear firstBeat flag
        secondBeat = TRUE;                   // set the second beat flag

        return;                              // IBI value is unreliable so discard it
      }


      // keep a running total of the last 10 IBI values
     runningTotal = 0;                  // clear the runningTotal variable

      for(i=0; i<=8; i++){                // shift data in the rate array
        rate[I] = rate[i+1];                  // and drop the oldest IBI value
        runningTotal += rate[I];              // add up the 9 oldest IBI values
      }

      rate[9] = IBI;                          // add the latest IBI to the rate array
      runningTotal += rate[9];                // add the latest IBI to runningTotal
      runningTotal /= 10;                     // average the last 10 IBI values
      BPM = 60000/runningTotal;               // how many beats can fit into a minute? that's BPM!
      QS = TRUE;                              // set Quantified Self flag
      // QS FLAG IS NOT CLEARED INSIDE THIS ISR
    }
  }

  if (Signal < thresh && Pulse == TRUE){   // when the values are going down, the beat is over

    Pulse = FALSE;                         // reset the Pulse flag so we can do it again
    amp = P_cnt - Trough;                           // get amplitude of the pulse wave
    thresh = amp/2 + Trough;                    // set thresh at 50% of the amplitude
    P_cnt = thresh;                            // reset these for next time
    Trough = thresh;
  }

  if (N_cnt > 2500){                           // if 2.5 seconds go by without a beat
    thresh = 512;                          // set thresh default
    P_cnt = 512;                               // set P default
    Trough = 512;                               // set T default
    lastBeatTime = sampleCounter;          // bring the lastBeatTime up to date
    firstBeat = TRUE;                      // set these to avoid noise
    secondBeat = FALSE;                    // when we get the heartbeat back
  }
   TMR0IF_bit = 0;
    TMR0L         = 0x06;
     GIE_bit =1;
}// end isr
       GIE_bit =1;                                 // enable interrupts when youre done!
}
void main() {
      int g;
       Pulse = FALSE;
       QS = FALSE;
       firstBeat = TRUE;
       secondBeat = FALSE;
     Lcd_Init();   //initialize LCD
     Lcd_Cmd(_LCD_CLEAR);                // Clear display
     Delay_ms(200);
     Lcd_Cmd(_LCD_CURSOR_OFF);
     Delay_ms(200);
     Lcd_Out(1,1, intro_msg);
     Delay_ms(1500);                // First row
     for(g=0; g<sizeof(intro_msg)-16; g++) {               // Move text to the right 4 times
        Lcd_Cmd(_LCD_SHIFT_LEFT);
        Delay_ms(250);

      }

     Lcd_Cmd(_LCD_CLEAR);                // Clear display
     Delay_ms(200);
     ADC_Init();
     InitTimer0();


     while(1){
           if (QS == TRUE){   //New Pulse detected
          Lcd_Out(1,1,"HEART RATE (BPM)");
          Lcd_Out(2,1,"     ");
          IntToStr(BPM, temp_str);
          Lcd_Out(2,8,temp_str);
          //Delay_ms(2000);
        }

   
     }
}
Mod edit: code tags-JohnInTX
 

Attachments

Last edited by a moderator:

Papabravo

Joined Feb 24, 2006
21,159
It depends on the sensor and the A/D converter, but they don't always go to zero when there is no input for a variety of reasons. You can add a bit of code that looks at the A/D output and reports zero if the actual reading is say less than 10 out of 1024 possible values. If you have more bits in your A/d you may want to include a higher threshold for reporting a non-zero value value.

I don't know the answer to the second question. The statement is more than just a bit confusing. Your intent is not clear.

A schematic might be of some help.
 

MrChips

Joined Oct 2, 2009
30,712
I have not examined the code but I can imagine the following happening.

Assuming that the program measures the time interval between two detected pulses, the heart rate can be determined by taking the reciprocal of the time interval. The results are then displayed.

The program then attempts to measure the time interval to the next detected pulse. No pulse is received because the finger has been withdrawn from the sensor. The program sits in limbo waiting for the next pulse that never comes. The display shows the last determined heart rate.
 

JohnInTX

Joined Jun 26, 2012
4,787
Also, do not set or clear GIE in the interrupt routine. The PIC clears GIE automatically when the interrupt occurs and the compiler will end the interrupt code with the RETFIE (return and re-enable interrupts). Setting GIE yourself can result in re-entering the interrupt routine before it is complete. That will cause problems.
 

Thread Starter

Hassan Abu al rub

Joined Feb 10, 2019
12
It depends on the sensor and the A/D converter, but they don't always go to zero when there is no input for a variety of reasons. You can add a bit of code that looks at the A/D output and reports zero if the actual reading is say less than 10 out of 1024 possible values. If you have more bits in your A/d you may want to include a higher threshold for reporting a non-zero value value.

I don't know the answer to the second question. The statement is more than just a bit confusing. Your intent is not clear.

A schematic might be of some help.

find attachment simulation file and circuit diagram

please give example code to to be zero -- when no puls in
 

Attachments

Papabravo

Joined Feb 24, 2006
21,159
Apache config:
a2d_result = Read_A2D_Channel(0) ; /* Assume A2d range is [0-1023] */
if(a2d_result < 50) {a2d_result = 0} /* All values in range [0-50] return zero */
Any reading less than 5% of full scale is considered to be zero.
 

BobaMosfet

Joined Jul 1, 2009
2,110
i find this code for heart rate monitor and its working 100%
but i have two question

1- this code read puls rate from pin AN0 analoge -- but when remove my finger why its not give me zero?? result will be same
how to add to the code to give me zero 0 when no puls in


2- another question why he program mcu to be as counter puls from timer 0 and other interrupt pin
why he put puls to analog pin


complete code file in attachment

C:
//    >>> Pulse Sensor purple wire goes to Analog Pin 0 <<<
//Pulse Sensor sample aquisition and processing happens in the background via Timer0 interrupt. 2mS sample rate.
//The following variables are automatically updated:
//Signal :    int that holds the analog signal data straight from the sensor. updated every 2mS.
//IBI  :      int that holds the time interval between beats. 2mS resolution.
//BPM  :      int that holds the heart rate value, derived every beat, from averaging previous 10 IBI values.
//QS  :       boolean that is made true whenever Pulse is found and BPM is updated. User must reset.
//Pulse :     boolean that is true when a heartbeat is sensed then false .
//THIS PROGRAM WAS TESTED ON A PIC18F452 RUNNING AT 32MHz HS_PLLx4
/*************************************************************************/
sbit LCD_RS at RD2_bit;
sbit LCD_EN at RD3_bit;
sbit LCD_D4 at RD4_bit;
sbit LCD_D5 at RD5_bit;
sbit LCD_D6 at RD6_bit;
sbit LCD_D7 at RD7_bit;

sbit LCD_RS_Direction at TRISD2_bit;
sbit LCD_EN_Direction at TRISD3_bit;
sbit LCD_D4_Direction at TRISD4_bit;
sbit LCD_D5_Direction at TRISD5_bit;
sbit LCD_D6_Direction at TRISD6_bit;
sbit LCD_D7_Direction at TRISD7_bit;

enum {FALSE = 0, TRUE = 1};
  char intro_msg[] = "ELECTRONIC HEART RATE MONITOR";
  char temp_str[8], disp_result;

int rate[10];                    // array to hold last ten IBI values
unsigned long sampleCounter = 0;          // used to determine pulse timing
unsigned long lastBeatTime = 0;           // used to find IBI
int Peak =512;                      // used to find peak in pulse wave, seeded
int Trough = 512;                     // used to find trough in pulse wave, seeded
int thresh = 512;                // used to find instant moment of heart beat, seeded
int amp = 100;                   // used to hold amplitude of pulse waveform, seeded
bit firstBeat;        // used to seed rate array so we startup with reasonable BPM
bit secondBeat;      // used to seed rate array so we startup with reasonable BPM
int pulsePin = 0;                 // Pulse Sensor purple wire connected to analog pin 0
int blinkPin = 13;                // pin to blink led at each beat

int  runningTotal = 0;                  // clear the runningTotal variable
// these variables are volatile because they are used during the interrupt service routine!
unsigned int BPM;                   // used to hold the pulse rate
unsigned int Signal;                // holds the incoming raw data
unsigned int IBI = 600;             // holds the time between beats, must be seeded!
bit Pulse ;     // true when pulse wave is high, false when it's low
bit QS;
int  N_cnt, P_cnt;
int i = 0;
void InitTimer0(){  //timer0 is set to Interrupt every 2ms. PIC18F452 is running at 32MHz
    T0CON         = 0xC5;
    TMR0L         = 0x06;
    GIE_bit         = 1;
    TMR0IE_bit         = 1;
  }

void Interrupt(){
     GIE_bit = 0;
  if (TMR0IF_bit){              //every 2ms

    //READ HEART RATE FROM LCD
   Signal = ADC_Get_Sample(0);
    sampleCounter += 2;
    N_cnt = sampleCounter - lastBeatTime;
     if(Signal < thresh && N_cnt > (IBI/5)*3){
      if (Signal < Trough){
        Trough = Signal;
      }
    }
    if(Signal > thresh && Signal > P_cnt){
        P_cnt = Signal;
      }
  
       //  NOW IT'S TIME TO LOOK FOR THE HEART BEAT
  // signal surges up in value every time there is a pulse
  if (N_cnt > 250){                                   // avoid high frequency noise
    if ( (Signal > thresh) && (Pulse == FALSE) && (N_cnt > (IBI/5)*3) ){
      Pulse = TRUE;                               // set the Pulse flag when we think there is a pulse
      IBI = sampleCounter - lastBeatTime;         // measure time between beats in mS
      lastBeatTime = sampleCounter;               // keep track of time for next pulse

      if(secondBeat){                        // if this is the second beat, if secondBeat == TRUE
        secondBeat = FALSE;                  // clear secondBeat flag
        for(i=0; i<=9; i++){             // seed the running total to get a realisitic BPM at startup
          rate[I] = IBI;
        }
      }

      if(firstBeat){                         // if it's the first time we found a beat, if firstBeat == TRUE
        firstBeat = FALSE;                   // clear firstBeat flag
        secondBeat = TRUE;                   // set the second beat flag

        return;                              // IBI value is unreliable so discard it
      }


      // keep a running total of the last 10 IBI values
     runningTotal = 0;                  // clear the runningTotal variable

      for(i=0; i<=8; i++){                // shift data in the rate array
        rate[I] = rate[i+1];                  // and drop the oldest IBI value
        runningTotal += rate[I];              // add up the 9 oldest IBI values
      }

      rate[9] = IBI;                          // add the latest IBI to the rate array
      runningTotal += rate[9];                // add the latest IBI to runningTotal
      runningTotal /= 10;                     // average the last 10 IBI values
      BPM = 60000/runningTotal;               // how many beats can fit into a minute? that's BPM!
      QS = TRUE;                              // set Quantified Self flag
      // QS FLAG IS NOT CLEARED INSIDE THIS ISR
    }
  }

  if (Signal < thresh && Pulse == TRUE){   // when the values are going down, the beat is over

    Pulse = FALSE;                         // reset the Pulse flag so we can do it again
    amp = P_cnt - Trough;                           // get amplitude of the pulse wave
    thresh = amp/2 + Trough;                    // set thresh at 50% of the amplitude
    P_cnt = thresh;                            // reset these for next time
    Trough = thresh;
  }

  if (N_cnt > 2500){                           // if 2.5 seconds go by without a beat
    thresh = 512;                          // set thresh default
    P_cnt = 512;                               // set P default
    Trough = 512;                               // set T default
    lastBeatTime = sampleCounter;          // bring the lastBeatTime up to date
    firstBeat = TRUE;                      // set these to avoid noise
    secondBeat = FALSE;                    // when we get the heartbeat back
  }
   TMR0IF_bit = 0;
    TMR0L         = 0x06;
     GIE_bit =1;
}// end isr
       GIE_bit =1;                                 // enable interrupts when youre done!
}
void main() {
      int g;
       Pulse = FALSE;
       QS = FALSE;
       firstBeat = TRUE;
       secondBeat = FALSE;
     Lcd_Init();   //initialize LCD
     Lcd_Cmd(_LCD_CLEAR);                // Clear display
     Delay_ms(200);
     Lcd_Cmd(_LCD_CURSOR_OFF);
     Delay_ms(200);
     Lcd_Out(1,1, intro_msg);
     Delay_ms(1500);                // First row
     for(g=0; g<sizeof(intro_msg)-16; g++) {               // Move text to the right 4 times
        Lcd_Cmd(_LCD_SHIFT_LEFT);
        Delay_ms(250);

      }

     Lcd_Cmd(_LCD_CLEAR);                // Clear display
     Delay_ms(200);
     ADC_Init();
     InitTimer0();


     while(1){
           if (QS == TRUE){   //New Pulse detected
          Lcd_Out(1,1,"HEART RATE (BPM)");
          Lcd_Out(2,1,"     ");
          IntToStr(BPM, temp_str);
          Lcd_Out(2,8,temp_str);
          //Delay_ms(2000);
        }

  
     }
}
Mod edit: code tags-JohnInTX
Have you flow-charted the code? Any serious developer would-- and that will explain all the logic in the program and allow you to find out where your problem is.
 
Any monitor that can pick up the heartbeat doesn’t require differential equations to count one, two, three, for one minute and then see what the last number is.
 
Top