Speedometer using UNO and GPS, evaluating

Hi!

Thanks to all of You supporting me in the previous topic, Speedometer using UNO and GPS.

Now I have tested the project in my car and in a railroad car.

The 2 different environments, car or railroad shows a little different strange things.

Common is:
Data is not uptated for some time. Speed, Altitude and Compass (direction) are on holiday for quite some time, say from 10 to 30 seconds.

In the railcar speed unexpectedly drops to 2/3 and stays there for some seconds before updating to proper values.

Being at home in the shop I added some debugging code using the ".age()" function printed out to Serial Monitor. The Project is at rest, no moves. Logging maximum age of Speed and Altitude I reach quite large time laps like 30 seconds or more.

I probably don't understand what is told in TinyGPS++ | Arduiniana.

The code is large…

boolean debug = false;

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

#include <Wire.h>
#include <hd44780.h>
#include <hd44780ioClass/hd44780_I2Cexp.h>

#define BACKLIGHT_PIN     13

hd44780_I2Cexp mylcd; // declare lcd object: auto locate & config exapander chip

// LCD geometry
const int LCD_COLS = 20;
const int LCD_ROWS = 4;

// The TinyGPS++ object
TinyGPSPlus gps;

//Create GPS channel
#define RX 2
#define TX 3
SoftwareSerial mySerial(RX, TX);  // pick any 2 unused digital pins (not pins 0 or 1)

long Start_Up_Time;

#define time_adj 2// adjust: +1 for winter time, +2 for summer time

int act_cnt = 0;
char activity[] = "<><>";
//int last_tmp_a, last_tmp_A, last_tmp_SP = 0;
int last_tmp_SP, last_tmp_A, last_tmp_D = 0;
unsigned long last_tmp_SP_time, last_tmp_A_time, last_tmp_D_time = millis();
bool clear_debug_print = true;
unsigned int speed_age_min, altitude_age_min = 65535;
unsigned int speed_age_max, altitude_age_max = 0;

void setup()
{
  int status;

  status = mylcd.begin(LCD_COLS, LCD_ROWS);
  if (status) // non zero status means it was unsuccesful
  {
    status = -status; // convert negative status value to positive number

    // begin() failed so blink error code using the onboard LED if possible
    hd44780::fatalError(status); // does not return
  }
  mylcd.clear();
  // initalization was successful, the backlight should be on now

  mySerial.begin(9600);  // 9600 is the default baud rate for my Neo6m units
  Serial.begin(9600);// For PC, serial monitor

  // Print start message to the LCD
  mylcd.print("Starting");
  // Print start message to the PC
  Serial.println("Starting");//sent to PC

  Start_Up_Time = millis();
  clear_debug_print = true;

}

void loop()
{
  int tmp;

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

  //Then query it for the desired information:
  //                                                   Altitude
  //if (gps.altitude.isUpdated())
  if (gps.altitude.isValid())
  {
    if (debug)
    {
      Serial.print("ALT               = ");
      Serial.println(gps.altitude.meters());
    }
    tmp = int(gps.altitude.meters() + 0.5);
    if (tmp > 9999) tmp = 9999;
    if (tmp < -999) tmp = -999;
    mylcd.setCursor(0, 1);
    mylcd.print("Alt: ");
    if (tmp < 1000) mylcd.print(" ");
    if (tmp < 100) mylcd.print(" ");
    if (tmp < 10) mylcd.print(" ");
    mylcd.print(tmp);
    //    mylcd.print(" "); //tested 180709 22.17
    if (last_tmp_A == tmp)
    {
      mylcd.setCursor (9, 2); mylcd.print("A    "); mylcd.setCursor(11, 2); mylcd.print( min( ( millis() - last_tmp_A_time) / 1000, 999));
      clear_debug_print = false;
    }
    else
    {
      last_tmp_A = tmp;
      last_tmp_A_time = millis();
      clear_debug_print = true;
    }
    tmp = gps.altitude.age();
    altitude_age_max = max(altitude_age_max, tmp);
    altitude_age_min = min(altitude_age_min, tmp);
    if(altitude_age_min == 0) altitude_age_min = 999;
    Serial.print("Altitude age: "); Serial.print(tmp); Serial.print(" ");
    Serial.print(altitude_age_min); Serial.print(" "); Serial.print(altitude_age_max); Serial.print(" ");

  }
 
  //                                                                Hdop
  if (gps.hdop.isValid())
  {
    tmp = gps.hdop.value();
    mylcd.setCursor(16, 3);
    if (tmp > 100) mylcd.print("H>1");
    else mylcd.print("H<1");
  }

 
  //                                                                 speed
  if (gps.speed.isValid())
  {
    tmp = int(gps.speed.kmph() + 0.5);
    mylcd.setCursor(0, 0);
    mylcd.print("Speed: ");
    if (tmp < 100)mylcd.print(" ");
    if (tmp < 10)mylcd.print(" ");
    mylcd.print(tmp);
    //   mylcd.print(" ");    remarking 180709 21.08
    if (last_tmp_SP == tmp)
    {
      mylcd.setCursor(15, 2); mylcd.print("S    "); mylcd.setCursor(17, 2); mylcd.print( min( ( millis() - last_tmp_SP_time) / 1000, 999));
      clear_debug_print = false;
    }
    else
    {
      last_tmp_SP = tmp;
      last_tmp_SP_time = millis();
      clear_debug_print = true;
    }
    tmp = gps.speed.age();
    speed_age_max = max(speed_age_max, tmp);
    speed_age_min = min(speed_age_min, tmp);
    if(speed_age_min == 0) speed_age_min = 999;
    Serial.print("Speed age: "); Serial.print(tmp); Serial.print(" ");
    Serial.print(speed_age_min); Serial.print(" "); Serial.println(speed_age_max);
  }

  //                                                             date
  if (gps.date.isUpdated())
  {
    mylcd.setCursor(0, 3);
    mylcd.print(gps.date.year());
    mylcd.print("-");
    if (gps.date.month() < 10) mylcd.print("0");
    mylcd.print(gps.date.month());
    mylcd.print("-");
    if (gps.date.day() < 10) mylcd.print("0");
    mylcd.print(gps.date.day());
  }
  //                                                     time
  if (gps.time.isUpdated())
  {
    if (debug)
    {
      Serial.print("Time  = "); Serial.print(gps.time.hour()); // Hour (0-23) (u8)
      Serial.print("."); Serial.print(gps.time.minute()); // Minute (0-59) (u8)
      Serial.print("."); Serial.println(gps.time.second()); // Second (0-59) (u8)
    }
    mylcd.setCursor(0, 2);
    tmp = gps.time.hour() + time_adj; // adjust: +1 for winter time, +2 for summer time
    if (tmp >= 24) tmp = tmp - 24;
    if (tmp < 10) mylcd.print("0");
    mylcd.print(tmp);
    mylcd.print("-");
    if (gps.time.minute() < 10) mylcd.print("0");
    mylcd.print(gps.time.minute());
    mylcd.print("-");
    if (gps.time.second() < 10) mylcd.print("0");
    mylcd.print(gps.time.second());
    mylcd.print(" ");
  }
  //                                                              Cource, Direction
  if (gps.course.isValid())
  {
    tmp = int(gps.course.deg());
    while (tmp >= 360) tmp = tmp - 360;
    mylcd.setCursor(11, 0);
    mylcd.print("Dir: ");
    if (tmp < 100 ) mylcd.print(" ");
    if (tmp < 10 ) mylcd.print(" ");
    mylcd.print(tmp);
    if (last_tmp_D == tmp)
    {
      mylcd.setCursor(11, 1); mylcd.print("D    "); mylcd.setCursor(13, 1); mylcd.print( min( ( millis() - last_tmp_D_time) / 1000, 999));
      clear_debug_print = false;
    }
    else
    {
      last_tmp_D = tmp;
      last_tmp_D_time = millis();
      clear_debug_print = true;
    }
  }
  //                                              satelites
  if (gps.satellites.isValid())
  {
    mylcd.setCursor(11, 3);
    mylcd.print("S:  ");
    mylcd.setCursor(13, 3);
    mylcd.print(gps.satellites.value());
  }

  mylcd.setCursor(19, 3);
  mylcd.print(activity[act_cnt++ % 4]);
  if (((millis() - Start_Up_Time) / 1000) > 60)
  {
    if (clear_debug_print)
    {
      clear_debug_print = false;
      mylcd.setCursor(8, 2); mylcd.print("            "); // clear pos 8 - 19 from startup msg
      mylcd.setCursor(11, 1); mylcd.print("         "); // clear pos 11 - 19 from startup msg
    }
  }
 }

