Add Altitude to GPS sketch

The sketch below displays Lon, Lat, and time from a VK2828U7GLF.
How do I add Altitude?

#include <SD.h>

// GPS with Date/Time/Lon/Lat 1602 LCD Display

// Board: Arduino/Genuine Uno

// http://allaboutee.com/2012/12/03/arduino-gps-tutorial-get-latitude-and-longitude-coordinates/

#include <Wire.h>
#include <SoftwareSerial.h>
#include <TinyGPS.h>
#include <SPI.h>
#include <SD.h>

const int chipSelect = 10;

File dataFile;
#define LOGFILE “gpslog.txt”

long lat,lon; // create variable for latitude and longitude object

SoftwareSerial gpsSerial(2, 3); // create gps sensor connection

TinyGPS gps; // create gps object

#include <LiquidCrystal_I2C.h>

// set the LCD address to 0x27 for a 16 chars 2 line display
// A FEW use address 0x3F
// Set the pins on the I2C chip used for LCD connections:
// addr, en,rw,rs,d4,d5,d6,d7,bl,blpol

LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); // Set the LCD I2C address

int gpsPin = 4; // GPS enable connected to digital pin 4

//_________________________________________

void setup(){
Serial.begin(9600); // connect serial
gpsSerial.begin(9600); // connect gps sensor

lcd.begin(16,2); // initialize the lcd for 16 chars 2 lines, turn on backlight

lcd.backlight(); // finish with backlight on

Serial.println(“Starting SDCard reader and card”);
pinMode(chipSelect, OUTPUT);
pinMode(SS, OUTPUT);

if (!SD.begin(chipSelect)) {
Serial.println(“SD Card initialization failed!”);
return;
}

// Serial.println(“Opening logfile for write.”);

dataFile = SD.open(LOGFILE, FILE_WRITE); // Open up the file we’re going to log to!
if (! dataFile) {
// Serial.println(“error opening log file”); // Wait forever since we cant write data
while (1) ; // Wait forever since we cant write data
}

pinMode(gpsPin, OUTPUT); // sets the digital pin as output

}

//_________________________________________

