GPS location acquisition

Hi fellas,
I have made the following code and I've been struggling finding a way to make sure I acquire always one value for the latitude and then that the program goes on to another task.
Here the Serial.println is an example to simplify but the result is that only this shows on the serial monitor and never the latitude.

I've tried using a bool function to get out of the loop, using a function called in the loop, using millis... always the same result.

If someone could help me I'd be so glad :slight_smile:
Thanks.

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

TinyGPSPlus gps;

static const int RXPin = 4, TXPin = 3;
static const uint32_t GPSBaud = 9600;
SoftwareSerial ss(RXPin, TXPin);

bool printGPS = false;
bool locationAcquired = false;

float const LONGITUDE = 6.29;
float const LATITUDE = 46.09;
int TiZ = 0; // 0 = UTC & 1 = GMT+1

void setup() {
  Serial.begin(9600);
  ss.begin(GPSBaud);
  TimeLord tardis; 

  tardis.TimeZone(TiZ * 60); 
  tardis.Position(LATITUDE, LONGITUDE); // tell TimeLord where in the world we are
  
  byte today[] = { 0, 0, 12, 22, 3, 12 }; // store today's date (at noon) in an array for TimeLord to use (format SS, MM, HH, DD, MM, YY)

if (tardis.SunRise(today)) // if the sun will rise today (it might not, in the [ant]arctic)
{   Serial.print("Sunrise: ");
    Serial.println((int) today[tl_hour] * 60 + today[tl_minute]);}
   
if (tardis.SunSet(today)) // if the sun will set today (it might not, in the [ant]arctic)
{   Serial.print("Sunset: ");
    Serial.println((int) today[tl_hour] * 60 + today[tl_minute]);}
}


void loop()
{
 while (ss.available() > 0)
  { gps.encode(ss.read());
    if (gps.location.isUpdated()) {
      Serial.print("");
      Serial.print("Latitude: " ); 
      Serial.println(gps.location.lat(), 6);
      //Serial.print(" Longitude= "); Serial.print(gps.location.lng(), 6);
      //Serial.print(" Altitude= "); Serial.println(gps.altitude.meters());
      delay(5000);
    }
    }

Serial.println("Again");
delay(5000);

  }

Get rid of the delays.

your GPS will spit out NMEA sentences probably once per second and so if you block the code for too long you don't feed the beast with gps.encode(ss.read()) and you'll likely get only part of the sentence and thus won't be able to get a proper fix

You do need to read characters from the GPS until the library reports the location has been updated, so good marks for doing most of that.

If you dont do this you might never read the NMEA sentences from the GPS that provide the location info.

However, if you have just read the last character the GPS has sent (there are no more available) dont wander off out of loop and do other stuff with delays etc, you could get into a sequence where you are always missing the sentences that have the location data, GPRMC and GPGGA.

Have a look to my post.
Maybe you can find the solution!!!
https://forum.arduino.cc/t/solucionado-gps-neom8n-y-arduino-nano/1098626/33

Hi Axelarnal,
I've read your thread and adapted your code to my project.

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

TinyGPSPlus gps;

static const int RXPin = 4, TXPin = 3;
static const uint32_t GPSBaud = 9600;
SoftwareSerial ss(RXPin, TXPin);

bool printGPS = false;
bool locationAcquired = false;

int num_sat;

float const LONGITUDE = 6.29;
float const LATITUDE = 46.09;
int TiZ = 0; // 0 = UTC & 1 = GMT+1

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

getSUN();
 
}

void loop(void)
{
 getGPS();
 Serial.print("Test");
 delay(5000);
}

void getSUN(){
TimeLord tardis; 
tardis.TimeZone(TiZ * 60); 
tardis.Position(LATITUDE, LONGITUDE); // tell TimeLord where in the world we are
  
byte today[] = { 0, 0, 12, 22, 3, 12 }; // store today's date (at noon) in an array for TimeLord to use (format SS, MM, HH, DD, MM, YY)

if (tardis.SunRise(today)) // if the sun will rise today (it might not, in the [ant]arctic)
{   Serial.print("Sunrise: ");
    Serial.println((int) today[tl_hour] * 60 + today[tl_minute]);}
if (tardis.SunSet(today)) // if the sun will set today (it might not, in the [ant]arctic)
{   Serial.print("Sunset: ");
    Serial.println((int) today[tl_hour] * 60 + today[tl_minute]);}
}


void getGPS(){
 num_sat = gps.satellites.value();
   if (gps.location.isValid() == 1) {
      Serial.print("Latitude: " ); 
      Serial.println(gps.location.lat(), 6);
   }
    }    

