[GPS Speedometer] Slow display updates on Mode 3 Fix.

Hi, Im new to programming and electronics so please be patient with my question and code in general. There's a lot I learnt through these forums and generally I strive to resolve issues with this minimal understanding of concepts, though here I need your help or suggestion with my new project.

This is a GPS speedometer with follwing constituents:

Arduino UNO
Generic 128x64 OLED display
GY-NEO6MV2 GPS Module

The configuration is as follows:

OLED
VCC --> +5V (Arduino)
GND --> GND
SDA --> A4
SCL --> A5

GPS
VCC --> +3.3V (Arduino)
GND --> GND
TXD --> 4
RXD --> 3

As I type this, I believe the hardware is configured correctly, please advice if otherwise. Following is the sketch from Arduino

#include <TinyGPS++.h>
#include <SoftwareSerial.h>
#include "U8glib.h"
#define OLED_RESET 4

U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_NONE | U8G_I2C_OPT_DEV_0);  // I2C / TWI

static const int RXPin = 4, TXPin = 3;
static const int GPSBaud = 9600;
const int timezonehr = +5; //Timezone hour offset
const int timezonemn = 30; //Timezone minute offset
int hr;
int mn;

// The TinyGPS++ object
TinyGPSPlus gps;
// TinyGPSPlus Custom Elements
TinyGPSCustom pdop(gps, "GPGSA", 15); // $GPGSA sentence, 15th element
TinyGPSCustom hdop(gps, "GPGSA", 16); // $GPGSA sentence, 16th element
TinyGPSCustom vdop(gps, "GPGSA", 17); // $GPGSA sentence, 17th element
TinyGPSCustom GPSMode(gps, "GPGSA", 2); // GPSMode over Ground
TinyGPSCustom GPSHorPosError(gps, "GPRME", 15); // GPS Data Check
TinyGPSCustom Altitude(gps, "GPGGA", 9); // Antenna Altitude above mean Sea level

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

// Display This section will contain elements for the OLED
void draw(void) {
// graphic commands to redraw the complete screen should be placed here


  //Seperator (Vertical Line)
  u8g.drawLine(69,27,69,64);


  //GPS:: Checks if GPS has valid fix, then display selective elements
  if (gps.location.isValid()){
  u8g.drawCircle(122,5,5, U8G_DRAW_ALL); // GPS fix circle Top Right
  u8g.drawDisc(122,5,3, U8G_DRAW_ALL); 
  }
  else{
  u8g.drawCircle(122,5,5, U8G_DRAW_ALL);
  }
  u8g.setFont(u8g_font_profont12);
  u8g.setPrintPos(78, 9);
  u8g.print(" SAT:"); u8g.print(gps.satellites.value());
  

  //TIME:: Time Elements including conversion of UTC to Local (IST +5.30)
  u8g.setFont(u8g_font_profont22r);
  u8g.setPrintPos(0, 14);
  hr = gps.time.hour();
  mn = gps.time.minute();
    mn = mn + timezonemn; //Add The Minute Time Zone Offset
    if (mn > 59) { //If The Minute Is Over 59 (From The Time Zone Conversion)
      mn = mn - 60; //Subtract 60 From It
      hr = hr + 1; //Then Add 1 To The Hour
    }
    else {
      if (mn < 0) { //If Minute Is Less Than 0, Do The Inverse Of Above
        mn = mn + 60;
        hr = hr - 1;
      }
    }
    hr = hr + timezonehr; //Add The Hour Time Zone Offset
    if (hr > 23) { //Do The Same Thing We Did Above
      hr = hr - 24;
    }
    else {
      if (hr < 0) {
        hr = hr + 24;
      }
    }
  if (hr < 10) u8g.print('0'); u8g.print(hr); u8g.print(":"); if (mn < 10) u8g.print('0'); u8g.print(mn); u8g.setFont (u8g_font_courR08r); u8g.print(""); if (gps.time.second() < 10) u8g.print('0');u8g.print(gps.time.second()); // u8g.print(" "); if (gps.date.day() < 10) u8g.print('0'); u8g.print(gps.date.day());
  
  u8g.setPrintPos(44, 11);


  //SPEED:: Displays Speed and other related graphics
  //u8g.drawBox(0,20,(gps.speed.kmph()+0),8);
  u8g.drawFrame(2,59,48,4);
  u8g.drawBox(2,59,((gps.speed.kmph()+0)/2.5),4);
  
  if ((gps.speed.kmph()) > 100)
  {
  u8g.drawBox(2,22,63,35);
  u8g.setColorIndex(0);
  u8g.setFont(u8g_font_fur30n);
  u8g.setPrintPos(4, 55);
  u8g.print(gps.speed.kmph(),0);
  }
  else
  {
  u8g.setFont(u8g_font_fur30n);
  u8g.setPrintPos(0, 56);
  u8g.print(gps.speed.kmph(),0);
  }
  u8g.setColorIndex(1);
  
  u8g.setFont(u8g_font_orgv01r);
  u8g.setPrintPos(0, 22);
  //u8g.print("SPEED >>");
  u8g.setFont(u8g_font_fur30n);
  u8g.setPrintPos(0, 56);
  u8g.print(gps.speed.kmph(),0);
  u8g.setFont(u8g_font_orgv01r);
  u8g.setPrintPos(53, 64);
  u8g.print("KM");
  

  //GPS DATA:: Other GPS related data and statistics
  u8g.setFont(u8g_font_profont10r);
  u8g.setPrintPos(75, 34); u8g.print("ACCU:");u8g.print(hdop.value());
  u8g.setPrintPos(75, 44); u8g.print("ALT:"); u8g.print(gps.altitude.meters()); u8g.print("m");
  u8g.setPrintPos(75, 54); u8g.print("GPS MODE:"); u8g.print(GPSMode.value());
  u8g.setPrintPos(75, 64); u8g.print("GPS ERR:"); u8g.print(GPSHorPosError.value());

}


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

  Serial.println(F("UsingCustomFields.ino"));
  Serial.println(F("Demonstrating how to extract any NMEA field using TinyGPSCustom"));
  Serial.print(F("Testing TinyGPS++ library v. ")); Serial.println(TinyGPSPlus::libraryVersion());
  Serial.println(F("by Mikal Hart"));
  Serial.println();
  
}


