![](/media/cache/img/default_profile.jpg.50x50_q85.jpg)
Optical heart rate monitor using photoresistor and LED to calculate BPM, which is outputted to LCD screen.
Dependencies: 4DGL-uLCD-SE mbed
Fork of uLCD144G2_demo by
main.cpp@9:b2a894f94cb7, 2015-10-22 (annotated)
- Committer:
- maryannionascu
- Date:
- Thu Oct 22 20:24:19 2015 +0000
- Revision:
- 9:b2a894f94cb7
- Parent:
- 8:31e63caf37e2
- Child:
- 10:ad2548407023
final;
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
4180_1 | 0:cfcf73272647 | 1 | #include "mbed.h" |
4180_1 | 2:75727e89a717 | 2 | #include "uLCD_4DGL.h" |
4180_1 | 0:cfcf73272647 | 3 | |
maryannionascu | 9:b2a894f94cb7 | 4 | //Calculates the BPM using a photoresistor as input and outputs the BPM to an LCD screen. |
maryannionascu | 9:b2a894f94cb7 | 5 | uLCD_4DGL uLCD(p28,p27,p30); // serial tx, serial rx, reset pin; |
maryannionascu | 9:b2a894f94cb7 | 6 | AnalogIn photoresistor(p15); |
maryannionascu | 9:b2a894f94cb7 | 7 | Timer t; //running timer |
maryannionascu | 9:b2a894f94cb7 | 8 | Ticker interrupt; //recurring 2 ms interrupt |
maryannionascu | 9:b2a894f94cb7 | 9 | |
maryannionascu | 9:b2a894f94cb7 | 10 | volatile float beat; //raw analog signal data from photoresistor |
maryannionascu | 9:b2a894f94cb7 | 11 | volatile int BPM = 60; //beats per minute |
maryannionascu | 9:b2a894f94cb7 | 12 | volatile int beatCount = 0; //number of beats found |
maryannionascu | 9:b2a894f94cb7 | 13 | volatile float currIBI = 0; // current InterBeat Interval: the time since the last beat |
maryannionascu | 9:b2a894f94cb7 | 14 | volatile float lastIBI = 1; //last InterBeat Interval: the time between beats |
maryannionascu | 9:b2a894f94cb7 | 15 | volatile float pastIBI[10]; //past ten IBI values |
maryannionascu | 9:b2a894f94cb7 | 16 | volatile float sumIBI = 0; ///sum of past ten IBI values |
maryannionascu | 9:b2a894f94cb7 | 17 | volatile unsigned long lastBeatTime = 0; //time of last beat |
maryannionascu | 9:b2a894f94cb7 | 18 | volatile float peak = 25; //peak of heartbeat wave; initilized to 20 to avoid noise at startup |
maryannionascu | 9:b2a894f94cb7 | 19 | volatile float trough = 15; //trough of heartbeat wave |
maryannionascu | 9:b2a894f94cb7 | 20 | volatile float thresh = 10; // used to reduce noice |
maryannionascu | 9:b2a894f94cb7 | 21 | volatile float amp = 5; // amplitude of heartbeat wave |
maryannionascu | 9:b2a894f94cb7 | 22 | volatile bool firstBeat = true; //keeps track of first beat found |
maryannionascu | 9:b2a894f94cb7 | 23 | volatile bool beatFound = false; //true when a beat is found, false in between beats |
maryannionascu | 9:b2a894f94cb7 | 24 | volatile bool BPMfound = false; //true when BPM is found, false otherwise. |
maryannionascu | 9:b2a894f94cb7 | 25 | |
maryannionascu | 9:b2a894f94cb7 | 26 | void findBPM(){ |
maryannionascu | 9:b2a894f94cb7 | 27 | __disable_irq(); // disable interrupts |
maryannionascu | 9:b2a894f94cb7 | 28 | beat = photoresistor * 100; // read the analog data from the photoresistor and multiply by 100 (amplifies small values) |
maryannionascu | 9:b2a894f94cb7 | 29 | currIBI = t.read() - lastBeatTime; |
maryannionascu | 9:b2a894f94cb7 | 30 | // avoid noise by waiting 3/5 of last IBI |
maryannionascu | 9:b2a894f94cb7 | 31 | if((beat < thresh) && (currIBI > (lastIBI*3)/5)){ |
maryannionascu | 9:b2a894f94cb7 | 32 | if (beat < trough){ |
maryannionascu | 9:b2a894f94cb7 | 33 | trough = beat; |
maryannionascu | 9:b2a894f94cb7 | 34 | } |
maryannionascu | 9:b2a894f94cb7 | 35 | } |
maryannionascu | 9:b2a894f94cb7 | 36 | if((beat > thresh) && (beat > peak)){ |
maryannionascu | 9:b2a894f94cb7 | 37 | peak = beat; |
maryannionascu | 9:b2a894f94cb7 | 38 | } |
maryannionascu | 9:b2a894f94cb7 | 39 | // photoresistor signal surges when there is a heartbeat |
maryannionascu | 9:b2a894f94cb7 | 40 | if ((currIBI > 0.25) && (beat > thresh) && (beat > 30) && (beatFound == false)){ |
maryannionascu | 9:b2a894f94cb7 | 41 | beatFound = true; |
maryannionascu | 9:b2a894f94cb7 | 42 | beatCount++; |
maryannionascu | 9:b2a894f94cb7 | 43 | thresh = beat - 0.7; //adjust threshold to account for any change in lighting and avoid noise |
maryannionascu | 9:b2a894f94cb7 | 44 | lastIBI = t.read() - lastBeatTime; |
maryannionascu | 9:b2a894f94cb7 | 45 | lastBeatTime = t.read(); |
maryannionascu | 9:b2a894f94cb7 | 46 | //if this is the first beat found, fill pastIBI values with this value until more beats are found |
maryannionascu | 9:b2a894f94cb7 | 47 | if(firstBeat){ |
maryannionascu | 9:b2a894f94cb7 | 48 | firstBeat = false; |
maryannionascu | 9:b2a894f94cb7 | 49 | for(int i=0; i< 10; i++){ |
maryannionascu | 9:b2a894f94cb7 | 50 | pastIBI[i] = lastIBI; |
maryannionascu | 9:b2a894f94cb7 | 51 | } |
maryannionascu | 9:b2a894f94cb7 | 52 | } |
maryannionascu | 9:b2a894f94cb7 | 53 | sumIBI = lastIBI; |
maryannionascu | 9:b2a894f94cb7 | 54 | //shift data over one value in pastIBI array to make room for next IBI value and discard oldest value |
maryannionascu | 9:b2a894f94cb7 | 55 | for(int i=0; i< 9; i++){ |
maryannionascu | 9:b2a894f94cb7 | 56 | pastIBI[i] = pastIBI[i+1]; |
maryannionascu | 9:b2a894f94cb7 | 57 | sumIBI += pastIBI[i]; |
maryannionascu | 9:b2a894f94cb7 | 58 | } |
maryannionascu | 9:b2a894f94cb7 | 59 | pastIBI[9] = lastIBI; //add most recent IBI value to array |
maryannionascu | 9:b2a894f94cb7 | 60 | sumIBI = sumIBI/10; //average the last 10 IBI values |
maryannionascu | 9:b2a894f94cb7 | 61 | BPM = 60/sumIBI; //BPM = 1 minute / average IBI |
maryannionascu | 9:b2a894f94cb7 | 62 | if(beatCount == 5){ //Only report BPM after at least 5 beats are found |
maryannionascu | 9:b2a894f94cb7 | 63 | BPMfound = true; |
maryannionascu | 9:b2a894f94cb7 | 64 | beatCount = 0; |
maryannionascu | 9:b2a894f94cb7 | 65 | } |
maryannionascu | 9:b2a894f94cb7 | 66 | } |
maryannionascu | 9:b2a894f94cb7 | 67 | //when the signal is less than the threshold, the beat is over |
maryannionascu | 9:b2a894f94cb7 | 68 | if (((beat < thresh) && beatFound) || (currIBI > 2)){ |
maryannionascu | 9:b2a894f94cb7 | 69 | beatFound = false; // reset the beatFound flag to find the next beat |
maryannionascu | 9:b2a894f94cb7 | 70 | amp = peak - trough; // calculate amplitude of heartbeat wave |
maryannionascu | 9:b2a894f94cb7 | 71 | thresh = amp/2 + trough; // set threshold to 50% of the amplitude |
maryannionascu | 9:b2a894f94cb7 | 72 | peak = thresh; |
maryannionascu | 9:b2a894f94cb7 | 73 | trough = thresh; |
maryannionascu | 9:b2a894f94cb7 | 74 | } |
maryannionascu | 9:b2a894f94cb7 | 75 | /* |
maryannionascu | 9:b2a894f94cb7 | 76 | //if 3 seconds pass since the last beat, reset all variables to default values |
maryannionascu | 9:b2a894f94cb7 | 77 | if (currIBI > 3){ |
maryannionascu | 9:b2a894f94cb7 | 78 | thresh = 10; |
maryannionascu | 9:b2a894f94cb7 | 79 | peak = 10; |
maryannionascu | 9:b2a894f94cb7 | 80 | trough = 10; |
maryannionascu | 9:b2a894f94cb7 | 81 | lastBeatTime = t.read(); |
maryannionascu | 9:b2a894f94cb7 | 82 | firstBeat = true; |
maryannionascu | 9:b2a894f94cb7 | 83 | } |
maryannionascu | 9:b2a894f94cb7 | 84 | */ |
maryannionascu | 9:b2a894f94cb7 | 85 | __enable_irq(); //enable interrupts |
maryannionascu | 9:b2a894f94cb7 | 86 | } |
4180_1 | 0:cfcf73272647 | 87 | |
4180_1 | 2:75727e89a717 | 88 | int main() |
4180_1 | 2:75727e89a717 | 89 | { |
4180_1 | 3:454d1f4c8fd7 | 90 | uLCD.color(RED); |
maryannionascu | 9:b2a894f94cb7 | 91 | uLCD.printf("\nHeart Beat Monitor\n"); |
maryannionascu | 9:b2a894f94cb7 | 92 | wait(1); |
4180_1 | 4:25a266a74a4c | 93 | uLCD.cls(); |
maryannionascu | 9:b2a894f94cb7 | 94 | t.start(); |
maryannionascu | 9:b2a894f94cb7 | 95 | interrupt.attach(&findBPM, 0.002); //throws interrupt every 2ms to read sensor data and calculate BPM |
maryannionascu | 9:b2a894f94cb7 | 96 | while(1){ |
maryannionascu | 9:b2a894f94cb7 | 97 | if(t.read() > 10){ //wait 10 seconds to get an accurate average |
maryannionascu | 9:b2a894f94cb7 | 98 | if(BPMfound){ |
maryannionascu | 9:b2a894f94cb7 | 99 | BPMfound = false; //start calculating next BPM |
maryannionascu | 9:b2a894f94cb7 | 100 | uLCD.locate(1,2); |
maryannionascu | 9:b2a894f94cb7 | 101 | uLCD.printf("\nYour BPM is:\n"); |
maryannionascu | 9:b2a894f94cb7 | 102 | uLCD.printf("%2D", BPM); |
maryannionascu | 9:b2a894f94cb7 | 103 | wait(3); |
maryannionascu | 9:b2a894f94cb7 | 104 | uLCD.cls(); |
maryannionascu | 9:b2a894f94cb7 | 105 | lastBeatTime = 0; |
maryannionascu | 9:b2a894f94cb7 | 106 | beatCount = 0; |
maryannionascu | 9:b2a894f94cb7 | 107 | t.start(); //restart timer |
4180_1 | 3:454d1f4c8fd7 | 108 | } |
maryannionascu | 9:b2a894f94cb7 | 109 | //No heartbeat detected wtihin 10 seconds |
maryannionascu | 9:b2a894f94cb7 | 110 | else{ |
maryannionascu | 9:b2a894f94cb7 | 111 | uLCD.printf("\nNo heartbeat detected!"); |
maryannionascu | 9:b2a894f94cb7 | 112 | wait(3); |
maryannionascu | 9:b2a894f94cb7 | 113 | uLCD.cls(); |
maryannionascu | 9:b2a894f94cb7 | 114 | lastBeatTime = 0; |
maryannionascu | 9:b2a894f94cb7 | 115 | beatCount = 0; |
maryannionascu | 9:b2a894f94cb7 | 116 | t.start(); //restart timer |
4180_1 | 5:a1ef40ff0f78 | 117 | } |
4180_1 | 5:a1ef40ff0f78 | 118 | } |
maryannionascu | 9:b2a894f94cb7 | 119 | else{ |
maryannionascu | 9:b2a894f94cb7 | 120 | uLCD.locate(1,2); |
maryannionascu | 9:b2a894f94cb7 | 121 | uLCD.printf("\nCalculating...\n"); |
maryannionascu | 9:b2a894f94cb7 | 122 | //uLCD.printf("%2F\n", (float) photoresistor*100); |
maryannionascu | 9:b2a894f94cb7 | 123 | //uLCD.printf("beatCount: %2F\n", (float) beatCount); |
maryannionascu | 9:b2a894f94cb7 | 124 | //uLCD.printf("currIBI: %2F\n", (float) currIBI); |
maryannionascu | 9:b2a894f94cb7 | 125 | wait(.1); |
maryannionascu | 9:b2a894f94cb7 | 126 | uLCD.cls(); |
maryannionascu | 9:b2a894f94cb7 | 127 | /* |
maryannionascu | 9:b2a894f94cb7 | 128 | uLCD.printf("%2F\n", (float) photoresistor*100); |
maryannionascu | 9:b2a894f94cb7 | 129 | //uLCD.printf("BPMfound: %2F\n", (float) BPMfound); |
maryannionascu | 9:b2a894f94cb7 | 130 | uLCD.printf("beatFound: %2F\n", (float) beatFound); |
maryannionascu | 9:b2a894f94cb7 | 131 | uLCD.printf("beatCount: %2F\n", (float) beatCount); |
maryannionascu | 9:b2a894f94cb7 | 132 | //uLCD.printf("firstBeat: %2F\n", (float) firstBeat); |
maryannionascu | 9:b2a894f94cb7 | 133 | uLCD.printf("thresh: %2F\n", (float) thresh); |
maryannionascu | 9:b2a894f94cb7 | 134 | //uLCD.printf("sumIBI: %2F\n", (float) sumIBI); |
maryannionascu | 9:b2a894f94cb7 | 135 | //uLCD.printf("time: %2F\n", (float) t.read()); |
maryannionascu | 9:b2a894f94cb7 | 136 | wait(.1); |
maryannionascu | 9:b2a894f94cb7 | 137 | uLCD.cls(); |
maryannionascu | 9:b2a894f94cb7 | 138 | */ |
maryannionascu | 9:b2a894f94cb7 | 139 | } |
4180_1 | 5:a1ef40ff0f78 | 140 | } |
4180_1 | 6:f752accd632c | 141 | } |
4180_1 | 7:7bd7397ab89f | 142 | |
4180_1 | 7:7bd7397ab89f | 143 | |
4180_1 | 8:31e63caf37e2 | 144 |