You are on page 1of 12

/*

Smart Heart Rate Monitor


Measures the user's heartbeat using an LED and a very sensitive photo-resistor attached to the user's ear.
A light will blink to the user's heartbeat, and the program will calculate the user's beats per minute. The
BPM will be displayed on a 7 segment display. If the user's BPM exceeds a user-set limit using a potentiometer, an
alarm will sound. Once the user's BPM lowers below that limit, the alarm will turn off.
The circuit:
* See circuit diagram for further information
Info:
>>> Pulse Sensor purple wire goes to Analog Pin 0 <<<
Pulse Sensor sample aquisition and processing happens in the background via Timer 1 interrupt. 1mS sample rate.
The following variables are automatically updated:
Pulse :
boolean that is true when a heartbeat is sensed then false in time with pin13 LED going out.
Signal :
int that holds the analog signal data straight from the sensor. updated every 1mS.
HRV :
int that holds the time between the last two beats. 1mS resolution.
B :
boolean that is made true whenever HRV is updated. User must reset.
BPM :
int that holds the heart rate value. derived from averaging HRV every 10 pulses.
QS :
boolean that is made true whenever BPM is updated. User must reset.
Scale :
int that works abit like gain. use to change the amplitude of the digital filter output. useful range
12<>20 : high<>low default = 12
FSignal : int that holds the output of the digital filter/amplifier. updated every 1mS.
lowerLimit : int that holds the lower value of what the heartbeatLimit can reach in BPM.
upperLimit : int that holds the upper value of what the heartbeatLimit can reach in BPM.
This code uses the library prototype for Pulse Sensor by Yury Gitman and Joel Murphy
found at www.pulsesensor.com
This code also uses a program for using a 4 digit 7 segment display edited by Holden Leslie-Bole
All code modified 08 Dec 2013
by Tanner Shaw
*/
Code modified by Elliot Cowan and Sonja Hindrum September 2015 New York to Launceston

long
long
long
long

Hxv[4]; // these arrays are used in the digital filter


Hyv[4]; // H for highpass, L for lowpass
Lxv[4];
Lyv[4];

unsigned long readings; // used to help normalize the signal


unsigned long peakTime; // used to time the start of the heart pulse
unsigned long lastPeakTime = 0;// used to find the time between beats
volatile int Peak;
// used to locate the highest point in positive phase of heart beat waveform
int rate;
// used to help determine pulse rate
volatile int BPM;
// used to hold the pulse rate
int offset = 0;
// used to normalize the raw data
int sampleCounter;
// used to determine pulse timing
int beatCounter = 1;
// used to keep track of pulses
volatile int Signal;
// holds the incoming raw data
int NSignal;
// holds the normalized signal
volatile int FSignal; // holds result of the bandpass filter
volatile int HRV;
// holds the time between beats
volatile int Scale = 20; // used to scale the result of the digital filter. range 12<>20 : high<>low amplification
volatile int Fade = 0;
int
int
int
int
int
int
int
int
int
int
int
int
int
int
int
int
int
int
int
int

heartbeatLimit;
lowerLimit = 60;
upperLimit = 200;
aPin = 2;
bPin = 3;
cPin = 4;
dPin = 5;
ePin = 6;
fPin = 7;
gPin = 8;
GND1 ;
GND2 = 9;
GND3 = 10;
GND4 = 11;
num;
dig1 = 0;
dig2 = 0;
dig3 = 0;
dig4 = 0;
DTime = 5;

//holds the limit for heart


//holds the lower limit for
//holds upper limit for the
//following initalized ints
// they declare the digital

//
//
//
//
//
//

rate set by the user later on.


the heartbeatLimit. You can customize this to make the lower limit different
heartbeatLimit. You can customize this to make the upper limit different.
are part of the 7-segment display
out pins

number that the 7 seg display should read