void loop() 
{
 
  // Every time anything is updated, print everything.
  if (gps.altitude.isUpdated() || gps.satellites.isUpdated() ||
      pdop.isUpdated() || hdop.isUpdated() || vdop.isUpdated() || gps.speed.isUpdated() ||
      GPSMode.isUpdated() || GPSHorPosError.isUpdated() || Altitude.isUpdated())
  {
    Serial.println(F("")); 
    Serial.print(F("ALT="));   Serial.print(gps.altitude.meters()); 
    Serial.print(F(" PDOP=")); Serial.print(pdop.value()); 
    Serial.print(F(" HDOP=")); Serial.print(hdop.value()); 
    Serial.print(F(" VDOP=")); Serial.print(vdop.value());
    Serial.print(F(" SATS=")); Serial.print(gps.satellites.value());
    Serial.print(F(" Altitude=")); Serial.print(gps.altitude.meters());
    Serial.print(F(" GPS Mode=")); Serial.print(GPSMode.value());
    Serial.print(F(" GPS Data=")); Serial.print(GPSHorPosError.value());
    Serial.print(F(" TIME=")); Serial.print(hr); Serial.print(mn); Serial.print(gps.time.second());
  }

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

  
  // This code was repeated because the GPS data would otherwise take about a minute to update on OLED even without Mode 3 Fix.
  // I believe this forces the page to be recreated whenever a component is updated.
  if (gps.altitude.isUpdated() || gps.satellites.isUpdated() ||
      pdop.isUpdated() || hdop.isUpdated() || vdop.isUpdated() || gps.speed.isUpdated() ||
      GPSMode.isUpdated() || GPSHorPosError.isUpdated() || Altitude.isUpdated() || gps.time.isUpdated())
  {
  
    {
    u8g.firstPage();
    do{
    draw();
    } 
    while(u8g.nextPage());
   }
  }
}

Issue: The GPS seems receive satellite data and after a few seconds establishes a Mode 1 fix. Things work fine untill this moment, the Speed is updated and displays acceptable values. However once the GPS establishes a 3D fix, the Speed data is updated about once a minute. I chose to display seconds on the display precisely to figure out when the data on the display becomes stagnant.

Once the code is uploaded to the arduino and the GPS goes through a cold start (powered fresh) the serial port updates occour fast, and usually they retain their interval of about once a second, but sometimes gradually slows down as the GPS accumulates more prcise fix, the updates begin to slumber untill a point where they seem to stall or occour about once a minute sometimes even more. This however defeats the intended purpose of a speedometer. Irrespective of the serial port data updates, the OLED display data essentially stalls for about a minute once on a 3D fix.

Please check the attached image for reference

