Eric yuan
Published © LGPL

Running Exercise Track

This is a device which can monitor running track via a GPS module and store data to a SD card. And then parse the data later from a app.

BeginnerShowcase (no instructions)1 hour848
Running Exercise Track

Things used in this project

Story

Read more

Code

run tracker

C/C++
/*

Create by Eric 3/15/2017

How it works:
1,insert a microSD card,no need to be formatted. 
2,After power up the module,the mcu will init with below things
    1. Turn off the GSM module for power saving, will turn it on in future's version maybe.
    2. Turn on onbloard blue led
    3. Check the battery capacity,to make sure the capacity should be bigger than 70%
    4. Generate a folder in microSD card's root directory named "Record" 
    5. Turn on the GPS module
    6, The RGB led start breathing white.
3,if the initialization is successful,turn off the blue led.
4,if the initializaiton is failed. then
    1. Blink RGB led with red color every 200ms if the battery capacity cannot reach 70%
    2. Blink RGB led with red color every 500ms if generating a folder failed.
    3. Blink RGB led with red color every 1s if other failurs.
5,Waiting the fix signal from GPS module.(make sure the fix led blink every 1second)
6,Press the MODE button with more than 1s to start the recording.
7,If the GPS data is not valid yet, then blink RGB led with red color four times every 200ms and go back to breathing white.
8,If the GPS data is valid, then parse both Data and time, and use them as a name to create a csv file under the Record folder.
9,If GPS data is valid and it was written into microSD card successfully,then breathing the RGB led with green color.
10,if the GPS data is not normal or met some error when writing into SD card, then blink the RGB led with red color every 200ms
   and never comes to normal process until the user's interference.
11,Press the MODE button with more than 1s will stop the recording.
12, After stop the recording. breathing the RGB led with white color.

NOTE:
the microSD recording need to be real-time record. To make sure the previous data remains even after a outage accidentally.

TODO:
Power saving process
Submit the data via GSM

*/



// This #include statement was automatically added by the Particle IDE.
#include "TinyGPS.h"

// This #include statement was automatically added by the Particle IDE.
#include <SdFat.h>



#define LED  D7
#define GPS_SWITCH  D6

#define ON 1
#define OFF 0

#define PASS 0
#define NORMAL_BATTERY      0
#define FAILURE_BATTERY  1
#define FAILURE_SD  2

#define NO_VALID    0   //gps data isn't valid
#define VALID       1   //gps data is valid

#define REC_READY   0   //ready for sd record 
#define REC_DUR     1   //sd recording
#define REC_END     2   //sd record end



#define VoltageThreshold 3.5    //the threshold voltage for battery 


TinyGPSPlus gps;

SdFat sd(1);        //use SPI1 
SdFile file;

FuelGauge fuel;


const uint8_t chipSelect = D5;


SYSTEM_MODE(MANUAL);



//handle the MODE button
void button_handler(system_event_t event, int duration, void* )
{
}

void portInit(void){
    pinMode(LED,OUTPUT);
    digitalWrite(LED,LOW);
    
    pinMode(GPS_SWITCH,OUTPUT);
    digitalWrite(GPS_SWITCH,HIGH);
}

void gpsPower(uint8_t state)
{
    switch(state){
        case ON:
            digitalWrite(GPS_SWITCH,LOW);
            break;
        case OFF:
            digitalWrite(GPS_SWITCH,HIGH);
            break;
        default:break;
    }
}




uint8_t dataValidCheck = NO_VALID;      //check the validation of the data before write into SD card
uint8_t recordStage = REC_READY;
char yearBuffer[4];
char monthBuffer[2];
char dayBuffer[2];
char dash='-';
char nameBuffer[10];
char name[22]={""};
long blinkDelay=0;




//char fileName[22]="Record/";