void Read_GPS(){
  boolean newData = false;
  for (unsigned long start = millis(); millis() - start < 5000;)
  {
    while (ss.available())
    {
      if (gps.encode(ss.read()))
      {
        newData = true;
        break;
      }
    }
  }
  //If newData is true
  if(newData == true){
    newData = false;
    getGPS();
  }
  else { 
    //no hace nada
  }
}

But this time I don't get any value anymore. My Neo-6M isn't getting a clear signal everytime but I let it run for 30 minutes and nothing appeared.

Thank you anyway for the link

Hi JML,
Exactly, I realized that, the data are not acquired at the same pace everytime and if the delay takes after the first value it fails.

One of the solution would be to keep the loop running but the problem is that I want this project on battery and it will kill the battery wouldn't it?

Maybe if I find a way to shutdown this part of the loop only for 24 hours I would be find.
It leads me to the millis() function but somehow it doesn't want to work either.

Be aware that as standard that GPS puts out around 8 different sentences and only two of them have the location data. So just because you have encoded a sentence it does not mean there is 'newData' to print\store etc.

More details ?

What type of battery, how long to run o battery etc ........

Hi Srnet,
Thank you for your answers.
You are perfectly right. But even when I use it, it doesn't acknowledge the part where I'm trying to get the latitude. I've changed to millis and still it never pickup like if this part of the code isn't given enough time to acquire the data. I tried with 1000 ms interval and even 30000 ms. Still no result.

Regarding the battery, it's just a 1000 mAh Lipo and ideally I only need this GPS data acquisition twice a day. I'm doing everything to maximize battery life and avoid to recharge it every week.

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

unsigned long previousMillis = 0;
const long interval = 1000;

TinyGPSPlus gps;

static const int RXPin = 4, TXPin = 3;
static const uint32_t GPSBaud = 9600;
SoftwareSerial ss(RXPin, TXPin);

bool printGPS = false;
bool locationAcquired = false;

int num_sat;

float const LONGITUDE = 6.29;
float const LATITUDE = 46.09;
int TiZ = 0; // 0 = UTC & 1 = GMT+1

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

getSUN();
 
}

void loop(void)
{
 Serial.println("Test");

  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= interval) {
      previousMillis = currentMillis;
      getGPS();
   }
}

void getSUN(){
TimeLord tardis; 
tardis.TimeZone(TiZ * 60); 
tardis.Position(LATITUDE, LONGITUDE); // tell TimeLord where in the world we are
  
byte today[] = { 0, 0, 12, 22, 3, 12 }; // store today's date (at noon) in an array for TimeLord to use (format SS, MM, HH, DD, MM, YY)

if (tardis.SunRise(today)) // if the sun will rise today (it might not, in the [ant]arctic)
{   Serial.print("Sunrise: ");
    Serial.println((int) today[tl_hour] * 60 + today[tl_minute]);}
if (tardis.SunSet(today)) // if the sun will set today (it might not, in the [ant]arctic)
{   Serial.print("Sunset: ");
    Serial.println((int) today[tl_hour] * 60 + today[tl_minute]);}
}


void getGPS(){
while (ss.available() > 0)
  { gps.encode(ss.read());
    if (gps.location.isUpdated()) {
      Serial.print("");
      Serial.print("Latitude: " ); 
      Serial.println(gps.location.lat(), 6);
      //Serial.print(" Longitude= "); Serial.print(gps.location.lng(), 6);
      //Serial.print(" Altitude= "); Serial.println(gps.altitude.meters());
    }
    }
    }    

Try to avoid using serial prints while your expecting characters to be received from the GPS, this is very important especially when using software serial.

A more reliable way to read the GPS is shown below. The read of the GPS is called from loop() with a timeout. When the gpsWaitFix() returns true, you can then print stuff, do calculations etc.

The gpsWaitFix() function thats reading the GPS is kept lean to minimise the risk of upsetting softwareserial.


/*******************************************************************************************************
  Programs for Arduino - Copyright of the author Stuart Robinson - 23/03/23

  This program is supplied as is, it is up to the user of the program to decide if the program is
  suitable for the intended purpose and free from errors.
*******************************************************************************************************/

/*******************************************************************************************************
  Program Operation -  This program is a portable GPS checker. It reads the GPS for 5 seconds and copies
  the characters from the GPS to the serial monitor.
  
  Serial monitor baud rate is set at 115200.
*******************************************************************************************************/