The serial port data (at arbitrary interval. please note that even though the updates over the serial port were regular, the OLED display reflected a change once a minute:

ALT=204.30 PDOP=3.14 HDOP=1.48 VDOP=2.76 SATS=4 Altitude=204.30 GPS Mode=3 GPS Data= TIME=1940
ALT=204.30 PDOP=3.14 HDOP=1.48 VDOP=2.76 SATS=4 Altitude=204.30 GPS Mode=3 GPS Data= TIME=1940
ALT=204.30 PDOP=3.14 HDOP=1.48 VDOP=2.76 SATS=4 Altitude=204.30 GPS Mode=3 GPS Data= TIME=1940

I hope i have rendered the issue correctly and I shall be glad if anyone is willing to help resolve it.

Thanks.

static const int RXPin = 4, TXPin = 3;

Do you know what the static keyword does? At global scope?

  // Every time anything is updated, print everything.
  if (gps.altitude.isUpdated() || gps.satellites.isUpdated() ||
      pdop.isUpdated() || hdop.isUpdated() || vdop.isUpdated() || gps.speed.isUpdated() ||
      GPSMode.isUpdated() || GPSHorPosError.isUpdated() || Altitude.isUpdated())
  {
    Serial.println(F(""));
    Serial.print(F("ALT="));   Serial.print(gps.altitude.meters());
    Serial.print(F(" PDOP=")); Serial.print(pdop.value());
    Serial.print(F(" HDOP=")); Serial.print(hdop.value());
    Serial.print(F(" VDOP=")); Serial.print(vdop.value());
    Serial.print(F(" SATS=")); Serial.print(gps.satellites.value());
    Serial.print(F(" Altitude=")); Serial.print(gps.altitude.meters());
    Serial.print(F(" GPS Mode=")); Serial.print(GPSMode.value());
    Serial.print(F(" GPS Data=")); Serial.print(GPSHorPosError.value());
    Serial.print(F(" TIME=")); Serial.print(hr); Serial.print(mn); Serial.print(gps.time.second());
  }

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

Use the data, if it is up-to-date. Then, get the data. Why?

You are writing a LOT of data to a slow device (the OLED). Do you suppose that contributes to the slowing down of the system, as data is lost because the GPS is not read often enough?

Break your drawing of data on the OLED into functions. Call no more than one of the functions on any given pass through loop().

Thank you Paul for your interest. I'll try to be specific here.

Do you know what the static keyword does? At global scope?

I borrowed the functional code and since it appeared to perform well, I did not change the structure. However, I would be thankful if you enlighten the dark areas for me.

You are writing a LOT of data to a slow device (the OLED). Do you suppose that contributes to the slowing down of the system, as data is lost because the GPS is not read often enough?

Break your drawing of data on the OLED into functions. Call no more than one of the functions on any given pass through loop().

I tried the apparatus without the code in question, reducing the OLED data to Speed itself. However it exhibits the same behaviour. Seems like its the code code that dumps too much data. That is all I could think of. I still don't understand why a stronger fix leads to erratic behaviour. Is there any way to read NMEA data without including a library (TinyGPS++ or such) or removing Sofware serial altogether and directly connect the TX and RX of GPS to corresponding pins of TX RX of Arduino? Would that make the system efficient?

Antrix:
I believe the hardware is configured correctly, please advice if otherwise.

Nope. The Arduino is a 5V device, and the GPS is a 3.3V device. You should not connect the Arduino transmit pin 4 directly to the GPS RX pin. Here is a post that describes several alternatives, with level shifting modules or discreet parts (resistors and a diode).

I would strongly suggest using AltSoftSerial on pins 8 & 9 for the GPS. SoftwareSerial is very inefficient, because it disables interrupts for long periods of time. This will interfere with other parts of your sketch, like updating the OLED.

If you can't use those pins, you should use my NeoSWSerial. It is almost as efficient as AltSoftSerial, and it can use any two pins.

And yes, you can hook the GPS TX pin to the Arduino RX pin 0. You can still do debug prints to the Serial Monitor, but you would have to disconnect the GPS TX wire if you upload over USB. That would be most efficient.

I would also suggest my NeoGPS library. It is smaller, faster and more accurate than all other libraries. It groups all those sentences into one coherent fix data structure, exactly once per second. This avoids the messy IsUpdated tests, and updates the OLED just when the GPS goes quiet for a while. This probably has the greatest impact on the performance, especially if you use a software serial port. As you noticed:

Seems like its the code code that dumps too much data. That is all I could think of.

Yes, many example programs from other libraries have this problem: printing too much at the wrong time.

And you are using the outdated U8glib. I would strongly suggest switching to the new U8G2lib.

I have attached a NeoGPS+NeoSWSerial+U8g2 version of your sketch. Notes:

* The correct way to perform timezone shifts is to convert the DDMMYY-HHMMS to a seconds count from a time "origin" (aka EPOCH). Then add the timezone shift, in seconds) and convert it back to a DDMMYY-HHMMYY structure. The code above shows how to set the localTime structure from the GPS time plus a seconds offset. This will produce the correct day, month and year, even if the time offset crosses one of those boundaries.