number that first part of 7 seg display should read (it will be off, so this one shouldn't do anything)
number that second part of 7 seg display reads
number that third part of 7 seg display reads
number that fourth part of 7 seg display reads
delay of flashing for the LEDS in the 7 seg display. I've found 5 to be pretty stable. Over 10 and it becomes noticeable

boolean first = true; // reminds us to seed the filter on the first go


volatile boolean Pulse = false; // becomes true when there is a heart pulse
volatile boolean B = false;
// becomes true when there is a heart pulse
volatile boolean QS = false;
// becomes true when pulse rate is determined. every 20 pulses
int pulsePin = 0;

// pulse sensor purple wire connected to analog pin 0

void setup(){
pinMode(13,OUTPUT);
// pin 13 will blink to your heartbeat!
Serial.begin(115200); // we agree to talk fast!
// this next bit will wind up in the library. it initializes Timer1 to throw an interrupt every 1mS.
TCCR1A = 0x00; // DISABLE OUTPUTS AND BREAK PWM ON DIGITAL PINS 9 & 10
TCCR1B = 0x11; // GO INTO 'PHASE AND FREQUENCY CORRECT' MODE, NO PRESCALER
TCCR1C = 0x00; // DON'T FORCE COMPARE
TIMSK1 = 0x01; // ENABLE OVERFLOW INTERRUPT (TOIE1)
ICR1 = 8000;
// TRIGGER TIMER INTERRUPT EVERY 1mS
sei();
// MAKE SURE GLOBAL INTERRUPTS ARE ENABLED
pinMode(aPin, OUTPUT); // setting up the pins we declared earlier
pinMode(bPin, OUTPUT);
pinMode(cPin, OUTPUT);
pinMode(dPin, OUTPUT);
pinMode(ePin, OUTPUT);
pinMode(fPin, OUTPUT);
pinMode(gPin, OUTPUT);
pinMode(GND1, OUTPUT);
pinMode(GND2, OUTPUT);
pinMode(GND3, OUTPUT);
pinMode(GND4, OUTPUT);
}
void loop(){
// Serial.print("S");
// S tells processing that the following string is sensor data
// Serial.println(Signal);
// Signal holds the latest raw Pulse Sensor signal
if (B == true){
// B is true when arduino finds the heart beat
// Serial.print("BEAT ");
// 'B' tells Processing the following string is HRV data (time
between beats in mS)
// Serial.println(HRV );
// HRV holds the time between this pulse and the last pulse in mS
B = false;
// reseting the QS for next time
}
if (QS == true){
// QS is true when arduino derives the heart rate by averaging HRV over 20
beats
// Serial.print("BPM over 5 beats = ");
// 'QS' tells Processing that the following string is
heart rate data

Serial.print("heartbeatLimit is: "); // displays user set heartbeat limit


Serial.println(heartbeatLimit);
QS = false;
// reset the B for next time
}
Fade -= 15;
// this set of code enables a fading portion for pin 11. We dont need it
because we are using pin 11 for the 7 seg
Fade = constrain(Fade,0,255); // getting ride of the first two lines however makes the readings very
spotty..
//analogWrite(11,Fade);

// digitalWrite( GND1, HIGH);


digitalWrite( GND2, HIGH);
digitalWrite( GND3, HIGH);
digitalWrite( GND4, HIGH);

//setting up more pins for the 7 seg

/*
This portion of the code takes the BPM of the user, and then divides it into 3 different ints for the 7
seg display to read.
Otherwise, the 7 seg would not be able to function.
*/
num = BPM;
// dig1 = num / 1000;
// num = num - (dig1 * 1000);
dig1 = num / 100;
num = num - (dig1 * 100);
dig2 = num / 10;
dig3 = num - (dig2 *10);
// }

/*
This portion of the code makes eacch digit flash it's corresponding number on the 7 seg display. Notice
that digit 4 is commented out.
This is for 2 reasons. 1) Your BPM should not go over 999, so a fourth digit is not needed.
2) There are not enough pins on a arduino uno to support all 4 digits for this porject
Some of the methods used to display a digit (such as pickNumber()) are described at the bottom of the
code.
*/
/*
digitalWrite( GND4, LOW);
//digit 4
pickNumber(dig4);
delay(DTime);
digitalWrite( GND4, HIGH);
*/
digitalWrite( GND4, LOW);
//digit 3
pickNumber(dig3);
delay(DTime);
digitalWrite( GND4, HIGH);
digitalWrite( GND3, LOW);
//digit 2
pickNumber(dig2);
delay(DTime);
digitalWrite( GND3, HIGH);
digitalWrite( GND2, LOW);
pickNumber(dig1);
delay(DTime);
digitalWrite( GND2, HIGH);
}

//digit 1

