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
+
}