7

I wrote the following codes in Arduino uno with the header file TinyGPSPlus,and uses GPS SKG 13BL(GPS module).

#include <TinyGPS++.h>
#include <SoftwareSerial.h>
  /*
    This program sketch obtain and print the lati,logi,speed,date and time
    It requires the use of SoftwareSerial, and assumes that you have a
    9600-baud serial GPS device hooked up on pins 4(rx) and 3(tx).
   */
static const int RXPin = 4, TXPin = 3;
static const uint32_t GPSBaud = 9600;

   // The TinyGPS++ object
TinyGPSPlus gps;

   // The serial connection to the GPS device
SoftwareSerial ss(RXPin, TXPin);

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

     Serial.println(F("GPS LOADING....."));
     Serial.println(F("Obtain and print lati,logi,speed,date and time"));
     Serial.println(F("Testing by : "));
     Serial.println(F("Billa"));
     Serial.println();
      }

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

      if (millis() > 5000 && gps.charsProcessed() < 10)
        {
         Serial.println(F("No GPS detected: check wiring."));
         while(true);
         }
    }

 void displayInfo()
     {
       Serial.print(F("Location: ")); 
       if (gps.location.isValid())
         {
          Serial.print(gps.location.lat(), 6);
          Serial.print(F(","));
          Serial.print(gps.location.lng(), 6);
      }
else
   {
    Serial.print(F("INVALID"));
    }

Serial.print(F("  Speed: ")); 
if (gps.speed.isValid())
  {
   Serial.print(gps.speed.kmph());
   Serial.print(F(" KMPH "));
   }
else
  {
   Serial.print(F("INVALID"));
   }

Serial.print(F("  Date : "));
if (gps.date.isValid())
  {
   Serial.print(gps.date.month());
   Serial.print(F("/"));
   Serial.print(gps.date.day());
   Serial.print(F("/"));
   Serial.print(gps.date.year());
   }
else
   {
    Serial.print(F("INVALID"));
    }

Serial.print(F("  Time : "));
if (gps.time.isValid())
  {
   int hour= gps.time.hour() + 5;
   if (hour < 10) Serial.print(F("0"));
   if(hour > 12) hour-=11;
   Serial.print(hour);
   Serial.print(F(":"));
   int minute = gps.time.minute() + 30;
   if(minute >= 60) minute-=60;
   if (minute < 10) Serial.print(F("0"));
   Serial.print(minute);
   Serial.print(F(":"));
  if (gps.time.second() < 10) Serial.print(F("0"));
  Serial.print(gps.time.second());
  }
else
  {
   Serial.print(F("INVALID"));
   }

Serial.println();
 }

It obtained the required output.Displays the lines of data continusly on serial monitor. But now i need to get these data exactly at every 5 secs (i.e At every 5 Secs the above code should generate output as per that instant).I tried to do this using delay and rewrote the loop code as follows

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

  if (millis() > 5000 && gps.charsProcessed() < 10)
    {
     Serial.println(F("No GPS detected: check wiring."));
     while(true);
     }
}

But this doesnt obtained the output as desired.Can anyone please help me to solve this.Where should i edit and what changes should i make.

Mathews Sunny
  • 1,796
  • 7
  • 21
  • 31
  • What kind of output are you seeing from your re-written loop? –  Mar 24 '17 at 18:51
  • I am displaying the time also in the above code, that time remains same but it's delayed. – Mathews Sunny Mar 24 '17 at 19:07
  • Are you wanting only the most recent GPS data to be printed every 5 seconds, or would you like to print the next available sample after 5 seconds? –  Mar 24 '17 at 19:27
  • I believe you are running into a UART buffer overflow in SerialSoftware. You can check if that's the case by using the following - completely untested - code: `void loop() { delay(5000); // This sketch displays information every time a new sentence is correctly encoded. // Look for a buffer overflow if (ss.overflow()) Serial.println("\n\nUART Overflow Occurred!\n\n"); while (ss.available() > 0) if (gps.encode(ss.read())) displayInfo(); }` –  Mar 24 '17 at 19:31

3 Answers3

10

This is EXACTLY why I wrote NeoGPS. The example programs for all other libraries are just not structured properly. I'm looking at you, smartDelay()...

NeoGPS is structured around receiving a complete fix from the GPS device. This usually requires several sentences to be received. Other GPS libraries only tell you when one sentence is received. Furthermore, it's difficult to tell if two sentences are from the same 1-second update interval, or two consecutive intervals.

