tinyGPS - trying to output status

I got the code working nicely, but sometimes my GPS needs to be reset and I cannot tell.

In the code below, I tried adding either of the two commented else statements, but that just made the output stuck in that state, even though the GPS was outputting valid data.

I'd like to print on the LCD "Wait for GPS Fix" the time of waiting, and a status of wheather the GPS is sending data at all (this is the bool variable added to the feedgps function at the bottom.

Maybe I just need to make the first else statement an if (gpsData = 0) statement?

#include <NewSoftSerial.h>
#include <TinyGPS.h>
#include <string.h>
#include <ctype.h>


/* This sample code demonstrates the normal use of a TinyGPS object.
   It requires the use of NewSoftSerial, and assumes that you have a
   4800-baud serial GPS device hooked up on pins 2(rx) and 3(tx).
*/

TinyGPS gps;
NewSoftSerial nss(2, 3);

void gpsdump(TinyGPS &gps);
bool feedgps();
void printFloat(double f, int digits = 2);

int tzone = -7;
bool gpsData = false;

//connect LCD
#define rxLCDPin 4  // rxPin is immaterial - not used - just make this an unused Arduino pin number
#define txLCDPin 14 // pin 14 is analog pin 0, on a BBB just use a servo cable :), see Reference pinMode
//SoftwareSerial LCD = SoftwareSerial(rxLCDPin, txLCDPin);
NewSoftSerial LCD = NewSoftSerial(rxLCDPin, txLCDPin);


int ledPin=13;  //LED test pin

void setup()
{
  Serial.begin(115200);
  nss.begin(4800);
  
  Serial.print("Testing TinyGPS library v. "); Serial.println(TinyGPS::library_version());
  Serial.println("by Mikal Hart");
  Serial.println();
  Serial.print("Sizeof(gpsobject) = "); Serial.println(sizeof(TinyGPS));
  Serial.println();
  
  //set up LCD
  pinMode(txLCDPin, OUTPUT);
  LCD.begin(9600);  // 9600 baud is chip comm speed
  LCD.print("?G216");  // set display geometry,  2 x 16 characters in this case
  delay(500);  // pause to allow LCD EEPROM to program
  LCD.print("?B50");  // set backlight from 00 to ff hex (minimum to maximum brightness)
  delay(1000);  // pause to allow LCD EEPROM to program
  LCD.print("?s05");  // set tabs to five spaces
  delay(1000);  // pause to allow LCD EEPROM to program
  LCD.print("?c0");  // set cursor style, 0= none 2= blinking 3=underline
  delay(5);
  LCD.print("?f");  // clear the LCD
  delay(5);
  LCD.print("Hello GPS");
  delay(1000);
  LCD.print("?f");  // clear the LCD
  LCD.print("Wait for GPS Fix");
}


void loop()
{
//  gpsData = false;
  digitalWrite(ledPin, HIGH);
  bool newdata = false;
  unsigned long start = millis();

  // Every 5 seconds we print an update
  //while (millis() - start < 5000) {
  if (feedgps()) {
      newdata = true;
      digitalWrite(ledPin, LOW);
  }
  
  /********************************
  else {
    LCD.print("?x00?y0Wait for GPS Fix");
    LCD.print("?x00?y1"); LCD.print(millis() / 1000);
    LCD.print("?x11?y1"); LCD.print(gpsData);
  }
 ********************************/
  
  if (newdata) {
    Serial.println("Acquired Data");
    Serial.println("-------------");
    gpsdump(gps);
    Serial.println("-------------");
    Serial.println();
    wait = 0;
  }
 /**********************
  else {
    LCD.print("?x00?y0Wait for GPS Fix");
    LCD.print("?x00?y1"); LCD.print(millis() / 1000);
    LCD.print("?x11?y1"); LCD.print(gpsData);
  }
// ************************/

}


