Gerrod Ubben / Mbed 2 deprecated ECE4180_Final_Project

Dependencies:   mbed mbed-rtos 4DGL-uLCD-SE RPCInterface

Files at this revision

API Documentation at this revision

Comitter:
Gerrod
Date:
Thu Dec 05 22:58:39 2019 +0000
Parent:
15:5120c88a7a87
Commit message:
Implemented Heart Rate sensor

Changed in this revision

main.cpp Show annotated file Show diff for this revision Revisions of this file
--- a/main.cpp	Wed Dec 04 19:09:11 2019 +0000
+++ b/main.cpp	Thu Dec 05 22:58:39 2019 +0000
@@ -7,6 +7,9 @@
 #include <math.h>
 #include "chime.h"
 
+#include "algorithm.h"
+#include "MAX30102.h"
+
 /* Example RPC commands that have currently been implemented
 
 /notify/run This_is_a_notification_message_that_should_be_displayed_on_the_lcd
@@ -15,8 +18,12 @@
 
 */
 
+enum displayApp{NOTIFICATION, TIME, HEARTBEAT}; //add to this enum for different things to display to screen
+
 volatile bool display_notification = false;
 volatile bool display_time = true;
+volatile bool display_heartbeat = false;
+
 
 volatile int utc_offset; //keeps track of the current timezone of the watch
 
@@ -25,16 +32,20 @@
 Serial pc(USBTX, USBRX);
 
 InterruptIn view_button(p12);
+DigitalIn INT(p26);  //pin P26 connects to the interrupt output pin of the MAX30102
 
 Mutex stdio_mutex; //mutex used when accessing stdio functions
 Mutex lcd_mutex; //mutex used when accessing the lcd object
 
 Thread bluetooth_thread; //thread responsible for receiving rpc commands over bluetooth
 Thread time_thread; //thread responsible for updating the lcd with the current time
+Thread heartbeat_thread; //thread responsible for updating and displaying heartbeat
 
 //rpc function prototypes
 void display_notification_rpc_func(Arguments *in, Reply *out);
 void set_time_rpc_func (Arguments *in, Reply *out);
+void display_heartbeat_rpc_func(Arguments *in, Reply *out);
+RPCFunction rpcHeartbeatLCD(&display_heartbeat_rpc_func, "heartbeat");
 RPCFunction rpcWriteLCD(&display_notification_rpc_func, "notify");
 RPCFunction rpcset_time_rpc_func(&set_time_rpc_func, "setTime");
 
@@ -52,12 +63,32 @@
         notification_chime_ticker.detach();
     }
 }
+//this function is used to turn off displaying everything except the argument passed in
+void display_x(enum displayApp i) {
+    display_notification = false;
+    display_heartbeat = false;
+    display_time = false;
+    switch(i) {
+        case NOTIFICATION:
+            display_notification = true;
+        break;
+        case TIME:
+            display_time = true;
+        break;
+        case HEARTBEAT:
+            display_heartbeat = true;
+        break;
+        default:
+            display_time = true;
+        break;
+    }
+    
+}
 
 //interrupt routine for when the input button is pressed
 //when the view button is pressed, dismiss the currently shown notification and display the current time
 void view_button_pressed(void){
-    display_notification = false;
-    display_time = true;
+    display_x(TIME);
 }
 
 //flip the y coordinate around so that standard cartesian coordinates can be used
@@ -223,8 +254,7 @@
     int i,j;
     bool break_out = false;
     
-    display_notification = true;
-    display_time = false;
+    display_x(NOTIFICATION);
     
     msg_str = in->getArg<const char*>(); //get a pointer to the location where the argument string is stored
     
@@ -255,6 +285,89 @@
     
 }
 
