hack gt final code
Dependencies: 4DGL-uLCD-SE BMP085 PinDetect SDFileSystem mbed wave_player
Heart.cpp@14:23390a020d1c, 2016-09-28 (annotated)
- Committer:
- otis22894
- Date:
- Wed Sep 28 00:16:16 2016 +0000
- Revision:
- 14:23390a020d1c
- Parent:
- 8:0620e2461b3a
initial commit;
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
otis22894 | 8:0620e2461b3a | 1 | #include "Heart.h" |
otis22894 | 8:0620e2461b3a | 2 | #include "mbed.h" |
otis22894 | 8:0620e2461b3a | 3 | |
otis22894 | 8:0620e2461b3a | 4 | volatile int rate[10]; // array to hold last ten IBI values |
otis22894 | 8:0620e2461b3a | 5 | volatile unsigned long sampleCounter = 0; // used to determine pulse timing |
otis22894 | 8:0620e2461b3a | 6 | volatile unsigned long lastBeatTime = 0; // used to find IBI |
otis22894 | 8:0620e2461b3a | 7 | volatile int P =512; // used to find peak in pulse wave, seeded |
otis22894 | 8:0620e2461b3a | 8 | volatile int T = 512; // used to find trough in pulse wave, seeded |
otis22894 | 8:0620e2461b3a | 9 | volatile int thresh = 512; // used to find instant moment of heart beat, seeded |
otis22894 | 8:0620e2461b3a | 10 | volatile int amp = 100; // used to hold amplitude of pulse waveform, seeded |
otis22894 | 8:0620e2461b3a | 11 | volatile bool firstBeat = true; // used to seed rate array so we startup with reasonable BPM |
otis22894 | 8:0620e2461b3a | 12 | volatile bool secondBeat = false; // used to seed rate array so we startup with reasonable BPM |
otis22894 | 8:0620e2461b3a | 13 | volatile int BPM; // used to hold the pulse rate |
otis22894 | 8:0620e2461b3a | 14 | volatile int Signal; // holds the incoming raw data |
otis22894 | 8:0620e2461b3a | 15 | volatile int IBI = 600; // holds the time between beats, must be seeded! |
otis22894 | 8:0620e2461b3a | 16 | volatile bool Pulse = false; // true when pulse wave is high, false when it's low |
otis22894 | 8:0620e2461b3a | 17 | volatile bool QS = false; // becomes true when Arduoino finds a beat. |
otis22894 | 8:0620e2461b3a | 18 | volatile bool isBeating = false; |
otis22894 | 8:0620e2461b3a | 19 | volatile int beatCount = 0; |
otis22894 | 8:0620e2461b3a | 20 | volatile int PulseLength = 0; |
otis22894 | 8:0620e2461b3a | 21 | |
otis22894 | 14:23390a020d1c | 22 | AnalogIn ain(p16); |
otis22894 | 8:0620e2461b3a | 23 | DigitalOut myLED(LED1); |
otis22894 | 8:0620e2461b3a | 24 | Ticker pulseReader; |
otis22894 | 8:0620e2461b3a | 25 | |
otis22894 | 8:0620e2461b3a | 26 | Heart :: Heart() { |
otis22894 | 8:0620e2461b3a | 27 | } |
otis22894 | 8:0620e2461b3a | 28 | |
otis22894 | 8:0620e2461b3a | 29 | void Heart :: startReading() { |
otis22894 | 8:0620e2461b3a | 30 | pulseReader.attach(this, &Heart::sample, .002f); |
otis22894 | 8:0620e2461b3a | 31 | } |
otis22894 | 8:0620e2461b3a | 32 | |
otis22894 | 8:0620e2461b3a | 33 | void Heart :: stopReading() { |
otis22894 | 8:0620e2461b3a | 34 | pulseReader.detach(); |
otis22894 | 8:0620e2461b3a | 35 | __enable_irq(); |
otis22894 | 8:0620e2461b3a | 36 | } |
otis22894 | 8:0620e2461b3a | 37 | |
otis22894 | 8:0620e2461b3a | 38 | void Heart :: reset() { |
otis22894 | 8:0620e2461b3a | 39 | sampleCounter = 0; // used to determine pulse timing |
otis22894 | 8:0620e2461b3a | 40 | lastBeatTime = 0; // used to find IBI |
otis22894 | 8:0620e2461b3a | 41 | P =512; // used to find peak in pulse wave, seeded |
otis22894 | 8:0620e2461b3a | 42 | T = 512; // used to find trough in pulse wave, seeded |
otis22894 | 8:0620e2461b3a | 43 | thresh = 512; // used to find instant moment of heart beat, seeded |
otis22894 | 8:0620e2461b3a | 44 | amp = 100; // used to hold amplitude of pulse waveform, seeded |
otis22894 | 8:0620e2461b3a | 45 | firstBeat = true; // used to seed rate array so we startup with reasonable BPM |
otis22894 | 8:0620e2461b3a | 46 | secondBeat = false; // used to seed rate array so we startup with reasonable BPM |
otis22894 | 8:0620e2461b3a | 47 | IBI = 600; // holds the time between beats, must be seeded! |
otis22894 | 8:0620e2461b3a | 48 | Pulse = false; // true when pulse wave is high, false when it's low |
otis22894 | 8:0620e2461b3a | 49 | QS = false; // becomes true when Arduoino finds a beat. |
otis22894 | 8:0620e2461b3a | 50 | isBeating = false; |
otis22894 | 8:0620e2461b3a | 51 | beatCount = 0; |
otis22894 | 8:0620e2461b3a | 52 | PulseLength = 0; |
otis22894 | 8:0620e2461b3a | 53 | } |
otis22894 | 8:0620e2461b3a | 54 | |
otis22894 | 8:0620e2461b3a | 55 | bool Heart :: beatDetected() { |
otis22894 | 8:0620e2461b3a | 56 | return isBeating; |
otis22894 | 8:0620e2461b3a | 57 | } |
otis22894 | 8:0620e2461b3a | 58 | |
otis22894 | 8:0620e2461b3a | 59 | int Heart :: beatsCounted() { |
otis22894 | 8:0620e2461b3a | 60 | return beatCount; |
otis22894 | 8:0620e2461b3a | 61 | } |
otis22894 | 8:0620e2461b3a | 62 | |
otis22894 | 8:0620e2461b3a | 63 | // THIS IS THE TIMER 2 INTERRUPT SERVICE ROUTINE. |
otis22894 | 8:0620e2461b3a | 64 | // Timer 2 makes sure that we take a reading every 2 miliseconds |
otis22894 | 8:0620e2461b3a | 65 | void Heart :: sample() { // triggered when Timer2 counts to 124 |
otis22894 | 8:0620e2461b3a | 66 | __disable_irq(); |
otis22894 | 8:0620e2461b3a | 67 | float readVal = ain; // disable interrupts while we do this |
otis22894 | 8:0620e2461b3a | 68 | Signal = ain * 1024; // read the Pulse Sensor |
otis22894 | 8:0620e2461b3a | 69 | sampleCounter += 2; // keep track of the time in mS with this variable |
otis22894 | 8:0620e2461b3a | 70 | int N = sampleCounter - lastBeatTime; // monitor the time since the last beat to avoid noise |
otis22894 | 8:0620e2461b3a | 71 | |
otis22894 | 8:0620e2461b3a | 72 | // find the peak and trough of the pulse wave |
otis22894 | 8:0620e2461b3a | 73 | if(Signal < thresh && N > (IBI/5)*3){ // avoid dichrotic noise by waiting 3/5 of last IBI |
otis22894 | 8:0620e2461b3a | 74 | if (Signal < T){ // T is the trough |
otis22894 | 8:0620e2461b3a | 75 | T = Signal; // keep track of lowest point in pulse wave |
otis22894 | 8:0620e2461b3a | 76 | } |
otis22894 | 8:0620e2461b3a | 77 | } |
otis22894 | 8:0620e2461b3a | 78 | |
otis22894 | 8:0620e2461b3a | 79 | if(Signal > thresh && Signal > P){ // thresh condition helps avoid noise |
otis22894 | 8:0620e2461b3a | 80 | P = Signal; // P is the peak |
otis22894 | 8:0620e2461b3a | 81 | } // keep track of highest point in pulse wave |
otis22894 | 8:0620e2461b3a | 82 | |
otis22894 | 8:0620e2461b3a | 83 | // NOW IT'S TIME TO LOOK FOR THE HEART BEAT |
otis22894 | 8:0620e2461b3a | 84 | // signal surges up in value every time there is a pulse |
otis22894 | 8:0620e2461b3a | 85 | if (N > 250){ // avoid high frequency noise |
otis22894 | 8:0620e2461b3a | 86 | if ( (Signal > thresh) && (Pulse == false) && (N > (IBI/5)*3) ){ |
otis22894 | 8:0620e2461b3a | 87 | Pulse = true; // set the Pulse flag when we think there is a pulse |
otis22894 | 8:0620e2461b3a | 88 | IBI = sampleCounter - lastBeatTime; // measure time between beats in mS |
otis22894 | 8:0620e2461b3a | 89 | lastBeatTime = sampleCounter; // keep track of time for next pulse |
otis22894 | 8:0620e2461b3a | 90 | |
otis22894 | 8:0620e2461b3a | 91 | if(secondBeat){ // if this is the second beat, if secondBeat == TRUE |
otis22894 | 8:0620e2461b3a | 92 | secondBeat = false; // clear secondBeat flag |
otis22894 | 8:0620e2461b3a | 93 | for(int i=0; i<=9; i++){ // seed the running total to get a realisitic BPM at startup |
otis22894 | 8:0620e2461b3a | 94 | rate[i] = IBI; |
otis22894 | 8:0620e2461b3a | 95 | } |
otis22894 | 8:0620e2461b3a | 96 | } |
otis22894 | 8:0620e2461b3a | 97 | |
otis22894 | 8:0620e2461b3a | 98 | if(firstBeat){ // if it's the first time we found a beat, if firstBeat == TRUE |
otis22894 | 8:0620e2461b3a | 99 | firstBeat = false; // clear firstBeat flag |
otis22894 | 8:0620e2461b3a | 100 | secondBeat = true; // set the second beat flag |
otis22894 | 8:0620e2461b3a | 101 | __enable_irq(); // enable interrupts again |
otis22894 | 8:0620e2461b3a | 102 | return; // IBI value is unreliable so discard it |
otis22894 | 8:0620e2461b3a | 103 | } |
otis22894 | 8:0620e2461b3a | 104 | |
otis22894 | 8:0620e2461b3a | 105 | |
otis22894 | 8:0620e2461b3a | 106 | // keep a running total of the last 10 IBI values |
otis22894 | 8:0620e2461b3a | 107 | unsigned short runningTotal = 0; // clear the runningTotal variable |
otis22894 | 8:0620e2461b3a | 108 | |
otis22894 | 8:0620e2461b3a | 109 | for(int i=0; i<=8; i++){ // shift data in the rate array |
otis22894 | 8:0620e2461b3a | 110 | rate[i] = rate[i+1]; // and drop the oldest IBI value |
otis22894 | 8:0620e2461b3a | 111 | runningTotal += rate[i]; // add up the 9 oldest IBI values |
otis22894 | 8:0620e2461b3a | 112 | } |
otis22894 | 8:0620e2461b3a | 113 | |
otis22894 | 8:0620e2461b3a | 114 | rate[9] = IBI; // add the latest IBI to the rate array |
otis22894 | 8:0620e2461b3a | 115 | runningTotal += rate[9]; // add the latest IBI to runningTotal |
otis22894 | 8:0620e2461b3a | 116 | runningTotal /= 10; // average the last 10 IBI values |
otis22894 | 8:0620e2461b3a | 117 | BPM = 60000/runningTotal; // how many beats can fit into a minute? that's BPM! |
otis22894 | 8:0620e2461b3a | 118 | QS = true; // set Quantified Self flag |
otis22894 | 8:0620e2461b3a | 119 | // QS FLAG IS NOT CLEARED INSIDE THIS ISR |
otis22894 | 8:0620e2461b3a | 120 | myLED = 1; |
otis22894 | 8:0620e2461b3a | 121 | } |
otis22894 | 8:0620e2461b3a | 122 | } |
otis22894 | 8:0620e2461b3a | 123 | |
otis22894 | 8:0620e2461b3a | 124 | if(Pulse) { |
otis22894 | 8:0620e2461b3a | 125 | PulseLength+=2; |
otis22894 | 8:0620e2461b3a | 126 | if (Signal < thresh){ // when the values are going down, the beat is over |
otis22894 | 8:0620e2461b3a | 127 | myLED = 0; // turn off pin 13 LED |
otis22894 | 8:0620e2461b3a | 128 | Pulse = false; // reset the Pulse flag so we can do it again |
otis22894 | 8:0620e2461b3a | 129 | amp = P - T; // get amplitude of the pulse wave |
otis22894 | 8:0620e2461b3a | 130 | //if (PulseLength > 4 && amp > (T/2)) { |
otis22894 | 8:0620e2461b3a | 131 | if (PulseLength > 4 && amp > (T/3)) { |
otis22894 | 8:0620e2461b3a | 132 | isBeating = true; |
otis22894 | 8:0620e2461b3a | 133 | beatCount++; |
otis22894 | 8:0620e2461b3a | 134 | } |
otis22894 | 8:0620e2461b3a | 135 | thresh = amp/2 + T; // set thresh at 50% of the amplitude |
otis22894 | 8:0620e2461b3a | 136 | P = thresh; // reset these for next time |
otis22894 | 8:0620e2461b3a | 137 | T = thresh; |
otis22894 | 8:0620e2461b3a | 138 | PulseLength = 0; |
otis22894 | 8:0620e2461b3a | 139 | } |
otis22894 | 8:0620e2461b3a | 140 | } |
otis22894 | 8:0620e2461b3a | 141 | |
otis22894 | 8:0620e2461b3a | 142 | if (N > 2500){ // if 2.5 seconds go by without a beat |
otis22894 | 8:0620e2461b3a | 143 | thresh = 512; // set thresh default |
otis22894 | 8:0620e2461b3a | 144 | P = 512; // set P default |
otis22894 | 8:0620e2461b3a | 145 | T = 512; // set T default |
otis22894 | 8:0620e2461b3a | 146 | lastBeatTime = sampleCounter; // bring the lastBeatTime up to date |
otis22894 | 8:0620e2461b3a | 147 | firstBeat = true; // set these to avoid noise |
otis22894 | 8:0620e2461b3a | 148 | secondBeat = false; // when we get the heartbeat back |
otis22894 | 8:0620e2461b3a | 149 | isBeating = false; |
otis22894 | 8:0620e2461b3a | 150 | } |
otis22894 | 8:0620e2461b3a | 151 | |
otis22894 | 8:0620e2461b3a | 152 | __enable_irq(); // enable interrupts when youre done! |
otis22894 | 8:0620e2461b3a | 153 | } |