* Remember that a GPS device may not know all the pieces of a fix, yet. If it does not have a valid speed, it will send an empty field to the Arduino. NeoGPS marks each piece with a valid flag, so your code can handle cases when a piece is really not available.

* All double-quoted string arguments are using the F macro to save RAM.

* I'm not certain of the correct u8g2 constructor:

    U8G2_SSD1306_128X64_NONAME_1_HW_I2C u8g2( U8G2_R0, U8X8_PIN_NONE ); // no rotation

... nor which of the new fonts you should use:

    u8g2.setFont(u8g2_font_inr21mr); // instead of profont22r?

* Be sure to review the OLED_RESET pin number. It looks like you were using the same pin for SoftwareSerial. :stuck_out_tongue:

* The attached sketch is significantly smaller:

The original sketch uses 25832 bytes of program space and 1085 bytes of RAM.
The NeoGPS version uses 20282 bytes of program space and 1042 bytes of RAM.

Some of the savings could be due to the fonts.

PaulS:
static const int RXPin = 4, TXPin = 3;

Do you know what the static keyword does? At global scope?

Hmm... Do you know what it does? There is absolutely nothing wrong with the usage here, as it will reduce name collisions. This name is not very unique, so I might even suggest it here. I would definitely suggest it for a routine called "draw". It's a good coding habit to have, although it seems the OP did not know it was a good thing to do...

@Antrix, it means that the variable name "RXPin" is visible only within this file. If another file, perhaps in a library, had also declared the same variable name (of the same "const int" type), you would have gotten a "multiply-defined symbol" link error.

If you want to try it, NeoGPS is available from the Arduino IDE Library Manager, under the menu Sketch -> Include Library -> Manage Libraries. Even if you don't try it, be sure to read the Troubleshooting page for other tips for GPS sketches.

Cheers,
/dev


P.S. You will have to enable the following items in GPSfix_cfg.h:

    #define GPS_FIX_HDOP
    #define GPS_FIX_VDOP
    #define GPS_FIX_PDOP
    #define GPS_FIX_LAT_ERR

... and the following items in NMEAGPS_cfg.h:

    #define NMEAGPS_PARSE_GSA
    #define NMEAGPS_PARSE_GST

Antrix.ino (5.31 KB)

Thank you Dev! I'm grateful that you have taken the time to make necessary changes, especially the complete shift to u8g2 graphics library and NeoSWSerial. It's 0130 hrs here in India, I shall return when the sun takes over.

Please keep this thread alive.

UPDATE

The sketch has metamorphosed into a falcon that soars into the sky! Thank you Dev, I knew programming is an art, now I can truly apreciate the glory of efficient coding and wise resource allocation. NeoSWSerial seems to have cast its magic and lo it flies! No more stagnant OLED data. While I acquaint myself with u8g2 library and NeoSWSerial protocols, I have taken certian liberties of amending the fonts to suite me preferences, as the core is now capable of such adornments.

Returning to the project itself, there are certian observations and questions that have cropped up in due course. There is debate over necessary implimentation. of logic shifters, some oppose citing the fact that the system is tolerant enough for frequent unintended voltage spikes, however it seems like its application assures the devices function within their optimal voltage range. An existing thread measures every aspect of logic shifting. So while mine are a couple of days from delivery, is it possible to shift to Arduino Pro Mini 3.3v module for this project? Will the 8 Mhz chip accomodate the requirements of GPS?

I would steer clear of SoftwareSerial, so NeoSWSerial can presumably become the status-quo for any such implementation. Although, coding falls beyond my jurisdiction, so I strive to make things functional.

The concept of Localtime is clever, most of the examples I stumbled upon employed variations of 'if, else' loops, though functional nonetheless. Though this is a global fix for the GPS timezone conversion. I wonder if such a function could be integrated into GPS libraries, so coding could be reduced.

Talking about fonts, I'm sure that shoudn't be a problem, your references are spot on! u8g2 appears more responsive compared to its previous iteration, a natural progression I believe. And, yes! I was indeed using OLED_RESET as TX pin for the GPS :sweat_smile:

Ah!, so now I can recall, in some isolated compilation incidents, the compiler would indicate such multiply-defined symbol linking errors. Prudent. it is to anticipate such declaration conflicts.