#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
#define LED1 8                                     //pin number for LED, turns on when printing Fix data 

#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
uint32_t GPSHdop;                                  //HDOP from GPS
uint8_t hours, mins, secs, day, month;
uint16_t year;
uint32_t startGetFixmS;
uint32_t endFixmS;

//GPS test co-ordinates to use for distance and direction calculatio
const float TestLatitude  = 51.50807;              //Tower of London
const float TestLongitude  = -0.07606;

void loop()
{
  if (gpsWaitFix(5))
  {
    digitalWrite(LED1, HIGH);                      //LED on to indicate fix
    Serial.println();
    Serial.println();
    Serial.print(F("Fix time "));
    Serial.print(endFixmS - startGetFixmS);
    Serial.println(F("mS"));

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

    hours = gps.time.hour();
    mins = gps.time.minute();
    secs = gps.time.second();
    day = gps.date.day();
    month = gps.date.month();
    year = gps.date.year();

    printGPSfix();
    startGetFixmS = millis();    //have a fix, next thing that happens is checking for a fix, so restart timer
    digitalWrite(LED1, LOW);
  }
  else
  {
    Serial.println();
    Serial.println();
    Serial.print(F("Timeout - No GPS Fix "));
    Serial.print( (millis() - startGetFixmS) / 1000 );
    Serial.println(F("s"));
  }
}

bool gpsWaitFix(uint16_t waitSecs)
{
  //waits a specified number of seconds for a fix, returns true for updated fix

  uint32_t startmS, waitmS;
  uint8_t GPSchar;

  Serial.print(F("Wait GPS Fix "));
  Serial.print(waitSecs);
  Serial.println(F(" seconds"));

  waitmS = waitSecs * 1000;                               //convert seconds wait into mS

  startmS = millis();

  while ( (uint32_t) (millis() - startmS) < waitmS)       //allows for millis() overflow
  {
    if (GPSserial.available() > 0)
    {
      GPSchar = GPSserial.read();
      gps.encode(GPSchar);
      Serial.write(GPSchar);
    }

    if (gps.speed.isUpdated() && gps.satellites.isUpdated()) //ensures that GGA and RMC sentences have been received
    {
      endFixmS = millis();                                //record the time when we got a GPS fix
      return true;
    }
  }
  return false;
}

void printGPSfix()
{
  float tempfloat;
  uint32_t distance;
  uint16_t direction;

  Serial.print(F("New GPS Fix "));

  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(",Time,"));

  if (hours < 10)
  {
    Serial.print(F("0"));
  }

  Serial.print(hours);
  Serial.print(F(":"));

  if (mins < 10)
  {
    Serial.print(F("0"));
  }

  Serial.print(mins);
  Serial.print(F(":"));

  if (secs < 10)
  {
    Serial.print(F("0"));
  }

  Serial.print(secs);
  Serial.print(F(",Date,"));

  Serial.print(day);
  Serial.print(F("/"));
  Serial.print(month);
  Serial.print(F("/"));
  Serial.print(year);

  distance = gps.distanceBetween(GPSLat, GPSLon, TestLatitude, TestLongitude);
  direction = gps.courseTo(GPSLat, GPSLon, TestLatitude, TestLongitude);

  Serial.println();
  Serial.print(F("Distance to Test Location ("));
  Serial.print(TestLatitude, 6);
  Serial.print((","));
  Serial.print(TestLongitude, 6);
  Serial.print((") "));
  Serial.print(distance);
  Serial.print(("m"));
  Serial.println();
  Serial.print(F("Direction to Test Location ("));
  Serial.print(TestLatitude, 6);
  Serial.print((","));
  Serial.print(TestLongitude, 6);
  Serial.print((") "));
  Serial.print(direction);
  Serial.print(("d"));
  Serial.println();
  Serial.println();
}

void setup()
{
  pinMode(LED1, OUTPUT);
  GPSserial.begin(9600);

  Serial.begin(115200);
  Serial.println();
  Serial.print(F(__FILE__));
  Serial.println();
  Serial.println(F("GPS Checker Starting"));
  Serial.println();

  startGetFixmS = millis();
}

Thanks for the further help. Well, I tried this and I only get timeout results. My Neo-6M never has a fixed led so it doesn't really have a stable signal maybe that is why this code doesn't work either.

Anyway, the goal is really to have a result every 24 hours and even when I test it with 10 minutes it doesn't get a result with your code.

Thank you anyway. I will keep digging keeping in mind that your code might have the solution also but clearly for the moment I don't see it.

