uBlox Neo GPS + Nano: missing altitude and number of sats

Hi all,

After some searching I couldn't find anything related to my issue with the uBlox Neo GPS module. When using the Nano + TinyGPS++ and SoftwareSerial the serial output shows a correct position but no number of satellites nor the altitude (both output is '0').

When changing to an ESP8266 and using a similar script with corresponding libraries the number of satellites sighted and actual altitude are shown correctly.

In both cases the GPS module is powered by 5V via the board, which is powered by USB.

What's going on...? Why isn't the Nano capable of this?

Greetz,
pe2kmv

It could be an error in your code, but we have not seen it.

Please find the code below. Also when inserting a print line to output the stream from the GPS module, the 'GPGSV' message is missing

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

// Choose two Arduino pins to use for software serial
int RXPin = 2;
int TXPin = 3;

int GPSBaud = 9600;

// Create a TinyGPS++ object
TinyGPSPlus gps;

// Create a software serial port called "gpsSerial"
SoftwareSerial gpsSerial(RXPin, TXPin);

void setup()
{
  // Start the Arduino hardware serial port at 9600 baud
  Serial.begin(9600);

  // Start the software serial port at the GPS's default baud
  gpsSerial.begin(GPSBaud);
  
}

void displayInfo()
{
  if (gps.location.isValid())
  {
    Serial.print("Latitude: ");
    Serial.println(gps.location.lat(), 6);
    Serial.print("Longitude: ");
    Serial.println(gps.location.lng(), 6);
    Serial.print("Altitude: ");
    Serial.println(gps.altitude.meters());
	Serial.print("Speed: ");
	Serial.println(gps.speed.kmph());
	Serial.print("Satellites: ");
	Serial.println(gps.satellites.value());

  }
  else
  {
    Serial.println("Location: Not Available");
  }
  
  Serial.print("Date: ");
  if (gps.date.isValid())
  {
    Serial.print(gps.date.month());
    Serial.print("/");
    Serial.print(gps.date.day());
    Serial.print("/");
    Serial.println(gps.date.year());
  }
  else
  {
    Serial.println("Not Available");
  }

  Serial.print("Time: ");
  if (gps.time.isValid())
  {
    if (gps.time.hour() < 10) Serial.print(F("0"));
    Serial.print(gps.time.hour());
    Serial.print(":");
    if (gps.time.minute() < 10) Serial.print(F("0"));
    Serial.print(gps.time.minute());
    Serial.print(":");
    if (gps.time.second() < 10) Serial.print(F("0"));
    Serial.print(gps.time.second());
    Serial.print(".");
    if (gps.time.centisecond() < 10) Serial.print(F("0"));
    Serial.println(gps.time.centisecond());
  }
  else
  {
    Serial.println("Not Available");
  }

  Serial.println();
  Serial.println();
  delay(1000);
}

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

  // If 5000 milliseconds pass and there are no characters coming in
  // over the software serial port, show a "No GPS detected" error
  if (millis() > 5000 && gps.charsProcessed() < 10)
  {
    Serial.println("No GPS detected");
    while(true);
  }
}

It might work if you use Serial.begin(115200); and switch the Serial monitor to 115,200.

But the code uses a flawed method for correctly reading the GPS which could cause the code to fail (miss GPS parameters) on slower Arduinos.

Hello OM PE2KMV

Does the GPS has a clear line of sight to satellites?

Nope, I'm indoors while experimenting. But that counts for both setups. When using the successful ESP the GPS module is at the same spot at the same table as when using the unsuccessful Nano.

I would get rid of the delay(1000) at the end of the displayInfo() function. If you really want to sit and do nothing for that long, you need to be feeding data from the GPS into tinyGPS inside a millis() timing loop, otherwise you will lose a lot of data. At 9600 baud from the GPS, you only have about 65mS until the buffer overflows, that is one reason you should take the advice of increasing the baud rate to the serial monitor.

There is more to it.

For one, lots of Serial prints do result in lots of background interrupts, and these very definetly interfere with the interrupts that softwareserial depends on to correctly decode characters, so lots of Serial prints can prevent the 'background' receipt of characters from the GPS, not good.

So its simple, dont Serial print stuff unless the parameters you want to display or print, have actually been updated. Waste of time printing stuff that has not been updated.

Then what makes the issue worse is the use of;

if (gps.encode(gpsSerial.read()))
      displayInfo();

Which is plain wrong in my view. It means that for every NMEA sentence received, you waste time printing a pile of parameters that have not changed, because parameters changes only happen when the GPRMC and GPGGA sentences are received.

