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@10:ad2548407023, 2015-10-28 (annotated)
- Committer:
- maryannionascu
- Date:
- Wed Oct 28 17:55:39 2015 +0000
- Revision:
- 10:ad2548407023
- Parent:
- 9:b2a894f94cb7
Updated comments
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 | 10:ad2548407023 | 20 | volatile float lowerThresh = 10; // used to reduce noice; change these values based on amount of light in room |
maryannionascu | 10:ad2548407023 | 21 | volatile float upperThresh = 30; // used to reduce noice; change these values based on amount of light in room |
maryannionascu | 9:b2a894f94cb7 | 22 | volatile float amp = 5; // amplitude of heartbeat wave |
maryannionascu | 9:b2a894f94cb7 | 23 | volatile bool firstBeat = true; //keeps track of first beat found |
maryannionascu | 9:b2a894f94cb7 | 24 | volatile bool beatFound = false; //true when a beat is found, false in between beats |
maryannionascu | 9:b2a894f94cb7 | 25 | volatile bool BPMfound = false; //true when BPM is found, false otherwise. |
maryannionascu | 9:b2a894f94cb7 | 26 | |
maryannionascu | 9:b2a894f94cb7 | 27 | void findBPM(){ |
maryannionascu | 9:b2a894f94cb7 | 28 | __disable_irq(); // disable interrupts |
maryannionascu | 9:b2a894f94cb7 | 29 | beat = photoresistor * 100; // read the analog data from the photoresistor and multiply by 100 (amplifies small values) |
maryannionascu | 9:b2a894f94cb7 | 30 | currIBI = t.read() - lastBeatTime; |
maryannionascu | 9:b2a894f94cb7 | 31 | // avoid noise by waiting 3/5 of last IBI |
maryannionascu | 10:ad2548407023 | 32 | if((beat < lowerThresh) && (currIBI > (lastIBI*3)/5)){ |
maryannionascu | 9:b2a894f94cb7 | 33 | if (beat < trough){ |
maryannionascu | 9:b2a894f94cb7 | 34 | trough = beat; |
maryannionascu | 9:b2a894f94cb7 | 35 | } |
maryannionascu | 9:b2a894f94cb7 | 36 | } |
maryannionascu | 10:ad2548407023 | 37 | if((beat > upperThresh) && (beat > peak)){ |
maryannionascu | 9:b2a894f94cb7 | 38 | peak = beat; |
maryannionascu | 9:b2a894f94cb7 | 39 | } |
maryannionascu | 9:b2a894f94cb7 | 40 | // photoresistor signal surges when there is a heartbeat |
maryannionascu | 10:ad2548407023 | 41 | if ((currIBI > 0.25) && (beat > upperThresh) && (beatFound == false)){ |
maryannionascu | 9:b2a894f94cb7 | 42 | beatFound = true; |
maryannionascu | 9:b2a894f94cb7 | 43 | beatCount++; |
maryannionascu | 10:ad2548407023 | 44 | lowerThresh = beat - 0.7; //adjust threshold to account for any change in lighting and avoid noise |
maryannionascu | 9:b2a894f94cb7 | 45 | lastIBI = t.read() - lastBeatTime; |
maryannionascu | 9:b2a894f94cb7 | 46 | lastBeatTime = t.read(); |
maryannionascu | 9:b2a894f94cb7 | 47 | //if this is the first beat found, fill pastIBI values with this value until more beats are found |
maryannionascu | 9:b2a894f94cb7 | 48 | if(firstBeat){ |
maryannionascu | 9:b2a894f94cb7 | 49 | firstBeat = false; |
maryannionascu | 9:b2a894f94cb7 | 50 | for(int i=0; i< 10; i++){ |
maryannionascu | 9:b2a894f94cb7 | 51 | pastIBI[i] = lastIBI; |
maryannionascu | 9:b2a894f94cb7 | 52 | } |
maryannionascu | 9:b2a894f94cb7 | 53 | } |
maryannionascu | 9:b2a894f94cb7 | 54 | sumIBI = lastIBI; |
maryannionascu | 9:b2a894f94cb7 | 55 | //shift data over one value in pastIBI array to make room for next IBI value and discard oldest value |
maryannionascu | 9:b2a894f94cb7 | 56 | for(int i=0; i< 9; i++){ |
maryannionascu | 9:b2a894f94cb7 | 57 | pastIBI[i] = pastIBI[i+1]; |
maryannionascu | 9:b2a894f94cb7 | 58 | sumIBI += pastIBI[i]; |
maryannionascu | 9:b2a894f94cb7 | 59 | } |
maryannionascu | 9:b2a894f94cb7 | 60 | pastIBI[9] = lastIBI; //add most recent IBI value to array |
maryannionascu | 9:b2a894f94cb7 | 61 | sumIBI = sumIBI/10; //average the last 10 IBI values |
maryannionascu | 9:b2a894f94cb7 | 62 | BPM = 60/sumIBI; //BPM = 1 minute / average IBI |
maryannionascu | 9:b2a894f94cb7 | 63 | if(beatCount == 5){ //Only report BPM after at least 5 beats are found |
maryannionascu | 9:b2a894f94cb7 | 64 | BPMfound = true; |
maryannionascu | 9:b2a894f94cb7 | 65 | beatCount = 0; |
maryannionascu | 9:b2a894f94cb7 | 66 | } |
maryannionascu | 9:b2a894f94cb7 | 67 | } |
maryannionascu | 9:b2a894f94cb7 | 68 | //when the signal is less than the threshold, the beat is over |
maryannionascu | 10:ad2548407023 | 69 | if (((beat < lowerThresh) && beatFound) || (currIBI > 2)){ |
maryannionascu | 9:b2a894f94cb7 | 70 | beatFound = false; // reset the beatFound flag to find the next beat |
maryannionascu | 9:b2a894f94cb7 | 71 | amp = peak - trough; // calculate amplitude of heartbeat wave |
maryannionascu | 10:ad2548407023 | 72 | upperThresh = amp/2 + trough; // set upper threshold to 50% of the amplitude |
maryannionascu | 10:ad2548407023 | 73 | peak = upperThresh; |
maryannionascu | 10:ad2548407023 | 74 | trough = lowerThresh; |
maryannionascu | 9:b2a894f94cb7 | 75 | } |
maryannionascu | 9:b2a894f94cb7 | 76 | __enable_irq(); //enable interrupts |
maryannionascu | 9:b2a894f94cb7 | 77 | } |
4180_1 | 0:cfcf73272647 | 78 | |
4180_1 | 2:75727e89a717 | 79 | int main() |
4180_1 | 2:75727e89a717 | 80 | { |
4180_1 | 3:454d1f4c8fd7 | 81 | uLCD.color(RED); |
maryannionascu | 9:b2a894f94cb7 | 82 | uLCD.printf("\nHeart Beat Monitor\n"); |
maryannionascu | 9:b2a894f94cb7 | 83 | wait(1); |
4180_1 | 4:25a266a74a4c | 84 | uLCD.cls(); |
maryannionascu | 9:b2a894f94cb7 | 85 | t.start(); |
maryannionascu | 9:b2a894f94cb7 | 86 | interrupt.attach(&findBPM, 0.002); //throws interrupt every 2ms to read sensor data and calculate BPM |
maryannionascu | 9:b2a894f94cb7 | 87 | while(1){ |
maryannionascu | 9:b2a894f94cb7 | 88 | if(t.read() > 10){ //wait 10 seconds to get an accurate average |
maryannionascu | 9:b2a894f94cb7 | 89 | if(BPMfound){ |
maryannionascu | 9:b2a894f94cb7 | 90 | BPMfound = false; //start calculating next BPM |
maryannionascu | 9:b2a894f94cb7 | 91 | uLCD.locate(1,2); |
maryannionascu | 9:b2a894f94cb7 | 92 | uLCD.printf("\nYour BPM is:\n"); |
maryannionascu | 9:b2a894f94cb7 | 93 | uLCD.printf("%2D", BPM); |
maryannionascu | 9:b2a894f94cb7 | 94 | wait(3); |
maryannionascu | 9:b2a894f94cb7 | 95 | uLCD.cls(); |
maryannionascu | 9:b2a894f94cb7 | 96 | lastBeatTime = 0; |
maryannionascu | 9:b2a894f94cb7 | 97 | beatCount = 0; |
maryannionascu | 9:b2a894f94cb7 | 98 | t.start(); //restart timer |
4180_1 | 3:454d1f4c8fd7 | 99 | } |
maryannionascu | 9:b2a894f94cb7 | 100 | //No heartbeat detected wtihin 10 seconds |
maryannionascu | 9:b2a894f94cb7 | 101 | else{ |
maryannionascu | 9:b2a894f94cb7 | 102 | uLCD.printf("\nNo heartbeat detected!"); |
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 | 5:a1ef40ff0f78 | 108 | } |
4180_1 | 5:a1ef40ff0f78 | 109 | } |
maryannionascu | 9:b2a894f94cb7 | 110 | else{ |
maryannionascu | 9:b2a894f94cb7 | 111 | uLCD.locate(1,2); |
maryannionascu | 9:b2a894f94cb7 | 112 | uLCD.printf("\nCalculating...\n"); |
maryannionascu | 9:b2a894f94cb7 | 113 | wait(.1); |
maryannionascu | 9:b2a894f94cb7 | 114 | uLCD.cls(); |
maryannionascu | 9:b2a894f94cb7 | 115 | } |
4180_1 | 5:a1ef40ff0f78 | 116 | } |
4180_1 | 6:f752accd632c | 117 | } |
4180_1 | 7:7bd7397ab89f | 118 | |
4180_1 | 7:7bd7397ab89f | 119 | |
4180_1 | 8:31e63caf37e2 | 120 |