RE: TinyGPS++ Library Issues

Hi Gang

I've recently purchased a GPS to use with my Arduino UNO. Unfortunately, I've encountered two problems.

  1. If I run the example sketch 'Device Example' on my Windows 8 machine I get the following via the Serial Monitor and nothing else.

DeviceExample.ino
A simple demonstration of TinyGPS++ with an attached GPS module
Testing TinyGPS++
by Mikal Hart

If I run the same sketch on my friend's Mac it works fine?

  1. I'd like to play a tone that reflects my position relative to home. I've written the following;
#include <SoftwareSerial.h>
#include <TinyGPS++.h>

SoftwareSerial ss(2,3);
TinyGPSPlus gps;

double courseToMe = 0;
double homeLat = 48.85823;
double homeLng = 2.29438;

void setup(){
  Serial.begin(115200);
  ss.begin(38400);
  while(!gps.location.isValid()){
    while(ss.available() > 0)
      gps.encode(ss.read());
    Serial.println("NOT VALID");
    delay(1000);
  }
  Serial.println("VALID");
}

void loop(){
  while(ss.available() > 0)
    gps.encode(ss.read());
  courseToMe= TinyGPSPlus::courseTo(homeLat, homeLng, gps.location.lat(), gps.location.lng());
  tone(10, 31 + (2 * courseToMe));
  delay(1000); 
}

I'm getting the most peculiar behavior. The tone pulses. :frowning:

Can anyone suggest a solution to either problem?

Cheers

Jase :slight_smile:

You are calling delay(), you are thus losing nearly all the output from
your GPS. Remove that call. Look at blinkWithoutDelay example if you want
to limit changes to the tone's frequency to regular intervals without stalling the
whole Arduino.

In addition, the encode() method returns true (the last character completed a sentence) or false (the data the instance has now is incomplete). It makes no sense to call lat() and lng() when encode() has returned false.

MarkT:
You are calling delay(), you are thus losing nearly all the output from
your GPS. Remove that call. Look at blinkWithoutDelay example if you want
to limit changes to the tone's frequency to regular intervals without stalling the
whole Arduino.

Hi MarkT

Thanks for the reply. I will check out the 'Blink Without Delay' example.

Cheers

Jase :slight_smile:

PaulS:
In addition, the encode() method returns true (the last character completed a sentence) or false (the data the instance has now is incomplete). It makes no sense to call lat() and lng() when encode() has returned false.

Hi PaulS

Sorry to take so long to reply. I've been trying to get my head around your suggestion. Am I correct in thinking that I should change the following;

while(ss.available() > 0)
     gps.encode(ss.read());

to

while (ss.available() > 0)
     if (gps.encode(ss.read()))

Specifically, the line 'gps.encode(ss.read())' performs two functions?

  1. It encodes and stores the NMEA sentence.

  2. It checks its validity.

As always I appreciate your help.

Cheers

Jase :slight_smile:

Am I correct in thinking that I should change the following;

Yes.

  1. It encodes and stores the NMEA sentence.

I'm not sure why Mikal Hart chose the name encode() for the function, since it really isn't encoding anything. What it does is append the character passed in to the array of characters, and checks to see if that character represents the end of a sentence.

PaulS:

Am I correct in thinking that I should change the following;

Yes.

  1. It encodes and stores the NMEA sentence.

I'm not sure why Mikal Hart chose the name encode() for the function, since it really isn't encoding anything. What it does is append the character passed in to the array of characters, and checks to see if that character represents the end of a sentence.

Hi PaulS

I was wondering if you could guide me a little with my setup routine? I've currently got the following.

