Hardware components | ||||||
![]() |
| × | 1 |
Every time I run, I want to track my running route, but I don't want to use a phone app. So why not a DIY running tracker.
I then use the asset tracker as well as the electron from Particle to work on this project.
The theory is quite simple, use the electron to readout the GPS data from asset tracker and then store the raw GPS position data to an SD card as a .txt file. After I finish my exercise, I then upload that .txt file into gpsvisualizer, and it will generate the trace automatically.
I use their updated asset tracker which uses a Ublox max-M8Q GPS module. Turns out the performance is much better than the previous version and it can get fixed in a very short time.
runing tracker with a microSD card holder soldered in the slot
Fully assembled running tracker
I upload the trace file to gpsvisualizer and below is my running trace.
/*
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;
}
}
}
Comments