Skip to content

Commit

Permalink
Added pulse sensing
Browse files Browse the repository at this point in the history
  • Loading branch information
Saurutobi committed Aug 24, 2013
1 parent eb1e59c commit 8c1aab9
Show file tree
Hide file tree
Showing 28 changed files with 482 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@



volatile int rate[10]; // array to hold last ten IBI values
volatile unsigned long sampleCounter = 0; // used to determine pulse timing
volatile unsigned long lastBeatTime = 0; // used to find IBI
volatile int P =512; // used to find peak in pulse wave, seeded
volatile int T = 512; // used to find trough in pulse wave, seeded
volatile int thresh = 512; // used to find instant moment of heart beat, seeded
volatile int amp = 100; // used to hold amplitude of pulse waveform, seeded
volatile boolean firstBeat = true; // used to seed rate array so we startup with reasonable BPM
volatile boolean secondBeat = false; // used to seed rate array so we startup with reasonable BPM


void interruptSetup(){
// Initializes Timer2 to throw an interrupt every 2mS.
TCCR2A = 0x02; // DISABLE PWM ON DIGITAL PINS 3 AND 11, AND GO INTO CTC MODE
TCCR2B = 0x06; // DON'T FORCE COMPARE, 256 PRESCALER
OCR2A = 0X7C; // SET THE TOP OF THE COUNT TO 124 FOR 500Hz SAMPLE RATE
TIMSK2 = 0x02; // ENABLE INTERRUPT ON MATCH BETWEEN TIMER2 AND OCR2A
sei(); // MAKE SURE GLOBAL INTERRUPTS ARE ENABLED
}