// THIS IS THE TIMER 1 INTERRUPT SERVICE ROUTINE. IT WILL BE PUT INTO THE
LIBRARY
ISR(TIMER1_OVF_vect){ // triggered every time Timer 1 overflows
// Timer 1 makes sure that we take a reading every milisecond
Signal = analogRead(pulsePin);
// First normailize the waveform around 0
readings += Signal; // take a running total
sampleCounter++;
// we do this every milisecond. this timer is used as
a clock
if ((sampleCounter %300) == 0){
// adjust as needed
offset = readings / 300;
// average the running total
readings = 0;
// reset running total
}
NSignal = Signal - offset;
// normalizing here
// IF IT'S THE FIRST TIME THROUGH THE SKETCH, SEED THE FILTER WITH CURRENT
DATA
if (first = true){
for (int i=0; i<4; i++){
Lxv[i] = Lyv[i] = NSignal <<10; // seed the lowpass filter
Hxv[i] = Hyv[i] = NSignal <<10; // seed the highpass filter
}
first = false;
// only seed once please
}

// THIS IS THE BANDPAS FILTER. GENERATED AT www-users.cs.york.ac.uk/~fisher/mkfilter/trad.html


// BUTTERWORTH LOWPASS ORDER = 3; SAMPLERATE = 1mS; CORNER = 5Hz
Lxv[0] = Lxv[1]; Lxv[1] = Lxv[2]; Lxv[2] = Lxv[3];
Lxv[3] = NSignal<<10;
// insert the normalized data into the lowpass filter
Lyv[0] = Lyv[1]; Lyv[1] = Lyv[2]; Lyv[2] = Lyv[3];
Lyv[3] = (Lxv[0] + Lxv[3]) + 3 * (Lxv[1] + Lxv[2])
+ (3846 * Lyv[0]) + (-11781 * Lyv[1]) + (12031 * Lyv[2]);
// Butterworth; Highpass; Order = 3; Sample Rate = 1mS; Corner = .8Hz
Hxv[0] = Hxv[1]; Hxv[1] = Hxv[2]; Hxv[2] = Hxv[3];
Hxv[3] = Lyv[3] / 4116; // insert lowpass result into highpass filter
Hyv[0] = Hyv[1]; Hyv[1] = Hyv[2]; Hyv[2] = Hyv[3];
Hyv[3] = (Hxv[3]-Hxv[0]) + 3 * (Hxv[1] - Hxv[2])
+ (8110 * Hyv[0]) + (-12206 * Hyv[1]) + (12031 * Hyv[2]);
FSignal = Hyv[3] >> Scale; // result of highpass shift-scaled
//PLAY AROUND WITH THE SHIFT VALUE TO SCALE THE OUTPUT ~12 <> ~20 = High <> Low Amplification.
if (FSignal >= Peak && Pulse == false){ // heart beat causes ADC readings to surge down in value.
Peak = FSignal;
// finding the moment when the downward pulse starts
peakTime = sampleCounter;
// recodrd the time to derive HRV.
}
// NOW IT'S TIME TO LOOK FOR THE HEART BEAT
if ((sampleCounter %20) == 0){// only look for the beat every 20mS. This clears out alot of high
frequency noise.
if (FSignal < 0 && Pulse == false){ // signal surges down in value every time there is a pulse
Pulse = true;
// Pulse will stay true as long as pulse signal < 0
digitalWrite(13,HIGH);
// pin 13 will stay high as long as pulse signal < 0
Fade = 255;
// set the fade value to highest for fading LED on pin 11
(optional)
HRV = peakTime - lastPeakTime;
// measure time between beats
lastPeakTime = peakTime;
// keep track of time for next pulse
B = true;
// set the Quantified Self flag when HRV gets updated. NOT cleared
inside this ISR
rate += HRV;
// add to the running total of HRV used to determine heart rate
beatCounter++;
//

beatCounter times when to calculate bpm by averaging the beat time values
if (beatCounter == 5){
// derive heart rate every 5 beats. adjust as needed
rate /= beatCounter;
// averaging time between beats
BPM = 60000/rate;
// how many beats can fit into a minute?
beatCounter = 0;
// reset counter
rate = 0;
// reset running total
QS = true;
// set Beat flag when BPM gets updated. NOT cleared inside this ISR
}
}
if (FSignal > 0 && Pulse == true){
// when the values are going up, it's the time between beats
digitalWrite(13,LOW);
// so turn off the pin 13 LED
Pulse = false;
// reset these variables so we can do it again!
Peak = 0;
//
}
}
heartbeatLimit = analogRead (A2);
potentiometer
heartbeatLimit = map(heartbeatLimit, 0, 1024, lowerLimit, upperLimit);
potentiometer,

// Sets up analog pin 2 to read the


// Depending on how far you twist the
// The BPM limit will change from the

limits set in the beginning of the code. Initially they are 60 and 200
if (BPM >= heartbeatLimit){
tone(12,500);
}else{
noTone(12);
}

// If your BPM rises above or hits the limit set


// the speaker will play a tone.

}// end isr


