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
main.cpp
- Committer:
- Gerrod
- Date:
- 2019-12-05
- Revision:
- 16:d4b686118853
- Parent:
- 15:5120c88a7a87
File content as of revision 16:d4b686118853:
#include "mbed.h" #include "rtos.h" #include "mbed_rpc.h" #include "uLCD_4DGL.h" #include <time.h> #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 /setTime/run <unix time> <UTC offset (-5 for Atlanta)> /setTime/run 1256729737 -5 */ 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 uLCD_4DGL uLCD(p9,p10,p11); // serial tx, serial rx, reset pin; //Serial bluetooth(p13,p14); 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"); #define sample_freq 16000.0 AnalogOut speaker(p18); Ticker notification_chime_ticker; //interrupt routine to play next audio sample from array in flash void audio_sample () { static int i=0; speaker.write_u16(((uint16_t)data[i]<<7)); i++; if (i>= sizeof(data)) { i = 0; 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_x(TIME); } //flip the y coordinate around so that standard cartesian coordinates can be used int flipy(int y_coord){ return (128-y_coord); } #define C_X 64 #define C_Y 64 #define M_PI 3.141592 //create the tick marks for an analog clock on the lcd display void setup_analog_clock(uint32_t color){ lcd_mutex.lock(); uLCD.filled_circle(64, 64, 5, color); //centercircle double angle; //start from 3 oclock and draw all the clock tick marks counter-clockwise for(angle = 0; angle < (2*M_PI)-(M_PI/12); angle += M_PI/6){ uLCD.line(54*cos(angle)+C_X,flipy(54*sin(angle)+C_Y), 64*cos(angle)+C_X,flipy(64*sin(angle)+C_Y), color); //3 oclock tick mark } lcd_mutex.unlock(); } #define RAD_PER_SEC (2*M_PI)/60 #define RAD_PER_MIN (2*M_PI)/60 #define RAD_PER_HOUR (2*M_PI)/12 //function to handle the display of the time on the lcd screen void show_time_analog(int sec, int minute, int hour, int day, int month, int year, uint32_t sec_color, uint32_t min_color, uint32_t hour_color, uint32_t back_color) { static double angle; static int prev_sec; static int prev_minute; static int prev_hour; lcd_mutex.lock(); //tear down the previous hands that were drawn angle = -(RAD_PER_SEC*prev_sec) + M_PI/2; uLCD.line(C_X,C_Y,64*cos(angle)+C_X, flipy(64*sin(angle)+C_Y),back_color); angle = -(RAD_PER_MIN*prev_minute) + M_PI/2; uLCD.line(C_X,C_Y,52*cos(angle)+C_X, flipy(52*sin(angle)+C_Y),back_color); angle = -(RAD_PER_HOUR*prev_hour) + M_PI/2; uLCD.line(C_X,C_Y,40*cos(angle)+C_X, flipy(40*sin(angle)+C_Y),back_color); //draw the new hands angle = -(RAD_PER_SEC*sec) + M_PI/2; uLCD.line(C_X,C_Y,64*cos(angle)+C_X, flipy(64*sin(angle)+C_Y),sec_color); angle = -(RAD_PER_MIN*minute) + M_PI/2; uLCD.line(C_X,C_Y,52*cos(angle)+C_X, flipy(52*sin(angle)+C_Y),min_color); angle = -(RAD_PER_HOUR*hour) + M_PI/2; uLCD.line(C_X,C_Y,40*cos(angle)+C_X, flipy(40*sin(angle)+C_Y),hour_color); stdio_mutex.lock(); //print the current date in a month/day/year format uLCD.locate(4,10); uLCD.printf("%2d/%2d/%4d",month, day, year); uLCD.locate(8,11); if (hour < 12) {uLCD.printf("AM");} else {uLCD.printf("PM");} stdio_mutex.unlock(); lcd_mutex.unlock(); //store the location of the current hands prev_sec = sec; prev_minute = minute; prev_hour = hour; } //function to update the time displayed on the lcd approximately every second void time_thread_func() { struct tm * t; //time struct defined in time.h static time_t unix_time; //the time in unix time static bool prev_display_time; //indicates whether time was being displayed the last time the thread ran while (true) { if (display_time == true) { if (prev_display_time == false){ //clear whatever was previously on the screen lcd_mutex.lock(); uLCD.cls(); lcd_mutex.unlock(); } unix_time = time(NULL); t = localtime(&unix_time); setup_analog_clock(WHITE); int hour = (t->tm_hour + utc_offset); if (hour < 0){ hour += 24;} else if (hour >= 24) {hour -= 24;} show_time_analog(t->tm_sec,t->tm_min,hour,t->tm_mday,t->tm_mon+1,t->tm_year+1900,RED+BLUE,WHITE,BLUE,BLACK); } prev_display_time = display_time; Thread::wait(1000); //only update every second } } //function to continuously take in characters over bluetooth serial and parse them as RPC commands void bluetooth_thread_func() { char buf[256], outbuf[256]; uint16_t buf_pos = 0; while(true) { if (pc.readable() == true) { //comment out when using bluetooth to receive rpc commands //if (bluetooth.readable() == true) { stdio_mutex.lock(); buf[buf_pos] = pc.getc(); //comment out when using bluetooth to receive rpc commands //buf[buf_pos] = bluetooth.getc(); stdio_mutex.unlock(); if (buf[buf_pos] == '\n') { //the end of the RPC command has been received buf[buf_pos] = '\0'; //replace the newline character with a null character buf_pos = 0; RPC::call(buf, outbuf); //make an RPC call stdio_mutex.lock(); pc.printf("%s\n", outbuf); //send the response stdio_mutex.unlock(); } else { buf_pos++; } } else { Thread::yield(); } } } //RPC function to receive the current time //the first argument is unix time and the second argument is the offset from UTC time void set_time_rpc_func (Arguments *in, Reply *out) { static const char * unix_time_str; uint32_t unix_time; int offset; unix_time_str = in->getArg<const char*>(); //get a pointer to the location where the argument string is stored offset = in->getArg<int>(); //get the second argument which indicates the offeset (in hours) from UTC time unix_time = atoll(unix_time_str); utc_offset = offset; set_time(unix_time); // Set RTC time to Wed, 28 Oct 2009 11:35:37 } //RPC function to receive notification strings //note notification strings should not contain the character ' ', in lieu //they should contain '_' to indicate spaces void display_notification_rpc_func (Arguments *in, Reply *out) { static char display_str[18]; static const char * msg_str; int i,j; bool break_out = false; display_x(NOTIFICATION); msg_str = in->getArg<const char*>(); //get a pointer to the location where the argument string is stored stdio_mutex.lock(); lcd_mutex.lock(); uLCD.cls(); uLCD.locate(0,0); i = 0; while(true){ for(j=0; j<18; j++){ if (msg_str[i+j] == '_'){ display_str[j] = ' '; } else { display_str[j] = msg_str[i+j]; } if (msg_str[i+j] == '\0') {break_out = true; break;} } i+= 18; uLCD.printf("%s\r\n",display_str); if (break_out){break;} } stdio_mutex.unlock(); lcd_mutex.unlock(); notification_chime_ticker.attach(&audio_sample, 1.0/sample_freq); } 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 //configure the input button and attach an interrupt routine to it view_button.mode(PullUp); view_button.fall(&view_button_pressed); 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 }