void printFloat(double number, int digits) {
  // Handle negative numbers
  if (number < 0.0) {
     Serial.print('-');
     number = -number;
  }

  // Round correctly so that print(1.999, 2) prints as "2.00"
  double rounding = 0.5;
  for (uint8_t i=0; i<digits; ++i)
    rounding /= 10.0;
   number += rounding;
  
  // Extract the integer part of the number and print it
  unsigned long int_part = (unsigned long)number;
  double remainder = number - (double)int_part;
  Serial.print(int_part);

  // Print the decimal point, but only if there are digits beyond
  if (digits > 0)
  {
    Serial.print(".");
  }

  // Extract digits from the remainder one at a time
  while (digits-- > 0)
  {
    remainder *= 10.0;
    int toPrint = int(remainder);
    Serial.print(toPrint);
    remainder -= toPrint; 
  }
}

void LCDprintFloat(double number, int digits)
{
  // Handle negative numbers
  if (number < 0.0)
  {
     LCD.print("-");
     number = -number;
  }

  // Round correctly so that print(1.999, 2) prints as "2.00"
  double rounding = 0.5;
  for (uint8_t i=0; i<digits; ++i)
    rounding /= 10.0;
   number += rounding;
  
  // Extract the integer part of the number and print it
  unsigned long int_part = (unsigned long)number;
  double remainder = number - (double)int_part;
  LCD.print(int_part);

  // Print the decimal point, but only if there are digits beyond
  if (digits > 0)
  LCD.print(".");

  // Extract digits from the remainder one at a time
  while (digits-- > 0)
  {
    remainder *= 10.0;
    int toPrint = int(remainder);
    LCD.print(toPrint);
    remainder -= toPrint; 
  }
}


void gpsdump(TinyGPS &gps)
{
  long lat, lon;
  float flat, flon;
  unsigned long age, date, time, chars;
  int year;
  byte month, day, hour, minute, second, hundredths;
  unsigned short sentences, failed;

  gps.get_position(&lat, &lon, &age);
  Serial.print("Lat/Long(10^-5 deg): "); Serial.print(lat); Serial.print(", "); Serial.print(lon); 
  Serial.print(" Fix age: "); Serial.print(age); Serial.println("ms.");
  //LCD.print("?x00?y0"); LCD.print(lat); 
  //LCD.print("?x00?y1"); LCD.print(lon); 
  
  feedgps(); // If we don't feed the gps during this long routine, we may drop characters and get checksum errors

  gps.f_get_position(&flat, &flon, &age);
  Serial.print("Lat/Long(float): "); printFloat(flat, 5); LCD.print("?x00?y0"); LCDprintFloat(flat, 5); Serial.print(", "); printFloat(flon, 5); LCD.print("?x00?y1"); LCDprintFloat(flon, 5);
  Serial.print(" Fix age: "); Serial.print(age); Serial.println("ms.");
  //LCD.print("?x00?y0"); LCD.print(flat);
  //LCD.print("?x00?y1");LCD.print(flon);

  feedgps();

  gps.get_datetime(&date, &time, &age);
  Serial.print("Date(ddmmyy): "); Serial.print(date); Serial.print(" Time(hhmmsscc): "); Serial.print(time);
  Serial.print(" Fix age: "); Serial.print(age); Serial.println("ms.");
  //LCD.print("?x08?y0"); LCD.print(time);
  LCD.print("?x10?y1");LCD.print(date);

  feedgps();

  gps.crack_datetime(&year, &month, &day, &hour, &minute, &second, &hundredths, &age);
  if (hour > 7)
    hour = hour + tzone;
  if (hour < 8)
    hour = hour + (tzone + 24);
  Serial.print("Date: "); Serial.print(static_cast<int>(month)); Serial.print("/"); Serial.print(static_cast<int>(day)); Serial.print("/"); Serial.print(year);
  Serial.print("  Time: "); Serial.print(static_cast<int>(hour)); Serial.print(":"); Serial.print(static_cast<int>(minute)); Serial.print(":"); Serial.print(static_cast<int>(second)); Serial.print("."); Serial.print(static_cast<int>(hundredths));
  
  Serial.println(millis());
  LCD.print("?x08?y0");
  if ((static_cast<int>(hour)) < 10)
    LCD.print("0");
  LCD.print(static_cast<int>(hour));

  LCD.print("?x10?y0"); LCD.print(":");
  if ((static_cast<int>(minute)) < 10)
    LCD.print("0");
  LCD.print(static_cast<int>(minute));

  LCD.print("?x13?y0"); LCD.print(":");
  if ((static_cast<int>(second)) < 10)
    LCD.print("0");
  LCD.print(static_cast<int>(second));
  Serial.println(millis());
  Serial.print("  Fix age: ");  Serial.print(age); Serial.println("ms.");
  
  feedgps();

  Serial.print("Alt(cm): "); Serial.print(gps.altitude()); Serial.print(" Course(10^-2 deg): "); Serial.print(gps.course()); Serial.print(" Speed(10^-2 knots): "); Serial.println(gps.speed());
  Serial.print("Alt(float): "); printFloat(gps.f_altitude()); Serial.print(" Course(float): "); printFloat(gps.f_course()); Serial.println();
  Serial.print("Speed(knots): "); printFloat(gps.f_speed_knots()); Serial.print(" (mph): ");  printFloat(gps.f_speed_mph());
  Serial.print(" (mps): "); printFloat(gps.f_speed_mps()); Serial.print(" (kmph): "); printFloat(gps.f_speed_kmph()); Serial.println();

  feedgps();

  gps.stats(&chars, &sentences, &failed);
  Serial.print("Stats: characters: "); Serial.print(chars); Serial.print(" sentences: "); Serial.print(sentences); Serial.print(" failed checksum: "); Serial.println(failed);
}
  