// the following method is a case method that tells the digit display of the 7 seg what segments to light up.
// If the digit is one, the instructions for one are given. etc.

void pickNumber(int x){


switch(x){
case 1: one(); break;
case 2: two(); break;
case 3: three(); break;
case 4: four(); break;
case 5: five(); break;
case 6: six(); break;
case 7: seven(); break;
case 8: eight(); break;
case 9: nine(); break;
default: zero(); break;
}
}

// a method to clear all the


void clearLEDs()
{
digitalWrite( 2, LOW); //
digitalWrite( 3, LOW); //
digitalWrite( 4, LOW); //
digitalWrite( 5, LOW); //
digitalWrite( 6, LOW); //
digitalWrite( 7, LOW); //
digitalWrite( 8, LOW); //
}

LEDs on the 7 seg

A
B
C
D
E
F
G

// a method telling which segments to display to make the digit 1 appear


void one()
{

digitalWrite( aPin, LOW);


digitalWrite( bPin, HIGH);
digitalWrite( cPin, HIGH);
digitalWrite( dPin, LOW);
digitalWrite( ePin, LOW);
digitalWrite( fPin, LOW);
digitalWrite( gPin, LOW);
}
// a method telling which segments to display to make the digit 2 appear
void two()
{
digitalWrite( aPin, HIGH);
digitalWrite( bPin, HIGH);
digitalWrite( cPin, LOW);
digitalWrite( dPin, HIGH);
digitalWrite( ePin, HIGH);
digitalWrite( fPin, LOW);
digitalWrite( gPin, HIGH);
}
// a method telling which segments to display to make the digit 3 appear
void three()
{
digitalWrite( aPin, HIGH);
digitalWrite( bPin, HIGH);
digitalWrite( cPin, HIGH);
digitalWrite( dPin, HIGH);
digitalWrite( ePin, LOW);
digitalWrite( fPin, LOW);
digitalWrite( gPin, HIGH);
}
// a method telling which segments to display to make the digit 4 appear
void four()
{
digitalWrite( aPin, LOW);
digitalWrite( bPin, HIGH);
digitalWrite( cPin, HIGH);
digitalWrite( dPin, LOW);


void

// a method telling
six()
{
digitalWrite( aPin,
digitalWrite( bPin,
digitalWrite( cPin,
digitalWrite( dPin,
digitalWrite( ePin,
digitalWrite( fPin,
digitalWrite( gPin,
}

which segments to display to make the digit 6 appear

HIGH);
LOW);
HIGH);
HIGH);
HIGH);
HIGH);
HIGH);

// a method telling which segments to display to make the digit 7 appear


void seven()
{
digitalWrite( aPin, HIGH);
digitalWrite( bPin, HIGH);
digitalWrite( cPin, HIGH);
digitalWrite( dPin, LOW);
digitalWrite( ePin, LOW);
digitalWrite( fPin, LOW);
digitalWrite( gPin, LOW);
}
// a method telling which segments to display to make the digit 8 appear
void eight()
{
digitalWrite( aPin, HIGH);
digitalWrite( bPin, HIGH);
digitalWrite( cPin, HIGH);
digitalWrite( dPin, HIGH);
digitalWrite( ePin, HIGH);
digitalWrite( fPin, HIGH);
digitalWrite( gPin, HIGH);
}

// a method telling which segments to display to make the digit 8 appear


void eight()
{
digitalWrite( aPin, HIGH);
digitalWrite( bPin, HIGH);
digitalWrite( cPin, HIGH);
digitalWrite( dPin, HIGH);
digitalWrite( ePin, HIGH);
digitalWrite( fPin, HIGH);
digitalWrite( gPin, HIGH);
}
// a method telling which segments to display to make the digit 9 appear
void nine()
{
digitalWrite( aPin, HIGH);
digitalWrite( bPin, HIGH);
digitalWrite( cPin, HIGH);
digitalWrite( dPin, HIGH);
digitalWrite( ePin, LOW);
digitalWrite( fPin, HIGH);
digitalWrite( gPin, HIGH);
}
// a method telling which segments to display to make the digit 0 appear
void zero()
{
digitalWrite( aPin, HIGH);
digitalWrite( bPin, HIGH);
digitalWrite( cPin, HIGH);
digitalWrite( dPin, HIGH);
digitalWrite( ePin, HIGH);
digitalWrite( fPin, HIGH);
digitalWrite( gPin, LOW);
} }

You might also like