Ultimate GPS update rate

I'm working on a bike speedometer using a Arduino Nano Every, the Adafruit Ultimate GPS breakout board and a 3.5" TFT display. I have a preliminary version breadboarded that will display latitude, longitude and speed. I took it for a ride in my truck to check it out, stopped a couple of places on the way and the latitude and longitude were right on. My problem, or question any ways, is the refresh rate of the speed. Although it was relatively accurate it was slow to update. I didn't expect instantaneous reading but it seems to update about every 15 seconds so I'm reading more of an average. The updated speed seems to coincided with the blinking of the GPS connected LED but not sure if that has anything to do with it.

Here is how things are connected to the Nano
GPS -- Nano
Vin -- +5v
GND -- GND
RX -- DI7 (I'm using SoftwareSerial)
TX -- DI8

TFT -- Nano
GND -- GND
Vin -- +5v
CLK -- DI13
MISO -- DI12
MOSI -- DI11
CS -- DI10
D/C -- DI9

My code, see below, is scanning for data from the GPS every 1.5 seconds. So is the delay a code problem or the GPS update rate? If the GPS can I modify that to 5 seconds, or would that be to fast for the GPS?

Thanks for any help, comments or suggestions
John

// Bike Speedometer using GPS
// Arduino Nano Every

#include <TinyGPS++.h>
#include <SoftwareSerial.h>
#include <SPI.h>
#include <Adafruit_GFX.h>    // Core graphics library
#include "Adafruit_HX8357.h"
//#include "TouchScreen.h"

// Connect the GPS Power pin to 5V
// Connect the GPS Ground pin to ground
// Connect the GPS TX (transmit) pin to Digital 8
// Connect the GPS RX (receive) pin to Digital 7

// You can change the pin numbers to match your wiring:
SoftwareSerial mySerial(8, 7);
TinyGPSPlus GPS;

// GPS Variables

String NMEA1; //Variable for first NMEA sentence
String NMEA2; //Variable for second NMEA sentence
char c; //to read characters coming from the GPS
float deg; //Will hold positin data in simple degree format
float degWhole; //Variable for the whole part of position
float degDec;  //Variable for the decimal part of degree
float GPS_Lon = 00;
float GPS_Lat = 00;
float Latitude = 0.0;
float Longitude = 0.0;

unsigned long int scan_time = millis();
int GPS_Scanned = 0;

/*
// for TFT display
// These are the four touchscreen analog pins
#define YP A5  // must be an analog pin, use "An" notation!
#define XM A6  // must be an analog pin, use "An" notation!
#define YM 5   // can be a digital pin
#define XP 6   // can be a digital pin

// For better pressure precision, we need to know the resistance
// between X+ and X- Use any multimeter to read it
// For the one we're using, its 300 ohms across the X plate
TouchScreen ts = TouchScreen(XP, YP, XM, YM, 300);

// This is calibration data for the raw touch data to the screen coordinates
#define TS_MINX 110
#define TS_MINY 80
#define TS_MAXX 900
#define TS_MAXY 940
#define MINPRESSURE 10
#define MAXPRESSURE 1000
*/
// Color definitions
#define BLACK    0x0000
#define BLUE     0x001F
#define RED      0xF800
#define GREEN    0x07E0
#define CYAN     0x07FF
#define MAGENTA  0xF81F
#define YELLOW   0xFFE0
#define WHITE    0xFFFF

// These are 'flexible' lines that can be changed
#define TFT_CS 10
#define TFT_DC 9
#define TFT_RST -1  // dont use a reset pin, tie to arduino RST if you like

// Use hardware SPI (on Nano Every, #13, #12, #11) and the above for CS/DC
Adafruit_HX8357 tft = Adafruit_HX8357(TFT_CS, TFT_DC, TFT_RST);

void setup() {
  // connect at 115200 so we can read the GPS fast enough and echo without dropping chars
  // also spit it out
  Serial.begin(115200);
  delay(5000);
  Serial.println("Start GPS");

// GPS
  mySerial.begin(9600); //Turn on GPS at 9600 baud

  tft.begin();
  tft.fillScreen(BLACK); //clears screen, sets to Black
  tft.setRotation(0);  // rotates screen 90' for landscape mode

    // Sets the background color of the screen to black
  tft.fillScreen(BLACK);

 // Back to Home button
  /*
  tft.fillRoundRect(30, 20, 50, 30, 10, BLUE);
  tft.drawRoundRect(30, 20, 50, 30, 10, WHITE);
  tft.setCursor(40, 27);
  tft.setTextColor(WHITE);
  tft.setTextSize(2);
  tft.print("<-");

  tft.setCursor(100, 30);
  tft.setTextColor(WHITE);
  tft.setTextSize(1);
  tft.print("Back to Main Menu");
*/
  // Prints the title on the screen
  tft.setCursor(65, 20);
  tft.setTextColor(WHITE);
  tft.setTextSize(3);
  tft.print("GPS Status");

  // Draws the red line under the title
  tft.drawFastHLine(10, 60, 320, RED);

 
 
} // end setup()

void loop() {
   // read GPS

  if (((millis() - scan_time) > 1500) && !GPS_Scanned) {

   // readGPS();
   // Serial.println("read GPS");

  // displays information every time a new sentence is correctly encoded.
  while (mySerial.available() > 0)
    if (GPS.encode(mySerial.read()))
   
   // Serial.print(F("Location: "));
  if (GPS.location.isValid())
  {
      Serial.println(F("VALID"));
      Serial.print("Lat = ");
      Serial.print(GPS.location.lat(), 4);
      Serial.print(F("\t"));
      Serial.print("Lon = ");
      Serial.print(GPS.location.lng(), 4);
      Serial.print(F("\t"));
      Serial.print("Speed = ");
      Serial.println(GPS.speed.mph()); // Speed in miles per hour (double)

      // Show green circle when GPS connected
    //  tft.setCursor(190, 25);
    // tft.setTextColor(WHITE);
    //  tft.setTextSize(1);
    //  tft.print("GPS Online");
      tft.fillCircle(290, 30, 10, GREEN);
  }
  else
  {
      Serial.println(F("INVALID"));
     
      // Show red circle when GPS disconnected
    //  tft.setCursor(185, 25);
    //  tft.setTextColor(WHITE);
    //  tft.setTextSize(1);
    //  tft.print("GPS Offline");
      tft.fillCircle(290, 30, 10, RED);
  }

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

  } // end (millis()- scan_time) > GPS



  if ((millis() - scan_time ) > 3000) {
    scan_time = millis();
    Serial.println("reset");
    GPS_Scanned = 0;


// Update screen variables
// Display Longitude
    tft.setCursor(50, 130);
    tft.setTextColor(WHITE);
    tft.setTextSize(2);
    tft.print("Longitude");
    tft.setCursor(50, 160);
    tft.setTextColor(WHITE, BLACK);
    tft.setTextSize(4);
    tft.print(GPS.location.lng(), 4);

// Display Latitude
    tft.setCursor(50, 200);
    tft.setTextColor(WHITE);
    tft.setTextSize(2);
    tft.print("Latitude");
    tft.setCursor(50, 230);
    tft.setTextColor(WHITE, BLACK);
    tft.setTextSize(4);
    tft.print(GPS.location.lat(), 4); 

// Display Speed
    tft.setCursor(50, 290);
    tft.setTextColor(WHITE);
    tft.setTextSize(2);
    tft.print("Speed");
    tft.setCursor(50, 320);
    tft.setTextColor(WHITE, BLACK);
    tft.setTextSize(6);
    tft.print(GPS.speed.mph(), 1);
   
  } // end (millis() - scan_time > 1500)


}

The delay is a problem. The GPS is emitting multiple sentences which total far more than 64 characters so in a second and a half, you're overflowing the buffer. You need to read the GPS continuously.

This is a serious mistake. Get rid of this line.

In loop(), ask whether a character is available from the GPS, and if so, send it to the encode method. The return result informs you whether a sentence and parse has been completed, in which case you take action. Otherwise, continue with loop(). NO delay() is allowed in loop().

You need to go back to the basic TinyGPS++ example, and see how that works.

@wildbill and @jremington thanks so much for your quick replies. This is some code I had copied from someone's project a while ago and have used on a previous project and up until now it seemed OK, I will have to work on it later tonight when I get home but I will remove the delay first thing. I'll update you tomorrow.

Thanks again

You can still keep the delay to avoid spamming the serial port with your output, but the reading of the GPS must never be held up so it'll need to move out of the 1.5 second if.

Thanks I'll see if I can straighten that out

I do have a text copy of my code here, and my boss thinks I'm working. Is it as simple as just moving these lines prior to the delay?

// displays information every time a new sentence is correctly encoded.
  while (mySerial.available() > 0)
    if (GPS.encode(mySerial.read()))
   

Yes.

The if/millis() construct is not a delay. It is a non-blocking method of achieving a timed action, in accordance with the project requirements.

There are other variations of the if/millis() scattered around the code, and you will need to evaluate each of those to see if the event timings are appropriate.

@wildbill and @jremington moving the read function out of the delay worked great, reading is much more responsive to changes in speed.

Thanks so much for your help and the education
John

I thought Every had at least one available hardware serial port. If so, you should be using that in preference to software serial, especially since you say performance is so important to you.

To clarify, I thought that, unlike classic Nano, the RX/TX pins are not shared with the USB-serial interface. They are for a separate serial port which can be accessed as Serial1 in your code.

@PaulRB I don't see anything that says that the USB port and DI0 & 1 (Tx and Rx) are isolated, they may be I'm just not finding anything that says that.

On the other hand I've used SoftwareSerial on several projects and like on this one I'm having no problems.

It was a timing issue I was having here not a communications

Thanks for the comment

Sorry, I should have given you a link for that, but I could not remember where I read it. So to back up claim, I did some searching and here it is:

The problem is that within the default Arduino environment, this is knocked back to 16MHz, 8 ADC and just 2 serial ports (“Serial” for USB serial; “Serial1” for D0/D1 RX/TX – still an improvement over the original Nano, but only half what is possible).

Thanks, that's good to know. I had looked at a couple of data sheets and a "The difference between a Nano and a Nano Every" and mostly it just said it had more memory.

That will come in handy

@PaulRB very interesting article, the problems are on a higher level then what I usually deal with but great to know the limitations. I started to use the Every after I was having a problem with a project I was working on kept crashing. I asked on the forum and someone explained that I was having a memory problem and switch to an Every, that did fix the problem.

Thanks again