Before I conclude, there appears a slight issue that prevents me from finalizing it. Though I have made the necessary changes to GPSfix_cfg.h and NMEAGPS_cfg.h, the values like HDOP, Altitude and Number of Satellites appears in the beginning, but as the GPS proceedes towards 3D fix, they simply disappear. I dont know if I have faltered in any procedure or perhaps its a systemic error. Please advise in this regard.

Finally a heartfelt thanks for all your kind efforts!

PS. Serial output for reference.

ALT= PDOP= HDOP=34.45 VDOP= SATS=0 GPS Mode=0 GPS Data= TIME=2017-05-02 21:02:21
ALT= PDOP= HDOP=34.45 VDOP= SATS=0 GPS Mode=0 GPS Data= TIME=2017-05-02 21:02:21
ALT= PDOP= HDOP=5.85 VDOP= SATS=4 GPS Mode=0 GPS Data= TIME=2017-05-02 21:02:22
ALT= PDOP= HDOP=5.85 VDOP= SATS=4 GPS Mode=0 GPS Data= TIME=2017-05-02 21:02:23
ALT= PDOP= HDOP=5.85 VDOP= SATS=4 GPS Mode=3 GPS Data= TIME=2017-05-02 21:02:24
ALT= PDOP= HDOP= VDOP= SATS= GPS Mode=3 GPS Data= TIME=2017-05-02 21:02:25
ALT= PDOP= HDOP= VDOP= SATS= GPS Mode=3 GPS Data= TIME=2017-05-02 21:02:26
ALT= PDOP= HDOP= VDOP= SATS= GPS Mode=3 GPS Data= TIME=2017-05-02 21:02:27
ALT= PDOP= HDOP= VDOP= SATS= GPS Mode=3 GPS Data= TIME=2017-05-02 21:02:28

Antrix:
I can recall, in some isolated compilation incidents, the compiler would indicate such multiply-defined symbol linking errors.

Compiler Incidents.jpg

There is debate over necessary implimentation. of logic shifters

There is no debate. You can check the Electrical Specifications of each device, if you like.

the system is tolerant enough for frequent unintended voltage spikes

It has nothing to do with spikes. The Arduino will maintain a constant 4.2V (minimum) output on its TX line when nothing is being sent to the GPS device. The GPS device can only accept 3.6V maximum, the point at which the input protection diodes begin shunting current to the 3.3V VCC pin. Exceeding 3.6V will shunt more and more current. Eventually, the GPS device will overheat or the protection diodes will fail, and then the RX input transistors will fail.

its application assures the devices function within their optimal voltage range.

There is no code that will prevent this. Digital outputs (from a serial port) can only output a logic one (4.2V minimum) or logic zero (0.9V maximum). There is no code that will output 3V for a logic one. When not transmitting, serial ports are in the logic one state.

is it possible to shift to Arduino Pro Mini 3.3v module for this project? Will the 8 Mhz chip accomodate the requirements of GPS?

Just DON'T connect the Arduino transmit pin to the GPS RX pin. You're not sending anything to the GPS device. Yes, 8MHz is fast enough.

the values like HDOP, Altitude and Number of Satellites appears in the beginning, but as the GPS proceedes towards 3D fix, they simply disappear.

Run NMEAorder.ino. I'll bet that you don't have the correct LAST_SENTENCE_IN_INTERVAL in NMEAGPS_cfg.h.

Cheers,
/dev

That newspaper clipping looks real :wink:

Yes, you were absolultely right about LAST_SENTENCE_IN_INTERVAL in NMEAGPS in cfg.h!, I ran NMEAorder.ino and it instructed me to change the value to NMEAGPS::NMEA_GLL, which I gladly did! Now all parameters are displayed as intended! You owe the credit for making this work Dev, thank you very much!

Before I take leave (for now :slight_smile: ), there's a bit of code that's troubling me since beginning:

      u8g2.drawBox(2,22,63,35);
      u8g2.setColorIndex(1);
      u8g2.setFont(u8g2_font_logisoso30_tn);
      u8g2.setCursor(4, 55);
      u8g2.print( (int)speed );

I have used this code for a time now without any issues, albeit with horrible screen refresh delays, thanks to my adherence to u8glib :), but, it has always worked, this however seems to have a peculiar behaviour in this project since its inception. Please take a look below:

What appears is that the text is being rendered alongwith its frame and not just the actual pixels of the font. Furthermore, I don't know if this is connected, all my previous sketches that involved u8glib library upon oploading show starfield like random pixels, and irrespective of any sketch compiled, the compiler displays a warning "Category 'Real-time clock' in library DS3231 is not valid. Setting to 'Uncategorized'"