void loop(){

digitalWrite(gpsPin, LOW); // sets the GPS on

while(gpsSerial.available()){ // check for gps data
if(gps.encode(gpsSerial.read())) { // encode gps data

gps.get_position(&lat,&lon); // get latitude and longitude

//date and time

int year;
byte month, day, hour, minute, second, hundredths;
gps.crack_datetime(&year, &month, &day, &hour, &minute, &second, &hundredths);

// Print date and time
Serial.print(“Date: “);
Serial.print(day, DEC); Serial.print(”/”);
Serial.print(month, DEC); Serial.print("/");
Serial.print(year);

Serial.print(" Time: “); Serial.print(hour, DEC); Serial.print(”:");

Serial.print(minute, DEC); Serial.print(":"); Serial.print(second, DEC);

// Serial.print("."); Serial.print(hundredths, DEC);

//position

float lonx = lon;
float latx = lat;

Serial.print(" Lon: ");
Serial.print(lonx/1000000,5);

Serial.print(" Lat: ");
Serial.println(latx/1000000,5);

//_______________________________

//write gpx file
// open the file. note that only one file can be open at a time,
// so you have to close this one before opening another.

File dataFile = SD.open(“gpslog.txt”, FILE_WRITE);

// if the file is available, write to it:

if (dataFile) {

dataFile.print(" <trkpt lon="");
dataFile.print(lonx/1000000,8);
dataFile.print("" lat="");
dataFile.print(latx/1000000,8);
dataFile.println("">");

dataFile.print(" “);
dataFile.print(year);
dataFile.print(”-");
dataFile.print(month, DEC);
dataFile.print("-");
dataFile.print(day, DEC);
dataFile.print(“T”);
dataFile.print(hour, DEC);
dataFile.print(":");
dataFile.print(minute, DEC);
dataFile.print(":");
dataFile.print(second, DEC);
dataFile.print(".000Z");
dataFile.println("");
dataFile.println(" ");

dataFile.close();}

// Send to LED

lcd.setCursor(0,0); //Start at character 0 on line 0
lcd.print("Lat: ");
lcd.print(latx*-1/1000000,5); // print latitude

lcd.setCursor(0,1);
lcd.print("Lon: ");
lcd.print(lonx/1000000,5); // print longitude

// digitalWrite(gpsPin, HIGH); // sets the GPS off

delay(29000);

}
}
}

Have a look into the TinyGPS.h file. The TinyGPS class has a couple of public functions. Just like

gps.get_position(&lat,&lon);

there is a function called altitude.

long altitude = gps.altitude();

Now you just need to add the value into the part that creates the data file.

There is other stuff, like speed that might be interesting for you as well.

Hint: For source code please use the </> source button instead of the quote button. Italic is not as easy to read as the source code window. :slight_smile:

Thank you Klaus_K

I added the lines marked //added 18nov

gps.get_position(&lat,&lon); // get latitude and longitude

long altitude = gps.altitude(); //added 18nov

float lonx = lon;
float latx = lat;
float altx = alt; //added 18nov

Serial.print(" ele: "); //added 18nov
Serial.print(alt/100000,3); //added 18nov

This "worked" except the altitude = 0, whereas the value should be something like 210m

Am I on the right track??

    Serial.print(alt/100000,3);    //added 18nov

Shouldn't that be:

    Serial.print(altx/100000,3);    //added 18nov

First you are storing the altitude from the gps object in a variable called altitude. The header file says its in cm. So lets asume its 210 m = 21000 cm

long altitude = gps.altitude(); //added 18nov

Then you copy the value into a floating variable altx. I assume you have a typo in the post and not in your code. You would get a compiler warning/error. Now altx is 21000.0.

float altx = alt; //added 18nov

float altx = altitude;

Now you divide and print. 21000.0 / 100000 = 0.21

Serial.print(alt/100000,3); //added 18nov

If you want the result in meters which is the unit used in the gpx format. You would only need to divide by 100. Also I assume the missing x is a typo. Additionally, GPS vertical resolution is far less accurate than horizontal and takes a longer time to aquire. You might need to wait a bit for a valid value.

Serial.print(altx/100,3); //added 18nov

IIRC there was a thread about this a few days ago and the upshot was that there is a function that will tell you when there is altitude data to be had. Something like altitudeValid perhaps - see what you can find in the header.

Thank you again for your help.

I made the changes suggested, thank you Klaus_K

float altx = altitude;
Serial.print(altx/100,3);

However Altitude now prints as 10000000, which is suspicious!

It should be 210m according to my Garmin and topo map, or something like 2xxxxxx
Time, Lon, and Lat are perfect

When you look at the TinyGPS.cpp file the second last line.

const float TinyGPS::GPS_INVALID_F_ALTITUDE = 1000000.0;

Which probably means, the library can not get a valid altitude.

Could you check if you can get the simple_test.ino running with your board? I have a different board which uses a different library. The simple_test.ino has a line of code that enables us to see the data from the module. Maybe this can help us.

Update: My board is starting to print out data with the TinyGPS library. So we can compare to what your board sends out. :slight_smile:

TinyGPS++ has functionality to indicate whether data is valid using something like

if (gps.altitude.isValid())
  {
    //do stuff if altitude is valid
  }

Thanks UKHeliBob. I wanted to look at the RAW data coming out of Johns gps module. Then we know for sure whether the module sends out the data or not. The function in the TinyGPS++ module will be useful once we know the module sends out some compatible altitude information, to make sure its only saved in the file when the data is valid.

Thank you for your continued interest.

When I ran simple_test.ino and I got:

** No characters received from GPS: check wiring **
CHARS=0 SENTENCES=0 CSUM ERR=0

I noticed my sketch had: SoftwareSerial gpsSerial(2, 3);

whereas simple_test.ino had: SoftwareSerial ss(4, 3);

I changed simple_test.ino to: SoftwareSerial ss(2, 3);

The serial monitor then produced:

CHARS=231 SENTENCES=0 CSUM ERR=25
CHARS=494 SENTENCES=0 CSUM ERR=57
CHARS=927 SENTENCES=0 CSUM ERR=106
CHARS=1159 SENTENCES=0 CSUM ERR=132
CHARS=1390 SENTENCES=0 CSUM ERR=157 etc

The wiring from the VK2828U7G5LF is grd, +3.3v, T to 2 on Arduino

I had the same issue. Change the serial speed setting for the GPS module. Try 9600.

ss.begin(9600);

Ah, that's better:

Simple TinyGPS library v. 13
by Mikal Hart

LAT=-41.202735 LON=174.880279 SAT=0 PREC=0 CHARS=422 SENTENCES=1 CSUM ERR=4
LAT=-41.202732 LON=174.880279 SAT=0 PREC=0 CHARS=721 SENTENCES=2 CSUM ERR=8
LAT=-41.202732 LON=174.880279 SAT=0 PREC=0 CHARS=1020 SENTENCES=3 CSUM ERR=12
LAT=-41.202739 LON=174.880249 SAT=0 PREC=0 CHARS=1318 SENTENCES=4 CSUM ERR=14

Well done. Now, this is already data that has been parsed trough the library.

If you uncomment the following line in your code,

Serial.write(c); // uncomment this line if you want to see the GPS data flowing

you should see more prints from the GPS module with lot of numbers and letters.

There are different type of sentences and we need to look at what types of sentences your module uses and if and where the altitude information is hidden.

Let the module run for a while to make sure you have the best possible data. Clear the output and copy just one or two complete blocks. Basically what you had before but with the raw data printed in between.

Below is a copy/paste from the serial monitor:

Simple TinyGPS library v. 13
by Mikal Hart

$GPRMC,064036.00,A,4112.16857,S,17452.81310,E,0.644,,211119,,,A*62
$GPVTG,,T,,M,0.644,N,1.13428,,4.3,14.19A,10,,,,.5166E$G,62702061211,512427
P,,0110,,,,07
PL428,1581E00.,A6LAT=-41.202808 LON=174.880218 SAT=0 PREC=0 CHARS=274 SENTENCES=1 CSUM ERR=1
$GPRMC,064037.00,A,4112.16876,S,17452.81262,E,0.169,,211119,,,A*6C
$GPVTG,,T,,M,0.169,N,0.334266,4.2,14519A,,,,,,,.5066E$G,0031,,7121,,512427
PV,011,,,,,07
GL4266,4.2,0470A0LAT=-41.202812 LON=174.880203 SAT=0 PREC=0 CHARS=448 SENTENCES=2 CSUM ERR=1
$GPRMC,064038.00,A,4112.16883,S,17452.81296,E,0.248,,211119,,,A*62
$GPVTG,,T,,M,0.248,N,0.4,1.8,1589E14.,5,0101,,,851.*
GG16,7320652113,3232E$G,265502233,7$PL1.8,1589E630A*
LAT=-41.202816 LON=174.880218 SAT=0 PREC=0 CHARS=622 SENTENCES=3 CSUM ERR=3
$GPRMC,064039.00,A,4112.16860,S,17452.81377,E,0.897,,211119,,,A*68
$GPVTG,,T,,M,0.897,N,1.62,260,4.17,0519A,,,,,,,.,067E
P20031,,71,,,,5120,*
GV,01,,,8,40*
GL4260,4.3,00.0ACLAT=-41.202808 LON=174.880218 SAT=0 PREC=0 CHARS=796 SENTENCES=4 CSUM ERR=5
$GPRMC,064040.00,A,4112.16890,S,17452.81412,E,0.571,,211119,,,A*68
$GPVTG,,T,,M,0.571,N,1.074260,4.4,14.19A,,,,,,,.5167E$G,02303,71211,59,,38E$G,265502233,F$PL1.8,1581E60.A*
LAT=-41.202816 LON=174.880233 SAT=0 PREC=0 CHARS=971 SENTENCES=5 CSUM ERR=6
$GPRMC,064041.00,A,4112.16876,S,17452.81435,E,0.122,,211119,,,A*66
$GPVTG,,T,,M,0.122,N,0.264286,4.4,14519A,1,,,,,.5067E$G,6270306121,,58,,38F$S,265502233,F
PL1.8,1583E640A*
LAT=-41.202812 LON=174.880233 SAT=0 PREC=0 CHARS=1146 SENTENCES=6 CSUM ERR=7
$GPRMC,064042.00,A,4112.16874,S,17452.81474,E,1.161,,211119,,,A*64
$GPVTG,,T,,M,1.161,N,2.10428,14.4,14.19,,10,,,,.5167E$G162702061211,57,,38
$S2265502233,F$PL1.8,1287E640A*
LAT=-41.202812 LON=174.880233 SAT=0 PREC=0 CHARS=1321 SENTENCES=7 CSUM ERR=8
$GPRMC,064043.00,A,4112.16867,S,17452.81474,E,0.570,,211119,,,A*62
$GPVTG,,T,,M,0.570,N,1.054267,4.4,10519A,,,,,,,.,067E$G,0031,,612,,,573,377$G,261502233,7
PL1.8,1587,64.A*
LAT=-41.202812 LON=174.880233 SAT=0 PREC=0 CHARS=1496 SENTENCES=8 CSUM ERR=9
$GPRMC,064044.00,A,4112.16801,S,17452.81481,E,2.515,,211119,,,A*6E
$GPVTG,,T,,M,2.515,N,4.6,4.8,15.4,14.,9,0101,,,851.7E$G,62702061211356,,38A$G2,65502233,F$G,110S1211,440,7
LAT=-41.202800 LON=174.880233 SAT=0 PREC=0 CHARS=1671 SENTENCES=9 CSUM ERR=9
$GPRMC,064045.00,A,

Some lines may be separated (ie split into two or more). Below is one complete line:

$GPVTG,,T,,M,0.248,N,0.4,1.8,1589E14.,5,0101,,,851.*
GG16,7320652113,3232E$G,265502233,7$PL1.8,1589E630A*
LAT=-41.202816 LON=174.880218 SAT=0 PREC=0 CHARS=622 SENTENCES=3 CSUM ERR=3

Hope that makes sense.

johngxnz:
Ah, that's better:
LAT=-41.202735 LON=174.880279 SAT=0 PREC=0 CHARS=422 SENTENCES=1 CSUM ERR=4

mmm, not good.

422 characters, 1 sentence 4 checksum errors, so looks like its only reading one sentence in 5.

Ok, are you up for some fun?

Right now your module does not send out the information you need. But you can configure it, if you are up for a little challenge.

Have a look at the datasheet. I got a copy from this website.

The datasheet shows you which NMEA0183 protocols are supported by the module. This is the raw data you have seen. They all start with $GPRMC, $GPVTG and others. You want GGA. Have a look at section 5.1 GGA. You need MSL amplitude. That is your elevation. No need to worry about the details. They will be handled by TinyGPS.

Then you need to download the configuration software from u-blox. They are a semiconductor company from Switzerland. There chip is likely inside your module, because the datasheet mentions the software in section 6.

Then we need to connect your module to the software. Because you already connected the module to the Arduino, we will send the data through the Arduino.

Create a new Sketch based on the Simple example. Replace the setup and loop by the code below. Make sure only the raw data is printed in the Serial Monitor window. Then close the Serial Monitor and start the u-blox software. Go to Receiver -> Connection and choose the COM port of your Arduino. You should see the software receiving the data from your module and display some information.

void setup()
{
  Serial.begin(9600);
  ss.begin(9600);
}

void loop()
{
  while( ss.available() )
  {
    char c = ss.read();
    Serial.write(c); // uncomment this line if you want to see the GPS data flowing
  }
}

For now, we are not sending any data from the u-center software to your module. My module is different and I did not want to send data to the module, before you have a connection to your module.

It looks like EVERY $GPVTG message is garbled starting around the "Speed in kph" field (just after the ",N," that ends the "Speed in Knots" field. Is there some way to do a factory reset on your GPS?

Thank you Klaus, you have gone to a lot of trouble, appreciated.

I've come to a bit of a road block, which I need to contemplate: I am using Linux, and the u-center software appears to be Windows only. I tried running u-center.exe under wine, but it did not like it.

johngxnz:
I am using Linux, and the u-center software appears to be Windows only. I tried running u-center.exe under wine, but it did not like it.

Here's a post from two years ago on the ublox forum. Maybe it will help:

KenMcGuire (Customer)
2 years ago

U-Center 8.25 and 8.26 run well under Wine on linux. I have been using U-center for years (since U-Center 8.11) and only the most recent 2 versions work well enough for me to say that. Over the years, ublox has improved their code and Wine has also improved its windows ABI emulation.

Wine will require you to install gecko and mono during the first install. This is somewhat automated and is not normally an issue. Setting up permissions for the Serial & USB interfaces is required but not overly complicated.

There is still the occasional crash due to who knows what, but I have run U-Center for days at a time using both USB and serial connections at 230000 baud. Text, binary and packet consoles work on 8.25 and 8.26 whereas earlier versions would crash as soon as any data was received with any of those consoles open. Those older versions did however function to some extent. I have also been able to flash new firmware to an M8N with 8.25 & 8.26.

I use U-Center on a 32bit dual core laptop running Linux Mint 18.2, a 64bit quad core laptop running linux Mint 18.2 and a 64 bit quad core desktop running Linux Mint 17.3. Even on the ancient 32 bit laptop, i have no performance issues. I sometimes run 2 instances of U-Center each connected to a different receiver.