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 jim hamblen

Files at this revision

API Documentation at this revision

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();
+            */
+        }
     }
 }