Data Logging Stops After Losing GPS Signal

So we're working on a data logger system that logs a bunch of physical parameters, one of them is GPS location and speed. Everything's working fine, the data is logging in the right format, GPS signal is outputting invalid for lines where there's no fix at first.

However, a problem arises when the GPS runs AFTER acquiring a first signal, and then subsequently losing it. The Arduino completely stops writing anything to the SD card after a fix is lost. I'm completely lost because when it starts, the GPS has no fix, and it logs fine. The problem only arises after the first fix is lost. I'm posting it here because I'm thinking it's a programming fault, anyone run into a similar problem?

JMP91:
However, a problem arises when the GPS runs AFTER acquiring a first signal, and then subsequently losing it. The Arduino completely stops writing anything to the SD card after a fix is lost. I'm completely lost because when it starts, the GPS has no fix, and it logs fine. The problem only arises after the first fix is lost. I'm posting it here because I'm thinking it's a programming fault, anyone run into a similar problem?

Possibly. Hard to tell, though, because we can't see your code.

Just to save time later, though, what would you have your code write to the SD card if there is no GPS data coming in? When the GPS stops after the first fix, what is coming from the GPS? When it has no fix, before the first fix, what is coming from the GPS?

If you want help, you're going to have to tell us a lot more than you have so far.

It logs stars in the latitude and longitude columns, and the string “invalid” in the date/time columns. I’ve attached the code below.

Trying_Speed.ino (9.75 KB)

  while (millis() - start < 1000)
  {
    if (feedgps())
      newdata = true; 
  }

Were you intending to use newdata anywhere?