the compiler displays a warning "Category 'Real-time clock' in library DS3231 is not valid. Setting to 'Uncategorized'"

The compiler does no such thing. The IDE does that, and it COMPLETELY unrelated to your issue. You COULD address the issue by updating the DS3231 library.

Your snippet of code does not appear in any other code you have posted. But, I have to wonder. If you want speed as an int, why would you define it as any other type?

PaulS:
Your snippet of code does not appear in any other code you have posted. But, I have to wonder. If you want speed as an int, why would you define it as any other type?

It is in the code I attached to reply #3. Here is the relevant section:

 if (fix.valid.speed) {
    //SPEED:: Displays Speed and other related graphics
    //u8g2.drawBox(0,20,(gps.speed.kmph()+0),8);
    float speed = fix.speed_kph();
    u8g2.drawFrame(2,59,48,4);
    u8g2.drawBox(2,59,(speed/2.5),4);
    
    if (speed > 100)
    {
      u8g2.drawBox(2,22,63,35);
      u8g2.setColorIndex(0);
      //u8g2.setFont(u8g_font_fur30n);
      u8g2.setCursor(4, 55);
      u8g2.print( (int)speed );
    }
    else
    {
      //u8g2.setFont(u8g_font_fur30n);
      u8g2.setCursor(0, 56);
      u8g2.print( (int)speed );
    }
    u8g2.setColorIndex(1);
    
    //u8g2.setFont(u8g_font_orgv01r);
    u8g2.setCursor(0, 22);
    //u8g2.print("SPEED >>");
    //u8g2.setFont(u8g_font_fur30n);
    u8g2.setCursor(0, 56);
    u8g2.print( (int)speed ); 
    //u8g2.setFont(u8g_font_orgv01r);
    u8g2.setCursor(53, 64);
    u8g2.print(F("KM"));
  }

He's using it two different ways. He originally used

    Serial.print( speed, 0 );

Either way works, although I lean slightly toward the int cast.

Antrix:
the text is being rendered along with its frame and not just the actual pixels of the font.

Hmm, make sure the font you have selected has the numeric characters (ASCII 48-57). It looks like those characters are not defined. Or try the original print statement, printing the float speed with 0 decimals, not cast to an int.

Hi.
Was the problem solved ? Can you share a new sketch ?
Thank you.

There had been a delay, I got engaged with work. This code has been in service for months now without any artefact. I wish to express my gratitude to the members of Arduino community for making this work. Please feel free to implement the code as per your requirements.

//  PICK ONE of these serial ports:

// BEST: AltSoftSerial on pins 8 & 9 (on an UNO)
//#include <AltSoftSerial.h>
//static AltSoftSerial gpsPort;

static const int RXPin = 4, TXPin = 3;

// 2nd BEST: NeoSWSerial on any two pins
#include <NeoSWSerial.h>
static NeoSWSerial gpsPort(RXPin, TXPin);
static const int GPSBaud = 9600;

// WORST: SoftwareSerial NOT RECOMMENDED

static const bool useOLED = true;

#include <U8g2lib.h>
#define OLED_RESET 5 // Should not be RXPin!

U8G2_SSD1306_128X64_NONAME_1_HW_I2C u8g2( U8G2_R2, U8X8_PIN_NONE ); // 180 degree rotation
static const int timezonehr = +5; //Timezone hour offset
static const int timezonemn = 30; //Timezone minute offset

#include <NMEAGPS.h>
static NMEAGPS         gps;
static gps_fix         fix;
static NeoGPS::time_t  localTime;

