Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependencies: mbed mbed-rtos 4DGL-uLCD-SE RPCInterface
Revision 16:d4b686118853, committed 2019-12-05
- 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 + }