bool feedgps()
{
  while (nss.available()) {
    if (gps.encode(nss.read()))
//    gpsData = true;
      return true;
  }
  return false;
//  gpsData = false;
}

I tried a couple of other things, no combination of my if statements do what I think they would.

void loop()
{
//  gpsData = false;
  digitalWrite(ledPin, HIGH);
  bool newdata = false;
  unsigned long start = millis();

  // Every 5 seconds we print an update
  //while (millis() - start < 5000) {
  if (feedgps()) {
      newdata = true;
      digitalWrite(ledPin, LOW);
  }
  
  /********************************
  if (gpsData == 1 && feedgps == 0) {
    LCD.print("?x00?y0Wait for GPS Fix");
    LCD.print("?x00?y1"); LCD.print(millis() / 1000);
    LCD.print("?x11?y1"); LCD.print(gpsData);
  }
// ********************************/

  /********************************
  if (gpsData == 0 && feedgps == 0) {
    LCD.print("?x00?y0No GPS Data!!!!!");
    LCD.print("?x00?y1"); LCD.print(millis() / 1000);
    LCD.print("?x11?y1"); LCD.print(gpsData);
  }
 ********************************/


  if (newdata) {
    Serial.println("Acquired Data");
    Serial.println("-------------");
    gpsdump(gps);
    Serial.println("-------------");
    Serial.println();
  }

  /********************************
  if (gpsData == 1 && newdata == 0) {
    LCD.print("?x00?y0Wait for GPS Fix");
    LCD.print("?x00?y1"); LCD.print(millis() / 1000);
    LCD.print("?x11?y1"); LCD.print(gpsData);
  }
// ********************************/

  /********************************
  if (gpsData == 0 && newdata == 0) {
    LCD.print("?x00?y0No GPS Data!!!!!");
    LCD.print("?x00?y1"); LCD.print(millis() / 1000);
    LCD.print("?x11?y1"); LCD.print(gpsData);
  }
 ********************************/

}

If you post code, can you please remove the code that is commented- out?
It makes your post more compact, and easier to read.

[edit]I removed the comments: here is the code from your last post without them[/edit]

void loop()
{
//  gpsData = false;
  digitalWrite(ledPin, HIGH);
  bool newdata = false;
  unsigned long start = millis();

  // Every 5 seconds we print an update
  //while (millis() - start < 5000) {
  if (feedgps()) {
      newdata = true;
      digitalWrite(ledPin, LOW);
  }

  if (newdata) {
    Serial.println("Acquired Data");
    Serial.println("-------------");
    gpsdump(gps);
    Serial.println("-------------");
    Serial.println();
  }
}

You may want to look at your code. :wink:

