|
| 1 | +volatile int rate[10]; // used to hold last ten IBI values |
| 2 | +volatile unsigned long sampleCounter = 0; // used to determine pulse timing |
| 3 | +volatile unsigned long lastBeatTime = 0; // used to find the inter beat interval |
| 4 | +volatile unsigned long lastLEDtime = 0; // measures the minimum time for lighting the LED |
| 5 | +volatile int P =512; // used to find peak in pulse wave |
| 6 | +volatile int T = 512; // used to find trough in pulse wave |
| 7 | +volatile int thresh = 512; // used to find instant moment of heart beat |
| 8 | +volatile int amp = 100; // used to hold amplitude of pulse waveform |
| 9 | +volatile boolean firstBeat = true; // used to seed rate array so we startup with reasonable BPM |
| 10 | +volatile boolean secondBeat = true; // used to seed rate array so we startup with reasonable BPM |
| 11 | + |
| 12 | +void interruptSetup(){ // Initializes Timer1 to throw an interrupt every 2mS. |
| 13 | + TCCR1A = 0x02; // Disable PWM on pins D9 and D10, go into CTC mode |
| 14 | + TCCR1B = 0x05; // Don't force compare, 64 prescalar |
| 15 | + OCR1A = 0X7C; // Set the top of the count to 124 for 500Hz sample rate |
| 16 | + TIMSK1 = 0x02; // Enable interrupt on match between TIMER1 and OCR1A |
| 17 | + sei(); // Enable global interrupts |
| 18 | +} |
| 19 | + |
| 20 | +// Service TIMER1 interrupts |
| 21 | +// Timer1 makes sure that we take a reading every 2 milliseconds |
| 22 | + |
| 23 | +ISR(TIMER1_COMPA_vect){ // triggered when Timer1 counts to 124 |
| 24 | + cli(); // disable interrupts while we do this |
| 25 | + Signal = analogRead(pulsePin); // read the Pulse Sensor |
| 26 | + sampleCounter = millis(); // keep track of the time in mS with this variable |
| 27 | + int N = sampleCounter - lastBeatTime; // monitor the time since the last beat to avoid noise |
| 28 | + |
| 29 | + // Find the peak and trough of the pulse wave |
| 30 | + if (Signal < thresh && N > (IBI/5)*3) { // avoid dichrotic noise by waiting 3/5 of last IBI |
| 31 | + if (Signal < T) { // T is the trough |
| 32 | + T = Signal; // keep track of lowest point in pulse wave |
| 33 | + } |
| 34 | + } |
| 35 | + |
| 36 | + if (Signal > thresh && Signal > P) { // thresh condition helps avoid noise |
| 37 | + P = Signal; // P is the peak |
| 38 | + } // keep track of highest point in pulse wave |
| 39 | + |
| 40 | + // Look for the heartbeat |
| 41 | + // Signal surges up in value every time there is a pulse |
| 42 | + if (N > 250) { // avoid high frequency noise |
| 43 | + if ( (Signal > thresh) && (Pulse == false) && (N > (IBI/5)*3) ) { |
| 44 | + Pulse = true; // set the Pulse flag when we think there is a pulse |
| 45 | + digitalWrite(blinkPin, HIGH); // turn on pin 13 LED |
| 46 | + display.setCursor(heartX, heartY); // draw the heart |
| 47 | + display.write(3); |
| 48 | + |
| 49 | + IBI = sampleCounter - lastBeatTime; // measure time between beats in mS |
| 50 | + lastLEDtime = sampleCounter; // remember when the LED was turned on |
| 51 | + lastBeatTime = sampleCounter; // keep track of time for next pulse |
| 52 | + |
| 53 | + if (firstBeat) { // if it's the first time we found a beat, firstBeat == TRUE |
| 54 | + firstBeat = false; // clear firstBeat flag |
| 55 | + return; // IBI value is unreliable so discard it |
| 56 | + } |
| 57 | + if (secondBeat) { // if this is the second beat, secondBeat == TRUE |
| 58 | + secondBeat = false; // clear secondBeat flag |
| 59 | + for (int i=0; i<=9; i++) { // seed the running total to get a realisitic BPM at startup |
| 60 | + rate[i] = IBI; |
| 61 | + } |
| 62 | + } |
| 63 | + |
| 64 | + // keep a running total of the last 10 IBI values |
| 65 | + word runningTotal = 0; // clear the runningTotal variable |
| 66 | + |
| 67 | + for (int i=0; i<=8; i++) { // shift data in the rate array |
| 68 | + rate[i] = rate[i+1]; // and drop the oldest IBI value |
| 69 | + runningTotal += rate[i]; // add up the 9 oldest IBI values |
| 70 | + } |
| 71 | + |
| 72 | + rate[9] = IBI; // add the latest IBI to the rate array |
| 73 | + runningTotal += rate[9]; // add the latest IBI to runningTotal |
| 74 | + runningTotal /= 10; // average the last 10 IBI values |
| 75 | + BPM = 60000/runningTotal; // how many beats can fit into a minute? that's BPM! |
| 76 | + QS = true; // set Quantified Self flag |
| 77 | + // NOTE: QS flag is not cleared inside the ISR |
| 78 | + } |
| 79 | + } |
| 80 | + |
| 81 | + if (Signal < thresh && Pulse == true) { // when the values are going down, the beat is over |
| 82 | + if ( sampleCounter > (lastLEDtime + 100) ) { // the LED should be on at least a quarter second so you can see it |
| 83 | + digitalWrite(blinkPin, LOW); // turn off pin 13 LED |
| 84 | + display.setCursor(heartX, heartY); // draw the heart |
| 85 | + display.write(0); // Erase the heart |
| 86 | + } |
| 87 | + Pulse = false; // reset the Pulse flag so we can do it again |
| 88 | + amp = P - T; // get amplitude of the pulse wave |
| 89 | + thresh = amp/2 + T; // set thresh at 50% of the amplitude |
| 90 | + P = thresh; // reset these for next time |
| 91 | + T = thresh; |
| 92 | + } |
| 93 | + |
| 94 | + if (N > 2500) { // if 2.5 seconds go by without a beat |
| 95 | + thresh = 512; // set thresh default |
| 96 | + P = 512; // set P default |
| 97 | + T = 512; // set T default |
| 98 | + lastBeatTime = sampleCounter; // bring the lastBeatTime up to date |
| 99 | + firstBeat = true; // set these to avoid noise |
| 100 | + secondBeat = true; // when we get the heartbeat back |
| 101 | + } |
| 102 | + |
| 103 | + sei(); // enable interrupts when you're done! |
| 104 | + |
| 105 | +}// end ISR |
0 commit comments