You want to display information once every 5 seconds, but that may be once every 20 sentences. And according to the Arduino millis() clock, it will be about 5000ms, but not exactly. millis() will drift against the GPS interval, depending on how accurate your crystal is. The GPS interval is very accurate,to the limits of the atomic clock, serial baud rate, and GPS device calculation time.

Here is your sketch, modified to use NeoGPS:

#include <NMEAGPS.h>
  /*
    This program sketch obtain and print the lati,logi,speed,date and time
    It requires the use of SoftwareSerial, and assumes that you have a
    9600-baud serial GPS device hooked up on pins 4(rx) and 3(tx).
   */
#include <NeoSWSerial.h>
static const int RXPin = 4, TXPin = 3;
NeoSWSerial gpsPort(RXPin, TXPin);
static const uint32_t GPSBaud = 9600;

NMEAGPS gps;
gps_fix fix;
uint8_t fixCount = 0;

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

  Serial.println(F("GPS LOADING....."));
  Serial.println(F("Obtain and print lati,logi,speed,date and time"));
  Serial.println(F("Testing by : "));
  Serial.println(F("Billa"));
  Serial.println();
}

void loop()
{
  while (gps.available( gpsPort )) {
    fix = gps.read();

    // Once every 5 seconds...    
    if (++fixCount >= 5) {
      displayInfo();
      fixCount = 0;
    }
  }

  if ((gps.statistics.chars < 10) && (millis() > 5000)) {
     Serial.println( F("No GPS detected: check wiring.") );
     while(true);
  }
}

void displayInfo()
{
  Serial.print(F("Location: ")); 
  if (fix.valid.location) {
    Serial.print( fix.latitude(), 5 );
    Serial.print( ',' );
    Serial.print( fix.longitude(), 5 );
  } else {
    Serial.print(F("INVALID"));
  }

  Serial.print(F("  Speed: ")); 
  if (fix.valid.speed) {
    Serial.print(fix.speed_kph());
    Serial.print(F(" KMPH "));
  } else {
    Serial.print(F("INVALID"));
  }

  // Shift the date/time to local time
  NeoGPS::clock_t localSeconds;
  NeoGPS::time_t  localTime;
  if (fix.valid.date && fix.valid.time) {
    using namespace NeoGPS; // save a little typing below...

    localSeconds = (clock_t) fix.dateTime; // convert structure to a second count
    localSeconds += 5 * SECONDS_PER_HOUR + 30 * SECONDS_PER_MINUTE; // shift timezone
    localTime = localSeconds;              // convert back to a structure
  }

  Serial.print(F("  Date : "));
  if (fix.valid.date) {
    Serial.print(localTime.month);
    Serial.print('/');
    Serial.print(localTime.date);
    Serial.print('/');
    Serial.print(localTime.year);
  } else {
    Serial.print(F("INVALID"));
  }

  Serial.print(F("  Time : "));
  if (fix.valid.time) {
    Serial.print(localTime.hours);
    Serial.print(':');
    if (localTime.minutes < 10) Serial.print('0');
    Serial.print(localTime.minutes);
    Serial.print(':');
    if (localTime.seconds < 10) Serial.print(F("0"));
    Serial.print(localTime.seconds);
  } else {
    Serial.print(F("INVALID"));
  }

  Serial.println();
}

This sketch displays every fifth fix, without using the inaccurate millis(), or the nasty delay(). Ain't nobody got time for dat!

And each fix is accumuated from all the sentences in each 1-second interval, whether it's one sentence (RMC?), or 8 sentences (GGA, GLL, RMC, GSV*3, GSA and VTG). In NeoGPS, counting fixes is equivalent to counting seconds.

NOTE: if you need a super-accurate 5-second interval, consider using the PPS pin, if available.

The local time is CORRECTLY computed in this sketch, even if the timezone shift steps into a new hour, day, month or year. Your sketch does not cross day boundaries correctly. 5.5 hour shift, if I read your sketch correctly.

Should I mention NeoGPS is smaller, faster and more accurate than all other libraries? :) It's available from the Arduino IDE Library Manager, under the menu Sketch -> Include Library -> Manage Libraries.

You should also consider using something besides SoftwareSerial. It is very inefficient, because it disables interrupts for long periods of time. This can interfere with other parts of your sketch or with other libraries.

The best software serial port library is AltSoftSerial. If you can switch to pins 8 & 9, I would strongly recommend doing so.