void setup() {

    uint8_t initState = PASS;       //initialization status.
    
    uint8_t flipFlag=0;
                       //this is just for debug so that i have much time to open the serial!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!1
    portInit();
    Cellular.off();             //1 turn off the GSM module
    digitalWrite(LED,HIGH);     //2turn on the led until finished the initialization.
    Serial.begin(9600);         
    Serial1.begin(9600);
    delay(4000); 
    Serial.println("Hello asset tracker!!");
    RGB.control(true);
    gpsPower(ON);
    uint8_t bright = 0;
    System.on(button_status, button_handler);
    if(VoltageThreshold>fuel.getVCell()){           //low voltage failure;
        delay(100);
        if(VoltageThreshold>fuel.getVCell()){
            initState=FAILURE_BATTERY; //   
            Serial.println("Low voltage failure");
        }
        else
            Serial.println("Pass the voltage capacity test");
    }
    else
        Serial.println("Pass the voltage capacity test");  
    
    if (!sd.begin(chipSelect, SPI_HALF_SPEED)) {
      //  sd.initErrorHalt();           //it will stop here if didnot delete this sentence
        Serial.println("SD card init failure");
        initState = FAILURE_SD;
    }
    else
        Serial.println("SD card had been detected");
        
    sd.vwd()->rewind(); //make sure it's in root directory.
    if (sd.exists("Record"))
        Serial.println("The Record folder already created");
    else
    if (!sd.mkdir("Record")) {      //create a folder under root directory
        Serial.println("Create Folder failed");
        initState = FAILURE_SD;
    }
    else
        Serial.println("Record folder had been created");
    if(initState==PASS){
        digitalWrite(LED,LOW);      //turn off the blue led
        RGB.color(255, 255, 255);   //start breathing white
        RGB.brightness(255);        
    }
    else
    {
        Serial.println("System will stop here because of the failure");
        uint16_t interval=0;
        if(initState==FAILURE_BATTERY)
            interval = 200;        //200ms
        else if(initState==FAILURE_SD){
            interval = 500;         //500MS
        }
        while(1){
            if(millis()-blinkDelay>interval)  //200MS
            {
                if(flipFlag==0){
                    flipFlag=1;
                    RGB.color(255,0,0);
                }else{
                    flipFlag = 0;
                    RGB.color(0,0,0);
                }
                blinkDelay = millis();
            }
            if(initState==FAILURE_BATTERY){
                if(System.buttonPushed()>3000){ //user can actually jump of of this error even if the battery is low by pressing the button more than 3 seconds
                    initState=PASS;
                    digitalWrite(D7,HIGH);
                    Serial.println("Jump out of the low battery error");
                    break;
                }     
            }
        }
    }
    while(1){   //waiting here until the gps get get the fixed signal so that it can use the date info from gps to name the file
        while (Serial1.available() > 0){
            if (gps.encode(Serial1.read())){
                if(gps.location.isValid()){
                    if(gps.date.isValid()&&(dataValidCheck!=VALID)){       //just want to double confirm the date is valid
                        itoa((int)gps.date.year(),yearBuffer,10);
                        itoa((int)gps.date.month(),monthBuffer,10);
                        itoa((int)gps.date.day(),dayBuffer,10);
                        strcat(nameBuffer,yearBuffer);
                        strcat(nameBuffer,&dash);
                        strcat(nameBuffer,monthBuffer);
                        strcat(nameBuffer,&dash);
                        strcat(nameBuffer,dayBuffer);    
                        dataValidCheck = VALID;
                        break;
                    }   
                }   
            }
        }
        if(dataValidCheck==VALID)
            break;
    }
    
    char fileName[22]={"Record/"};
   // strcat(name,"Record/");   --->I will get some error when i use this sentence quite strange-:(
    strcat(fileName,nameBuffer);
    strcat(fileName,".txt");
    
    Serial.printf("The file name is:%s\n",fileName);
    strcpy(name,fileName);
    Serial.printf("The file name is:%s\n",name);
    /*
    if(sd.exists(fileName)){
        Serial.println("This file is already exists");
    }
    */
    if (!file.open(fileName, O_CREAT | O_WRITE | O_AT_END)) {
        Serial.println("create csv file failed");
    }
    else
        Serial.println("The new file had been created in sd card!");
    file.println("latitude,longitude,speed,time");      //this is the headfile for http://www.gpsvisualizer.com/ to parse the data
    file.close();   
    RGB.color(0,0,255);  //change to another color once it's ok to record.
} 