If the GPS is working, has a working antenna, and is outside, then the printout would typically show evidence of picking up GPS satellites within about 15 seconds of being powered on, the time within about 30 seconds and a full location fix would come in circa 45 seconds.

If the GPS was working properly that is.

My bad I didn't look at the pin difference. It's working when I change the pin number. I will try to adapt it to my need and come back here.
I takes about 900 ms for the satelite to get fixed value. So any value I put in the GPS fix will hold for that long? I will try and come back :slight_smile:

No idea what you mean.

This time I think we're getting somewhere. I simplified the code to make it easier.
Now it can be used with another part of the loop and both are acting independantly. However, the value of the latitude is somehow not updated at all.

Any idea why it doesn't update?
Thanks.

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

#define RXpin 4                                   //pin number for GPS RX input into Arduino - TX from GPS
#define TXpin 3                                   //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
uint8_t GPSSats;                                   //number of GPS satellites in use
uint8_t hours, mins, secs, day, month;
uint16_t year;
uint32_t startGetFixmS;
uint32_t endFixmS;

void setup()
{
  GPSserial.begin(9600);
  Serial.begin(9600);
  startGetFixmS = millis();
}

void TesT()
{  if (gpsWaitFix(10))
  {
    GPSLat = gps.location.lat();
    printGPSfix();
    startGetFixmS = millis();    //have a fix, next thing that happens is checking for a fix, so restart timer
  }
  }

void loop()
{
  TesT();
  Serial.println("Test");
  delay(1000);
}

bool gpsWaitFix(uint16_t waitSecs)
{
  //waits a specified number of seconds for a fix, returns true for updated fix
  uint32_t startmS, waitmS;
  uint8_t GPSchar;

  waitmS = waitSecs * 1000;                               //convert seconds wait into mS
  startmS = millis();

  while ( (uint32_t) (millis() - startmS) < waitmS)       //allows for millis() overflow
  {
    if (GPSserial.available() > 0)
    {
      GPSchar = GPSserial.read();
      gps.encode(GPSchar);
    }

    if (gps.speed.isUpdated() && gps.satellites.isUpdated()) //ensures that GGA and RMC sentences have been received
    {
      endFixmS = millis();                                //record the time when we got a GPS fix
      return true;
    }
  }
  return false;
}

void printGPSfix()
{
  Serial.println(GPSLat,6);
  delay(1000);
}


Again not sure what you mean.

Does the code I posted in post #10 work, yes or no ?

It is working but it gives way too much result per minute and I couldn't make it to slow.

Therefore I simplified it (I'm a beginner and too much information is overwhelming to me).
Then I figured out a way to achieve the goal I was looking at.
Maybe the solution is very newbie but it works fine.
I reset the arduino using a delay. :slight_smile:
So now I can have a fresh value of the GPS location everyday and run other thing during that time.

Here is the code.

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

#define RXpin 4                                   //pin number for GPS RX input into Arduino - TX from GPS
#define TXpin 3                                   //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
uint8_t GPSSats;                                   //number of GPS satellites in use
uint8_t hours, mins, secs, day, month;
uint16_t year;
uint32_t endFixmS;

void(*resetFunc)(void) = 0; //declare reset function at address 0

void setup()
{
  GPSserial.begin(9600);
  Serial.begin(9600);
  TesT();
}

void TesT()
{  if (gpsWaitFix(5))
  {
    GPSLat = gps.location.lat();
    printGPSfix();
  }
  }

void loop()
{
resetFunc();
delay(10000);
}

bool gpsWaitFix(uint16_t waitSecs)
{
  //waits a specified number of seconds for a fix, returns true for updated fix
  uint32_t startmS, waitmS;
  uint8_t GPSchar;

  waitmS = waitSecs * 1000;                               //convert seconds wait into mS
  startmS = millis();

  while ( (uint32_t) (millis() - startmS) < waitmS)       //allows for millis() overflow
  {
    if (GPSserial.available() > 0)
  {
    GPSchar = GPSserial.read();
    gps.encode(GPSchar);
  }
    if (gps.speed.isUpdated() && gps.satellites.isUpdated()) //ensures that GGA and RMC sentences have been received
  {
    return true;
  }
  }
  return false;
  }

void printGPSfix()
{
  Serial.println(GPSLat,6);
  delay(5000);
}

I now need to find a way to make it without resetting.
Thank you for your great help so far.

And what does that mean ?

That currently the only solution to make it happen is to reset the board. It is a bit silly to do that and I am looking for a solution where I don't have to reset the board.

And I have no idea what you mean by that either.

Good luck with your project.