My main interest is to have a higher update frequency and, of course, knowing the freschness of data shown.

Anybody that will try to read the code and point out things that can be coded in a better way? I'm prepared to assist with more of my thinking to ease the Reading of the code.

    gps.encode(mySerial.read());

The encode() method returns a value. Why do you ignore that value?

Checking that there is valid data on every pass through loop() is stupid, when encode() already told you whether there was, or not. If only you'd been listening...

Hi PaulS!

I did some test Serial Printings of the number returned by encode(). I burst of zeroes and now and then issues a "1". Supplying the beginning of loop().

void loop()
{
  int tmp;

  while (mySerial.available() > 0)
    tmp = gps.encode(mySerial.read());
    if (tmp != 0 )
    {
      Serial.print(" Encode: ");Serial.print(tmp);Serial.print(" Time: ");Serial.println(millis()/1000);
    }

The serial logg looks like:

Starting
 Encode: 1 Time: 4
 Encode: 1 Time: 21
 Encode: 1 Time: 68
 Encode: 1 Time: 132
 Encode: 1 Time: 186
 Encode: 1 Time: 268
 Encode: 1 Time: 278
 Encode: 1 Time: 289
 Encode: 1 Time: 364
 Encode: 1 Time: 505
 Encode: 1 Time: 622
 Encode: 1 Time: 629
 Encode: 1 Time: 694
 Encode: 1 Time: 758
 Encode: 1 Time: 781
 Encode: 1 Time: 803
 Encode: 1 Time: 807
 Encode: 1 Time: 812
 Encode: 1 Time: 835
 Encode: 1 Time: 836
 Encode: 1 Time: 907
 Encode: 1 Time: 933
 Encode: 1 Time: 1015
 Encode: 1 Time: 1036
 Encode: 1 Time: 1037
 Encode: 1 Time: 1081
 Encode: 1 Time: 1088
 Encode: 1 Time: 1094
 Encode: 1 Time: 1144
 Encode: 1 Time: 1184
 Encode: 1 Time: 1219
 Encode: 1 Time: 1231
 Encode: 1 Time: 1345
 Encode: 1 Time: 1360
 Encode: 1 Time: 1415
 Encode: 1 Time: 1420
 Encode: 1 Time: 1469

Not very often Encode() returns a none zero. Can You explain what that encode return tells? I have failed to find out rading the TinyGPS++ | Arduiniana

Using "valid()" saves me from none valid data at startup I believe. Time and date appear quite quickly. Satteleites, Direction, Speed and Altitude appears after quite some time, up to some 20 - 30 seconds. Altitude is often comming rather late.

Try again PaulS. I don't find what I search in this last answer.

Not very often Encode() returns a none zero. Can You explain what that encode return tells?

The encode() method returns a boolean. Is that enough of a clue?

Take a look at the encode() method in the cpp file (snippets of which follow). There a 4 return statements.

  case '*':
    {
      bool isValidSentence = false;
      if (curTermOffset < sizeof(term))
      {
        term[curTermOffset] = 0;
        isValidSentence = endOfTermHandler();
      }
      ++curTermNumber;
      curTermOffset = 0;
      isChecksumTerm = c == '*';
      return isValidSentence;
    }

  case '

The last one should never be reached, but it is proper programming practice to include it.

So, when you pass the function a comma, a line feed, a carriage return, or an asterisk, encode() determines whether the sentence is complete, and valid.

If you pass it a dollar sign, indicating the start of a sentence, obviously the sentence is not complete.

If you pass it any other character, that is part of, not the end of, a sentence, so the sentence is not complete, and the function returns false.

ALL of your code to use the data belongs in the body of an if statement:

   if(gps.encode(mySerial.read())
   {
      // A complete sentence was received. It might not have been a useful one,
      // so you still need to test that that data is valid.
   }

Unless you do this, you will be processing old data over and over, instead of only processing new data once.

And doing the same thing over and over with old data is pointless, no?: // sentence begin
    return false;

default: // ordinary characters
    return false;

return false;


The last one should never be reached, but it is proper programming practice to include it.

So, when you pass the function a comma, a line feed, a carriage return, or an asterisk, encode() determines whether the sentence is complete, and valid.

If you pass it a dollar sign, indicating the start of a sentence, obviously the sentence is not complete.

If you pass it any other character, that is part of, not the end of, a sentence, so the sentence is not complete, and the function returns false.

ALL of your code to use the data belongs in the body of an if statement:

§DISCOURSE_HOISTED_CODE_1§


Unless you do this, you will be processing old data over and over, instead of only processing new data once.

And doing the same thing over and over with old data is pointless, no?

Thank You very much!
Now things have improved, and some other difficulties occured.

I implemented the embodying of my code in "if (encode) {process gps data}".
Time and Speed now updates at least twice per second. Beautiful!

However I don't get the Altitude data printed and the number of Sattelites shows zero.

First a log of debug printout and after that the code.

Age: Speed Hdop, Alt, Dir, Sat 488 53760 0 512 53800
Age: Speed Hdop, Alt, Dir, Sat 93 54304 0 350 54737
Age: Speed Hdop, Alt, Dir, Sat 489 54762 0 512 54800
Age: Speed Hdop, Alt, Dir, Sat 94 55305 0 351 55737
Age: Speed Hdop, Alt, Dir, Sat 489 55763 0 513 55802
Age: Speed Hdop, Alt, Dir, Sat 93 56304 0 350 56737
Age: Speed Hdop, Alt, Dir, Sat 489 56761 0 512 56801
Age: Speed Hdop, Alt, Dir, Sat 93 57306 0 350 57739
Age: Speed Hdop, Alt, Dir, Sat 490 57766 0 514 57805
A
#include <TinyGPS++.h>
#include <SoftwareSerial.h>

#include <Wire.h>
#include <hd44780.h>
#include <hd44780ioClass/hd44780_I2Cexp.h>

#define BACKLIGHT_PIN     13

hd44780_I2Cexp mylcd; // declare lcd object: auto locate & config exapander chip

// LCD geometry
const int LCD_COLS = 20;
const int LCD_ROWS = 4;

// The TinyGPS++ object
TinyGPSPlus gps;

//Create GPS channel
#define RX 2
#define TX 3
SoftwareSerial mySerial(RX, TX);  // pick any 2 unused digital pins (not pins 0 or 1)

#define time_adj 2// adjust: +1 for winter time, +2 for summer time

int act_cnt = 0;
char activity[] = "<><>";
unsigned long next_act_print, Start_Up_Time = millis();
unsigned int speed_age, hdop_age, alt_age, dir_age, sat_age;

void setup()
{
  int status;

 status = mylcd.begin(LCD_COLS, LCD_ROWS);
  if (status) // non zero status means it was unsuccesful
  {
    status = -status; // convert negative status value to positive number

    // begin() failed so blink error code using the onboard LED if possible
    hd44780::fatalError(status); // does not return
  }
  mylcd.clear();
  // initalization was successful, the backlight should be on now

  mySerial.begin(9600);  // 9600 is the default baud rate for my Neo6m units
  Serial.begin(9600);// For PC, serial monitor

  // Print start message to the LCD
  mylcd.print("Starting");
  // Print start message to the PC
  Serial.println("Starting up");//sent to PC

  next_act_print, Start_Up_Time = millis();
  speed_age, hdop_age, alt_age, dir_age, sat_age = 999;
}

void loop()
{
  int tmp;

  while (mySerial.available() > 0)
    //  gps.encode(mySerial.read());

  {
    if ( gps.encode(mySerial.read()))
    {
      //Data received. Process them
     //                                                   Altitude
      if (gps.altitude.isValid())
      {
        tmp = int(gps.altitude.meters() + 0.5);
        if (tmp > 9999) tmp = 9999;
        if (tmp < -999) tmp = -999;
        mylcd.setCursor(0, 1);
        mylcd.print("Alt: ");
        if (tmp < 1000) mylcd.print(" ");
        if (tmp < 100) mylcd.print(" ");
        if (tmp < 10) mylcd.print(" ");
        mylcd.print(tmp);

        tmp = gps.altitude.age();
        alt_age = tmp;

        mylcd.setCursor(9, 2); mylcd.print("A    "); mylcd.setCursor(11, 2); mylcd.print( min( tmp, 999));

      }

      //                                                                Hdop
      if (gps.hdop.isValid())
      {
        tmp = gps.hdop.value();
        mylcd.setCursor(16, 3);
        if (tmp > 100) mylcd.print("H>1");
        else mylcd.print("H<1");
        hdop_age = gps.hdop.age();
      }


      //                                                                 speed
      if (gps.speed.isValid())
      {
        tmp = int(gps.speed.kmph() + 0.5);
        mylcd.setCursor(0, 0);
        mylcd.print("Speed: ");
        if (tmp < 100)mylcd.print(" ");
        if (tmp < 10)mylcd.print(" ");
        mylcd.print(tmp);

        tmp = gps.speed.age();
        speed_age = tmp; //For Serial monitoring

        mylcd.setCursor(15, 2); mylcd.print("S    "); mylcd.setCursor(17, 2); mylcd.print( min( tmp, 999));
      }

      //                                                             date
      if (gps.date.isValid())
      {
        mylcd.setCursor(0, 3);
        mylcd.print(gps.date.year());
        mylcd.print("-");
        if (gps.date.month() < 10) mylcd.print("0");
        mylcd.print(gps.date.month());
        mylcd.print("-");
        if (gps.date.day() < 10) mylcd.print("0");
        mylcd.print(gps.date.day());
      }
      //                                                     time

      if (gps.time.isValid())
      {
        mylcd.setCursor(0, 2);
        tmp = gps.time.hour() + time_adj; // adjust: +1 for winter time, +2 for summer time
        if (tmp >= 24) tmp = tmp - 24;
        if (tmp < 10) mylcd.print("0");
        mylcd.print(tmp);
        mylcd.print("-");
        if (gps.time.minute() < 10) mylcd.print("0");
        mylcd.print(gps.time.minute());
        mylcd.print("-");
        if (gps.time.second() < 10) mylcd.print("0");
        mylcd.print(gps.time.second());
        mylcd.print(" ");
      }
      //                                                              Cource, Direction
      if (gps.course.isValid())
      {
        tmp = int(gps.course.deg() + 0.5);
        while (tmp >= 360) tmp = tmp - 360;
        mylcd.setCursor(11, 0);
        mylcd.print("Dir: ");
        if (tmp < 100 ) mylcd.print(" ");
        if (tmp < 10 ) mylcd.print(" ");
        mylcd.print(tmp);

        tmp = gps.course.age();
        dir_age = tmp;

        mylcd.setCursor(15, 1); mylcd.print("D    "); mylcd.setCursor(17, 1); mylcd.print( min( tmp, 999));
      }
      //                                              satelites
      if (gps.satellites.isValid())
      {
        mylcd.setCursor(11, 3);
        mylcd.print("S:  ");
        mylcd.setCursor(13, 3);
        mylcd.print(gps.satellites.value());
        sat_age = gps.satellites.age();
        mylcd.setCursor(9, 1); mylcd.print("s    "); mylcd.setCursor(11, 1); mylcd.print( min( sat_age, 999));
      }

      if (millis() > next_act_print)
      {
        mylcd.setCursor(19, 3);
        mylcd.print(activity[act_cnt++ % 4]);
        next_act_print = millis() + 500;
      }
      //Print for serial monitoring
      Serial.print("Age: Speed Hdop, Alt, Dir, Sat ");
      Serial.print(speed_age); Serial.print(" ");
      Serial.print(hdop_age); Serial.print(" ");
      Serial.print(alt_age); Serial.print(" ");
      Serial.print(dir_age); Serial.print(" ");
      Serial.println(sat_age);

    }//end of if encode
  }// end of while chars to receive
}//end of loop()

/code]

I'm not sure what you are expecting for satellite age. Unless the GPS talked to a minimum number of satellites, it is not going to be sending you valid data. So, once it starts receiving data, the time since it last got data from one or more satellites will be 0.

The first piece of info You sent last time, the one about the cpp-file, I don't manage to understand.

My intention was to use the age as a quality indicator, how fresh the data is. The less of age, the more fresh.

I have been testing, test printing both data and age using both Serial.print and mylcd.print to figure out what is happening. For the moment things looks strange to me.

The Altitude data seem to be hard to get. No valid Altitude…

Sometimes the number of Satellites are 10-11, sometimes 38-45. That looks strange to me.

The present code is:

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

#include <Wire.h>
#include <hd44780.h>
#include <hd44780ioClass/hd44780_I2Cexp.h>

#define BACKLIGHT_PIN     13

hd44780_I2Cexp mylcd; // declare lcd object: auto locate & config exapander chip

// LCD geometry
const int LCD_COLS = 20;
const int LCD_ROWS = 4;

// The TinyGPS++ object
TinyGPSPlus gps;

//Create GPS channel
#define RX 2
#define TX 3
SoftwareSerial mySerial(RX, TX);  // pick any 2 unused digital pins (not pins 0 or 1)

#define time_adj 2// adjust: +1 for winter time, +2 for summer time

int act_cnt = 0;
char activity[] = "<><>";
unsigned long next_act_print, Start_Up_Time = millis();
unsigned int speed_age, hdop_age, alt_age, dir_age, sat_age;

void setup()
{
  int status;

  status = mylcd.begin(LCD_COLS, LCD_ROWS);
  if (status) // non zero status means it was unsuccesful
  {
    status = -status; // convert negative status value to positive number

    // begin() failed so blink error code using the onboard LED if possible
    hd44780::fatalError(status); // does not return
  }
  mylcd.clear();
  // initalization was successful, the backlight should be on now

  mySerial.begin(9600);  // 9600 is the default baud rate for my Neo6m units
  Serial.begin(9600);// For PC, serial monitor

  // Print start message to the LCD
  mylcd.print("Starting");
  // Print start message to the PC
  Serial.println("Starting up");//sent to PC

  next_act_print, Start_Up_Time = millis();
  speed_age, hdop_age, alt_age, dir_age, sat_age = 999;
}

void loop()
{
  int tmp;

  while (mySerial.available() > 0)
    //  gps.encode(mySerial.read());

  {
    if ( gps.encode(mySerial.read()))
    {
      //Data received. Process them
      //                                                                  Altitude
      if (gps.altitude.isValid())
      {
        tmp = int(gps.altitude.meters() + 0.5);
        if (tmp > 999) tmp = 999;
        if (tmp < -99) tmp = -99;
        mylcd.setCursor(0, 1);
        mylcd.print("Alt: ");
        //        if (tmp < 1000) mylcd.print(" ");
        if (tmp < 100) mylcd.print(" ");
        if (tmp < 10) mylcd.print(" ");
        mylcd.print(tmp);

        alt_age = gps.altitude.age();
        mylcd.setCursor(9, 2); mylcd.print("A    "); mylcd.setCursor(11, 2); mylcd.print( min( alt_age, 999));
      }

      //                                                                Hdop
      if (gps.hdop.isValid())
      {
        tmp = gps.hdop.value();
        mylcd.setCursor(16, 3);
        if (tmp > 100) mylcd.print("H>1");
        else mylcd.print("H<1");
        hdop_age = gps.hdop.age();
      }

     //                                                                 speed
      if (gps.speed.isValid())
      {
        tmp = int(gps.speed.kmph() + 0.5);
        mylcd.setCursor(0, 0);
        mylcd.print("Speed: ");
        if (tmp < 100)mylcd.print(" ");
        if (tmp < 10)mylcd.print(" ");
        mylcd.print(tmp);

        tmp = gps.speed.age();
        speed_age = tmp; //For Serial monitoring

        //        mylcd.setCursor(15, 2); mylcd.print("S    "); mylcd.setCursor(17, 2); mylcd.print( min( tmp, 999));
      }

      //                                                             date
      if (gps.date.isValid())
      {
        mylcd.setCursor(0, 3);
        mylcd.print(gps.date.year());
        mylcd.print("-");
        if (gps.date.month() < 10) mylcd.print("0");
        mylcd.print(gps.date.month());
        mylcd.print("-");
        if (gps.date.day() < 10) mylcd.print("0");
        mylcd.print(gps.date.day());
      }
      //                                                     time

      if (gps.time.isValid())
      {
        mylcd.setCursor(0, 2);
        tmp = gps.time.hour() + time_adj; // adjust: +1 for winter time, +2 for summer time
        if (tmp >= 24) tmp = tmp - 24;
        if (tmp < 10) mylcd.print("0");
        mylcd.print(tmp);
        mylcd.print("-");
        if (gps.time.minute() < 10) mylcd.print("0");
        mylcd.print(gps.time.minute());
        mylcd.print("-");
        if (gps.time.second() < 10) mylcd.print("0");
        mylcd.print(gps.time.second());
        mylcd.print(" ");
      }
      //                                                              Cource, Direction
      if (gps.course.isValid())
      {
        tmp = int(gps.course.deg() + 0.5);
        while (tmp >= 360) tmp = tmp - 360;
        mylcd.setCursor(11, 0);
        mylcd.print("Dir: ");
        if (tmp < 100 ) mylcd.print(" ");
        if (tmp < 10 ) mylcd.print(" ");
        mylcd.print(tmp);

        tmp = gps.course.age();
        dir_age = tmp;
     }
      //                                              satelites
      if (gps.satellites.isValid())
      {
        mylcd.setCursor(11, 3);
        mylcd.print("S:  ");
        mylcd.setCursor(13, 3);
        mylcd.print(gps.satellites.value());
        sat_age = gps.satellites.age();
     }

      if (millis() > next_act_print)
      {
        mylcd.setCursor(19, 3);
        mylcd.print(activity[act_cnt++ % 4]);
        next_act_print = millis() + 500;

      //Print out for serial monitoring
     
        Serial.print("Time: "); Serial.print(millis() / 1000);
        Serial.print(" Speed: "); Serial.print(int(gps.speed.kmph() + 0.5));
        Serial.print(" Alt: "); Serial.print(int(gps.altitude.meters() + 0.5));
        Serial.print(" Hdop: "); Serial.print(gps.hdop.value());
        Serial.print(" Dir: "); Serial.print(int(gps.course.deg()+0.5));
        Serial.print(" Sats: "); Serial.println(gps.satellites.value());
/*
              Serial.print("Age: Speed Hdop, Alt, Dir, Sat ");
              Serial.print(speed_age); Serial.print(" ");
              Serial.print(hdop_age); Serial.print(" ");
              Serial.print(alt_age); Serial.print(" ");
              Serial.print(dir_age); Serial.print(" ");
              Serial.println(sat_age);
        */
      }
    }//end of if encode
  }// end of while chars to receive
}//end of loop()

I have been testing, test printing both data and age using both Serial.print and mylcd.print to figure out what is happening. For the moment things looks strange to me.

Personally, I'd put the LCD in a drawer, and lock the drawer for now.

You have checks that various pieces of data are valid, and in the body of those statements, you print to the LCD. Then, regardless of whether the data is valid, or not, you print to the Serial port. No wonder you are confused.

Even 10 or 11 satellites seems like a lot. 38 to 45 just can't be right.

Print each character read from the GPS to the serial port as well as passing it to the encode() method. Perhaps a clue will present itself.

In sharp use I run my project out in a real train, without any Pc, so I feel that some well choosen debug data written to the LCD will be valuable so faar. Of cource, overloading the I2C connected LCD can cause problems I guess.

The confusement is that the Altitude data takes much longer time to appear after I introduced the inbodying You tipped me about. Also the strange number of satellites staretd now.

The very positive effect arising from Your tip is a much faster update of speed. Showing speed is the main goal for the project. Of course, showing Altitude and Course is interesting. Number of Satellites is fun to se.
The apps I tested in my smartphone never spotted 10,11,12 satellites.

How do I pick up, and print, the chars coming from the GPS without crashing the ongoing flow?
The Reading is done in " if ( gps.encode(mySerial.read()))". To me it looks like a string of chars are red. How do I refer to that string?

Today the project was taken out for a car ride. Speed is now upted beautifully as well as the Heading the Direction.

What is not working ok is Altitude ans Satellites. It takes a very long time before any value passes the test "isValid()". Strangly both values are way out of limits. 34 or 69 satellites, 30 meters wrong, or more in Altitude.

The current code is:

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

#include <Wire.h>
#include <hd44780.h>
#include <hd44780ioClass/hd44780_I2Cexp.h>

#define BACKLIGHT_PIN     13

hd44780_I2Cexp mylcd; // declare lcd object: auto locate & config exapander chip

// LCD geometry
const int LCD_COLS = 20;
const int LCD_ROWS = 4;

// The TinyGPS++ object
TinyGPSPlus gps;

//Create GPS channel
#define RX 2
#define TX 3
SoftwareSerial mySerial(RX, TX);  // pick any 2 unused digital pins (not pins 0 or 1)

#define time_adj 2// adjust: +1 for winter time, +2 for summer time

int act_cnt = 0;
char activity[] = "<><>";
unsigned long next_act_print, Start_Up_Time = millis();
unsigned int speed_age, hdop_age, alt_age, dir_age, sat_age;

void setup()
{
  int status;

  status = mylcd.begin(LCD_COLS, LCD_ROWS);
  if (status) // non zero status means it was unsuccesful
  {
    status = -status; // convert negative status value to positive number

    // begin() failed so blink error code using the onboard LED if possible
    hd44780::fatalError(status); // does not return
  }
  mylcd.clear();
  // initalization was successful, the backlight should be on now

  mySerial.begin(9600);  // 9600 is the default baud rate for my Neo6m units
  Serial.begin(9600);// For PC, serial monitor

  // Print start message to the LCD
  mylcd.print("Starting");
  // Print start message to the PC
  Serial.println("Starting up");//sent to PC

  next_act_print, Start_Up_Time = millis();
  speed_age, hdop_age, alt_age, dir_age, sat_age = 999;
}

void loop()
{
  int tmp;

  while (mySerial.available() > 0)
    //  gps.encode(mySerial.read());

  {
    if ( gps.encode(mySerial.read()))
    {
      //Data received. Process them
      //                                                   Altitude
      if (gps.altitude.isValid())
      {
        tmp = int(gps.altitude.meters() + 0.5);
        if (tmp > 999) tmp = 999;
        if (tmp < -99) tmp = -99;
        mylcd.setCursor(0, 1);
        mylcd.print("Alt: ");
        //        if (tmp < 1000) mylcd.print(" ");
        if (tmp < 100) mylcd.print(" ");
        if (tmp < 10) mylcd.print(" ");
        mylcd.print(tmp);

        alt_age = gps.altitude.age();
        mylcd.setCursor(9, 2); mylcd.print("A    "); mylcd.setCursor(11, 2); mylcd.print( min( int(alt_age / 1000), 999));
      }

      //                                                                Hdop
      if (gps.hdop.isValid())
      {
        tmp = gps.hdop.value();
        mylcd.setCursor(16, 3);
        if (tmp > 100) mylcd.print("H>1");
        else mylcd.print("H<1");
        hdop_age = gps.hdop.age();
      }

       //                                                                 speed
      if (gps.speed.isValid())
      {
        tmp = int(gps.speed.kmph() + 0.5);
        mylcd.setCursor(0, 0);
        mylcd.print("Speed: ");
        if (tmp < 100)mylcd.print(" ");
        if (tmp < 10)mylcd.print(" ");
        mylcd.print(tmp);

        tmp = gps.speed.age();
        speed_age = tmp; //For Serial monitoring

        //        mylcd.setCursor(15, 2); mylcd.print("S    "); mylcd.setCursor(17, 2); mylcd.print( min( tmp, 999));
      }

      //                                                             date
      if (gps.date.isValid())
      {
        mylcd.setCursor(0, 3);
        mylcd.print(gps.date.year());
        mylcd.print("-");
        if (gps.date.month() < 10) mylcd.print("0");
        mylcd.print(gps.date.month());
        mylcd.print("-");
        if (gps.date.day() < 10) mylcd.print("0");
        mylcd.print(gps.date.day());
      }
      //                                                     time

      if (gps.time.isValid())
      {
        mylcd.setCursor(0, 2);
        tmp = gps.time.hour() + time_adj; // adjust: +1 for winter time, +2 for summer time
        if (tmp >= 24) tmp = tmp - 24;
        if (tmp < 10) mylcd.print("0");
        mylcd.print(tmp);
        mylcd.print("-");
        if (gps.time.minute() < 10) mylcd.print("0");
        mylcd.print(gps.time.minute());
        mylcd.print("-");
        if (gps.time.second() < 10) mylcd.print("0");
        mylcd.print(gps.time.second());
        mylcd.print(" ");
      }
      //                                                              Cource, Direction
      if (gps.course.isValid())
      {
        tmp = int(gps.course.deg() + 0.5);
        while (tmp >= 360) tmp = tmp - 360;
        mylcd.setCursor(11, 0);
        mylcd.print("Dir: ");
        if (tmp < 100 ) mylcd.print(" ");
        if (tmp < 10 ) mylcd.print(" ");
        mylcd.print(tmp);

        tmp = gps.course.age();
        dir_age = tmp;

        //        mylcd.setCursor(15, 1); mylcd.print("D    "); mylcd.setCursor(17, 1); mylcd.print( min( tmp, 999));
      }
      //                                              satelites
      if (gps.satellites.isValid())
      {
        mylcd.setCursor(11, 3);
        mylcd.print("S:  ");
        mylcd.setCursor(13, 3);
        mylcd.print(min(gps.satellites.value(),99));
        sat_age = gps.satellites.age();
        //        mylcd.setCursor(9, 1); mylcd.print("s    "); mylcd.setCursor(11, 1); mylcd.print( min( sat_age, 999));
      }

      if (millis() > next_act_print)
      {
        mylcd.setCursor(19, 3);
        mylcd.print(activity[act_cnt++ % 4]);
        next_act_print = millis() + 500;

        //Print out for serial monitoring

        Serial.print("Time: "); Serial.print(millis() / 1000);
        Serial.print(" Speed: "); Serial.print(int(gps.speed.kmph() + 0.5));
        Serial.print(" Alt: "); Serial.print(int(gps.altitude.meters() + 0.5));
        Serial.print(" Hdop: "); Serial.print(gps.hdop.value());
        Serial.print(" Dir: "); Serial.print(int(gps.course.deg() + 0.5));
        Serial.print(" Sats: "); Serial.println(gps.satellites.value());

      }



    }//end of if encode
  }// end of while chars to receive
}//end of loop()

The project now starts up showing all wanted data within a few seconds. Thinking outside the box, making lots of test printouts, printing out simple variables, and using coding fantazy make things at least start up, and update regularly, in seconds, not in hours.
Next step is to take the project out for a sharp test, go for a car ride.
Cross Your fingers....

The Reading is done in " if ( gps.encode(mySerial.read()))". To me it looks like a string of chars are red. How do I refer to that string?

char c = mySerial.read();
Serial.print(c);
if(gps.encode(c))
{
}

and using coding fantazy make things at least start up

Using fantasy generally makes for more problems than solutions.

Hi and thanks!

I will incorporate the code You sent me for future use, commenting it out for the moment. Tomorrow 3 days of sharp test in the fields, on a real train, begins and I don't want to take the risc of any set back now. The project seams to run pretty well with the present code, testing it from car driving.

Why, following Your tip to check the status from Encode, went from poor to partly faulty surprice me.
That code was sent in my previous post from 2018-07-13.
May I ask You to take a look at the beginning of loop()? Can You see any obvious mistake there?
Collecting entire message streams will take a lot of power and time to perform and result in plenty of posts from me I think.

For curioucity I supply the present code below.

//boolean debug = false;

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

#include <Wire.h>
#include <hd44780.h>
#include <hd44780ioClass/hd44780_I2Cexp.h>

hd44780_I2Cexp mylcd; // declare lcd object: auto locate & config exapander chip

// LCD geometry
#define LCD_COLS 20
#define LCD_ROWS 4

// The TinyGPS++ object
TinyGPSPlus gps;
TinyGPSCustom vdop(gps, "GPGSA", 17);//Pick upVdop

//Create GPS channel
#define RX 2
#define TX 3
SoftwareSerial mySerial(RX, TX);  // pick any 2 unused digital pins (not pins 0 or 1)

#define time_adj 2// adjust: +1 for winter time, +2 for summer time

int act_cnt = 0;
char activity[] = "<><>";
unsigned long next_act_print, Start_Up_Time = millis();
unsigned long speed_age, hdop_age, vdop_age, alt_age, dir_age, sat_age;

unsigned long speed_print, dir_print, alt_print, time_print, date_print, sat_print, hdop_print, vdop_print = 0;

void setup()
{
  int status;

 status = mylcd.begin(LCD_COLS, LCD_ROWS);
  if (status) // non zero status means it was unsuccesful
  {
    status = -status; // convert negative status value to positive number

    // begin() failed so blink error code using the onboard LED if possible
    hd44780::fatalError(status); // does not return
  }
  mylcd.clear();
  // initalization was successful, the backlight should be on now

  mySerial.begin(9600);  // 9600 is the default baud rate for my Neo6m units
  Serial.begin(9600);// For PC, serial monitor

  // Print start message to the LCD
  mylcd.print("Starting");
  // Print start message to the PC
  Serial.println("Starting up");//sent to PC

  next_act_print, Start_Up_Time = millis();
  speed_age, hdop_age, alt_age, dir_age, sat_age = 999;
  speed_print, dir_print, alt_print, time_print, date_print, sat_print, hdop_print, vdop_print = millis();

}

void loop()
{
  int tmp, tmpa;//
  boolean data_received_flag;
  float ftmp;

  data_received_flag = false;
  while (mySerial.available())
  {
    tmp = gps.encode(mySerial.read());
    data_received_flag = true;
  }
  data_received_flag = true;// being brutal, just go for it.

  /*
    Serial.print(" Updates: ");Serial.print(gps.speed.isValid());Serial.print(" ");
    Serial.print(gps.course.isValid());Serial.print(" ");
    Serial.print(gps.altitude.isValid());Serial.print(" ");
    Serial.print(gps.time.isValid());Serial.print(" ");
    Serial.print(gps.date.isValid());Serial.print(" ");
    Serial.print(gps.satellites.isValid());Serial.print(" ");
    Serial.print(gps.hdop.isValid());Serial.println(" ");
  */
  /*
    Serial.print("Ages: "); Serial.print(gps.speed.age()); Serial.print(" ");
    Serial.print(gps.course.age()); Serial.print(" ");
    Serial.print(gps.altitude.age()); Serial.print(" ");
    Serial.print(gps.time.age()); Serial.print(" ");
    Serial.print(gps.date.age()); Serial.print(" ");
    Serial.print(gps.satellites.age()); Serial.print(" ");
    Serial.print(gps.hdop.age()); Serial.println(" ");
  */
  if (data_received_flag)
  {
    //  Serial.print("Available: "); Serial.print(tmpb);
    //  Serial.print(" lpcnt: "); Serial.print(lpcnt);
    //  Serial.print(" tmp1: "); Serial.print(tmp1);
    //  Serial.print(" Millis: "); Serial.println(millis());

    //Data received. Process them
    //                                                                 speed
    if (gps.speed.isValid() && ((millis() - speed_print) > 300))
    {
      tmp = int(gps.speed.kmph() + 0.5);
      mylcd.setCursor(0, 0);
      mylcd.print("Speed: ");
      if (tmp < 100)mylcd.print(" ");
      if (tmp < 10)mylcd.print(" ");
      mylcd.print(tmp);
      speed_print = millis();

      //debug print on LCD collides with Hdop debug
      //      speed_age = gps.speed.age();
      //      mylcd.setCursor(16, 1); mylcd.print("S   "); mylcd.setCursor(18, 1); mylcd.print( min( speed_age / 1000, 99));
    }

    //                                                              Cource, Direction
    //    if (gps.course.isValid())
    if (gps.course.isValid() && ((millis() - dir_print) > 500))
    {
      tmp = int(gps.course.deg() + 0.5);
      while (tmp >= 360) tmp = tmp - 360;
      mylcd.setCursor(11, 0);
      mylcd.print("Dir: ");
      if (tmp < 100 ) mylcd.print(" ");
      if (tmp < 10 ) mylcd.print(" ");
      mylcd.print(tmp);

      dir_age = gps.course.age();

      //      mylcd.setCursor(16, 2); mylcd.print("D   "); mylcd.setCursor(18, 2); mylcd.print( min( dir_age / 1000, 99));
      dir_print = millis();
    }

    //                                                   Altitude
    //    if (gps.altitude.isValid())
    if (gps.altitude.isValid() && ((millis() - alt_print) > 500))
    {
      tmp = int(gps.altitude.meters() + 0.5);
      if (tmp > 9999) tmp = 9999;
      if (tmp < -99) tmp = -99;
      mylcd.setCursor(0, 1);
      mylcd.print("Alt: ");
      if (tmp < 1000) mylcd.print(" ");
      if (tmp < 100) mylcd.print(" ");
      if (tmp < 10) mylcd.print(" ");
      mylcd.print(tmp);
      alt_print = millis();

      //Debug print on LCD collides with Hdop debug
      //      alt_age = gps.altitude.age();
      //      mylcd.setCursor(11, 1); mylcd.print("A    "); mylcd.setCursor(13, 1); mylcd.print( min( int(alt_age / 1000), 99));
    }

    //                                                     time
    //    if (gps.time.isValid())
    if (gps.time.isValid() && ((millis() - time_print) > 300))
    {
      mylcd.setCursor(0, 2);
      tmp = gps.time.hour() + time_adj; // adjust: +1 for winter time, +2 for summer time
      if (tmp >= 24) tmp = tmp - 24;
      if (tmp < 10) mylcd.print("0");
      mylcd.print(tmp);
      mylcd.print("-");
      if (gps.time.minute() < 10) mylcd.print("0");
      mylcd.print(gps.time.minute());
      mylcd.print("-");
      if (gps.time.second() < 10) mylcd.print("0");
      mylcd.print(gps.time.second());
      mylcd.print(" ");
      time_print = millis();
    }
    //                                                      Vdop
    if (vdop.isValid() && ((millis() - vdop_print) > 1000))
    {
      mylcd.setCursor(9, 2);
      mylcd.print("VDop:      ");
      mylcd.setCursor(15, 2);
      //      ftmp = float(vdop.value());
      //      if (ftmp > 9.99)ftmp = 9.99;
      //      mylcd.print(ftmp);
      mylcd.print(vdop.value());
      vdop_print = millis();
   }


    //                                                             date
    if (gps.time.isValid() && ((millis() - date_print) > 1000))
    {
      mylcd.setCursor(0, 3);
      mylcd.print(gps.date.year());
      mylcd.print("-");
      if (gps.date.month() < 10) mylcd.print("0");
      mylcd.print(gps.date.month());
      mylcd.print("-");
      if (gps.date.day() < 10) mylcd.print("0");
      mylcd.print(gps.date.day());
      date_print = millis();
    }

    //                                                                                      satelites
    if (gps.satellites.isValid() && ((millis() - sat_print) > 5000))
    {
      mylcd.setCursor(12, 3);
      mylcd.print("S:   ");
      mylcd.setCursor(15, 3);
      mylcd.print(min(gps.satellites.value(), 99));

      //      sat_age = gps.satellites.age();
      //      mylcd.setCursor(11, 2); mylcd.print("s  "); mylcd.setCursor(13, 2); mylcd.print( min( int(sat_age / 1000), 99));
      sat_print = millis();
    }

    //                                                                                   Hdop
    if (gps.hdop.isValid() && ((millis() - hdop_print) > 2000))
    {
      tmp = min(gps.hdop.value(), 999);
      mylcd.setCursor(11, 1); mylcd.print("HDop:    ");
      mylcd.setCursor(17, 1);
      mylcd.print(tmp);
      hdop_age = gps.hdop.age();// for Serial.print debugging
      hdop_print = millis();
    }

    if (millis() > next_act_print)
    {
      mylcd.setCursor(19, 3);
      mylcd.print(activity[act_cnt++ % 2]);
      next_act_print = millis() + 500;
    }

  }//if (data_received_flag)
}//end of loop()

Pestering the TinyGPS++ instance, when it is in the middle of receiving a sentence, just strikes me as a bad idea.

When it reads to the end of a sentence, then you can ask questions about the sentence that it just read (or any of the ones before that).

What I would be doing is printing each character as it is received, and then calling isValid() and printing the results of the parsing (latitude, longitude, altitude, heading, speed, number of satellites, etc.), and comparing them to what I read from the sentence.

The GPS is sending several types of sentences to the Arduino. It is (remotely) possible that receiving one kind of sentence is causing parsing issues. But, without seeing the GPS data vs. the parsed output, some cards are missing. Not playing with a full deck can be problematic.

Thanks a lot for taking Your time!
Yes, pestering the TinyGPS++ like You describe is bad. How can I know when a complete sentence has been read, processed and is ready?
Using isValid or isUpdated would make sure that data are ready was my thought.
I have not seen any info about it. For me there are lots of unknowns about what is done in the TinyGPS++ routines.
I could only guess what is going on behind the Serial.read and Encode. In the first try I used "Basic..." from the examples in the library, hooked it up to the hardware and got data.....
I have read the explanation given for TinyGPS++ several times but I have not caught the key to this.

Filtering the entire stream of incoming chars will delay the process, woun't it?

How can I know when a complete sentence has been read, processed and is ready?

The encode() method returns true when the last character of the sentence is passed to it.

Before encode() actually returns, the parsing of the sentence is done.

Filtering the entire stream of incoming chars will delay the process, woun't it?

What do you mean by "filtering the entire stream"?

Ok.
I beleive I used that, after Your tip, waiting for Encode to return true. Then loop() was continued but values flagged "isValid()"were strange.
Yes, that was my thought that when Encode returned a sentence was processed completely. However something was not good…

Most likeley I didn't understand what read char by char would give…

Hi PaulS!

Being back home I implemented Your last tip.
I get plenty of $G messages on the Serial monitor.

However altitude is completly wrong, satellites shows impossible values for a long time before showing reasonable values. Parameter Hdop gives overflow and Vdop is not received at all.

I attach the last code in case I have made mistakes in implementing Your code.

//boolean debug = false;

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

#include <Wire.h>
#include <hd44780.h>
#include <hd44780ioClass/hd44780_I2Cexp.h>

//#define BACKLIGHT_PIN     13

hd44780_I2Cexp mylcd; // declare lcd object: auto locate & config exapander chip

// LCD geometry
#define LCD_COLS 20
#define LCD_ROWS 4

// The TinyGPS++ object
TinyGPSPlus gps;
TinyGPSCustom vdop(gps, "GPGSA", 17);//Pick upVdop

//Create GPS channel using digital i/o
#define RX 2
#define TX 3
SoftwareSerial mySerial(RX, TX);  // pick any 2 unused digital pins (not pins 0 or 1)

#define time_adj 2// adjust: +1 for winter time, +2 for summer time

int act_cnt = 0;
char activity[] = "<><>";
unsigned long next_act_print, Start_Up_Time = millis();
//unsigned int speed_age, hdop_age, alt_age, dir_age, sat_age;
unsigned long speed_age, hdop_age, vdop_age, alt_age, dir_age, sat_age;

unsigned long speed_print, dir_print, alt_print, time_print, date_print, sat_print, hdop_print, vdop_print = 0;

void setup()
{
  int status;

  status = mylcd.begin(LCD_COLS, LCD_ROWS);
  if (status) // non zero status means it was unsuccesful
  {
    status = -status; // convert negative status value to positive number

    // begin() failed so blink error code using the onboard LED if possible
    hd44780::fatalError(status); // does not return
  }
  mylcd.clear();
  // initalization was successful, the backlight should be on now

  mySerial.begin(9600);  // 9600 is the default baud rate for my Neo6m units
  Serial.begin(9600);// For PC, serial monitor

  // Print start message to the LCD
  mylcd.print("Starting");
  // Print start message to the PC
  Serial.println("Starting up");//sent to PC

  //initialize age and print flags
  next_act_print, Start_Up_Time = millis();
  speed_age, hdop_age, alt_age, dir_age, sat_age = 999;
  speed_print, dir_print, alt_print, time_print, date_print, sat_print, hdop_print, vdop_print = millis();

}

void loop()
{
  int tmp, tmpa;//
  boolean data_received_flag, rec_flg;
  float ftmp;

  data_received_flag, rec_flg = false;
  while (mySerial.available())
  {
    char c = mySerial.read();
    Serial.print(c);
    if (gps.encode(c))
    {

     data_received_flag = true;


    if (data_received_flag)
      {
       //Data received. Process them
        //                                                                 speed
        if (gps.speed.isValid() && ((millis() - speed_print) > 300))
        {
          tmp = int(gps.speed.kmph() + 0.5);
          mylcd.setCursor(0, 0);
          mylcd.print("Speed: ");
          if (tmp < 100)mylcd.print(" ");
          if (tmp < 10)mylcd.print(" ");
          mylcd.print(tmp);
          speed_print = millis();

      }

        //                                                              Cource, Direction
        if (gps.course.isValid() && ((millis() - dir_print) > 500))
        {
          tmp = int(gps.course.deg() + 0.5);
          while (tmp >= 360) tmp = tmp - 360;
          mylcd.setCursor(11, 0);
          mylcd.print("Dir: ");
          if (tmp < 100 ) mylcd.print(" ");
          if (tmp < 10 ) mylcd.print(" ");
          mylcd.print(tmp);
          dir_age = gps.course.age();
          dir_print = millis();
        }

        //                                                   Altitude
        if (gps.altitude.isValid() && ((millis() - alt_print) > 500))
        {
          tmp = int(gps.altitude.meters() + 0.5);
          if (tmp > 9999) tmp = 9999;
          if (tmp < -99) tmp = -99;
          mylcd.setCursor(0, 1);
          mylcd.print("Alt: ");
          if (tmp < 1000) mylcd.print(" ");
          if (tmp < 100) mylcd.print(" ");
          if (tmp < 10) mylcd.print(" ");
          mylcd.print(tmp);
          alt_print = millis();

      }

        //                                                     time
        if (gps.time.isValid() && ((millis() - time_print) > 300))
        {
          mylcd.setCursor(0, 2);
          tmp = gps.time.hour() + time_adj; // adjust: +1 for winter time, +2 for summer time
          if (tmp >= 24) tmp = tmp - 24;
          if (tmp < 10) mylcd.print("0");
          mylcd.print(tmp);
          mylcd.print("-");
          if (gps.time.minute() < 10) mylcd.print("0");
          mylcd.print(gps.time.minute());
          mylcd.print("-");
          if (gps.time.second() < 10) mylcd.print("0");
          mylcd.print(gps.time.second());
          mylcd.print(" ");
          time_print = millis();
        }
        //                                                      Vdop
        if (vdop.isValid() && ((millis() - vdop_print) > 1000))
        {
          mylcd.setCursor(9, 2);
          mylcd.print("VDop:      ");
          mylcd.setCursor(15, 2);
          ftmp = atof(vdop.value());
          if (ftmp > 9.99)ftmp = 9.99;
          mylcd.print(ftmp);
          vdop_print = millis();
        }

        //                                                             date
        if (gps.time.isValid() && ((millis() - date_print) > 1000))
        {
          mylcd.setCursor(0, 3);
          mylcd.print(gps.date.year());
          mylcd.print("-");
          if (gps.date.month() < 10) mylcd.print("0");
          mylcd.print(gps.date.month());
          mylcd.print("-");
          if (gps.date.day() < 10) mylcd.print("0");
          mylcd.print(gps.date.day());
          date_print = millis();
        }

        //                                                                  satelites
        if (gps.satellites.isValid() && ((millis() - sat_print) > 5000))
        {
          mylcd.setCursor(12, 3);
          mylcd.print("S:   ");
          mylcd.setCursor(15, 3);
          mylcd.print(min(gps.satellites.value(), 99));
          sat_print = millis();
        }

        //                                                                Hdop
        if (gps.hdop.isValid() && ((millis() - hdop_print) > 2000))
        {
          tmp = min(gps.hdop.value(), 999);
          mylcd.setCursor(11, 1); mylcd.print("HDop:    ");
          mylcd.setCursor(17, 1);
          mylcd.print(tmp);
         hdop_age = gps.hdop.age();
          hdop_print = millis();
        }

        if (millis() > next_act_print)
        {
          mylcd.setCursor(19, 3);
          mylcd.print(activity[act_cnt++ % 2]);
          next_act_print = millis() + 500;
       }
      }
    }
  }//if (data_received_flag)
}//end of loop()
  data_received_flag, rec_flg = false;

Please explain what you expect this line of code to do. It almost certainly isn't doing that.

     data_received_flag = true;


    if (data_received_flag)

It is pointless to test that data_received_flag is true right after setting it to true.

          tmp = int(gps.speed.kmph() + 0.5);

The compiler knows how to store a float value in an int variable without your "help".

        if (vdop.isValid() && ((millis() - vdop_print) > 1000))
        if (gps.hdop.isValid() && ((millis() - hdop_print) > 2000))

Why are hdop and vdop handled differently?

You don't need to spend time reading the entire code. The reason for this topic was to execute loop() when new data have arrived and awoid waisting computer power processing old data over and over.
Today I get the LCD updates at the rate I want. That's really god. Progress.

data_received_flag… I know it looks odd.

I tried to pick up the status of encode, in different ways. Either one valid Encode == true would be "Go" for loop(), or the last Encode value would give the "Go" for loop(). Testing different approaches was handy this way.

Every time I let Encode == true be the criteria for executing the rest of the code in loop() either got wrong data or I didn't get any at all.

The difference between VDop and HDop…
This way I control how often I want the parameter to be sent through the I2C channel to the LCD. All in order not to exceed the transmission capacity of the 9600 baud I2C channel.

tmp = int(gps.speed.kmph() + 0.5);.... I don't know whether the compiler rounds off or truncates. This way I take control myself.

I got stuck in the idea to pick up Your early posts, to understand what status Encode is giving.