The HexiHeart is a demo project product that takes advantage of many of the onboard Hexiwear sensors and capabilities to create a multifunctional fitness and safety watch.
Dependencies: FXAS21002 FXOS8700 Hexi_KW40Z Hexi_OLED_SSD1351 MAXIM W25Q64FVSSIG HTU21D MPL3115A2 TSL2561
Fork of HexiHeart_Alex by
Diff: main.cpp
- Revision:
- 16:537ef0c72084
- Parent:
- 15:330794a9f347
- Child:
- 17:746dc1b7b218
--- a/main.cpp Wed Apr 11 02:23:07 2018 +0000 +++ b/main.cpp Thu Apr 12 22:18:24 2018 +0000 @@ -20,6 +20,11 @@ Display data screen update routines ************** Versions **************** +v2.14 - Retasked hr_led Ticker to turn off heart rate zone LEDs thereby preventing the +system from having to spend half its time in a wait state. added HR screen #7 to +screens that get refreshed with new data twice a second. Reduced WDT back to 2 seconds. +Increased rolling average to 10 samples. removed 0.05s wait during HR read. + v2.13 - Added heart rate measurement, heart rate simulation, improved heart rate leds, and optimized heart rate functionalities @@ -70,11 +75,12 @@ /* General Definitions */ -#define SW_Ver 2.13 // For displaying software version +#define SW_Ver 2.14 // For displaying software version #define LED_ON 0 #define LED_OFF 1 #define SCRN_TIME 10.0 // Set OLED screen turn off time to 10.0 seconds -#define WDT_TIME 5.0 // Set Watch Dog timer to 1.5 seconds (1.5s reset in certain subroutines) +#define WDT_TIME 2.0 // Set Watch Dog timer to 1.5 seconds (1.5s reset in certain subroutines) +#define MAX_AVE_NUM 10 // maximum averaging depth of rolling average for batt, temp and humid #define Debug 1 // If "Debug" is defined, our code will compile for debug. Comment out for Production code. #define FXOS8700_I2C_ADDRESS_ (0x1E<<1) //pins SA0,SA1=0 @@ -90,6 +96,8 @@ #define EXIT_BELOW 75 #define EXIT_ABOVE 100 #define VIB_OPT_2 75 // Haptic vibration pattern option for heart rate functions +#define HR_LED_on_time 0.5 //how long should zone LED stay on for? +#define HR_LED_period 1.0 //how often should zone LED blink? /* I2C Address */ #define HR_W_ADDR 0xAE #define HR_R_ADDR 0xAF @@ -153,6 +161,7 @@ void Enable_Heart_Rate(); void Disable_Heart_Rate(); void Led_Zone_Indicator(); +void Led_Zone_Indicator_off(); // turns off LEDs after 0.5 seconds instead of using a wait command void Heat_Index_Calculation(); void fall_config(uint8_t); //function call to setup fall detecting modes void accel_sensor_config(uint8_t); @@ -251,14 +260,16 @@ uint8_t heat_index; // used in Heat index calc int hi_calc; // used in Heat index calc int adjustment; // used in Heat index calc +bool randomized = 0; //Initialize to 0, since srand has not been called +int simulation_stage = 0; +uint32_t hr_data[100]; // slow changing variables int sample_ftemp = 0; // used in Heat index calc int sample_humid = 0; // used in Heat index calc uint8_t batt_per_level = 0; // uint8_t Ave_Num = 0; -bool randomized = 0; //Initialize to 0, since srand has not been called -int simulation_stage = 0; -uint32_t hr_data[100]; + + // Pointers for screen images const uint8_t *Hexi_Heart_ = Hexi_Heart_bmp; //const uint8_t *NB_Linkedin = NB_Linkedin_bmp; @@ -342,7 +353,7 @@ void timout_timer(){ // turn off display mode HexiwearBattery battery; battery.sensorOn(); - if (battery.isBatteryCharging() || batt_per_level > 99) { + if (battery.isBatteryCharging() || (uint8_t)battery.readLevelPercent() > 99) { oled.DimScreenOFF(); Screen_Timer.attach(&timout_timer,(SCRN_TIME));// Reset/restart ticker timer for OLED while fully charged } //end if @@ -444,8 +455,8 @@ StartHaptic(); maxInit(); maxEnable(); - hr_led.attach(&Led_Zone_Indicator, 1); - hr_ticker.attach(&processHeartRate, 5); + hr_led.attach(&Led_Zone_Indicator, HR_LED_period); // blink every second + hr_ticker.attach(&processHeartRate, 5); break; } case 8: {// Alert History @@ -540,14 +551,14 @@ break; } case 32: {//Turn on HR led blinking for manual demonstration - hr_led.attach(&Led_Zone_Indicator, 1); + hr_led.attach(&Led_Zone_Indicator, HR_LED_period); break; } case 33:{//Start heart rate simulation StartHaptic(); if(maxim == 0) { - hr_led.attach(&Led_Zone_Indicator, 1); + hr_led.attach(&Led_Zone_Indicator, HR_LED_period); hr_simulation.attach(&HR_Simulation, 5.0); } update_display(); @@ -807,6 +818,9 @@ } case 32: {//End HR led used for manual demonstration hr_led.detach(); + RED_Led = LED_OFF; + GRN_Led = LED_OFF; + BLU_Led = LED_OFF; break; } case 33: {//End HR Simulation early @@ -816,6 +830,9 @@ hr_simulation.detach(); simulation_stage = 0; hr_led.detach(); + RED_Led = LED_OFF; + GRN_Led = LED_OFF; + BLU_Led = LED_OFF; } update_display(); break; @@ -1403,7 +1420,7 @@ { //__STATIC_INLINE void __set_PMCTRL(0b01000000);// set K64 SMC_PMCTRL register to VLPR (Very low power run) mode // SMC->PMCTRL = (uint8_t)((SYSTEM_SMC_PMCTRL_VALUE) & (SMC_PMCTRL_RUNM_MASK)); // Enable VLPR mode - SMC->PMCTRL = (0b01000000);// set K64 SMC_PMCTRL register to VLPR (Very low power run) mode + // SMC->PMCTRL = (0b01000000);// set K64 SMC_PMCTRL register to VLPR (Very low power run) mode << did nothing for battery time //set_time(1256729737); // Set RTC time to Wed, 28 Oct 2009 11:35:37 //set_time((Year-1970)*365*24*3600+(days of this year)*24*3600+(hr)*3600 + (min)*60 + (Sec) - fudge factor); // Set RTC time to Mon, 19 Feb 2018 10:00 @@ -1433,7 +1450,7 @@ fall_config(10); // SW reset accell and gyro sensor // gyro_sensor_config(10); // SW reset gyro sensor press_config(0); // put pressure sensor into 2uA standby, we're not using it - MAX30101_test_config(10); // SW reset accell sensor + // MAX30101_test_config(10); // SW reset HR sensor << power is off oled.FillScreen(COLOR_BLACK); // Clear screen // ***************** Local variables *********************** @@ -1487,7 +1504,7 @@ /* oled.FillScreen(COLOR_BLACK); // Clear screen oled.DrawImage(Hexi_Heart_,0,0); // my Hexi_Heart image is offset for some reason - wait(0.5); // wait 3 seconds + wait(0.5); // wait 0.5 seconds */ oled.FillScreen(COLOR_BLACK); // Clear screen @@ -1529,26 +1546,27 @@ { Thread::wait(500); // wait 0.5 sec each loop CLRWDT(); - // SCB->SCR &= ~SCB_SCR_SLEEPDEEP_Msk; - // __WFI(); - // deepsleep(); + if(OLED_PWR==1){ - update_display_date(); // refresh display date w/o updating entire display + update_display_date(); // refresh display data twice a second w/o updating entire display }// end if i++; - }// end while(i<20) + }// end 10 second while(i<20) loop + if(maxim==0){// don't use red LED while heart rate zone routine is active RED_Led = LED_ON; // Used only for diagnostic of wait command + } Led_clk3 = 1; // Used only for diagnostic of wait command wait(0.01); // BLIP led 1/10 sec each loop -// NVIC_VLPW(0.01); // BLIP led 1/10 sec each loop -// NVIC_SystemReset(); // software reset + if(maxim==0){// don't use red LED while heart rate zone routine is active RED_Led = LED_OFF; // Used only for diagnostic of wait command + } Led_clk3 = 0; + Thread::wait(490); // keep up the pace, at 0.5 sec (0.01s+0.49s) update date CLRWDT(); - UpDate_Ave(); // Update slow changing measurements once every 30 seconds + UpDate_Ave(); // Update slow changing measurements once every 10.5 seconds } // end of while(true) } @@ -1998,7 +2016,8 @@ oled.TextBox((uint8_t *)text,55,23,15,15); //Increase textbox for more digits /* Display Units */ - strcpy((char *) text,"dF");oled.Label((uint8_t *)text,71,23); + strcpy((char *) text,"dF"); + oled.Label((uint8_t *)text,71,23); break; } @@ -2836,8 +2855,14 @@ Determine_Current_Zone(); } +/***************************************************************************** +Name: Led_Zone_Indicator() +Purpose: Ticker interupt routine called every 1.0 Seconds in order to Blink LED to indicate current Heart Rate zone +Inputs: hr_led Ticker +******************************************************************************/ void Led_Zone_Indicator() { + CLRWDT();// Reset watchdog timer before we do this long opperation if(Led_Zones == true) { if(Current_Zone == 1) @@ -2845,10 +2870,10 @@ BLU_Led = LED_OFF; RED_Led = LED_ON; GRN_Led = LED_ON; - - wait(0.5); - RED_Led = LED_OFF; - GRN_Led = LED_OFF; + hr_led.attach(&Led_Zone_Indicator_off, HR_LED_on_time); // in 0.5 seconds turn off LED + //wait(0.5); + //RED_Led = LED_OFF; + // GRN_Led = LED_OFF; } else if(Current_Zone == 2) { @@ -2862,8 +2887,9 @@ GRN_Led = LED_OFF; } BLU_Led = LED_ON; - wait(0.5); - BLU_Led = LED_OFF; + hr_led.attach(&Led_Zone_Indicator_off, HR_LED_on_time); // in 0.5 seconds turn off LED + // wait(0.5); + // BLU_Led = LED_OFF; } else if(Current_Zone == 3) { @@ -2876,20 +2902,36 @@ RED_Led = LED_OFF; } GRN_Led = LED_ON; - wait(0.5); - GRN_Led = LED_OFF; + hr_led.attach(&Led_Zone_Indicator_off, HR_LED_on_time); // in 0.5 seconds turn off LED + // wait(0.5); + // GRN_Led = LED_OFF; } else if(Current_Zone == 4) { GRN_Led = LED_OFF; RED_Led = LED_ON; - wait(0.5); - RED_Led = LED_OFF; + hr_led.attach(&Led_Zone_Indicator_off, HR_LED_on_time); // in 0.5 seconds turn off LED + // wait(0.5); + // RED_Led = LED_OFF; } } }//end of Led_Zone_Indicator /***************************************************************************** +Name: Led_Zone_Indicator_off() +Purpose: Ticker interupt routine called every 0.5 Seconds after LEDs turned on to turn them back +off. This avoids the 0.5 wait every second, freeing up the system. +Inputs: hr_led Ticker +******************************************************************************/ + //turns off LEDs after 0.5 seconds instead of using a wait command +void Led_Zone_Indicator_off(){ + RED_Led = LED_OFF; + GRN_Led = LED_OFF; + BLU_Led = LED_OFF; + hr_led.attach(&Led_Zone_Indicator, HR_LED_period); // blink every second + }//end void Led_Zone_Indicator_off() + +/***************************************************************************** Name: Heat_Index_Calculation() Purpose: Calculates the heat index using the temperature and humidity sensors Inputs: None @@ -4249,6 +4291,23 @@ break; }// end case 0 + + case 7: {// Heart Rate Zone + textProperties.fontColor = COLOR_WHITE; + oled.SetTextProperties(&textProperties); + sprintf(display_buff, "%u", Heart_Rate); + textProperties.fontColor = COLOR_RED; //Change font to red + oled.SetTextProperties(&textProperties);//Implement color change + oled.Label((uint8_t *)display_buff,43,25); // Display at x,y + textProperties.fontColor = COLOR_GREEN; + oled.SetTextProperties(&textProperties); //implements the color change + sprintf(display_buff, "%u", Age); //Convert int to char array for displaying user age + oled.Label((uint8_t *)display_buff,43,45); // Display at x,y + textProperties.fontColor = COLOR_WHITE; + oled.SetTextProperties(&textProperties); + + break; + }// end case 7 case 21: {// Fall Alert Diagnostic Screen if(Fall_Alert_Mode == 0){ @@ -4417,15 +4476,16 @@ }//end if else{ // updated measurments on a rolling average basis: 1/4 new measurement to 3/4 running average - batt_per_level = (Ave_Num-1)*batt_per_level/Ave_Num + (uint8_t)battery.readLevelPercent()/Ave_Num; + batt_per_level = (uint8_t)((Ave_Num-1)*batt_per_level/Ave_Num + battery.readLevelPercent()/Ave_Num); sample_ftemp = (Ave_Num-1)*sample_ftemp/Ave_Num + temphumid.sample_ftemp()/Ave_Num; sample_humid = (Ave_Num-1)*sample_humid/Ave_Num + temphumid.sample_humid()/Ave_Num; }//end else Ave_Num++; - if(Ave_Num>4){ - Ave_Num = 4;//set limit + if(Ave_Num > MAX_AVE_NUM){ + Ave_Num = MAX_AVE_NUM;//set limit } }// end UpDate_Ave + void HR_Simulation(void) { if(randomized == 0) @@ -4464,7 +4524,7 @@ } simulation_stage++; Determine_Current_Zone(); - update_display(); + //update_display(); // I don't think we should update display, let update_data() do that } void readRegs(int addr, uint8_t * data, int len) @@ -4680,6 +4740,9 @@ hr_measure_ticker.detach(); Disable_Heart_Rate(); hr_led.detach(); + RED_Led = LED_OFF; + GRN_Led = LED_OFF; + BLU_Led = LED_OFF; } void processHeartRate(void) @@ -4690,9 +4753,9 @@ for(int i = 0; i < 100; i++) { hr_data[i] = readFIFO(); - if(i % 3 == 1) - wait(0.05); - } + //if(i % 3 == 1) // there seems to be a missing "{" here + // wait(0.01); // every third read wait 0.05s, this adds about 1.65s + } peak = isPeak(hr_data); @@ -4701,7 +4764,7 @@ if(hr >= HR_Zone1[0] && hr <= HR_Zone4[1]) { Heart_Rate = hr; - update_display(); + // update_display();//I don't think we need to update display here, let update_data do that } Determine_Current_Zone();