Parsing GPS Altitude Data for 16x2 LCD Display

I'm trying to do the following:
Using a Digispark as the controller, pull data from a GPS (EM-406A - https://www.sparkfun.com/datasheets/GPS/EM-406A_User_Manual.PDF) via the SoftwareSerial and outputting to a Serial LCD (https://www.sparkfun.com/products/9395). Digispark doesn't have hardware serial. According to this post (https://digistump.com/board/index.php?topic=927.0) there is some software serial written into TinyCore that the Digispark uses, so I'm using that for output to the LCD.

The Digispark doesn't have enough memory to use TinyGPS - nor do I really need that. The only GPS data I'm interested in is the altitude. (I'm trying to build a small, lightweight altimeter to put on a cheap quadcopter - WLtoys v222 - I happened to already have all these parts already).

I think I'm close - the logic seems to be fine and the hardware seems to work as expected. The LCD works fine and the GPS seems to be acquiring a position (based on it's blinking LED). But, I suspect there's something wrong in the code and I'm not very strong in that area. I modified the code from here: http://www.cooking-hacks.com/documentation/tutorials/arduino-gps#nmea_arduino

Can someone help me out and take a look at the code to see what might be happening? Or sugget an alternative way to get the Altitude data? You'll see that I added some "DEBUG" prints in the code. The farthest it seems to get is to print "Waiting GPS data..." and then "DEBUG1". Nothing gets printed after that. The code is below.

I appreciate any help! Thanks!

// From http://www.cooking-hacks.com/documentation/tutorials/arduino-gps#nmea_arduino

#include <SoftwareSerial.h>

#define txPin 5      //tx pin in GPS connection
#define rxPin 3      //rx pin in GPS connection

// Set up the GPS serial port
SoftwareSerial gps = SoftwareSerial(rxPin, txPin);

byte byteGPS = 0;  
int i = 0;
int state = 0;
char dataGPG[100]="";
char *pch;
char *GGA[15];
int sat = 0;

void setup()
{
//setup for Serial Port
Serial.begin(9600);

//setup for GPS Serial Port  
pinMode(rxPin, INPUT);
pinMode(txPin, OUTPUT);
gps.begin(4800);


//setup satellites signal
pinMode(1, OUTPUT);
digitalWrite(1, LOW);     // Turn off the led until a satellite signal
}


void loop()
{
Serial.write(254); // clear display
Serial.write(1);

Serial.write(254); // cursor to beginning of first line
Serial.write(128);

Serial.print("Sleeping for 60 seconds");
delay(60000);

Serial.write(254); // clear display
Serial.write(1);

Serial.write(254); // cursor to beginning of first line
Serial.write(128);

Serial.print("Waiting GPS data...");

// Prepare all for reading GPS Serial Port
memset(dataGPG, 0, sizeof(dataGPG));    // Remove previous readings
byteGPS = 0;                            // Remove data
byteGPS = gps.read();                   // Read the byte that is in the GPS serial port
delay(1000);

Serial.print("DEBUG1");
delay(1000);

// Find the desired string
while(byteGPS != '

)
{
byteGPS = gps.read();
}

Serial.print("DEBUG2");
delay(1000);

// Save the string in an array
i=1;
dataGPG[0] = '


;

Serial.print("DEBUG3");
delay(1000);


while(byteGPS != '*' )
{
byteGPS = gps.read();
dataGPG[i]=byteGPS; 
i++; 
}

dataGPG[i]= '\0';
string();                                 // Call to the function that manipulates our string

Serial.print("DEBUG4");
delay(1000);
}



/*
This function will allow us to identify the data we need to get the longitude, latitude ...
*/

void string()
{
i=0;
memset(GGA, 0, sizeof(GGA));          // Remove previous readings

pch = strtok (dataGPG,",");

// Analyze the saved interval in pch to see if it the needed string
if (strcmp(pch,"$GPGGA")==0)
{
while (pch != NULL)
{
pch = strtok (NULL, ",");
GGA[i]=pch;    
i++;
} 

plot(GGA,"$GPGGA");         // Call to the function that is going to display the data
}
}

/*
This function organize the gps data received for printing in the serial monitor.
*/

void plot(char **GGAPrint, char *trama)
{
state = atoi(GGAPrint[5]);
sat = atoi(GGAPrint[6]);

if(trama=="$GPGGA" && state==1)
{
digitalWrite(1, HIGH);            // Then there are satellites, the LED switch ON

//Serial.println("");
//Serial.println("----------------------------------------------");
//Serial.print("UTC Hour -> ");
//Serial.println(GGAPrint[0]);
//Serial.print("Latitude -> ");
//Serial.print(GGAPrint[1]);
//Serial.println(GGAPrint[2]);
//Serial.print("Longitude -> ");
//Serial.print(GGAPrint[3]);
//Serial.println(GGAPrint[4]);
//Serial.print("GPS quality: 0=null; 1=GPS fixed -> ");
//Serial.println(GGAPrint[5]);
//Serial.print("Number of satellites -> ");
//Serial.println(sat);
//Serial.print("Horizontal Dilution of Precision -> ");
//Serial.println(GGAPrint[7]);
Serial.print("Antenna altitude -> ");
Serial.print(GGAPrint[8]);
Serial.println(GGAPrint[9]);
//Serial.print("Geoid Separation -> ");
//Serial.print(GGAPrint[10]);
//Serial.println(GGAPrint[11]);
//Serial.println("----------------------------------------------");
//Serial.println("");

}

else                                // If no satellite connection
{
digitalWrite(1, LOW);                                              // Turn off the LED
//Serial.println("");
//Serial.println("-----------------------------");
//Serial.print("|--- Satellites Used -->");
//Serial.print(sat);
//Serial.println(" |");
Serial.println("Waiting location");
//Serial.println("-----------------------------");
//Serial.println("");
}
}

Adding a picture link...

All you need is altitude ?
Use a digital pressure sensor (0,5m resolution) , not a GPS ( 10s of meters)

[...]
Serial.print("DEBUG1");
delay(1000);

// Find the desired string
while(byteGPS != '

I'd put a debug print inside of the while loop. If you have a baud rate mismatch you'll never get out of the loop. Plus it might be interesting to see what you're getting back.

  while(byteGPS != '*' )
  {
    byteGPS = gps.read();
    dataGPG[i]=byteGPS; 
    i++; 
  }

You should really make sure that you don't overflow the buffer. What happens if an "*" gets dropped?

I'd also add more debug prints in "string" (you should also add protection to not overrun GGA here too) and "plot" when you get there (I'd be worried that atoi takes a string as input to convert so I don't think you'll be getting the correct values in 'plot' for 'state' or 'sat' but I could be wrong).

That should at least get you to the next problem.

Hope this helps,

Brad
KF7FER

PS I was going to suggest what knut_ny said; something like the BMP085 might work and will be much cheaper.)
{
byteGPS = gps.read();
}