// Display: This section will contain elements for the OLED
static void draw(void) {
  // graphic commands to redraw the complete screen should be placed here


  //Separator (Vertical Line)
  u8g2.drawLine(68,27,68,64);


  //GPS:: Checks if GPS has valid fix, then display selective elements
  if (fix.valid.location) {
    u8g2.drawCircle(122,5,5, U8G2_DRAW_ALL); // GPS fix circle Top Right
    u8g2.drawDisc(122,5,3, U8G2_DRAW_ALL); 
  } else {
    u8g2.drawCircle(122,5,5, U8G2_DRAW_ALL);
  }
  u8g2.setFont(u8g2_font_7x13_tr);
  u8g2.setCursor(72, 9);
  u8g2.print( F(" SAT:") );
  if (fix.valid.satellites)
    u8g2.print( fix.satellites );
  

  if (fix.valid.time) {
    // TIME:: Time Elements including conversion of UTC to Local (IST +5.30)
    u8g2.setFont(u8g2_font_crox4h_tn); // instead of profont22r?
    u8g2.setCursor(0, 14);

    if (localTime.hours < 10)
      u8g2.print( '0' );
    u8g2.print( localTime.hours );
    u8g2.print( ':' );
    if (localTime.minutes < 10)
      u8g2.print( '0' );
    u8g2.print( localTime.minutes );
    u8g2.print( ' ' );
    u8g2.setFont(u8g2_font_7x13_tr); 
    if (localTime.seconds < 10)
      u8g2.print( '0' ); u8g2.print( localTime.seconds );
  }
    
  u8g2.setCursor(44, 11);

  if (fix.valid.speed) {
    //SPEED:: Displays Speed and other related graphics
    float speed = fix.speed_kph();
    u8g2.drawFrame(2,59,48,4);
    u8g2.drawBox(2,59,(speed/2.5),4);
    
    if ( (int)speed  > 100)
    {
      u8g2.drawFrame(0,22,64,35);
      u8g2.setFont(u8g2_font_logisoso30_tn);
      u8g2.setCursor(0, 54);
      u8g2.print( (int)speed );
    }
    else
    {
      u8g2.setFont(u8g2_font_logisoso30_tn);
      u8g2.setCursor(0, 56);
      u8g2.print( (int)speed );
    }
    u8g2.setColorIndex(1);
    
    //u8g2.setFont(u8g_font_orgv01r);
    u8g2.setCursor(0, 22);
    //u8g2.print("SPEED >>");
    //u8g2.setFont(u8g_font_fur30n);
    u8g2.setCursor(0, 56);
    u8g2.print( (int)speed ); 
    u8g2.setFont(u8g2_font_5x7_mr);
    u8g2.setCursor(53, 64);
    u8g2.print(F("KM"));
  }  

  //GPS DATA:: Other GPS related data and statistics
  u8g2.setFont(u8g2_font_5x7_mr);
  u8g2.setCursor(74, 34); u8g2.print( F("HDOP ") );
  if (fix.valid.hdop) u8g2.print(fix.hdop / 1000.0);

  u8g2.setCursor(74, 44); u8g2.print( F("ALT ") );
  if (fix.valid.altitude) u8g2.print(fix.altitude()); u8g2.print( 'm' );

  u8g2.setCursor(74, 54); u8g2.print( F("GPS MODE:") );
  if (fix.valid.status) u8g2.print(fix.status);
  
  u8g2.setCursor(74, 64); u8g2.print( F("GPS ERR:") );
  if (fix.valid.lat_err) u8g2.print(fix.lat_err());
    // lon_err and alt_err also available

}


void setup() 
{
  Serial.begin(115200);
  gpsPort.begin(GPSBaud);
  u8g2.begin();

  Serial.println( F("NeoGPS display\n") );
}