// THIS IS THE TIMER 2 INTERRUPT SERVICE ROUTINE.
// Timer 2 makes sure that we take a reading every 2 miliseconds
ISR(TIMER2_COMPA_vect){ // triggered when Timer2 counts to 124
cli(); // disable interrupts while we do this
Signal = analogRead(pulsePin); // read the Pulse Sensor
sampleCounter += 2; // keep track of the time in mS with this variable
int N = sampleCounter - lastBeatTime; // monitor the time since the last beat to avoid noise

// find the peak and trough of the pulse wave
if(Signal < thresh && N > (IBI/5)*3){ // avoid dichrotic noise by waiting 3/5 of last IBI
if (Signal < T){ // T is the trough
T = Signal; // keep track of lowest point in pulse wave
}
}

if(Signal > thresh && Signal > P){ // thresh condition helps avoid noise
P = Signal; // P is the peak
} // keep track of highest point in pulse wave

// NOW IT'S TIME TO LOOK FOR THE HEART BEAT
// signal surges up in value every time there is a pulse
if (N > 250){ // avoid high frequency noise
if ( (Signal > thresh) && (Pulse == false) && (N > (IBI/5)*3) ){
Pulse = true; // set the Pulse flag when we think there is a pulse
digitalWrite(fadePin,HIGH); // turn on pin 13 LED
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(int 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
sei(); // enable interrupts again
return; // IBI value is unreliable so discard it
}


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

for(int 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 - T; // get amplitude of the pulse wave
thresh = amp/2 + T; // set thresh at 50% of the amplitude
P = thresh; // reset these for next time
T = thresh;
}

if (N > 2500){ // if 2.5 seconds go by without a beat
thresh = 512; // set thresh default
P = 512; // set P default
T = 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
}

sei(); // enable interrupts when youre done!
}// end isr





Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
//Author: Saurutobi
#include <OneWire.h>
#include <DallasTemperature.h>

enum LINE {
lineA = 2,
lineB,
lineC,
lineD,
lineE,
lineF,
lineG
} line;

int statusLED = 10;
int testLED = 13;
float inputTemp = 12;
int freq1 = 100;
int freq2 = 200;
int freq3 = 300;
int freq4 = 400;
int freq5 = 500;
int freq6 = 600;
int freq7 = 700;
int freq8 = 800;
int freq9 = 900;
int freq10 = 1000;
int lowthreshold = 0;
int threshold1 = 1;
int threshold2 = 2;
int threshold3 = 3;
int threshold4 = 4;
int threshold5 = 5;
int threshold6 = 6;
int threshold7 = 7;
int highthreshold = 8;

// Data wire is plugged into pin 3 on the Arduino
#define ONE_WIRE_BUS 3

// Setup a oneWire instance to communicate with any OneWire devices
OneWire oneWire(ONE_WIRE_BUS);

// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);

//USE THE DS18B20_address_finder to get this address
DeviceAddress tempSensor = { 0x28, 0x94, 0xE2, 0xDF, 0x02, 0x00, 0x00, 0xFE };

//Pulse Sensor Variables
int pulsePin = 0; // Pulse Sensor purple wire connected to analog pin 0
int fadePin = 9; // pin to do fancy classy fading blink at each beat
int fadeRate = 0; // used to fade LED on with PWM on fadePin
// these variables are volatile because they are used during the interrupt service routine!
volatile int BPM; // used to hold the pulse rate
volatile int Signal; // holds the incoming raw data
volatile int IBI = 600; // holds the time between beats, must be seeded!
volatile boolean Pulse = false; // true when pulse wave is high, false when it's low
volatile boolean QS = false; // becomes true when Arduoino finds a beat.

void setup()
{
//set up testLED, statusLED for output
pinMode(testLED, OUTPUT);
pinMode(statusLED, OUTPUT);

//set up all lines for output
pinMode(lineA, OUTPUT);
pinMode(lineB, OUTPUT);
pinMode(lineC, OUTPUT);
pinMode(lineD, OUTPUT);
pinMode(lineE, OUTPUT);
pinMode(lineF, OUTPUT);
pinMode(lineG, OUTPUT);

// Start up the library
sensors.begin();
// set the resolution to 10 bit (good enough?)
sensors.setResolution(tempSensor, 9);

//Pulse Sensor Setup
pinMode(fadePin,OUTPUT); // pin that will fade to your heartbeat!
interruptSetup(); // sets up to read Pulse Sensor signal every 2mS
}

void loop()
{
//read temperature here
sensors.requestTemperatures();
inputTemp = sensors.getTempC(tempSensor);
dWrite();

//Pulse Sensor
if (QS == true) // Quantified Self flag is true when arduino finds a heartbeat
{
fadeRate = 255; // Set 'fadeRate' Variable to 255 to fade LED with pulse
QS = false; // reset the Quantified Self flag for next time
}
ledFadeToBeat();

//delay(20); // take a break
}

void dWrite()
{
//we want to stop when we've started over the 3rd time
enum LINE line = lineA;
int frequency = HIGH;
for(int counter = 0; counter < 2; line = static_cast<LINE>(static_cast<int>(line) + 1))
{
digitalWrite(testLED, frequency); //do the first test
digitalWrite(line, frequency); //write to the specific line and freq
if (line == lineG) //if we're at the end do something different
{
/*** Depending on HIGH or LOW we will do freq# or 1000 - freq# ***/
if (inputTemp <= lowthreshold)
{
delayMicroseconds((counter * 1000) - freq1);
}
else if (inputTemp > lowthreshold && inputTemp <= threshold1)
{
delayMicroseconds((counter * 1000) - freq2);
}
else if (inputTemp > threshold1 && inputTemp <= threshold2)
{
delayMicroseconds((counter * 1000) - freq3);
}
else if (inputTemp > threshold2 && inputTemp <= threshold3)
{
delayMicroseconds((counter * 1000) - freq4);
}
else if (inputTemp > threshold3 && inputTemp <= threshold4)
{
delayMicroseconds((counter * 1000) - freq5);
}
else if (inputTemp > threshold4 && inputTemp <= threshold5)
{
delayMicroseconds((counter * 1000) - freq6);
}
else if (inputTemp > threshold5 && inputTemp <= threshold6)
{
delayMicroseconds((counter * 1000) - freq7);
}
else if (inputTemp > threshold6 && inputTemp <= threshold7)
{
delayMicroseconds((counter * 1000) - freq8);
}
else if (inputTemp > threshold7 && inputTemp <= highthreshold)
{
delayMicroseconds((counter * 1000) - freq9);
}
else
{
delayMicroseconds((counter * 1000) - freq10);
}

if (counter == 0) //only do this stuff the first time
{
frequency = LOW; //change the frequency
digitalWrite(testLED, frequency); //do a test write
}

counter++; //change the next set of writes
line = lineA;
}
}
}

void ledFadeToBeat()
{
fadeRate -= 15; // set LED fade value
fadeRate = constrain(fadeRate,0,255); // keep LED fade value from going into negative numbers!
analogWrite(fadePin,fadeRate); // fade LED
}
109 changes: 109 additions & 0 deletions Sample-Reference Programs/PulseSensorAmped_Arduino_1dot2/Interrupt.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@



volatile int rate[10]; // array to hold last ten IBI values
volatile unsigned long sampleCounter = 0; // used to determine pulse timing
volatile unsigned long lastBeatTime = 0; // used to find IBI
volatile int P =512; // used to find peak in pulse wave, seeded
volatile int T = 512; // used to find trough in pulse wave, seeded
volatile int thresh = 512; // used to find instant moment of heart beat, seeded
volatile int amp = 100; // used to hold amplitude of pulse waveform, seeded
volatile boolean firstBeat = true; // used to seed rate array so we startup with reasonable BPM
volatile boolean secondBeat = false; // used to seed rate array so we startup with reasonable BPM


void interruptSetup(){
// Initializes Timer2 to throw an interrupt every 2mS.
TCCR2A = 0x02; // DISABLE PWM ON DIGITAL PINS 3 AND 11, AND GO INTO CTC MODE
TCCR2B = 0x06; // DON'T FORCE COMPARE, 256 PRESCALER
OCR2A = 0X7C; // SET THE TOP OF THE COUNT TO 124 FOR 500Hz SAMPLE RATE
TIMSK2 = 0x02; // ENABLE INTERRUPT ON MATCH BETWEEN TIMER2 AND OCR2A
sei(); // MAKE SURE GLOBAL INTERRUPTS ARE ENABLED
}


// THIS IS THE TIMER 2 INTERRUPT SERVICE ROUTINE.
// Timer 2 makes sure that we take a reading every 2 miliseconds
ISR(TIMER2_COMPA_vect){ // triggered when Timer2 counts to 124
cli(); // disable interrupts while we do this
Signal = analogRead(pulsePin); // read the Pulse Sensor
sampleCounter += 2; // keep track of the time in mS with this variable
int N = sampleCounter - lastBeatTime; // monitor the time since the last beat to avoid noise

// find the peak and trough of the pulse wave
if(Signal < thresh && N > (IBI/5)*3){ // avoid dichrotic noise by waiting 3/5 of last IBI
if (Signal < T){ // T is the trough
T = Signal; // keep track of lowest point in pulse wave
}
}

if(Signal > thresh && Signal > P){ // thresh condition helps avoid noise
P = Signal; // P is the peak
} // keep track of highest point in pulse wave

// NOW IT'S TIME TO LOOK FOR THE HEART BEAT
// signal surges up in value every time there is a pulse
if (N > 250){ // avoid high frequency noise
if ( (Signal > thresh) && (Pulse == false) && (N > (IBI/5)*3) ){
Pulse = true; // set the Pulse flag when we think there is a pulse
digitalWrite(blinkPin,HIGH); // turn on pin 13 LED
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(int 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
sei(); // enable interrupts again
return; // IBI value is unreliable so discard it
}


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

for(int 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
digitalWrite(blinkPin,LOW); // turn off pin 13 LED
Pulse = false; // reset the Pulse flag so we can do it again
amp = P - T; // get amplitude of the pulse wave
thresh = amp/2 + T; // set thresh at 50% of the amplitude
P = thresh; // reset these for next time
T = thresh;
}

if (N > 2500){ // if 2.5 seconds go by without a beat
thresh = 512; // set thresh default
P = 512; // set P default
T = 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
}

sei(); // enable interrupts when youre done!
}// end isr





Loading

0 comments on commit 8c1aab9

Please sign in to comment.