Serial.print("DEBUG2");
delay(1000);


I'd put a debug print **inside** of the while loop. If you have a baud rate mismatch you'll never get out of the loop. Plus it might be interesting to see what you're getting back.

§DISCOURSE_HOISTED_CODE_1§


You should really make sure that you don't overflow the buffer. What happens if an "*" gets dropped?

I'd also add more debug prints in "string" (you should also add protection to not overrun GGA here too) and "plot" when you get there (I'd be worried that atoi takes a string as input to convert so I don't think you'll be getting the correct values in 'plot' for 'state' or 'sat' but I could be wrong).

That should at least get you to the next problem.

Hope this helps,

Brad
KF7FER

PS I was going to suggest what knut_ny said; something like the BMP085 might work and will be much cheaper.

knut_ny:
All you need is altitude ?
Use a digital pressure sensor (0,5m resolution) , not a GPS ( 10s of meters)

Thanks - but like I said I already have the GPS on hand. I'm not too worried about exact accuracy as I just would like an "idea" of how high the quad is going. I'm also hoping for some decent accuracy since I'll be outdoors, away for any obstacles, and I'll be moving fairly slowly. But, I'll look into the pressure sensor as well.

Oh yeah I forgot...

 if(trama=="$GPGGA" && state==1)

You need to use strcmp or some other string function to compare strings. This won't give you the desired results.

Regards,

Brad

Great idea. I added the following:

Serial.print("DEBUG1");
delay(1000);

// Find the desired string
while(byteGPS != '

And when it gets to that spot the LCD starts displaying an unreadable character over and over and over again really fast - which doesn't make since because I added a delay. Here's a picture:

Thanks also for your other suggestions. I'll keep poking at it.)
Serial.print ("DEBUG9");
delay(1000);
{
byteGPS = gps.read();
}

Serial.print("DEBUG2");
delay(1000);


And when it gets to that spot the LCD starts displaying an unreadable character over and over and over again really fast - which doesn't make since because I added a delay. Here's a picture:
https://www.dropbox.com/s/g7i3shsfh63k43o/2014-03-27%2011.43.20.jpg

Thanks also for your other suggestions. I'll keep poking at it.

tycen:
Thanks also for your other suggestions. I'll keep poking at it.

I can't view the image but if it's a "y" with an umlaut that's because you're reading serial data and there is nothing to read (usually it's common to do a call to "available" to make sure data is pending before you read. Or read does return the number of characters read so you might want to make sure it's greater than zero).

If you're getting the "y" / umlaut I'd suggest perhaps you've got send/receive wired backwards?

If you're getting other characters try a different baud rate.

Hope this helps,

Brad
KF7FER

GPS altitude data is not very reliable.

The only GPS data I'm interested in is the altitude.

The Adafruit GPS lib inherits from the Serial class and is very easy to gut to reduce resources and just parse what you need. I just wanted time / date from the RMC sentences in my clock. Code here:
http://forum.arduino.cc/index.php?topic=199216.0

Ray