//Headers
//GPS on Software Serial
#include <SoftwareSerial.h>
#include "TinyGPS++.h"
//SD-Card
//#include <SPI.h>
#include <SD.h>
//Simulator
#define WOKWI
//Port usage
#define GPS_RX 2
#define GPS_TX 3
#define BUZZER 4
#define SHIFTREG_DATA 5 //DS = SER = Data Signal
#define SHIFTREG_LATCH 6 //STCP store value in Output
#define SHIFTREG_CLOCK 7 //SRCLK = SHCP Shift Clock Pin Steigende Flanke = Übernahme Data bit
#define SD_CS 10
#define SD_MOSI 11 //constant unused, declared in SD-Library
#define SD_MISO 12 //constant unused, declared in SD-Library
#define SD_CLK 13 //constant unused, declared in SD-Library
//Display Codes
#define DC0 B00111111 //63
#define DC1 B00000110 //6
#define DC2 B01011011 //91
#define DC3 B01001111 //79
#define DC4 B01100110 //102
#define DC5 B01101101 //109
#define DC6 B01111101 //125
#define DC7 B00000111 //7
#define DC8 B01111111 //127
#define DC9 B01101111 //111
#define DCa B01110111 //119
#define DCb B01111100 //124
#define DCc B01011000 //88
#define DCd B01011110 //94
#define DCe B01111001 //121
#define DCf B01110001 //113
#define DCblank B00000000 //0
#define DCr B01010000 //80
#define DCu B00011100 //28
#define DCn B01010100 //84
#define DCh B01110100 //113
#define DCl B00111000 //56
#define DCo B01011100 //92
#define DCp B01110011 //115
#define DCt B01111000 //120
//File header
//#define XML "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
//#define GPXHDR "<gpx xmlns=\"http://www.topografix.com/GPX/1/1\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" version=\"1.1\" creator=\"Fugawi 4.5.56.5615 http://www.fugawi.com/\" xsi:schemaLocation=\"http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd\">"
#define SPC4 " "
#define SPC6 " "
#define SPC8 " "
#define GPXEND "</gpx>"
#define TRK " <trk>"
#define TRKEND " </trk>"
#define TRKSEG " <trkseg>"
#define TRKSEGEND " </trkseg>"
#define DESC " <desc></desc>"
#define TRKPTLAT " <trkpt lat=\""
#define TRKPTLON "\" lon=\""
#define TRKPTEND "\"></trkpt>"
#define ELE " <ele>"
#define ELEEND "</ele>"
#define TIME " <time>"
#define TIMEEND "Z</time>"
//debug Strings
#define SERRDY "Serial ready"
#define SDCINI "SD card init... "
#define SDCFAIL "failed"
#define OK "OK"
#define SETUPOK "Setup OK"
#define GPSDATE "gpsDate="
#define STRGPSD "String(gpsDate)="
#define STRLEN "len="
#define DDMM "ddmm="
#define FILEXT ".gpx"
#define FILENAME "FN="
#define ERRFN "Err FN="
#define ERROPEN "Err open "
#define FHDROK "File Hdr OK"
#define CASEDEF "case def"
//Public Variables and instances
//bool gpsUSB = false; //use USB Input for GPS: if no input on Softwareserial, switch to USB
unsigned long myTime;
SoftwareSerial sermsg(GPS_RX, GPS_TX); // RX, TX GPS input from 2,3
TinyGPSPlus gps; //TinyGPS instance
String fileName; //fileName = "DDMMhhmm.gpx"
File logFile; //Log File on SD Card
long gpsDate, gpsTime, satellites, tempLong;
char inByte; //read char
String tempStr;
uint32_t stringPtr;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////7
void setup()
{
//debugging
Serial.begin(9600);
while (!Serial)
{ ; // wait for serial port to connect. Needed for native USB port only
}
// Serial.println(SERRDY);
while (Serial.available() > 0)
inByte = Serial.read(); //empty receive buffer
//Buzzer
pinMode(BUZZER, OUTPUT);
for (myTime = 0; myTime < 2000; myTime++) //signal reset
{ digitalWrite(BUZZER, HIGH);
delayMicroseconds(1800);
digitalWrite(BUZZER, LOW);
delayMicroseconds(200);
}
//1-digit Display
pinMode(SHIFTREG_DATA, OUTPUT);
pinMode(SHIFTREG_CLOCK, OUTPUT);
pinMode(SHIFTREG_LATCH, OUTPUT);
// check SD Card response
// Serial.print(SDCINI);
delay(1000);
if (!SD.begin(SD_CS))
{ //Serial.println(SDCFAIL); //Init SC-Card
delay(1000);
updateLEDs(DCc); //show c(ard)
while (1); //stall program
}
// Serial.println(OK);
// delay(1000);
//GPS
#if not defined WOKWI
sermsg.begin(9600);
#endif
updateLEDs(DCr);
// Serial.println(SETUPOK);
Serial.println(OK);
delay(1000);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////7
void loop()
{ //local variables
static bool dateValid, locationValid, nmeaIn;
static char step;
double latd, longd, altituded;
/* Serial.print("Starting loop at ");
Serial.println(millis());
*/
#if not defined WOKWI
{ while (sermsg.available() > 0) //get all input from SoftwareSerial GPS
{ gps.encode(sermsg.read());
nmeaIn = true;
}
}
#else //not defined WOKWI
{ while (Serial.available() > 0) //get all input from SoftwareSerial GPS
{ gps.encode(Serial.read());
nmeaIn = true;
}
}
#endif
if (nmeaIn)
{ updateLEDs(DCn); //diplay 0 = gps from Softwareserial
nmeaIn = false;
}
else
{ updateLEDs(DC0); //diplay 0 = gps from Softwareserial
}
// Serial.print((int)gps.satellites.value);
// Create and open file if fileName = nullstring
if (gps.date.isValid() && gps.time.isValid()) //gps date+time availale when first satellite is detected
{ //updateLEDs(DC2);
if (gps.date.isUpdated())
{ //updateLEDs(DC3);
gpsDate = gps.date.value(); //ddmmyy
gpsTime = gps.time.value(); //hhmmss.ss
dateValid = true;
//=========== Create filename and write gpx file header
if ((fileName.length() == 0) && (gpsDate != 0))
{
//Serial.print(GPSDATE);
//Serial.println(gpsDate);
if (gpsDate < 100000) //dmmyy
{ tempStr = String('0');
//Serial.println(tempStr);
tempStr.concat(String(gpsDate, DEC)); //0dmmyy
}
else
{ tempStr = String(gpsDate, DEC);
}
//Serial.println(STRGPSD);
//Serial.println(tempStr);
//Serial.println(STRLEN);
//Serial.println(tempStr.length());
fileName = tempStr.substring(0,4); //ddmm
//Serial.println(fileName);
// Serial.print(DDMM);
// Serial.print(gpsDate);
// Serial.print('=');
// Serial.println(fileName);
if (gpsTime < 10000000) //hmmssss seconds in centisec
{ tempStr = '0';
tempStr.concat(String(gpsTime)); //0hmmssss
}
else
{ tempStr = String(gpsTime); //hhmmssss
}
fileName.concat(tempStr.substring(0,4)); //hhmm
fileName.concat(FILEXT); //
Serial.print(FILENAME);
Serial.println(fileName);
WriteGPXHeader();
/* logFile = SD.open(fileName, FILE_WRITE);
if (!logFile)
{ updateLEDs(DCe); //display E for Error on SD-open
Serial.print(ERRFN);
Serial.print(ERROPEN);
Serial.println(fileName);
}
else
{ //write gpx header
logFile.println(XML);
logFile.println(GPXHDR);
logFile.println(TRK);
tempStr += " <name>" + String(gpsDate) + ' ' + String(gpsTime) + "</name";
logFile.println(tempStr);
logFile.println(DESC);
logFile.println(TRKSEG);
logFile.println(TRKSEGEND);
logFile.println(TRKEND);
logFile.println(GPXEND);
logFile.close();
logFile.flush();
updateLEDs(DCf); //display F for File write done
Serial.println(FHDROK);
};
*/ Serial.print(gpsDate);
Serial.print("D / T=");
Serial.print(gpsTime);
// Serial.print(" FN=");
// Serial.println(fileName);
}
}
}
if (gps.location.isValid())
{ //updateLEDs(DC4);
if (gps.location.isUpdated())
{ //updateLEDs(DC5);
latd = gps.location.lat();
longd = gps.location.lng();
locationValid = true;
}
}
if (gps.altitude.isValid())
{ altituded = gps.altitude.meters();
//updateLEDs(DC6);
}
switch (step)
{ case 0: //wait for valid data
{ if (dateValid && locationValid)
{ //updateLEDs(DC7);
myTime = millis();
// Serial.println(myTime);
step = 1; //data valid
}
}
break;
case 1: //wait for valid values and time lapse
{ //Serial.println(millis() - myTime);
if ((millis() - myTime) > 10000) //time lapse
{ //if (dateValid && locationValid) //valid update
//updateLEDs(DC8);
Serial.print(millis());
Serial.print(" gpx D+T = ");// write gpx record
Serial.print(String(gpsDate));
Serial.println(String(gpsTime));
WriteGPX(gpsDate, gpsTime, latd, longd, altituded);
dateValid = false;
locationValid = false;
myTime += 10000;
step = 0;
}
}
break;
case 2: //initialization running
{ if (dateValid && locationValid)
{ //updateLEDs(DC6);
//Serial.println(myTime);
step = 1; //Initialization completed
}
}
default:
{ Serial.println(CASEDEF);
}
} //switch
updateLEDs(255);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void WriteGPXHeader(void)
#define LF 10
#define CR 13
{ File hdrFile;
String myStr;
char inByte;
int strPtr;
uint8_t lineCntr; //line Counter
for(lineCntr = 1; lineCntr < 14; lineCntr++) //read line by line
{ hdrFile = SD.open("gpxhead.txt", FILE_READ);
if (!hdrFile)
{ Alarm(DCe);
Serial.println("Open Headerfile failed");
}
Serial.println("Headerfile Open");
strPtr = 0;
Serial.print(lineCntr);
Serial.print("lineCntr strPtr");
Serial.println(strPtr);
while (strPtr < lineCntr) //read lines up to current lineCntr
{ do
{ inByte = hdrFile.read();
Serial.print(inByte);
if (inByte != '\n') //not end of line
{ if (inByte != '\r') //skip CR
myStr.concat(inByte); //concat characters
}
} while (inByte != LF);
myStr.concat(0); //end of string
stringPtr ++;
Serial.println(myStr);
}
hdrFile.close();
logFile = SD.open(fileName, FILE_WRITE);
if (!logFile)
{ Alarm(DCf);
Serial.println("Open Logfile failed");
}
logFile = SD.open(fileName, FILE_WRITE);
if ((stringPtr < 1) || (stringPtr > 7))
logFile.println(myStr);
else
logFile.print(myStr);
logFile.close();
Serial.println(myStr);
}
logFile = SD.open(fileName, FILE_WRITE);
if (!logFile)
Alarm(DCf);
logFile.println(TRKSEGEND);
logFile.println(TRKEND);
logFile.println(GPXEND);
logFile.close();
logFile.flush();
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void Alarm(uint8_t disp)
{ digitalWrite(SHIFTREG_LATCH, LOW);
// shiftOut(SHIFTREG_DATA, SHIFTREG_CLOCK, MSBFIRST, ~value);
#if not defined WOKWI
shiftOut(SHIFTREG_DATA, SHIFTREG_CLOCK, MSBFIRST, disp);
#else
shiftOut(SHIFTREG_DATA, SHIFTREG_CLOCK, MSBFIRST, ~disp);
#endif
digitalWrite(SHIFTREG_LATCH, HIGH); // Interlock
while (1);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool WriteGPX(uint32_t date, uint32_t time, double lat, double lon, double alt)
{ String str, dtstr;
uint32_t fsize;
logFile = SD.open(fileName, FILE_WRITE);
if (!logFile)
{ updateLEDs(DCf);
return(true);
}
else
{ fsize = logFile.size();
logFile.seek(fsize-30);
str = SPC6;
str.concat(TRKPTLAT); //trackpoint location
str.concat(String(lat, 6));
str.concat(TRKPTLON);
str.concat(String(lon,6));
str.concat(TRKPTEND);
logFile.println(str);
str.concat(ELE); //elevation
str.concat(String(alt));
str.concat(ELEEND);
logFile.println(str);
dtstr = String(date); //date
if (dtstr.length() < 8)
dtstr = '0' + dtstr;
str.concat(TIME);
dtstr = String(date);
str.concat(dtstr.substring(4,4)); //yy
str.concat("-");
str.concat(dtstr.substring(2,4)); //mm
str.concat("-");
str.concat(dtstr.substring(0,2)); //d
str.concat("T");
dtstr = String(time); //time
if (dtstr.length() < 8)
dtstr = "0" + dtstr;
str.concat(dtstr.substring(0,2)); //hh
str.concat(":");
str.concat(dtstr.substring(2,4)); //mm
str.concat(":");
str.concat(dtstr.substring(4,6)); //ss
str.concat(".");
str.concat(dtstr.substring(6,8)); //.ss
str.concat(TIMEEND);
logFile.println(str);
logFile.println(TRKPTEND);
logFile.println(TRKSEGEND);
logFile.println(TRKEND);
logFile.println(GPXEND);
logFile.close();
logFile.flush();
return (false);
}
Serial.println(str);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////7
void updateLEDs(int value)
{ static uint8_t buffer[8], bin, bout, dp = 128;
static uint32_t ledTime;
//buffer input
if ((value < 100) && (value != buffer[bin]))
{ bin++;
bin %= 8; //bin limited to 8 char in queue
buffer[bin] = uint8_t(value);
}
//buffer output
if ((millis() - ledTime) > 500)
{ ledTime = millis();
if (bin != bout)
{ bout += 1;
bout %= 8;
// Serial.println(buffer[bout] + dp);
}
Serial.println(buffer[bout] + dp);
dp += 128; //blink DP
digitalWrite(SHIFTREG_LATCH, LOW);
// shiftOut(SHIFTREG_DATA, SHIFTREG_CLOCK, MSBFIRST, ~value);
#if not defined WOKWI
shiftOut(SHIFTREG_DATA, SHIFTREG_CLOCK, MSBFIRST, (buffer[bout] + dp));
#else
shiftOut(SHIFTREG_DATA, SHIFTREG_CLOCK, MSBFIRST, ~(buffer[bout] + dp));
#endif
digitalWrite(SHIFTREG_LATCH, HIGH); // Interlock
}
}