Basic Teensy 4.1 GPX Logger

TeaOnPC

New member
Hi everyone! I wrote a basic script for the Teensy 4.1 that receives GPS data from an Adafruit module. When you press a button on GPIO, it starts logging and when pressed again it finalizes the file. I want to update the script to update the filename so you can record multiple unique files.

The way I have it right now, it records each point as a "route point" but you can easily adjust the code to save as way points or as a track.

It's definitely simple and nothing to go crazy over however I figured I would share! Please feel free to give feedback(I'm sure the way I handle the button triggering logging starting/stopping could be better)!

 
Your button debounce is not ideal, nothing that involves a delay in the main loop is ideal, but and you are looking to log things at 1 Hz so even 250ms of delay isn't going to cause any big issues.
The main issue I can see is if someone holds the button down it will constantly start and stop the logs.

A nicer way would be to note the time of the last edge and then only change state when the button changes from released to pressed and it's over a certain amount of time since the last button press.

Something like this:

Code:
// global variable
uint32_t lastButtonTime = 0;
int lastButtonState = 1;

// in loop()

int button = digitalReadFast(captureControl);
if ((lastButtonState == 1) && (button == 0)) { // button press
  uint32_t now = millis()
  if ((now - lastButtonTime) > 100) {  // over 100ms since last press
    lastButtonTime = now;
    if (!LogActive) {
      LogActive = startLog();  // change logActive to a bool. Create a startLog function that returns true if started, false if fails (e.g. no sd card)
    } else {
      stopLog();
      LogActive = false;
    }
  }
}
lastButtonState = button;


For reference my method of handling auto incrementing files is:

Code:
unsigned int NextFileNumber = 0;

FILE *OpenLogFile() {
   const int FileNameLength = 15;
   char fileNameBuffer[16];
   while (true) {
      char testName[tmp_bufferLen];
      bool numberInUse = false;
      snprintf(fileNameBuffer, FileNameLength, "log%04d.gpx", NextFileNumber);
      FILE *f = fopen(testName, "r");
      if (f) {
         fclose(f);
         NextFileNumber++;
      } else {
         f = fopen(testName, "w");
         return f;
      }
   } // end while
}

This can be a little slow the first time you open a file if there are already a log of logs on the card but other than that works fine.

Having said that there are lots of other ways to do both of these things, everyone will do it slightly differently.

Edit - just to add a note saying good work if the post above seemed to be purely criticism, I may have been overly focused on the feedback part ;-). And it's good to see someone learning without just throwing the problem at an AI and asking it to write the code.
 
Last edited:
Your button debounce is not ideal, nothing that involves a delay in the main loop is ideal, but and you are looking to log things at 1 Hz so even 250ms of delay isn't going to cause any big issues.
The main issue I can see is if someone holds the button down it will constantly start and stop the logs.

A nicer way would be to note the time of the last edge and then only change state when the button changes from released to pressed and it's over a certain amount of time since the last button press.

...

Edit - just to add a note saying good work if the post above seemed to be purely criticism, I may have been overly focused on the feedback part ;-). And it's good to see someone learning without just throwing the problem at an AI and asking it to write the code.
Thanks so much for the reply! I 100% agree with your conclusions, the delay for de bounce definitely isn't ideal, I just wanted to get something basic to initially work and then I can go back and fix it for it to be more functional and less prone to the errors you had mentioned!

Unfortunately, I only had one Intro to CS class for my degree, so I'm trying to get better at embedded and coding! I appreciate all the constructive criticism :)
 
Incrementing a filename will work, but upon restart you risk writing to a file that exists. I have a similar logger, and I get the date / time from the GPS and build a filename from that. In my case it made finding the right log easier. I also have code to check if the file exist and increments NN until that filename is available

YYYY_MM_DD_HH_NN.csv or something similar
 
Incrementing a filename will work, but upon restart you risk writing to a file that exists. I have a similar logger, and I get the date / time from the GPS and build a filename from that. In my case it made finding the right log easier. I also have code to check if the file exist and increments NN until that filename is available

YYYY_MM_DD_HH_NN.csv or something similar
Keep a log of filename(s) in EEPROM.
 
Incrementing a filename will work, but upon restart you risk writing to a file that exists. I have a similar logger, and I get the date / time from the GPS and build a filename from that. In my case it made finding the right log easier. I also have code to check if the file exist and increments NN until that filename is available

YYYY_MM_DD_HH_NN.csv or something similar
This was exactly the plan, use the GPS to update the teensy's clock and base naming convention around that.

Whether or not I can update the clock at startup is contingent on if the GPS module is already getting a proper fix(usually takes a minute or two). So I might have it so when you start a log it tries to establish an updated time, and wont start the log if the GPS module isn't ready. Alternatively I could omit the real time clock update all together and just base the file name off the first GPS pull of the log.

Thanks for the reply!
 
Code I use after I've tested and found the RTC is not correct. There may be better (other ways) but this works for me.

Code:
    // check the accuracy of the RTC
    // if the RTC battey died, we will still get a date/time but will wrong
    // if so, get the GPS time (when available and update the RTC)
    // I've not found a better way to determine if RTC is not correct
    if ((abs(GPS.date.day() - day()) > 0) || (abs(GPS.time.minute() - minute()) > 0)) {
      // waiting for GPS
      // Serial.println("Waiting for GPS to get the correct time.");
      if (GPS.location.isValid()) {
        SyncTimeFromGPS();
      }
    }
    
...

void SyncTimeFromGPS() {

  uint8_t TempH, TempD;
  // If the datalogger RTC is incorrect (if the CR2032 is dead), we will get the time from the
  // GPS and adjust according to CST non daylight savings time
  // I'll admit this is lame...guessing at the timezone
  // but for my needs this works
  // GPS will return GMT and we're 5 hours behind
  if (GPS.time.hour() < 7) {
    TempH = GPS.time.hour() + 7;
    TempD = GPS.date.day() - 1;
  } else {
    TempH = GPS.time.hour() - 5;
    TempD = GPS.date.day();
  }

  // update datalogger RTC
  setTime(TempH, GPS.time.minute(), GPS.time.second(), TempD, GPS.date.month(), GPS.date.year());

  Teensy3Clock.set(now());
}
 
One thing to watch out for if you want the clock correct to the second, depending on the GPS used the time you get on startup may be correct or it may be wrong by up to 18 seconds. Most common from my experience is 2 seconds off. At some point in the first ~20 minutes the time will then jump to the correct time.

This is due to the fun and games caused by leap seconds and GPS time being different from UTC time. Most of the time this doesn't matter, especially if it's just to time stamp a log file, but it is something to be aware of. If nothing else to explain why it looks like you always miss a couple of points near the start of the first log after power cycle but never at any other times.
 
Back
Top