void setup(){ 
  ss.begin(38400);  //SPEED OF MY GPS
  while(!gps.location.isValid())  //CHECK THAT NO GPS LOCK IS TRUE
    while(ss.available() > 0)  //GET DATA FROM SERIAL PORT IF AVAILABLE
      if(gps.encode(Serial.read())){  //CHECK WE HAVE A COMPLETE SENTENCE
        Serial.println("NOT VALID");  //PRINT NOT VALID
        delay(1000);  //WAIT A SECOND BEFORE TRYING AGAIN
      }  //WE NOW HAVE GPS LOCK
  homeLat = gps.location.lat(); //RECORD LAT
  homeLng = gps.location.lng();  //RECORD LON
  Serial.print(homeLat, 6);  //PRINT LAT
  Serial.print(", ");  //PRINT ,
  Serial.println(homeLng, 6);  //PRINT LNG
}

Although the above appears to work, I just want to make sure that my lat and lng are correct before proceeding to the main loop. Really appreciate the help.

Cheers

Jase :slight_smile:

I have real problems reading your code. Every while statement should have { and }. Every { goes on a new line, in my opinion.

Comments do not, in my opinion, go on the line with the code. If there is a need to document something with comments, the comments are important enough to precede the code.

    while(ss.available() > 0)  //GET DATA FROM SERIAL PORT IF AVAILABLE
      if(gps.encode(Serial.read())){  //CHECK WE HAVE A COMPLETE SENTENCE
        Serial.println("NOT VALID");  //PRINT NOT VALID
        delay(1000);  //WAIT A SECOND BEFORE TRYING AGAIN
      }  //WE NOW HAVE GPS LOCK

If the character that was read completes a sentence, encode returns true, so you print NOT VALID. I can't imagine why.

If the character completes a sentence, then is the time to check that the sentence included enough data to be a valid location. If that's the case, you should break out of the while data is available loop, right away, not a second later. If the character does not complete a sentence, you most certainly do NOT want to wait a second to read another character.

Therefore, that delay() is completely misguided.

Although the above appears to work

For some definition of work, I guess.

I just want to make sure that my lat and lng are correct before proceeding to the main loop.

Well, that's not the way to do it.

Hi PaulS

OK, how about the following?

void setup(){ 
  ss.begin(38400); 
  while(ss.available() > 0)
  { 
    if(gps.encode(Serial.read()))
    {
      if(gps.location.isValid())
      {
        homeLat = gps.location.lat();
        homeLng = gps.location.lng();
        break;
      }
    }
    Serial.println("NOT VALID");
  }
}

Will the 'while(ss.available() > 0)' keep looping until there is sufficient data? I really hope this is better.

Cheers

Jase :slight_smile:

    if(gps.encode(Serial.read()))

There is data on the software serial port, so read from the hardware serial port...

I really hope this is better.

Hate to rain on your parade...

Now, you are going to print "NOT VALID after every character read, until the end of the sentence containing valid data is read.

You are getting closer, though. Spitting distance, even.

Hi PaulS

I hope this is on the money.

Cheers

Jase :slight_smile:

void setup(){ 
  ss.begin(38400); 
  while(ss.available() > 0)
  { 
    if(gps.encode(ss.read())
    {
      if(gps.location.isValid())
      {
        homeLat = gps.location.lat();
        homeLng = gps.location.lng();
        break;
      }
      Serial.println("NOT VALID");
    }
  }
}

Hi PaulS

Sorry, slightly revised. It's making more sense.

Cheers

Jase :slight_smile:

#include <SoftwareSerial.h>
#include <TinyGPS++.h>

SoftwareSerial ss(2, 3);
TinyGPSPlus gps;

double homeLat = 0;
double homeLng = 0;

void setup(){ 
  ss.begin(38400); 
  while(ss.available() > 0)
  { 
    if(gps.encode(ss.read()))
    {
      if(gps.location.isValid())
      {
        homeLat = gps.location.lat();
        homeLng = gps.location.lng();
        break;
      }
      else
      {
      Serial.println("NOT VALID");
      }
    }
  }
}

void loop(){
}

There may be no data available immediately after calling ss.begin(). It may (probably is) necessary to delay() a bit to give the GPS time to wake up and start sending data.

Otherwise the code looks good now.

Hi PaulS

Would a second be sufficient? I'm unsure where and when I can use delay() when dealing with serial data? Any hard and fast rules?

void setup(){
  ss.begin(38400);
  delay(1000); 
  while(ss.available() > 0)

Thanks so much for your help.

Cheers

Jase :slight_smile:

Hi Gang

I've run the code but it's not working. Watching the serial monitor it seems to jump out of the first 'while' loop after one run.

NOT VALID SETUP
NOT VALID LOOP
NOT VALID LOOP
NOT VALID LOOP

My code is as follows;

#include <SoftwareSerial.h>
#include <TinyGPS++.h>

SoftwareSerial ss(2, 3);
TinyGPSPlus gps;

double homeLat = 0;
double homeLng = 0;

void setup(){ 
  ss.begin(38400); 
  while(ss.available() > 0)
  { 
    if(gps.encode(ss.read()))
    {
      if(gps.location.isValid())
      {
        homeLat = gps.location.lat();
        homeLng = gps.location.lng();
        Serial.print(homeLat, 6);
        Serial.print(", ");
        Serial.println(homeLng, 6);
        break;
      }
      else
      {
      Serial.println("NOT VALID SETUP");
      }
    }
  }
}

void loop(){
  Serial.println("NOT VALID LOOP");
}

I want to wait until I have valid GPS coordinates before proceeding. The following line should ensure my sketch keeps running indefinitely as the software serial buffer will be flooded with NMEA sentences.

while(ss.available() > 0)

The only way out from my understanding is if the following line returns TRUE.

if(gps.location.isValid())

Then the following line get us out of the while loop.

break;

It just doesn't make sense to me. :~

Any help would be greatly appreciated.

Cheers

Jase :slight_smile:

  while(ss.available() > 0)
  { 
    if(gps.encode(ss.read()))
    {
      Serial.println("Sentence complete");
      // Add more print statements here to dump the GPS data
      // The class has several. gps.charsProcessed(), sentencesWithFix(),
      // failedChecksum() and/or passedChecksum()

      if(gps.location.isValid())
      {

What do you see then?

Personally, I think that the class could use a method to dump the data in the private field named term to some output Stream, like Serial, to make debugging easier.

Hi PaulS

Thanks for getting back to me. I'm not quite sure what you would like me to do? I've written the following which works. I must admit I do find the number of conditionals confusing. Specifically where each one exits. I really appreciate your perseverance.

Cheers

Jase :slight_smile:

void setup(){
  ss.begin(38400);
  while(!gps.location.isValid())
  {
    while(ss.available() > 0)
    {
      if(gps.encode(ss.read()))
      {
        if(gps.location.isValid())
        {
          homeLat = gps.location.lat();
          homeLng = gps.location.lng();
        }
      }
    }
  }
}

Cheers

Jase :slight_smile:

I'm not quite sure what you would like me to do? I've written the following which works.

Well, then, nothing. You've determined for yourself what the problem was. I was just suggesting more debug output to lead you to see the problem, and the solution. But, you got there already, and hopefully you understand why the works, and why that order is necessary.

Hi PaulS

I hope I haven't offended. I seriously wasn't sure what you were suggesting. I understand now and will try running your code.

To be honest I'm still not happy with what I've written. There must be a cleaner way. Just thinking out loud.

Really appreciate your input and insight.

Cheers

Jase :slight_smile:

To be honest I'm still not happy with what I've written. There must be a cleaner way. Just thinking out loud.

It's good. If you want to stay in setup until you get a valid location, you need to first determine if you have a valid location. If not, you spin reading any available data. Obviously this while loop will be execute many times, before the complete sentence arrives. On any given execution, the loop will probably only iterate a time or two. Serial data can be read from the buffer far faster than it can be received and put in the buffer. Just like you can read far faster than I can type.

You still should have a break; statement after setting the home location, so that the code doesn't start reading the next sentence, if there is data pending, before the outer while discovers that the location is valid.