I found the problem, with the standard TinyGPS++ examples, some years ago, I was also getting an altidude of 0 when reading the GPS with a Pro Mini tracker. For the reasons mentioned above.

The solution was not difficult, maybe I should document it one day.

How not to read a GPS

I often see posts on the Arduino forums where users are having problems reading some parameters from a GPS. TinyGPSplus is a common library to use but care is needed when reading the serial data characters from the GPS and sending them to the Arduino library for decoding items such as Latitude, Longitude, Altitude, Satellites, Speed etc.

The TinyGPSplus library examples do read the GPS in a way that is not very robust and you can have problems especially with slower Arduinos such as the 3.3V Pro Mini which runs at 8Mhz.

I first became aware of a problem with standard examples some years ago when my tracker software was often showing good location data but reporting an altitude of 0. This note will discuss how to use TinyGPSplus to read a more GPS reliably.

The first issue to be aware of is that when your using say an Arduino UNO, Nano or Pro Mini and your using the SoftwareSerial library to create a serial port to read the GPS characters; is that using Serial.Print() functions at the same time that SoftwareSerial is reading the GPS can cause SoftwareSerial to miss characters. The interrupts used by Serial.print() and SoftwareSerial often don’t work well together. There is a discussion of this issue here;

Do appreciate that a single serial character from the GPS that is missed, or not decoded correctly, results in a corrupt NMEA sentence which is ignored. If the missed sentence is one that contains data that you want you have problems.

The second issue to be aware of is that the encode() method used by TinyGPSplus returns true for every NMEA sentence correctly received. TinyGPSplus gets its position and other data from the $GPRMC and $GPGGA sentences and these are the only two sentences needed. However as default a GPS puts out quite a few more NMEA sentences, this is a typical sequence;

$GPGSA,A,3,27,18,23,26,08,10,07,16,,,,,1.42,1.12,0.8708
$GPGSV,3,1,12,16,74,202,37,27,67,287,40,18,42,058,38,23,40,103,34
7C
$GPGSV,3,2,12,26,39,163,35,08,32,279,30,10,27,142,24,07,20,314,2673
$GPGSV,3,3,12,02,04,053,,15,03,054,,21,01,229,,30,01,342,70
$GPRMC,153535.000,A,####.####,N,#####.####,,W,0.56,283.08,240323,,,A
7F
$GPVTG,283.08,T,,M,0.56,N,1.04,K,A
3A
$GPGGA,153536.000,####.####,N,#####.####,W,1,8,1.12,29.8,M,49.9,M,,7F
$GPGLL,####.####,N,#####.####,,W,153536.000,A,A
47

