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
Revision 9:b2a894f94cb7, committed 2015-10-22
- Comitter:
- maryannionascu
- Date:
- Thu Oct 22 20:24:19 2015 +0000
- Parent:
- 8:31e63caf37e2
- Child:
- 10:ad2548407023
- Commit message:
- final;
Changed in this revision
main.cpp | Show annotated file Show diff for this revision Revisions of this file |
--- a/main.cpp Sat Nov 30 02:06:03 2013 +0000 +++ b/main.cpp Thu Oct 22 20:24:19 2015 +0000 @@ -1,169 +1,142 @@ -// uLCD-144-G2 demo program for uLCD-4GL LCD driver library -// #include "mbed.h" #include "uLCD_4DGL.h" -uLCD_4DGL uLCD(p9,p10,p11); // serial tx, serial rx, reset pin; +//Calculates the BPM using a photoresistor as input and outputs the BPM to an LCD screen. +uLCD_4DGL uLCD(p28,p27,p30); // serial tx, serial rx, reset pin; +AnalogIn photoresistor(p15); +Timer t; //running timer +Ticker interrupt; //recurring 2 ms interrupt + +volatile float beat; //raw analog signal data from photoresistor +volatile int BPM = 60; //beats per minute +volatile int beatCount = 0; //number of beats found +volatile float currIBI = 0; // current InterBeat Interval: the time since the last beat +volatile float lastIBI = 1; //last InterBeat Interval: the time between beats +volatile float pastIBI[10]; //past ten IBI values +volatile float sumIBI = 0; ///sum of past ten IBI values +volatile unsigned long lastBeatTime = 0; //time of last beat +volatile float peak = 25; //peak of heartbeat wave; initilized to 20 to avoid noise at startup +volatile float trough = 15; //trough of heartbeat wave +volatile float thresh = 10; // used to reduce noice +volatile float amp = 5; // amplitude of heartbeat wave +volatile bool firstBeat = true; //keeps track of first beat found +volatile bool beatFound = false; //true when a beat is found, false in between beats +volatile bool BPMfound = false; //true when BPM is found, false otherwise. + +void findBPM(){ + __disable_irq(); // disable interrupts + beat = photoresistor * 100; // read the analog data from the photoresistor and multiply by 100 (amplifies small values) + currIBI = t.read() - lastBeatTime; + // avoid noise by waiting 3/5 of last IBI + if((beat < thresh) && (currIBI > (lastIBI*3)/5)){ + if (beat < trough){ + trough = beat; + } + } + if((beat > thresh) && (beat > peak)){ + peak = beat; + } + // photoresistor signal surges when there is a heartbeat + if ((currIBI > 0.25) && (beat > thresh) && (beat > 30) && (beatFound == false)){ + beatFound = true; + beatCount++; + thresh = beat - 0.7; //adjust threshold to account for any change in lighting and avoid noise + lastIBI = t.read() - lastBeatTime; + lastBeatTime = t.read(); + //if this is the first beat found, fill pastIBI values with this value until more beats are found + if(firstBeat){ + firstBeat = false; + for(int i=0; i< 10; i++){ + pastIBI[i] = lastIBI; + } + } + sumIBI = lastIBI; + //shift data over one value in pastIBI array to make room for next IBI value and discard oldest value + for(int i=0; i< 9; i++){ + pastIBI[i] = pastIBI[i+1]; + sumIBI += pastIBI[i]; + } + pastIBI[9] = lastIBI; //add most recent IBI value to array + sumIBI = sumIBI/10; //average the last 10 IBI values + BPM = 60/sumIBI; //BPM = 1 minute / average IBI + if(beatCount == 5){ //Only report BPM after at least 5 beats are found + BPMfound = true; + beatCount = 0; + } + } + //when the signal is less than the threshold, the beat is over + if (((beat < thresh) && beatFound) || (currIBI > 2)){ + beatFound = false; // reset the beatFound flag to find the next beat + amp = peak - trough; // calculate amplitude of heartbeat wave + thresh = amp/2 + trough; // set threshold to 50% of the amplitude + peak = thresh; + trough = thresh; + } + /* + //if 3 seconds pass since the last beat, reset all variables to default values + if (currIBI > 3){ + thresh = 10; + peak = 10; + trough = 10; + lastBeatTime = t.read(); + firstBeat = true; + } + */ + __enable_irq(); //enable interrupts +} int main() { - // basic printf demo = 16 by 18 characters on screen - uLCD.printf("\nHello uLCD World\n"); //Default Green on black text - uLCD.printf("\n Starting Demo..."); - uLCD.text_width(4); //4X size text - uLCD.text_height(4); uLCD.color(RED); - for (int i=10; i>=0; --i) { - uLCD.locate(1,2); - uLCD.printf("%2D",i); - wait(.5); - } - uLCD.cls(); - uLCD.printf("Change baudrate......"); - uLCD.baudrate(3000000); //jack up baud rate to max for fast display - //if demo hangs here - try lower baud rates - // - // printf text only full screen mode demo - uLCD.background_color(BLUE); - uLCD.cls(); - uLCD.locate(0,0); - uLCD.color(WHITE); - uLCD.textbackground_color(BLUE); - uLCD.set_font(FONT_7X8); - uLCD.text_mode(OPAQUE); - int i=0; - while(i<64) { - if(i%16==0) uLCD.cls(); - uLCD.printf("TxtLine %2D Page %D\n",i%16,i/16 ); - i++; //16 lines with 18 charaters per line - } - wait(0.5); - //demo graphics commands - uLCD.background_color(BLACK); - uLCD.cls(); - uLCD.background_color(DGREY); - uLCD.filled_circle(60, 50, 30, 0xFF00FF); - uLCD.triangle(120, 100, 40, 40, 10, 100, 0x0000FF); - uLCD.line(0, 0, 80, 60, 0xFF0000); - uLCD.filled_rectangle(50, 50, 100, 90, 0x00FF00); - uLCD.pixel(60, 60, BLACK); - uLCD.read_pixel(120, 70); - uLCD.circle(120, 60, 10, BLACK); - uLCD.set_font(FONT_7X8); - uLCD.text_mode(TRANSPARENT); - uLCD.text_bold(ON); - uLCD.text_char('B', 9, 8, BLACK); - uLCD.text_char('I',10, 8, BLACK); - uLCD.text_char('G',11, 8, BLACK); - uLCD.text_italic(ON); - uLCD.text_string("This is a test of string", 1, 4, FONT_7X8, WHITE); - wait(2); - -//Bouncing Ball Demo - float fx=50.0,fy=21.0,vx=1.0,vy=0.4; - int x=50,y=21,radius=4; - uLCD.background_color(BLACK); + uLCD.printf("\nHeart Beat Monitor\n"); + wait(1); uLCD.cls(); - //draw walls - uLCD.line(0, 0, 127, 0, WHITE); - uLCD.line(127, 0, 127, 127, WHITE); - uLCD.line(127, 127, 0, 127, WHITE); - uLCD.line(0, 127, 0, 0, WHITE); - for (int i=0; i<1500; i++) { - //draw ball - uLCD.filled_circle(x, y, radius, RED); - //bounce off edge walls and slow down a bit? - if ((x<=radius+1) || (x>=126-radius)) vx = -.90*vx; - if ((y<=radius+1) || (y>=126-radius)) vy = -.90*vy; - //erase old ball location - uLCD.filled_circle(x, y, radius, BLACK); - //move ball - fx=fx+vx; - fy=fy+vy; - x=(int)fx; - y=(int)fy; - } - wait(0.5); -//draw an image pixel by pixel - int pixelcolors[50][50]; - uLCD.background_color(BLACK); - uLCD.cls(); -//compute Mandelbrot set image for display -//image size in pixels - const unsigned ImageHeight=128; - const unsigned ImageWidth=128; - //"c" region to display - double MinRe = -0.75104; - double MaxRe = -0.7408; - double MinIm = 0.10511; - double MaxIm = MinIm+(MaxRe-MinRe)*ImageHeight/ImageWidth; - double Re_factor = (MaxRe-MinRe)/(ImageWidth-1); - double Im_factor = (MaxIm-MinIm)/(ImageHeight-1); - unsigned MaxIterations = 2048; - for(unsigned y=0; y<ImageHeight; ++y) { - double c_im = MaxIm - y*Im_factor; - for(unsigned x=0; x<ImageWidth; ++x) { - double c_re = MinRe + x*Re_factor; - double Z_re = c_re, Z_im = c_im; - int niterations=0; - for(unsigned n=0; n<MaxIterations; ++n) { - double Z_re2 = Z_re*Z_re, Z_im2 = Z_im*Z_im; - if(Z_re2 + Z_im2 > 4) { - niterations = n; - break; - } - Z_im = 2*Z_re*Z_im + c_im; - Z_re = Z_re2 - Z_im2 + c_re; + t.start(); + interrupt.attach(&findBPM, 0.002); //throws interrupt every 2ms to read sensor data and calculate BPM + while(1){ + if(t.read() > 10){ //wait 10 seconds to get an accurate average + if(BPMfound){ + BPMfound = false; //start calculating next BPM + uLCD.locate(1,2); + uLCD.printf("\nYour BPM is:\n"); + uLCD.printf("%2D", BPM); + wait(3); + uLCD.cls(); + lastBeatTime = 0; + beatCount = 0; + t.start(); //restart timer } - if (niterations!=(MaxIterations-1)) - uLCD.pixel(x,y,((niterations & 0xF00)<<12)+((niterations & 0xF0)<<8)+((niterations & 0x0F)<<4) ); - } - } - wait(5); -// PLASMA wave BLIT animation -//draw an image using BLIT (Block Image Transfer) fastest way to transfer pixel data - uLCD.cls(); - int num_cols=50; - int num_rows=50; - int frame=0; - double a,b,c=0.0; - while(frame<75) { - for (int k=0; k<num_cols; k++) { - b= (1+sin(3.14159*k*0.75/(num_cols-1.0)+c))*0.5; - for (int i=0; i<num_rows; i++) { - a= (1+sin(3.14159*i*0.75/(num_rows-1.0)+c))*0.5; - // a and b will be a sine wave output between 0 and 1 - // sine wave was scaled for nice effect across array - // uses a and b to compute pixel colors based on rol and col location in array - // also keeps colors at the same brightness level - if ((a+b) <.667) - pixelcolors[i][k] = (255-(int(254.0*((a+b)/0.667)))<<16) | (int(254.0*((a+b)/0.667))<<8) | 0; - else if ((a+b)<1.333) - pixelcolors[i][k] = (0 <<16) | (255-(int (254.0*((a+b-0.667)/0.667)))<<8) | int(254.0*((a+b-0.667)/0.667)); - else - pixelcolors[i][k] = (int(255*((a+b-1.333)/0.667))<<16) | (0<<8) | (255-(int (254.0*((a+b-1.333)/0.667)))); + //No heartbeat detected wtihin 10 seconds + else{ + uLCD.printf("\nNo heartbeat detected!"); + wait(3); + uLCD.cls(); + lastBeatTime = 0; + beatCount = 0; + t.start(); //restart timer } } - uLCD.BLIT(39, 39, 50, 50, &pixelcolors[0][0]); - c = c + 0.0314159*3.0; - if (c > 6.2831) c = 0.0; - frame++; - } - //Load Image Demo - uLCD.cls(); - //SD card needed with image and video files for last two demos - uLCD.cls(); - uLCD.media_init(); - uLCD.printf("\n\nAn SD card is needed for image and video data"); - uLCD.set_sector_address(0x001D, 0x4C01); - uLCD.display_image(0,0); - wait(10); - //Play video demo - while(1) { - uLCD.cls(); - uLCD.media_init(); - uLCD.set_sector_address(0x001D, 0x4C42); - uLCD.display_video(0,0); + else{ + uLCD.locate(1,2); + uLCD.printf("\nCalculating...\n"); + //uLCD.printf("%2F\n", (float) photoresistor*100); + //uLCD.printf("beatCount: %2F\n", (float) beatCount); + //uLCD.printf("currIBI: %2F\n", (float) currIBI); + wait(.1); + uLCD.cls(); + /* + uLCD.printf("%2F\n", (float) photoresistor*100); + //uLCD.printf("BPMfound: %2F\n", (float) BPMfound); + uLCD.printf("beatFound: %2F\n", (float) beatFound); + uLCD.printf("beatCount: %2F\n", (float) beatCount); + //uLCD.printf("firstBeat: %2F\n", (float) firstBeat); + uLCD.printf("thresh: %2F\n", (float) thresh); + //uLCD.printf("sumIBI: %2F\n", (float) sumIBI); + //uLCD.printf("time: %2F\n", (float) t.read()); + wait(.1); + uLCD.cls(); + */ + } } }