+void display_heartbeat_rpc_func(Arguments *in, Reply *out){
+    
+    //check if heartbeat thread is sleeping
+    Thread::State hbs = heartbeat_thread.get_state();
+    if (hbs != Thread::Running && hbs != Thread::Ready && hbs != Thread::Inactive && hbs != Thread::Deleted){ //don't know which one wait state thread gets set to when it sleeps
+        display_x(HEARTBEAT);
+        heartbeat_thread.signal_set(0x1); //wake up heartbeat thread
+    }//there is a funky scenario where you can hit the back button, but also send a heartbeat rpc command leading to undefined behavior
+     //don't know which should take priority
+     
+}
+void draw_heartbeat(int32_t hr, int8_t hr_valid, int32_t sp02, int8_t sp02_valid, int8_t cls) {
+    //first time draw when heartbeat is called
+    //text_string(char *s, char col, char row, char font, int color)
+    stdio_mutex.lock(); //maybe i need this
+    lcd_mutex.lock();
+    if (cls) {
+        uLCD.cls(); 
+        uLCD.text_string("BPM:", 0, 1, 0, WHITE);
+        uLCD.text_string("SPO2:", 0, 3, 0, WHITE);
+    }
+    int hrcolor = hr_valid ? GREEN : RED;
+    int spcolor = sp02_valid ? GREEN : RED;
+    char bpm[] = "   ";
+    char sp[] = "   "; 
+    sprintf(bpm, "%d", hr);
+    sprintf(sp, "%d", sp);
+    
+    uLCD.text_string(bpm, 3, 1, 0, hrcolor);
+    uLCD.text_string(sp, 3, 3, 0, spcolor);
+    
+    lcd_mutex.unlock();
+    stdio_mutex.unlock();
+}
+
+void heartbeat_thread_func() {
+    uint32_t aun_ir_buffer[200]; //IR LED sensor data
+    int32_t n_ir_buffer_length = 0;    //data length
+    uint32_t aun_red_buffer[200];    //Red LED sensor data
+    int32_t n_sp02; //SPO2 value
+    int8_t ch_spo2_valid;   //indicator to show if the SP02 calculation is valid
+    int32_t n_heart_rate;   //heart rate value
+    int8_t  ch_hr_valid;    //indicator to show if the heart rate calculation is valid
+    int i = 0;
+    uint8_t dummy;
+    maxim_max30102_reset(); //resets the MAX30102
+    wait(1);
+    maxim_max30102_read_reg(0,&dummy);
+    maxim_max30102_init();  //initializes the MAX30102
+    if(!display_heartbeat)
+        Thread::signal_wait(0x1); //sleep until called on
+    draw_heartbeat(0, 0, 0, 0, 1); //sets up the screen with default values
+    while (true) {
+        //Take 50 samples each half second, then check if we should relinquish control
+        if (n_ir_buffer_length == 200) {
+            //dump first 50 sets of samples, shift the last 150 to the top
+            for (i = 50; i < 200; ++i) {
+                aun_red_buffer[i-50]=aun_red_buffer[i];
+                aun_ir_buffer[i-50]=aun_ir_buffer[i];   
+            } 
+            //take 50 sets of sample before calculating heart rate
+            for (i = 150; i < 200; ++i) {
+                while(INT.read()==1);
+                maxim_max30102_read_fifo((aun_red_buffer+i), (aun_ir_buffer+i));   
+            }
+            maxim_heart_rate_and_oxygen_saturation(aun_ir_buffer, n_ir_buffer_length, aun_red_buffer, &n_sp02, &ch_spo2_valid, &n_heart_rate, &ch_hr_valid);   
+        } else {
+            for (i = n_ir_buffer_length; i < n_ir_buffer_length+50; ++i) {
+                while(INT.read()==1);   //wait until the interrupt pin asserts
+                maxim_max30102_read_fifo((aun_red_buffer+i), (aun_ir_buffer+i));  //read from MAX30102 FIFO   
+            }  
+            n_ir_buffer_length += 50;
+            maxim_heart_rate_and_oxygen_saturation(aun_ir_buffer, n_ir_buffer_length, aun_red_buffer, &n_sp02, &ch_spo2_valid, &n_heart_rate, &ch_hr_valid);
+        } 
+        if (display_heartbeat) {
+            draw_heartbeat(n_heart_rate, ch_hr_valid, n_sp02, ch_spo2_valid, 0); //draw heartbeat
+        } else {
+            Thread::signal_wait(0x1); //sleep till woken by rpc command
+            draw_heartbeat(n_heart_rate, ch_hr_valid, n_sp02, ch_spo2_valid, 1);  //clear and setup screen upon return
+        }     
+    }       
+}
+
 int main() {
     
     uLCD.baudrate(3000000); //increase the lcd baud rate
@@ -266,5 +379,6 @@
     
     bluetooth_thread.start(bluetooth_thread_func); //start the thread that takes in characters to construct RPC commands
     time_thread.start(time_thread_func); //start the thread that updates the displayed time
-
+    heartbeat_thread.start(heartbeat_thread_func); //start the thread that only runs when a heartbeat RPC command is called
+    
 }