TinyGPS++ - Failed checksum

Hello Forum!

My name is Chris and this is my first post here.
Iam very new to C++ and still learning a lot.

However...

I have an Arduino MEGA, an Adafruit 2,8" Touchscreen Shield v2 and an ITead Studio GPS shield with antenna and 3 meter cable.

Everything works very nice without any problems.
I get 10 satellites and very accurate position data (even in the house).

The problem starts only when i include the GPS sketch into my main sketch with all the
display drawing stuff.

As it seems its like the TinyGPS++ object doesn't get fed fast enough or doesn't get whole "sentences" anymore.

I wrote a little debug function and it shows 600 checksum errors compared to 100 passed checksums.
(Which also doesn't result in an output).
Maybe i must store a complete sentence in a buffer everytime and move on afterwards?

I just dont know how yet.

Thanks in advance, and if i should post my code please let me know.

Serial printing Is a blocking function and is a big chunk of time that must.be shared with the GPS sentence parsing. My answer was to print the support text and only update the changed data. I had a baro sensor an RH/temp sensor and tested two GPS Devices both with the same issues until I split the main data form or screen into drawing the static data (the labels) and when required data changed I updated that data as required. Since the RH data requires ~2.5 seconds between measurements I updated the barometer at twice the rate of the RH/Temp data and the display at the same time.
Time on the second, and the RH data once every 3 seconds. The BMP 085 was updated every 5 seconds.
This worked well with all but the Adafruit 1.8 display which requires 'clearing' or printing a blank field or box prior to printing new data I cleared the field first then wrote the data to the cleared area. The '7735 adafruit library only updates the new pixels it doesn't clear them first.

Doc

if i should post my code

Yes, you should :wink:

Okay, here we go.

I think i just need a loop that reads the GPS data until it gets a valid sentence
and then it can move on.

There must be something wrong with this while loop that i have.

#include <Adafruit_GFX.h>
#include <Adafruit_ILI9341.h>
#include <Adafruit_STMPE610.h>
#include <TinyGPS++.h>
#include <SPI.h>
#include <Wire.h>
#include <avr/pgmspace.h>

// Erstelle Objekte
Adafruit_STMPE610 ts = Adafruit_STMPE610(44);
Adafruit_ILI9341 tft = Adafruit_ILI9341(45,46);
TinyGPSPlus gps;

// Interface Variablen
int battPower        = 100;
int batteryChange    = 0;
int GPSSatChange     = 0;
int GPSErrChange     = 0;

// Serial Communication Variablen
char    serialInData[10];
int     serialIndex;
boolean serialStarted = false;
boolean serialEnded   = false;

// GPS Variablen
float latitude;
float longitude;
float aSeaLevel;
int UTCDay;
int UTCMonth;
int UTCYear;
int UTCHour;
int UTCMinute;
int UTCSecond;
boolean validGPS = false;
int timeout;

// Sprite Grafiken
//Batterie Icons
const unsigned char BattFrame [] PROGMEM = {
	Hidden
};
const unsigned char BattBar_1 [] PROGMEM = {
	Hidden
};
const unsigned char BattBar_2 [] PROGMEM = {
	Hidden
};
const unsigned char BattBar_3 [] PROGMEM = {
	Hidden
};
const unsigned char BattBar_4 [] PROGMEM = {
	Hidden
};
const unsigned char BattBar_5 [] PROGMEM = {
	Hidden
};
const unsigned char BattBar_6 [] PROGMEM = {
	Hidden
};
const unsigned char BattBar_7 [] PROGMEM = {
	Hidden
};
const unsigned char BattBar_8 [] PROGMEM = {
	Hidden
};
// GPS Icons
const unsigned char GPSBar_1 [] PROGMEM = {
	Hidden
};
const unsigned char GPSBar_2 [] PROGMEM = {
	Hidden
};
const unsigned char GPSBar_3 [] PROGMEM = {
	Hidden
};
const unsigned char GPSBar_4 [] PROGMEM = {
	Hidden
const unsigned char GPSBar_5 [] PROGMEM = {
	Hidden
};
const unsigned char GPSBar_6 [] PROGMEM = {
	Hidden
};
const unsigned char GPSBar_clear [] PROGMEM = {
	Hidden
};

void setup() {
	
	tft.begin();
	tft.fillScreen(ILI9341_WHITE);
	tft.setRotation(1);
	tft.fillRect(0,0,320,26,ILI9341_BLACK);
	tft.drawBitmap(260,1,GPSBar_1,24,24,ILI9341_WHITE);
	tft.setTextWrap(false);
	tft.setTextColor(ILI9341_BLACK,ILI9341_WHITE);
	tft.setTextSize(1);
	
	Serial.begin(9600);
	Serial1.begin(38400);                                // Standard Baud Rate des iTead GPS Shield
	Serial1.print("$PUBX,41,1,0007,0003,4800,0*13\r\n"); // Ändern der iTead Baud Rate
	Serial1.flush();
	delay(50);

	Serial1.begin(4800);                                 // Reset Serial Baud Rate
	Serial1.flush();
	delay(500);

}
void loop() {
	
	battPower = serialCom(battPower);
	batteryStatus();
	GPSStatus ();
	GUIUpdate();
	//deBug ();
}

// Funktionen
int serialCom (int battPower){
	
	while(Serial.available() > 0)
	{
		char aChar = Serial.read();
		if(aChar == '<')
		{
			serialStarted = true;
			serialIndex = 0;
			serialInData[serialIndex] = '\0';
		}
		else if(aChar == '>')
		{
			serialEnded = true;
		}
		else if(serialStarted)
		{
			serialInData[serialIndex] = aChar;
			serialIndex++;
			serialInData[serialIndex] = '\0';
		}
	}


	if(serialStarted && serialEnded)
	{
		// Convert the string to an integer
		int serialInput = atoi(serialInData);
		battPower   = serialInput;
		
		Serial.print("Eingabe war: ");
		Serial.println(serialInput);
		Serial.print("Batterie: ");
		Serial.println(battPower);
		
		// Get ready for the next time
		serialStarted = false;
		serialEnded   = false;
		serialIndex = 0;
		serialInData[serialIndex] = '\0';
		
	}

	return battPower;
}
void batteryStatus (){
	
	if (batteryChange != battPower){

		tft.drawBitmap(298,9,BattBar_8,24,16,ILI9341_BLACK);{tft.drawBitmap(298,9,BattFrame,24,16,ILI9341_WHITE); }
		if (battPower >= 1  && battPower <= 12) {tft.drawBitmap(298,9,BattBar_1,24,16,ILI9341_RED);   }
		if (battPower >= 13 && battPower <= 25) {tft.drawBitmap(298,9,BattBar_2,24,16,ILI9341_WHITE); }
		if (battPower >= 26 && battPower <= 37) {tft.drawBitmap(298,9,BattBar_3,24,16,ILI9341_WHITE); }
		if (battPower >= 38 && battPower <= 50) {tft.drawBitmap(298,9,BattBar_4,24,16,ILI9341_WHITE); }
		if (battPower >= 51 && battPower <= 62) {tft.drawBitmap(298,9,BattBar_5,24,16,ILI9341_WHITE); }
		if (battPower >= 63 && battPower <= 75) {tft.drawBitmap(298,9,BattBar_6,24,16,ILI9341_WHITE); }
		if (battPower >= 76 && battPower <= 87) {tft.drawBitmap(298,9,BattBar_7,24,16,ILI9341_WHITE); }
		if (battPower >= 88 && battPower <= 100){tft.drawBitmap(298,9,BattBar_8,24,16,ILI9341_WHITE); }
		batteryChange = battPower;
	}
}
void GPSStatus (){
	
	while (Serial1.available() >0)    // <-----  Here is the Problem!!
	{                                 // Maybe a loop that reads until a valid sentence is received?
		gps.encode(Serial1.read());
	}
	
	
	
	if  (Serial.available()==0)
	{
		timeout++;
	}
	if (millis() > 5000 && gps.charsProcessed() < 10)
	{
		Serial.println(F("No GPS detected!"));
		while(true);
	}
	
	latitude  = gps.location.lat();
	longitude = gps.location.lng();
	aSeaLevel = gps.altitude.meters();
	
	UTCDay    = gps.date.day();
	UTCMonth  = gps.date.month();
	UTCYear   = gps.date.year();
	
	UTCHour   = gps.time.hour();
	UTCMinute = gps.time.minute();
	UTCSecond = gps.time.second();
}
void GUIUpdate(){
	
	
	// Location
	if (gps.location.isValid() && gps.location.isUpdated())      // VALID Location ?
	{
		tft.setCursor(5, 40);
		tft.print("Lat  : ");
		tft.print(latitude, 6);
		tft.print("        ");
		tft.setCursor(5, 50);
		tft.print("Long : ");
		tft.print(longitude, 6);
		tft.print("        ");
		tft.setCursor(5, 60);
		tft.print("Level: ");
		tft.print(aSeaLevel);
		tft.print("        ");
		GPSErrChange == 0;
	}
	
	// Date
	if (gps.date.isValid() && gps.date.isUpdated())
	{
		tft.setCursor(5, 70);
		tft.print("Date : ");
		tft.print(UTCDay);
		tft.print(".");
		tft.print(UTCMonth);
		tft.print(".");
		tft.print(UTCYear);
		tft.print("  ");
		GPSErrChange == 0;
	}
	
	// Time
	if (gps.time.isValid() && gps.time.isUpdated())
	{
		tft.setCursor(5, 80);
		tft.print("Time :               ");
		tft.setCursor(5, 80);
		tft.print("Time : ");
		if (UTCHour < 10) tft.print("0");
		tft.print(UTCHour);
		tft.print(":");
		if (UTCMinute < 10) tft.print("0");
		tft.print(UTCMinute);
		tft.print(":");
		if (UTCSecond < 10) tft.print("0");
		tft.print(UTCSecond);
		GPSErrChange == 0;
	}
	
	if (GPSSatChange != gps.satellites.value()){                 // Update HUD Satellite Icon
		
		tft.drawBitmap(260,1,GPSBar_clear,24,24,ILI9341_BLACK);
		if (gps.satellites.value() == 0  ) {tft.drawBitmap(260,1,GPSBar_1,24,24,ILI9341_WHITE);   }
		if (gps.satellites.value() >= 1  && gps.satellites.value() <=  2) {tft.drawBitmap(260,1,GPSBar_2,24,24,ILI9341_WHITE);   }
		if (gps.satellites.value() >= 3  && gps.satellites.value() <=  4) {tft.drawBitmap(260,1,GPSBar_3,24,24,ILI9341_WHITE);   }
		if (gps.satellites.value() >= 5  && gps.satellites.value() <=  6) {tft.drawBitmap(260,1,GPSBar_4,24,24,ILI9341_WHITE);   }
		if (gps.satellites.value() >= 7  && gps.satellites.value() <=  9) {tft.drawBitmap(260,1,GPSBar_5,24,24,ILI9341_WHITE);   }
		if (gps.satellites.value() >= 10 ) {tft.drawBitmap(260,1,GPSBar_6,24,24,ILI9341_WHITE);   }
		
		
		tft.setCursor(5, 30);                                    // Update Satellites Info
		tft.print("Satellites: ");
		tft.print(gps.satellites.value());
		tft.print("             ");
		
		GPSSatChange = gps.satellites.value();
	}


	if (GPSErrChange == 0 && gps.satellites.value() == 0) {      // INVALID GPS DATA TFT DISPLAY (Once)
		
		tft.setCursor(5, 30);                                    // Update Satellites Info
		tft.print("Satellites: No Signal!");
		tft.setCursor(5, 40);
		tft.print("Lat  : --.------");
		tft.setCursor(5, 50);
		tft.print("Long : --.------");
		tft.setCursor(5, 60);
		tft.print("Level: --.------");
		
		GPSErrChange = 1;
	}





}
void deBug (){
	
	tft.setCursor(5, 140);
	tft.print("------- Debugging -------");
	
	tft.setCursor(5, 170);
	tft.print("Error Switch: ");
	tft.print(GPSErrChange);
	
	tft.setCursor(5, 180);
	tft.print("Satellites  : ");
	tft.print(gps.satellites.value());
	
	tft.setCursor(5, 190);
	tft.print("Chars proc  : ");
	tft.print(gps.charsProcessed());
	
	tft.setCursor(5, 200);
	tft.print("Sent w Fix  : ");
	tft.print(gps.sentencesWithFix());
	
	tft.setCursor(5, 210);
	tft.print("Failed Check: ");
	tft.print(gps.failedChecksum());
	
	tft.setCursor(5, 220);
	tft.print("Passed Check: ");
	tft.print(gps.passedChecksum());
	
	
	
	tft.setCursor(150, 170);
	tft.print("Serial1 read: ");
	tft.print(Serial1.read());
	tft.print("    ");
	
	tft.setCursor(150, 180);
	tft.print("Seria1 avail: ");
	tft.print(Serial1.available(),DEC);
	tft.print("    ");
	
	tft.setCursor(150, 190);
	tft.print("Timeouts    : ");
	tft.print(timeout);
	tft.print("    ");

}
		gps.encode(Serial1.read());

The encode() method returns a value. Quit discarding it.

It returns true when the end of a sentence is received, at which time you should break out of the loop and use the data.

Hello.

I read already a lot in the last days and found plenty of code for GPS parsing.
Like this:

// This sketch displays information every time a new sentence is correctly encoded.
  while (ss.available() > 0)
    if (gps.encode(ss.read()))
      displayInfo();

I don't completly understand a few things in this code.

  1. Why are there no {} in "while" and "if" ? Is this kind of sloppy shortcut programming?
  2. Why is my "gps.encode(Serial1.read());" also exiting the "while" loop? Because it gets smaller than zero?
  3. Even if i use " if (gps.encode(Serial1.read()))" in the while loop my code doesn't work as it should.
while (Serial1.available() >0)               // As long as data arrives Do the loop
	{                                 
		if(gps.encode(Serial1.read()));       // If TRUE exit the while loop
	}

This makes me think.
What if the MEGA arrives in the while loop when an already started, incomplete sentence was sent?
Then it still does the loop because serial1.available is still > 0
Then it gets the encode() = TRUE and exits the loop
The result is a incomplete sentence of GPS data.

Am i right so far?

  1. Why are there no {} in "while" and "if" ? Is this kind of sloppy shortcut programming?

In my mind, yes. The {} are only required when there is more than one statement in the block. But, I always use them for for and while statements, and almost always use them for if statements.

  1. Why is my "gps.encode(Serial1.read());" also exiting the "while" loop? Because it gets smaller than zero?

It doesn't. It simply suspends reading data while if calls displayInfo().

  1. Even if i use " if (gps.encode(Serial1.read()))" in the while loop my code doesn't work as it should.

Because you are not using it correctly.

		if(gps.encode(Serial1.read()));       // If TRUE exit the while loop

Why do you assume that? What you do if the statement is true is ;. What you do if the statement is false is nothing. ; and nothing amount to the same thing.

		if(gps.encode(Serial1.read()))
                  break;       // If TRUE exit the while loop

Now, it will exit the while loop.

Thank you Paul for explaining essential stuff to a noob.

It doesn't. It simply suspends reading data while if calls displayInfo().

This point i dont understand.

I got this code from the TinyGPS++ website.
I found it a little "ugly" to put the while loop in the main loop.
So i made a function.

The thing is that i dont call any function within the "while" or "if" loop.
Im sure there is a logical explaination why it still exits.

But i see the difference between the code on the TinyGPS++ website
and mine.

He put the gps.encode() loop in the main loop and calls a function which suspends the while loop
but i made a function instead with the gps.encode() loop inside which didnt have a break;
(Im just puzzled why it still leaves the loop).

However, even if i use the break; now it doesent seem to work.
After several loops my debugging shows that the Serial1.available() goes down to -1.

I was reading trough some comments on the TinyGPS++ website and i found this code:

static char GpsSentence[100];
static int GpsOffset = 0;
while (Serial1.available())
	{
	char c = Serial1.read();
	if (c == '\n') // newline?
	{
	GpsSentence[GpsOffset] = 0;
	GpsOffset = 0;
	Serial.print("Got a new sentence: ");
	Serial.println(GpsSentence);
	}
	else
	{
	GpsSentence[GpsOffset] = c;
	GpsOffset++;
	gps.encode(c);
	}
	}

With this it seems to work. (Until i unplug my USB and plug my 9 Volt wall power supply)

But i guess this is another story.... :slight_smile:

Okay, after further testing even this new Parsing loop seems not to work.

If i add some "heavy" tft.print(); stuff
the GPS parsing gives back only crap.

I dont know why this is happening. But it does....

Maybe this loop is still not waiting for a sentence to begin AND to end before passing the data to the
gps.encode(); object...

blaxxun:
If i add some "heavy" tft.print(); stuff
the GPS parsing gives back only crap.

I dont know why this is happening. But it does....

It sounds like you're taking too long to get back to service the serial port before data is getting lost (or perhaps interrupts are being disabled too long?)

If you look at some of the TinyGPS/TinyGPS++ examples (such as 'FullExample') you'll see that it's not uncommon to call encode() from your print functions or after repeated calls to print.

So try to add some extra calls to encode() and see if that helps.

Just a thought.

Regards,

Brad
KF7FER

Im sure there is a logical explaination why it still exits.

Because the Arduino can read serial data faster than it arrives. When it has read every available, it ends the while loop.

I corrected a massive error in my first answer... there are 6 or 7? total massages /second @ 4800 IIRC and the function for printing for both the Adafruit '7735 controller and most if not all of the UTFT library is blocking thus one of the issues. If the NMEA 0183 serial data is decoded and and the print function then displays the data either an interrupt is required or a simple solution as I wrote in my first reply it is necessary to print the whole screen in setup() and then ONLY print the data (Most Important) returned in the loop() that has CHANGED. while the GPS sentences are short... they still call the isr and will make printing stall (and everything else) stop until the isr completes, in this case again IIRC ... that's about 500 mS.. the easy answer for all of this is a state machine IMO. The short answer for all of this is a is to make everything work around 1 second as an interrupt and keep the functions that are required from a flag in the ISR as short as possible. Since the TimeGPS++ library isn't dependent on the 'age' of the GPS sentence as a whole it will anywhere it can hear.. GPS data as indicated by valid checksums. It Will return accurate time. AS I said before my Skylabs SKM53 can be instructed to listen to the most useful for time/static location data which are GGA and RMC. I bring it up again to point out that it should be looked for on the Mfr's datasheet for the device you own as it will free up a large chunk of time.
I made these measurements with a Saleae logic analyzer last year and I'm working from memory... IHTH.

Doc