I haven’t done much with GPS, other than hook up and run a small Skylab module, but I did find this thread (URL points to “Solved” code message. Hit the << link at upper left to get to the top post in the thread.

if(SD_date_time != "invalid")
    digitalWrite(GPSIndicate, HIGH);
  else (SD_date_time = "invalid");
  digitalWrite(GPSIndicate, LOW);
  {
  }

Could you rewrite that code in some meaningful fashion… because I doubt it is doing what you think it is.

This is what it does:

if(SD_date_time != "invalid"){
    digitalWrite(GPSIndicate, HIGH);
} else {
    SD_date_time = "invalid";
}
digitalWrite(GPSIndicate, LOW);

Perhaps this is what you meant:

if(SD_date_time != "invalid"){
    digitalWrite(GPSIndicate, HIGH);
} else {
    digitalWrite(GPSIndicate, LOW);
}

ALWAYS USE ‘{’ and ‘}’. To not do so is only likely to lead to confusion and making the code difficult to read.


Next thing is to do with checking the ‘dataFile’ variable is actually valid:

if (hasrun == false)
{
  ...
  ... //you write to the file here, but haven't checked whether or not it is a valid file handle.
}

if (dataFile)
{
  ...
  ...//here you also write to file, but this time you have correctly checked it isn't a null pointer.
}

Although to be honest, I don’t know why you don’t open the file in the setup() and write the headers, rather than having a variable controlling an if statement which causes the code to only execute on the first pass of loop().


  if (val == invalid)
    strcpy(sz, "*******");
  else
    sprintf(sz, "%ld", val);
  sz[len] = 0;
  for (int i=strlen(sz); i<len; ++i)
    sz[i] = ' ';
  if (len > 0) 
    sz[len-1] = ' ';

Again, use the {}, they are there to make the code readable.
Secondly, I don’t think you mean ‘++i’, I think you are looking for ‘i++’. Otherwise there is little point adding the spaces as sz[strlen(sz)] is a null termination that you never remove, meaning the spaces will never be included as part of your string. It also explains why you have to have the line sz[len-1] = ’ '; which would not be necessary if you use the correct ++ operator. There is a big difference between “pre-increment” and “post-increment”
The same is true in your print_float function.


static void print_str(const char *str, int len)
{
  int slen = strlen(str);
  for (int i=0; i<len; ++i)
    Serial.print(i<slen ? str[i] : ' ');
  feedgps();
}

That seems like a very convoluted way of doing it. Try this:

static void print_str(const char *str, int len)
{
  int slen = strlen(str);
  Serial.print(str);
  for (int i=slen; i<len; i++) {
    Serial.print(' ');
  }
  feedgps();
}

Next thing to do is to ditch the ‘String’ class completely. It will just fragment the RAM which after extended periods of time will become a big problem. There is nowhere in your current code that you actually need to use the String class.

For example:

char SD_date_time [32];
void setup(){
   SD_date_time[0]  = '\0'; //invalid if the first character is a null termination (zero-length string)
   ...
}
...
void loop(){
...
if (SD_date_time[0] != '\0') {
  ...
} 
...
...
static void print_date(TinyGPS &gps)
{
  int year;
  byte month, day, hour, minute, second, hundredths;
  unsigned long age;
  gps.crack_datetime(&year, &month, &day, &hour, &minute, &second, &hundredths, &age);
  if (age == TinyGPS::GPS_INVALID_AGE)
  {
    Serial.print("*******    *******    ");
    SD_date_time[0] = '\0'; //set first character to null to indicate invalid.
  }
  else
  {
    sprintf(SD_date_time, "%02d/%02d/%02d %02d:%02d:%02d   ",
    month, day, year, hour, minute, second);
    Serial.print(SD_date_time);
  }
  print_int(age, TinyGPS::GPS_INVALID_AGE, 5);
  feedgps();
}
...

Then this one:

String dataString;

Can be removed completely and replaced with 5 separate calls to dataFile.print(…):

 //dataString = SD_date_time + "," + SD_lat + "," + SD_lon; <-- remove
 ...
   // dataFile.println(dataString); <-- remove
    dataFile.println(SD_date_time);
    dataFile.println(",");
    dataFile.println(SD_lat );
    dataFile.println(",");
    dataFile.println(SD_lon);

One thing you might want to consider is that you never use the return value from feedgps(), and there is certainly at least one thing that will cause you to not print any INVALID messages, and that is if the GPS module does not respond at all.

Tom has pointed out a number of other things that can easily be the cause of your problems, and especially, the use of the String class. If your memory gets fragmented enough, you can be in real trouble, and that could be the cause of the GPS not responding, and in fact, it may not even be executing the feedgps() function.

I had some problems with TinyGPS when I was trying out my module, and ended up using TinyGPS++, which worked flawlessly, and has the added bonus that you don't need to use a "keepalive" like feedgps().

Amazing, big thanks to both of you. I will try all of those fixes you suggested, and perhaps use TinyGPS++ instead of TinyGPS, since I'm also having trouble getting speed as well

I was also having a similar problem JMP91. Reading through this I have realized that I really am lacking most of the basic coding knowledge that most of you have. I was just wondering if you guys could explain the segments again but in layman's. For instance I was wondering what a string class was. If someone could help me I would really appreciate it. Thank you

lar3ry:
I had some problems with TinyGPS when I was trying out my module, and ended up using TinyGPS++, which worked flawlessly, and has the added bonus that you don't need to use a "keepalive" like feedgps().

keepalive? How's that?

Looks like both libraries need to call encode for each character received thru the serial port and as such, work exactly the same way.

Regards,

Brad
KF7FER

You're probably right. I was going by something I read about TinyGPS that seemed to say that you had to keep sending it a command, whether or not you were using the data, or it stopped working. I looked around (just now) for that post, and checked the libraries, and it would appear that they are indeed the same, at least in that regard. TinyGPS++ does have more features, of course.

Personally, I ended up not bothering with either library in the few sketches I wrote.

I did mess around with TinyGPS++, and it's much easier to work with. I was having some trouble setting variables equal to function outputs and having them update, but writing the function values directly to the serial monitor worked just fine.

However I still want to print a line of data when the GPS does NOT have a fix, have you done something similar? I tried setting up variables for latitude, longitude, and speed, setting them equal to 0 at the beginning of the loop, then having them change through the gps.location.isUpdated and gps.speed.isUpdated conditions, but they don't change.

JMP91:
However I still want to print a line of data when the GPS does NOT have a fix, have you done something similar?

Certainly. In fact I believe that one of the example programs that comes with TinyGPS does that. Of course it doesn't log to SD but it should display a line of data (or "*'s") even if there is no fix.

So your code, as posted, doesn't correctly display data every second. It calls gpsdump every pass thru loop (including a 200ms delay).

Have you made any changes to the code since it was posted? If so, could you repost?

Regards,

Brad
KF7FER

JMP91:
However I still want to print a line of data when the GPS does NOT have a fix, have you done something similar? I tried setting up variables for latitude, longitude, and speed, setting them equal to 0 at the beginning of the loop, then having them change through the gps.location.isUpdated and gps.speed.isUpdated conditions, but they don't change.

Reposting your code would probably help.

I am having a hard time getting my SkyLab GPS to report invalid. I even put it into a tin can, but it just merrily keeps on reporting good position info. I know it isn't reporting something cached, because the lat/long decimal parts keep changing slightly.

One thing, though... I'd like to know what happens if you unplug the TX or Rx pins from the GPS. If you don't get a response from the GPS at all, I think it will not report anything. If it does, it might be something cached from the last successful reading.

This is the beginning of the loop, the part that controls the GPS functions.

void loop()
{
  
  //LED Condition
  sentences = (gps.sentencesWithFix());
  //Digital Inputs
  durationHigh = pulseIn(PulsePin, HIGH , 500000);
  durationLow = pulseIn(PulsePin, LOW, 500000);
  
  timer = millis();
  
  //Analog Inputs
  FanTemp = analogRead(0);
  AirTemp = analogRead(1);
  value_x = analogRead(2);
  value_y = analogRead(3);
  value_z = analogRead(4);
 
  //Acceleration Manipulation
  xv = (value_x/analog_resolution*ADC_ref-zero_x)/sensitivity_x;
  yv = (value_y/analog_resolution*ADC_ref-zero_y)/sensitivity_y;
  zv = (value_z/analog_resolution*ADC_ref-zero_z)/sensitivity_z;
  
  //Tilt Angle
  angle_x = atan2(-yv,-zv)*RAD_TO_DEG;
  angle_y = atan2(-xv,-zv)*RAD_TO_DEG;
  angle_z = atan2(-yv,-xv)*RAD_TO_DEG;
  
  //GPS
    while (Serial3.available() > 0)
  {
   gps.encode(Serial3.read());
  }
  
  if (gps.location.isUpdated())
  {
  latitude = (gps.location.lat() , 8);
  longitude = (gps.location.lng(), 8);
  }
  
  else if (gps.speed.isUpdated())
  {
    vel = (gps.speed.mph());
  }
  
  else if (gps.time.isUpdated())
  {
  hourdata =(gps.time.hour());
  mindata = (gps.time.minute());
  secdata = (gps.time.second());
  }

else if (gps.date.isUpdated());
{
  monthdata = (gps.date.month());
  daydata = (gps.date.day());
}
if ( sentences == 0)
{
digitalWrite(GPSOff, LOW);
delay(300);
digitalWrite(GPSOff,HIGH);
delay(300);
digitalWrite(GPSOn,LOW);
}

else
{
  digitalWrite(GPSOn,HIGH);
}

if (dataFile)
  {
    
    //Writing Data to SD Card
    dataFile.print(timer/1000);
    dataFile.print(',');
    
    dataFile.print(durationHigh);
    dataFile.print(',');
    
    dataFile.print(durationLow);
    dataFile.print(',');
    
    dataFile.print(xv);
    dataFile.print(" , ");
  
    dataFile.print(yv);
    dataFile.print(" , ");
  
    dataFile.print(zv);
    dataFile.print(" , ");
    
    dataFile.print(angle_x);
    dataFile.print(" , ");
  
    dataFile.print(angle_y);
    dataFile.print(" , ");
  
    dataFile.print(angle_z);
    dataFile.print(",");
    
    
    dataFile.print(FanTemp*0.484375-1.6875);
    dataFile.print(',');
    
    dataFile.println(AirTemp*0.484375-1.6875);
    dataFile.close();

    GPSFile.print(monthdata  );
    GPSFile.print(daydata  );
    GPSFile.print(hourdata  );
    GPSFile.print(mindata  );
    GPSFile.print(secdata  );
    GPSFile.print(",");
    
    GPSFile.print(latitude);
    GPSFile.print(",");
    
    GPSFile.print(longitude);
    GPSFile.print(",");
    
    GPSFile.println(vel);
    
    GPSFile.close();

You should really post code with either code tags or an attachment... can you see how your posting got mangled?

Having said that... You really need to look at what you're doing after the encode command.

So as written, you only update your speed if the location hasn't changed? Same with the time, you only check the time if both the location and speed didn't change? Don't you always want to check to see if the time is updated?

You don't want the if/else structure here. Worst case something like:

if (gps.location.isUpdated()) {
  latitude = ...
  longitude = ...
}

if (gps.speed.isUpdated()) {
  vel = ...
}

if (gps.time.isUpdated()) {
  hourdata = ...
  mindata = ...
  secdata = ...
}

Does that help?

Regards,

Brad
KF7FER

if (gps.location.isUpdated())
{
latitude = (gps.location.lat() , smiley-cool;
longitude = (gps.location.lng(), smiley-cool;
}
What do you think this code is doing? Why dis you write it that way? Even without the stupid smiley faces, it is nearly certain that the comma operator is NOT doing what you are assuming it does.

Your code looks wrong to me.

There are two ways to write files on SD cards.

You can open the file once, and keep writing to it.

or

You can open the file, write something to it, and the close it. And the next time you want to write something, open it again. And then close it again.

Both of these methods seem to have reliability issue on the arduino.

But your method seems to be to open the file once, write some stuff, and then close it. And when you get the next gps fix, you try to write more stuff ( without opening the file ), and then close it again. And so on. I am pretty sure that you cannot open the file once and then close it many times, and expect it to work.

michinyon, writing the code to the SD file as is works just fine. The syntax of that is not the problem I'm having, the SD writing is pretty flawless at the moment, and even works when writing 10-20 lines that is 8-10 variables long in a single second.

PaulS,

The comma in the code is part of the gps.location.lng and gps.location.lat functions when printing the function value. The number after the comma should be an 8.

Running the loop and instead just doing

Serial.print(gps.location.lng(), 8);
Serial.print(gps.locating.lat(), 8);

Prints the global latitude and longitude with 8 decimal places. What it does when setting longitude and latitude (variable names) equal to those function outputs instead just sets both latitude and longitude equal to 8.00. If you know why that is happening, please enlighten me.

Brad,

Thanks for letting me know about the code tags!

The if/else structure was pulled from an example in Mikal Hart's library, I will definitely try your method of writing it. However, do you think this will cause problems with printing a new value to the SD card every iteration of the main Arduino loop?

JMP91:
do you think this will cause problems with printing a new value to the SD card every iteration of the main Arduino loop?

Well, how about something like

void loop() {
  boolean isUpdated = false;
  
  if (gps.location.isUpdated()) {
    [...]
    isUpdated = true;
  }
  
  if (gps.speed.isUpdated()) }
    [...]
    isUpdated = true;
  }
  
  [...]
  
  if (isUpdated) {
    // Write to SD card here
  }
}

Hope this helps,

Brad
KF7FER