(The actual Latitude and Longitude has been replaced with # characters)

So eight NMEA sentences but only two are used by the TinyGPSplus library for data. So when a bit of typical Arduino code does this;

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

gps.encode() returns true for all eight NMEA sentences and the program calls displayInfo() for all eight sentences which typically prints all this stuff, if the location and time is valid;

Serial.print(gps.location.lat(), 6);
Serial.print(F(","));
Serial.prin(gps.location.lng(), 6);
Serial.print(F("  Date/Time: "));
Serial.print(gps.date.month());
Serial.print(F("/"));
Serial.print(gps.date.day());
Serial.print(F("/"));
Serial.print(gps.date.year());
Serial.print(F(" "));
if (gps.time.hour() < 10) Serial.print(F("0"));
Serial.print(gps.time.hour());
Serial.print(F(":"));
if (gps.time.minute() < 10) Serial.print(F("0"));
Serial.print(gps.time.minute());
Serial.print(F(":"));
if (gps.time.second() < 10) Serial.print(F("0"));
Serial.print(gps.time.second());
Serial.print(F("."));
if (gps.time.centisecond() < 10) Serial.print(F("0"));
Serial.print(gps.time.centisecond());

Thats a lot of Serial.print() stuff and 6 times out of 8 there has been no change in the data printed because, remember, changes in the data printed only occur when the $GPRMC or $GPGGA sentences themselves are encoded.

The third issue to be aware of is that the all the information that TinyGPSplus collects needs it to receive both $GPRMC and $GPGGA, this is the information contained in these two sentences;

$GPRMC sentence;

Current time (if available; UTC)
Position status (A for valid, V for invalid)
Latitude compass direction
Longitude (in DDDMM.MMM format)
Longitude compass direction
Speed (in knots per hour)
Heading
Date (DDMMYY)
Magnetic variation
Magnetic variation direction
The checksum validation value (in hexadecimal)

$GPGGA sentence;

Current time (if available; UTC)
Latitude (in DDMM.MMM format)
Latitude compass direction
Longitude (in DDDMM.MMM format)
Longitude compass direction
Fix type (0 for no fix, 1 for GPS, 2 for DGPS)
Number of satellites used for fix
Horizontal dilution of precision
Altitude above mean sea level
Altitude units (M for meters)
Height of mean sea level above WGS-84 earth ellipsoid
Units of the above geoid separation (M for meters)
Time since last differential correction (ignored if inactive)
Differential station ID (ignored if inactive)
The checksum validation value (in hexadecimal)

Note that although the Latitude and Longitude are in $GPRMC and $GPGGA, altitude and satellites are only in GPGGA and speed and heading are only in $GPRMC.

So what can happen when we are reading a GPS ?

The amount of Serial.prints() in displayInfo() can mean there are a large number of interrupts to interfere with SoftwareSerial reading the GPS characters (see above). Typically $GPRMC is received and decoded first but if we spend too long in displayInfo() we might miss $GPGGA, thus although the location will be updated its possible that altitude and satellites will not be and can show as 0.

So what do we do about this ?

TinyGPSplus has a method that allows you to check whether a parameter has actually been updated, its the .isUpdated() method.

Whilst you do need to use encode() there is no point in printing a pile of stuff if its not been updated since the previous print. To prevent printing stuff when there has been no updates this is what a modified GPS read function looks like

while (1)
  {
    if (GPSserial.available() > 0)
    {
      gps.encode(GPSserial.read());
    }

    if (gps.speed.isUpdated() && gps.satellites.isUpdated()) //ensures that GGA and RMC sentences have been received
    {
      displayInfo();
    }
  }

Note that no time is wasted printing stuff until both the speed and satellites values have been updated, which means that $GPRMC and $GPGGA have been both been received since the last update. If we now use displayInfo() we have a much longer period to print stuff before the next $GPRMC and $GPGGA are received.

Here is the full test program that should not have errors reading altitude or speed;

#include <TinyGPS++.h>                             //get library here > http://arduiniana.org/libraries/tinygpsplus/
TinyGPSPlus gps;                                   //create the TinyGPS++ object

#define RXpin A3                                   //pin number for GPS RX input into Arduino - TX from GPS
#define TXpin A2                                   //pin number for GPS TX output from Arduino- RX into GPS

#include <SoftwareSerial.h>
SoftwareSerial GPSserial(RXpin, TXpin);

float GPSLat;                                      //Latitude from GPS
float GPSLon;                                      //Longitude from GPS
float GPSAlt;                                      //Altitude from GPS
uint8_t GPSSats;                                   //number of GPS satellites in use by GPS
uint32_t GPSHdop;                                  //HDOP from GPS
float GPSSpeed;                                    //Speed of GPS, mph

void loop()
{
  while (1)
  {
    if (GPSserial.available() > 0)
    {
      gps.encode(GPSserial.read());
    }

    if (gps.speed.isUpdated() && gps.satellites.isUpdated()) //ensures that GPGGA and GPRMC sentences have both been received
    {
      displayInfo();
    }
  }
}

void displayInfo()
{
  float tempfloat;

  Serial.print(F("Updated GPS Fix > "));

  GPSLat = gps.location.lat();
  GPSLon = gps.location.lng();
  GPSAlt = gps.altitude.meters();
  GPSSats = gps.satellites.value();
  GPSHdop = gps.hdop.value();
  GPSSpeed = gps.speed.mph();

  tempfloat = ( (float) GPSHdop / 100);

  Serial.print(F("Lat,"));
  Serial.print(GPSLat, 6);
  Serial.print(F(",Lon,"));
  Serial.print(GPSLon, 6);
  Serial.print(F(",Alt,"));
  Serial.print(GPSAlt, 1);
  Serial.print(F("m,Sats,"));
  Serial.print(GPSSats);
  Serial.print(F(",HDOP,"));
  Serial.print(tempfloat, 2);
  Serial.print(F(",Speed,"));
  Serial.print(GPSSpeed, 1);
  Serial.println();
}

void setup()
{
  GPSserial.begin(9600);

  Serial.begin(115200);
  Serial.println();
  Serial.print(F(__FILE__));
  Serial.println();

  Serial.println(F("GPS Checker Starting"));
  Serial.println();
}

Have fun.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.