This is a repository for code relating to mbed Fitness Tracker
Dependencies: mbed PulseSensor2 SCP1000 mbed-rtos 4DGL-uLCD-SE LSM9DS1_Library_cal PinDetect FatFileSystemCpp GP-20U7
main.cpp
- Committer:
- dyu2021
- Date:
- 2020-04-28
- Revision:
- 43:81f314c92d9f
- Parent:
- 42:30d1dfc5adaf
File content as of revision 43:81f314c92d9f:
#include "mbed.h" #include "rtos.h" #include "LSM9DS1.h" #include "SCP1000.h" #include "PulseSensor.h" #include "PinDetect.h" #include "uLCD_4DGL.h" #include "GPS.h" #include "MSCFileSystem.h" SCP1000 scp1000(p5,p6,p7,p8); LSM9DS1 IMU(p9, p10, 0xD6, 0x3C); PulseSensor PPG(p17); uLCD_4DGL uLCD(p28,p27,p29); Serial pc(USBTX, USBRX); DigitalOut one = LED1; DigitalOut two = LED2; DigitalOut three = LED3; DigitalOut four = LED4; AnalogIn pot(p20); PinDetect pb(p21); GPS gps(p13, p14); #define FSNAME "msc" MSCFileSystem msc(FSNAME); int bpm; int steps = 0; int flights = 0; float distance = 0.0; float calories = 0; int oldSteps = 0; int stepGoal = 100; float stride_length = 0.0; unsigned long pressure; float latitude = 0; float longitude = 0; #define PI 3.14159 unsigned long p_buff[4]; int count = 0; int mode = 1; int oldMode = 1; bool usePrev = false; bool readConfig = false; bool run = true; int gender; int weight; int age; int screen = 1; int oldScreen = 1; bool setup_state = true; char date[32]; Timer usb_timer; Thread thread1; Thread thread2; Thread thread3; Thread thread4; Thread thread5; Thread thread6; Mutex serial_mtx; Mutex usb_mtx; //Saves current configuration in a text file //Saved in format gender, age, weight and step goal separated by tabs void save_config() { usb_mtx.lock(); two = 1; FILE *fp = fopen( "/msc/config.txt", "w"); if(fp == NULL) { pc.printf("Could not open file for write\n"); } else { fprintf(fp, "%d\t%d\t%d\t%d\n\r", gender, age, weight, stepGoal); //pc.printf("Config saved\r\n"); fclose(fp); } two = 0; usb_mtx.unlock(); } //Reads configuration file and sets up user specific values and the last saved //activity date //If files don't exist or are empty, will return false, else returns true bool read_config() { usb_mtx.lock(); three = 1; FILE *fp1 = fopen( "/msc/config.txt", "r"); FILE *fp2 = fopen("/msc/data.txt", "r"); if(fp1 == NULL || fp2 == NULL) { pc.printf("Could not open file for read\n"); //Go back to first setup screen if files don't exist screen = 1; usb_mtx.unlock(); return false; } else { char buf[128]; //Read config file to setup user specific values and data file to load last //stored data. If config file empty, then go back to setup screen if (fscanf(fp1, "%d\t%d\t%d\t%d", &gender, &age, &weight, &stepGoal) != EOF) { fscanf(fp2, "%s\t%d\t%d\t%f\t%f", buf, &steps, &flights, &calories, &distance); //pc.printf("Data loaded\r\n"); } else { //files empty, go back to setup screen screen = 1; fclose(fp1); fclose(fp2); three = 0; usb_mtx.unlock(); return false; } fclose(fp1); fclose(fp2); three = 0; usb_mtx.unlock(); return true; } } // when the pushbotton is pressed the run flag is set to false and the main // function while loop exits so that the data file can be closed // so press the button when you're ready to be done collecting data void button (void) { run = false; } //Switches to next screen in setup mode with a button press void next() { oldScreen = screen; screen++; if (usePrev) { readConfig = true; } else if(screen == 6) { setup_state = false; } } // Reads the value of the potentiometer and averages over 3 readings to get rid // of random spikes/zero values. Returns either a number 1-5 that indicates // which screen should be displayed void read_pot() { float m1; float m2; float m3; oldMode = mode; m1 = pot.read(); m2 = pot.read(); m3 = pot.read(); if(m1 < 0.2 && m2 < 0.2 && m3 < 0.2) { mode = 1; } else if(m1 >= 0.2 && m1 < 0.4 && m2 >= 0.2 && m2 < 0.4 && m3 >= 0.2 && m3 < 0.4) { mode = 2; } else if(m1 >= 0.4 && m1 < 0.6 && m2 >= 0.4 && m2 < 0.6 && m3 >= 0.4 && m3 < 0.6) { mode = 3; } else if(m1 >= 0.6 && m1 < 0.8 && m2 >= 0.6 && m2 < 0.8 && m3 >= 0.6 && m3 < 0.8) { mode = 4; } else if(m1 >= 0.8 && m2 >= 0.8 && m3 >= 0.8) { mode = 5; } } //Display the time on the top of the screen void display_time() { while(1) { serial_mtx.lock(); uLCD.locate(1, 1); uLCD.color(WHITE); uLCD.text_width(2); uLCD.text_height(3); time_t seconds = time(NULL); char timeBuffer[32]; strftime(timeBuffer, 32, "%I:%M %p\r\n", localtime(&seconds)); uLCD.printf("%s", timeBuffer); serial_mtx.unlock(); Thread::wait(700); } } //Setup mode where user enters their gender, age, weight, and daily step goal void setup_screen(void) { while(1) { serial_mtx.lock(); //when chnage in screen, clear the LCD screen if (oldScreen != screen) { uLCD.filled_rectangle(0,0, 128, 128, BLACK); oldScreen++; } switch(screen) { //Checks if user wants to enter new configuration or use previous one case 1: uLCD.locate(3, 1); uLCD.text_width(1); uLCD.text_height(1); uLCD.puts("Use Previous"); uLCD.locate(2, 3); uLCD.puts("Configuration?"); uLCD.text_width(3); uLCD.text_height(3); uLCD.locate(1, 3); uLCD.putc('Y'); uLCD.locate(4, 3); uLCD.putc('N'); if(pot.read() > 0.5) { usePrev = false; uLCD.rectangle(13, 60, 48, 100, BLACK); uLCD.rectangle(75, 60, 110, 100, GREEN); } else { usePrev = true; uLCD.rectangle(75, 60, 110, 100, BLACK); uLCD.rectangle(13, 60, 48, 100, GREEN); } break; case 2: //Gender uLCD.locate(2, 1); uLCD.text_width(2); uLCD.text_height(2); uLCD.puts("Gender"); uLCD.text_width(3); uLCD.text_height(3); uLCD.locate(1, 3); uLCD.putc('M'); uLCD.locate(4, 3); uLCD.putc('F'); if(pot.read() > 0.5) { gender = 0; uLCD.rectangle(13, 60, 48, 100, BLACK); uLCD.rectangle(75, 60, 110, 100, GREEN); } else { gender = 1; uLCD.rectangle(75, 60, 110, 100, BLACK); uLCD.rectangle(13, 60, 48, 100, GREEN); } break; case 3: //Weight uLCD.color(WHITE); uLCD.locate(9, 14); uLCD.text_width(1); uLCD.text_height(1); uLCD.puts("lbs"); uLCD.locate(2, 1); uLCD.text_width(2); uLCD.text_height(2); uLCD.puts("Weight"); weight = (90 + pot.read() * 210); char weight_string[3]; if(weight < 100) { sprintf(weight_string, " %d", weight); } else { sprintf(weight_string, "%d", weight); } weight = 0.45*weight; uLCD.text_width(3); uLCD.text_height(3); uLCD.locate(2, 3); uLCD.color(GREEN); uLCD.puts(weight_string); uLCD.line(35, 100, 110, 100, WHITE); break; case 4: //Age uLCD.color(WHITE); uLCD.locate(3, 1); uLCD.text_width(2); uLCD.text_height(2); uLCD.puts("Age"); age = (int) (10 + pot.read() * 89); char age_string[2]; sprintf(age_string, "%d", age); uLCD.text_width(3); uLCD.text_height(3); uLCD.locate(2, 3); uLCD.color(GREEN); uLCD.puts(age_string); uLCD.line(40, 100, 90, 100, WHITE); break; case 5: //Step goal uLCD.color(WHITE); uLCD.locate(2, 2); uLCD.text_width(1); uLCD.text_height(1); uLCD.puts("Daily Step Goal"); char step_string[5]; if(pot.read() < 0.2) { stepGoal = 100; sprintf(step_string, " %d", stepGoal); }else if(pot.read() < 0.4) { stepGoal = 1000; sprintf(step_string, " %d", stepGoal); }else if(pot.read() < 0.6) { stepGoal = 5000; sprintf(step_string, " %d", stepGoal); }else if(pot.read() < 0.8) { stepGoal = 10000; sprintf(step_string, "%d", stepGoal); }else { stepGoal = 20000; sprintf(step_string, "%d", stepGoal); } uLCD.text_width(2); uLCD.text_height(2); uLCD.locate(2, 4); uLCD.color(GREEN); uLCD.puts(step_string); uLCD.line(30, 85, 100, 85, WHITE); break; } serial_mtx.unlock(); Thread::wait(100); } } //Displays live activity data on LCD Display void update_screen(void) { while(1) { read_pot(); serial_mtx.lock(); //If change in mode, clear screen if (oldMode != mode) { uLCD.filled_rectangle(0,0, 128, 128, BLACK); } switch(mode) { case 1: //Step count uLCD.media_init(); uLCD.set_sector_address(0x0000, 0x0005); uLCD.display_image(50, 45); uLCD.filled_rectangle(10, 110, 118, 115, BLACK); uLCD.locate(3, 11); uLCD.text_height(1); uLCD.text_width(1); uLCD.color(WHITE); uLCD.printf("%4d steps",steps); uLCD.filled_rectangle(10, 110, 10 + int(steps * (110/stepGoal)), 115, WHITE); break; case 2: // Heart rate uLCD.media_init(); uLCD.set_sector_address(0x0000, 0x000A); uLCD.display_image(50, 45); uLCD.locate(5, 11); uLCD.text_height(1); uLCD.text_width(1); uLCD.color(WHITE); uLCD.printf("%3d BPM", bpm); break; case 3: //Distance uLCD.media_init(); uLCD.set_sector_address(0x0000, 0x000F); uLCD.display_image(50, 45); uLCD.locate(6, 11); uLCD.text_height(1); uLCD.text_width(1); uLCD.color(WHITE); uLCD.printf("%4.2f ft", distance); break; case 4: //Calories uLCD.media_init(); uLCD.set_sector_address(0x0000, 0x0000); uLCD.display_image(50, 45); uLCD.locate(4, 11); uLCD.text_height(1); uLCD.text_width(1); uLCD.color(WHITE); uLCD.printf("%4d cal", (int)calories); break; case 5: //Floors uLCD.media_init(); uLCD.set_sector_address(0x0000, 0x0014); uLCD.display_image(50, 45); uLCD.locate(4, 11); uLCD.text_height(1); uLCD.text_width(1); uLCD.color(WHITE); uLCD.printf("%2d floors", flights); break; } serial_mtx.unlock(); Thread::wait(100); } } //Read heart rate sensor and calculates calories burned void readHR(){ while(1) { bpm = PPG.get_BPM(); //Formula that calculates calories based off heart rate and user data calories = calories + (.0083)*0.239*(gender*(-55.0969+.6309*bpm+.1988*0.453592*weight +.2017*age)+(1-gender)*(-20.4022+.4472*bpm-.1263*0.453592*weight+.074*age)); //Alternate way to calculate distance (likely more accurate) //distance = distance + (steps - oldSteps)* stride_length; //oldSteps = steps; Thread::wait(500); } } //Reads barometer and detects floors climbed void readBarometer() { while(1) { pressure = scp1000.readPressure(); if(count >= 0) count--; unsigned long dif; if(pressure < p_buff[0]) { dif = p_buff[0] - pressure; } else { dif = 0; } if(pressure != 0 && p_buff[0] != 0 && dif > 40 && dif < 60 && count < 0) { flights++; count = 2; } p_buff[0] = p_buff[1]; p_buff[1] = p_buff[2]; p_buff[2] = p_buff[3]; p_buff[3] = pressure; Thread::wait(2000); } } //Read GPS to get current longitude and latitude and also calculate distance traveled void readGPS(){ float old_lat = 0; float old_lon = 0; while(1) { serial_mtx.lock(); if(gps.connected()) { if(gps.sample()) { if(gps.ns == 'S') { longitude = gps.longitude*PI/180; } else { longitude = -gps.longitude*PI/180; } if(gps.ew == 'W') { latitude = gps.latitude*PI/180; } else { latitude = -gps.latitude*PI/180; } if(latitude != 0 && longitude != 0 && old_lat != 0 && old_lon != 0) { float a = sinf(old_lat)*sinf(latitude)+cosf(old_lat)*cosf(latitude)*cosf(longitude-old_lon); if(a > 1) a = 1; distance = distance + (.75*acosf(a)); } old_lat = latitude; old_lon = longitude; //pc.printf("%f, %f, %f\r\n", latitude, longitude, distance); } } serial_mtx.unlock(); Thread::wait(10000); } } //Saves current activity data to data.txt //Data saved in the format date, steps, flights, calories, and distance separated by tabs //Prev_data.txt stores the daily activity data over the past week void save_data() { // Save the data to the usb flash drive and print to the terminal usb_mtx.lock(); two = 1; FILE *fp = fopen( "/msc/data.txt", "w"); if(fp == NULL) { error("Could not open file for write\n"); } time_t seconds = time(NULL); char temp_date[32]; strftime(temp_date, 32, "%m/%d/%y", localtime(&seconds)); //If the date has changed, indicating new day, then append current data to prev_data.txt //Reset all activity data to zero if (strcmp(temp_date, date) != 0) { FILE *fp2 = fopen("/msc/prev_data.txt", "a"); if(fp2 == NULL) { error("Could not open file for append\n"); } //pc.printf("Writing to prev_data.txt\r\n"); fprintf(fp2, "%s\t%d\t%d\t%0.2f\t%0.2f\n", date, steps, flights, calories, distance); fclose(fp2); strcpy(date, temp_date); steps = 0; flights = 0; calories = 0; distance = 0; } else if (strcmp(temp_date, date) == 0) { fprintf(fp, "%s\t%d\t%d\t%0.2f\t%0.2f\n", date, steps, flights, calories, distance); } fclose(fp); usb_mtx.unlock(); } //Waits for command from GUI and once command received, transfer all the data stored //in prev_data.txt to the GUI which will be plotted void serial_USB() { float buf_distance[2], buf_calories[2]; int buf_steps[2],buf_flights[2]; char buf_date[100]; char buffer[100]; while (1) { usb_mtx.lock(); if (pc.readable()) { if (pc.getc() =='!') { if(pc.getc() == 'p') { FILE *fp = fopen( "/msc/prev_data.txt", "r"); if(fp == NULL) { error("Could not open file for write\n"); } while (fscanf(fp,"%s\t%d\t%d\t%f\t%f\n\r", &buf_date[0], &buf_steps[0], &buf_flights[0], &buf_calories[0], &buf_distance[0]) != EOF) { //fscanf(fp,"%s\t%d\t%d\t%f\t%f\n\r", &buf_date[0], &buf_steps[0], &buf_flights[0], &buf_calories[0], &buf_distance[0]); sprintf(buffer,"%s\t%d\t%d\t%f\t%f\n", buf_date, buf_steps[0], buf_flights[0], buf_calories[0], buf_distance[0]); pc.printf("%s",buffer); } fclose(fp); } } } usb_mtx.unlock(); Thread::yield(); } } int main() { //Set RTC time set_time(1256729737); time_t seconds = time(NULL); //Keep track of the date mbed Fitbit was first turned on strftime(date, 32, "%m/%d/%y", localtime(&seconds)); // Next screen button pb.mode(PullUp); pb.attach_deasserted(&next); pb.setSampleFrequency(); //set up the display uLCD.baudrate(3000000); uLCD.background_color(BLACK); uLCD.cls(); //Thread to display setup screens thread1.start(setup_screen); while(setup_state) { if (readConfig) { if (read_config()) { setup_state = false; } else { setup_state = true; usePrev = false; readConfig = false; } } Thread::yield(); } thread1.terminate(); save_config(); // Off button pb.attach_deasserted(&button); // set up the display for displaying real time data and clock uLCD.cls(); thread1.start(update_screen); thread2.start(display_time); // LED indicates whether or not data is being collected one = 0; two = 0; three = 0; four = 0; // Start sensors int sample_num = 1; PPG.start(); IMU.begin(); IMU.calibrate(1); float ax, ay, az; float mag = 0; float buffer[2] = {0}; float avg_buffer[2] = {0}; float avg; //float max = 1.0; //float min = 1.0; //float K = 0.55; //Start threads for sensors and serial command thread3.start(readBarometer); thread4.start(readGPS); thread5.start(readHR); thread6.start(serial_USB); usb_timer.start(); while(run) { // Read IMU and get acceleration in all three axis IMU.readAccel(); ax = IMU.calcAccel(IMU.ax); ay = IMU.calcAccel(IMU.ay); az = IMU.calcAccel(IMU.az); // Calculate the 3 point moving average of the magnitude of the // acceleration vector mag = sqrt((ax*ax) + (ay*ay) + (az*az)); avg = (buffer[0] + buffer[1] + mag) / 3; buffer[0] = buffer[1]; buffer[1] = mag; // Count a step if the difference between the current and previous avg // point crosses a threshold if(sample_num > 1) { float dif1 = avg_buffer[1] - avg_buffer[0]; float dif2 = avg_buffer[1] - avg; float peak_prominence = 0.01; if(dif1 > peak_prominence && dif2 > peak_prominence) { steps++; //Dynamic stride length calculation using max and min values of //z acceleration //stride_length = K * pow((double)(max - min), 0.25) * 3.28084; //max = 1.0; //min = 1.0; } } avg_buffer[0] = avg_buffer[1]; avg_buffer[1] = avg; //if (az > max) { max = az;} //else if (az < min) {min = az;} sample_num++; one = !one; //led indicating if main is still running //Every minute, save current activity data to data.txt if(usb_timer.read() >= 60) { save_data(); usb_timer.stop(); usb_timer.reset(); usb_timer.start(); two = 0; } Thread::wait(200); } one = 0; //Turn off all threads reading sensors thread3.terminate(); thread4.terminate(); thread5.terminate(); usb_timer.stop(); usb_timer.reset(); save_data(); //For demo usb_mtx.lock(); FILE *fp1 = fopen("/msc/prev_data.txt", "a"); fprintf(fp1, "%s\t%d\t%d\t%0.2f\t%0.2f\n", date, steps, flights, calories, distance); fclose(fp1); usb_mtx.unlock(); return 0; }