If you can't switch pins (are you really sure?), you should use my NeoSWSerial. It works on any two pins, and is almost as efficient. It supports the 9600 baud rate you are using.

slash-dev
  • 1,569
  • 2
  • 9
  • 9
  • 1
    Thank you slash-dev,The code you provided works fine and obtains output as i thought.It also solved some bugs in the previous code. – Mathews Sunny Mar 25 '17 at 06:09
4

You should avoid using the normal delay function when working with this library, as it requires a new fix at a regular interval. In the example code you will find a function called smartDelay()copy this function in to your code and use this instead. It looks like this.

static void smartDelay(unsigned long ms)
{
  unsigned long start = millis();
  do 
  {
    while (ss.available())
      gps.encode(ss.read());
  } while (millis() - start < ms);
}

Put this code in the bottom of your code at call smartDelay(5000); instead of delay(5000); in the bottom of the void loop() You should also place the call to displayInfo(); just below smartDelay() like this.

    void loop()
    {
      while (ss.available() > 0)
          if (gps.encode(ss.read()))

      if (millis() > 5000 && gps.charsProcessed() < 10) {
         Serial.println(F("No GPS detected: check wiring."));
         while(true);
      }
      smartDelay(5000);
      displayInfo();
    }

Edit: A even better way

A even better way, especially if you like to do other stuff while you showing the data, is to use millis(). this will also be called more precise every 5 sec. You wil have to declare a variable at the top for this to work. It will look like this.

long timeToDisplay = 0; // Declare this at the top

void loop()
{
  while (ss.available() > 0)
      if (gps.encode(ss.read()))

  if (millis() > 5000 && gps.charsProcessed() < 10) {
     Serial.println(F("No GPS detected: check wiring."));
     while(true);
  }
  if(timeToDisplay <= millis()) {
    timeToDisplay = millis() + 5000;
    displayInfo();
  }
}
XerXeX
  • 784
  • 4
  • 16
  • I'm not sure that you'll still need the while loop in `loop()` if you swap to this method. This will also call `displayInfo()` repeatedly even if no GPS is detected. –  Mar 24 '17 at 19:40
  • 1
    I would recommend changing loop to something like: –  Mar 24 '17 at 19:41
  • `void loop() { charCount = gps.charsProcessed(); smartDelay(5000); if (millis() > 5000 && ((gps.charsProcessed()-charCount) < 10)) { Serial.println(F("No GPS detected: check wiring.")); while(true); } else if((gps.charsProcessed()-charCount) >= 10) { displayInfo(); } }` Where charCount is an unsigned long declared at the top of the file. –  Mar 24 '17 at 19:47
  • @Slater No it will not, cause the displayInfo() checks if the data is valid! – XerXeX Mar 24 '17 at 19:47
  • It checks if the data is valid before adding any data, but not before printing the section headers. –  Mar 24 '17 at 19:48
  • Note that lines like: `Serial.print(F("Location: "));` exist outside of the validity checking. –  Mar 24 '17 at 19:49
  • I'm not very familiar with TinyGPS, but I'm not convinced your implementation wouldn't end up repeating the same stale data if the GPS loses its connection after having a valid entry. –  Mar 24 '17 at 19:50
  • But this is not what the question is about, it's about showing data every 5 sec. – XerXeX Mar 24 '17 at 19:52
  • From the OPs question: _"At every 5 Secs the above code should generate output as per that instant"_ which sounds like he only wants the most recent, valid data. –  Mar 24 '17 at 19:55
  • For the record, I voted for your answer. I'm just trying to help make sure the OP gets the best possible solution. –  Mar 24 '17 at 19:56
  • If you want to make sure the data is the most recent you can check if `gps.location.age()`is lower than the update frequency of the GPS unit `if(gps.location.age() < 1000) { displayInfo(); }` in your case. – XerXeX Mar 24 '17 at 20:09
  • Yeah, that sounds like a better solution. Like I said, I'm not familiar with TinyGPS. –  Mar 24 '17 at 20:13
2

slash-dev give me an answer on how to get data in 5 secs interval from GPS.He also provided some improved methods, which when i followed obtains perfect output.It solved some bugs in the code i provided too.Link to the answer :

https://stackoverflow.com/a/43009260/7699452

result : https://i.stack.imgur.com/pzvsu.png

Community
  • 1
  • 1
Mathews Sunny
  • 1,796
  • 7
  • 21
  • 31