Good advice, AWOL. My question was about the code that I had block commented, but not all of the commented code so I can understand it not being easy to read.

I've cleaned up the main loop below and it works perfectly, but I need to change it a bit.

void loop() {

  bool newdata = false;

  if (feedgps()) {
      newdata = true;

  if (newdata) {
    gpsdump(gps);
}

The funtions that you cannot see:

feedgps() reads the GPS serial port and does some stuff in the tinyGPS library, which I do not understand. It returns true only when the data is valid.

gpsdump(gps) uses the tinyGPS library to extract some of the gps data and print it to the LCD. It prints the coordinates, the time, and the date, which completely fills the LCD:


00.00000hh:mm:ss
-000.00000mmddyy

The problem is if the gps data is not valid or if the gps freezes, one cannot tell because the screen is just not updated; unless one happens to notice the clock is not counting. But it's still not known if the data is invalid or if the GPS failed and needs reset.

So I wanted to add some code to print on the LCD screen that would indicate that:

  1. No GPS data is being received (because the GPS is dead)

  2. GPS data is being received, but the GPS does not have lock (or the data is not valid)

I tried that by adding the two else statements as shown below, either one at a time or both:

void loop() {

  bool newdata = false;

  if (feedgps()) {
      newdata = true;
  else {
    LCD.print("?x00?y0No GPS Data");

  if (newdata) {
    gpsdump(gps);
  else {
    LCD.print("?x00?y0Wait for GPS Fix");
}

The problem now is the LCD only shows: "Wait for GPS Fix" or "No GPS Data" whether GPS data is received and valid or not.

I think I get why that is. It is because the GPS only sends data once a second and the loop probably cycles thousands of times a second. The GPS data on the LCD would immediately be overwritten by "Wait for GPS Fix" or "No GPS Data"

But I would expect that some of the GPS data on the LCD would persist (espcially on the 2nd line). That is not happening either. The LCD just shows the LCD.print statments and I have not yet found out the correct approach.

OK, one more time.

The below code works and almost does exactly what I want.

When GPS looses lock, it prints a message on the LCD:
"Wait for GPS Fix"

When GPS dies (or I unplug the serial line), it alternates two messages on the LCD:
"Wait for GPS Fix" and then "No GPS Data!!!"

I'd rather it just print "No GPS Data!!!" when data is not received.

In both cases, the program recovers and prints GPS data when it is again valid.

Also, I don't really understand one piece of the code as commented below:

void loop() {

  if (feedgps())
    gpsdump(gps); //this function just formats and prints GPS data to LCD

  // test if the GPS has sent any kind of data in the past 5 seconds
  if (millis() - lastGPSdata > 5000) {
    LCD.print("?x00?y0No GPS Data!!!  ");
    LCD.print("?x00?y1"); LCD.print(millis() - lastGPSdata);
    delay (500);
    //I don't know why I need this next line, but if it is not here the program never breaks out of this if condition
    //why is lastGPSdata not reset in the next feedgps() function call when data is being received?
    lastGPSdata = millis();
  }

  // test if the GPS has sent valid data in the past 5 seconds
  else if (millis() - lastGPSvalid > 5000) {
    LCD.print("?x00?y0Wait for GPS Fix");
    LCD.print("?x00?y1"); LCD.print(millis() - lastGPSvalid);
    //I don't know why I need this next line, but if it is not here the program never breaks out of this if condition
    //why is lastGPSvalid not reset in the next feedgps() function call when valid data is being received?
    lastGPSvalid = millis();
  }
}

bool feedgps() {
  while (nss.available()) {
    lastGPSdata = millis();
    if (gps.encode(nss.read())) {
      lastGPSvalid = millis();
      return true;
    }
  }
  return false;
}

In your first test block, you are looking to see if now (as reported by the millis function) minus the last time lastGPSdata was set is greater than 5 seconds. If it is, you refresh the LCD screen.

In this context, lastGPSdata would be better named lastLCDupdate.

After you update the LCD, you need to record when that happened, so that 5 seconds later, assuming no data arrives from the GPS, you can do it again.

lastGPSdata is not updated in the call to feedGPS if no data has been sent by the GPS.

Perhaps you should have three variables to hold time. One for when the GPS sent data (lastGPSdata), one for when the GPS sent data you could interpret (lastGPSvalid), and the third for when you last updated the LCD (lastLCDupdate).

feedGPS would set lastGPSdata and lastGPSvalid, and the main loop would set lastLCDupdate.

Your tests for when to update the lcd would be based on millis() and lastLCDupdate. What to display would be based on lastGPSdata and lastGPSvalid.

Paul, thanks a lot for the help.

I really do not understand why the code does not work as I think it should. When I run if (feedgps()), this should ALWAYS set lastGPSdata to now, when data is received, but it does not.

For example, if I comment out lastGPSdata = millis() in the main loop, the LCD continues to update with GPS data (I'm not sure how often, but its pretty quick), so that means that feedgps() updates lastGPSdata to now.

Now if I unplug the GPS serial line to Arduino for about 5 seconds, I get the no data message, but after plugging the serial line back in, lastGPSdata is never set to now again, so my Arduino code never recovers from lost data, and I don't understand why?

I originally tried something with updating the LCD once a second, but since I display time with seconds, I found that by using a timer on LCD update, I might miss a second or the seconds update would be noticeably erratic. That was probably just my sloppy timing in the code. However, I really want the LCD to display data as real-time as possible.

When you unplug the serial connection, it's possible that nss does not re-establish a connection. So, nss.available() never returns true. This would result in lastGPSdata never getting reset, which appears to be what you are seeing.

Perhaps if a certain time expires without data appearing, you need to close and re-open the serial connection.

nss does recover because if I "manually" reset lastGPSdata in my if condition, when I plug GPS serial back in, current GPS data comes back to the LCD (via gpsdump). For that to happen, nss.available must be true.

That's good news. Why don't you add some Serial.print statements to the feedgps routine to see where it's going in the various loops. Print out something to the effect that data is available just after the while(nss.available()) statement's opening brace. Add additional print statements inside each if block. Post the results.

I don't (yet) see why lastGPSdata isn't being reset.

This is interesting. If I comment the LCD.print line, the code works, printing GPS data to LCD recovers:

void runGPS() {

  if (feedgps()) {
    gpsdump(gps);
  }
  
  if ((millis() - lastGPSdata) > 2000) {
    //LCD.print("?x00?y0No GPS Data!!!  ");
    //LCD.print("?x00?y1"); LCD.print(millis() - lastGPSdata);
  }

  else if ((millis() - lastGPSvalid) > 2000) {
    //LCD.print("?x00?y0Wait for GPS Fix");
    //LCD.print("?x00?y1"); LCD.print(millis() - lastGPSvalid);
  }
}


// ***********************************************
// read GPS serial data and send to tinyGPS
// ***********************************************
bool feedgps() {
  while (nss.available()) {
    lastGPSdata = millis();
    if (gps.encode(nss.read())) {
      lastGPSvalid = millis();
      return true;
    }
  }
  return false;
}

But if I uncomment any 1 LCD.print line, it does not work at all.

So I think it is some sort of conflict with using two software serial ports (one for GPS and one for LCD).

I've proven something. If I redefine the GPS to use the hardware serial, the code works exactly as I expected, and it prints the accumalated time of lost fix or lost data...

void loop() {
  runGPS();
}

void runGPS() {
  
  if (feedgps()) {
    gpsdump(gps);
  }
  
  if ((millis() - lastGPSdata) > 2000) {
    LCD.print("?x00?y0No GPS Data!!!  ");
    LCD.print("?x00?y1"); LCD.print(millis() - lastGPSdata);
  }

  else if ((millis() - lastGPSvalid) > 2000) {
    LCD.print("?x00?y0Wait for GPS Fix");
    LCD.print("?x00?y1"); LCD.print(millis() - lastGPSvalid);
  }
}


// ***********************************************
// read GPS serial data and send to tinyGPS
// ***********************************************
bool feedgps() {
  while (Serial.available()) {
    lastGPSdata = millis();
    if (gps.encode(Serial.read())) {
      lastGPSvalid = millis();
      return true;
    }
  }
  return false;
}