void loop() 
{
  if (gps.available( gpsPort )) {
    fix = gps.read(); // get the entire, assembled fix structure

    // Shift the date/time to local time
    NeoGPS::clock_t localSeconds;
    {
      using namespace NeoGPS; // save a little typing below...

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

    Serial.println(); 
    Serial.print( F("ALT=")       );
    if (fix.valid.altitude) Serial.print(fix.altitude()); 
    Serial.print( F(" PDOP=")     );
    if (fix.valid.pdop) Serial.print(fix.pdop * 0.001); 
    Serial.print( F(" HDOP=")     );
    if (fix.valid.hdop) Serial.print(fix.hdop * 0.001); 
    Serial.print( F(" VDOP=")     );
    if (fix.valid.vdop) Serial.print(fix.vdop * 0.001);
    Serial.print( F(" SATS=")     );
    if (fix.valid.satellites) Serial.print(fix.satellites);
    Serial.print( F(" GPS Mode=") );
    if (fix.valid.status) Serial.print(fix.status);
    Serial.print( F(" GPS Data=") );
    if (fix.valid.lat_err) Serial.print(fix.lat_err()); // m
    Serial.print( F(" TIME=")     );
    if (fix.valid.time) Serial << localTime;

    if (useOLED) {
      // Update the display, all at once, during the GPS quiet time
      u8g2.firstPage();
      do{
        draw();
      } 
      while(u8g2.nextPage());
    }
  }
}

Hello,
I tried using your sketch but keep on getting errors as below, can you help ?
I'm trying to build a GPS based speedo for a boat and don't know much about arduino

Arduino: 1.8.5 (Windows 10), Board: "Arduino/Genuino Uno"
sketch_may24a:29: error: 'gps_fix' does not name a type

 static gps_fix         fix;

        ^

sketch_may24a:30: error: 'NeoGPS' does not name a type

 static NeoGPS::time_t  localTime;

        ^

C:\Users\Jack\Documents\Arduino\sketch_may24a\sketch_may24a.ino: In function 'void draw()':

sketch_may24a:42: error: 'fix' was not declared in this scope

   if (fix.valid.location) {

       ^

sketch_may24a:51: error: 'fix' was not declared in this scope

   if (fix.valid.satellites)

       ^

sketch_may24a:55: error: 'fix' was not declared in this scope

   if (fix.valid.time) {

       ^

sketch_may24a:60: error: 'localTime' was not declared in this scope

     if (localTime.hours < 10)

         ^

sketch_may24a:62: error: 'localTime' was not declared in this scope

     u8g2.print( localTime.hours );

                 ^

sketch_may24a:75: error: 'fix' was not declared in this scope

   if (fix.valid.speed) {

       ^

sketch_may24a:110: error: 'fix' was not declared in this scope

   if (fix.valid.hdop) u8g2.print(fix.hdop / 1000.0);

       ^

sketch_may24a:113: error: 'fix' was not declared in this scope

   if (fix.valid.altitude) u8g2.print(fix.altitude()); u8g2.print( 'm' );

       ^

sketch_may24a:116: error: 'fix' was not declared in this scope

   if (fix.valid.status) u8g2.print(fix.status);

       ^

sketch_may24a:119: error: 'fix' was not declared in this scope

   if (fix.valid.lat_err) u8g2.print(fix.lat_err());

       ^

C:\Users\Jack\Documents\Arduino\sketch_may24a\sketch_may24a.ino: In function 'void loop()':

sketch_may24a:137: error: no matching function for call to 'NMEAGPS::available(NeoSWSerial&)'

   if (gps.available( gpsPort )) {

                              ^

C:\Users\Jack\Documents\Arduino\sketch_may24a\sketch_may24a.ino:137:30: note: candidate is:

In file included from C:\Users\Jack\Documents\Arduino\sketch_may24a\sketch_may24a.ino:27:0:

C:\Users\Jack\Documents\Arduino\libraries\ArduinoLibrary_NMEAGPS-master/NMEAGPS.h:98:9: note: int NMEAGPS::available()

     int available();

         ^

C:\Users\Jack\Documents\Arduino\libraries\ArduinoLibrary_NMEAGPS-master/NMEAGPS.h:98:9: note:   candidate expects 0 arguments, 1 provided

sketch_may24a:138: error: 'fix' was not declared in this scope

     fix = gps.read(); // get the entire, assembled fix structure

     ^

sketch_may24a:138: error: 'class NMEAGPS' has no member named 'read'

     fix = gps.read(); // get the entire, assembled fix structure

               ^

sketch_may24a:141: error: 'NeoGPS' has not been declared

     NeoGPS::clock_t localSeconds;

     ^

sketch_may24a:143: error: 'NeoGPS' is not a namespace-name

       using namespace NeoGPS; // save a little typing below...

                       ^

sketch_may24a:143: error: expected namespace-name before ';' token

       using namespace NeoGPS; // save a little typing below...

                             ^

sketch_may24a:145: error: 'localSeconds' was not declared in this scope

       localSeconds  = (clock_t) fix.dateTime;  // convert structure to a second count

       ^

sketch_may24a:145: error: 'clock_t' was not declared in this scope

       localSeconds  = (clock_t) fix.dateTime;  // convert structure to a second count

                        ^

sketch_may24a:147: error: 'SECONDS_PER_HOUR' was not declared in this scope

         timezonehr * SECONDS_PER_HOUR + 

                      ^

sketch_may24a:148: error: 'SECONDS_PER_MINUTE' was not declared in this scope

         timezonemn * SECONDS_PER_MINUTE;       // shift to desired timezone

                      ^

sketch_may24a:149: error: 'localTime' was not declared in this scope

       localTime     = localSeconds;            // convert back to a structure

       ^

sketch_may24a:168: error: 'localTime' was not declared in this scope

     if (fix.valid.time) Serial << localTime;

                                   ^

It looks like you did not correctly install the NeoGPS library. The error message says

    ...Arduino\libraries\ArduinoLibrary_NMEAGPS-master/NMEAGPS.h

That file should be in

    ...Arduino\libraries\NeoGPS\src\NMEAGPS.h

I would suggest deleting the entire ArduinoLibrary_NMEAGPS-master subdirectory, and any other subdirectories that have NeoGPS files.

Then install NeoGPS according to the Installation instructions.

If you also have trouble with other libraries, it may be better to delete your entire Arduino\Libraries directory and re-install all the libraries you are using. I strongly suggest using the Arduino Library Manager, under the menu Sketch -> Include Library -> Manage Libraries. Then type "NeoGPS" or any other library's name in the Search box.