uint8_t recState=REC_READY;

long interval = 0;
uint8_t commit = 0;
uint8_t flushUpdate=0;
uint8_t hourConvert=0;
uint8_t brightVal = 0;
unsigned long brightDelay=0;
unsigned long batteryCheckPeriod=0;
unsigned long ledIndicate = 0;
uint8_t brightDirection=0;
uint8_t lowVoltageFlag=0;

void loop()
{
    if(recState==REC_DUR)
    {
        while(Serial1.available()>0){
            if(gps.encode(Serial1.read())){
                if(gps.location.isValid()){
                    if(millis()-interval>400)       //record every 300ms
                    {
                        interval = millis();
                        file.print(gps.location.lat(),6);
                        file.print(",");
                        file.print(gps.location.lng(),6);
                        file.print(",");
                        file.print(gps.speed.kmph(),6);
                        file.print(","); 
                        
                        hourConvert = gps.time.hour();
                        hourConvert+=8;     //BeijingTimezone
                        if(hourConvert>12)
                            hourConvert-=12;
                        file.print(hourConvert);
                        file.print(":");
                        file.print(gps.time.minute());
                        file.print(":");
                        file.println(gps.time.second());
                        flushUpdate++;
                        if(flushUpdate>=200){
                            flushUpdate=0;
                            file.flush();
                        }
                        if(file.getWriteError()){
                            Serial.println("Get a error when write the file");
                            file.close();       //close the file to make sure the previous file had been stored in sd card successfully.
                            if (!file.open(name, O_CREAT | O_WRITE | O_AT_END)) {       //open the file again to 
                                Serial.println("open csv file failed");
                                RGB.color(255,0,0);
                                while(1);       //just stop here since the the info cannot be recorded
                            }
                        }
                    }

                }
            }
        }   
        if(millis()-ledIndicate>=1000){  //about 1S
            ledIndicate = millis();
            static uint8_t ledState=0;
            ledState++;
            digitalWrite(D7,(ledState%2==0)?0:1);
        }
    }
    if((System.buttonPushed()>1000)&&(recState==REC_READY)){
        if (!file.open(name, O_CREAT | O_WRITE | O_AT_END)) {
            Serial.println("open csv file failed");
            RGB.color(255,0,0);
            recState = REC_READY;
        }
        else{
            Serial.println("Open file success,recording the GPS data...");
            RGB.color(0,255,0);    
            recState=REC_DUR;
            
        }
        while(1){
                if(System.buttonPushed()<100)    //release the button
                    break;
            }
    }
    else
    if((System.buttonPushed()>1000)&&(recState==REC_DUR)){ 
        file.close();
        Serial.println("Fail close successfully!!");
        recState = REC_READY;
        RGB.color(0,0,255);   
        digitalWrite(D7,LOW);       //turn off the blue led when in this mode
        while(1){
            if(System.buttonPushed()<100)    //released the button
                break;
            }
    }
    if((millis()-brightDelay>70))
    {
        brightDelay = millis();
        if(brightVal<=10)
            brightDirection=0;      //decrease
        else
        if(brightVal>=240)
            brightDirection=1;
        if(brightDirection==0)
            brightVal+=10;
        else
            brightVal-=10;
        RGB.brightness(brightVal);
    }
    if(millis()-batteryCheckPeriod>=4000){      //battery check.
        batteryCheckPeriod = millis();
        if(VoltageThreshold>fuel.getVCell()){
            lowVoltageFlag = 1;     //enable the low voltage flag.
            RGB.color(255,0,0);
        }else{
            lowVoltageFlag = 0;
        }
    }
}

Credits

Eric yuan

Eric yuan

4 projects • 1 follower

Comments