Show last GPS lecture on this project

Hello everybody,
I've made this sketch to make an arduino connected to a photocell and a GPS module, show the hour in a LCD when an object cuts the photocell, but it only works when the GPS module gets NMEA update, i've got the module updating at 3,33 Hz so if there's an excellent connection it only works every 0,30 seccond, so do you know how to show the last lecture when the object cuts the photocell? I know it's not 100% accurated, but I prefer this to nothing being showed because there's no update of NMEA at that moment.

here's the code

#include <SoftwareSerial.h>//incluimos SoftwareSerial
#include <TinyGPS.h>//incluimos TinyGPS
#include <Wire.h>
#include <LCD.h>
#include <LiquidCrystal_I2C.h>

#define I2C_ADDR    0x3F // <<----- Add your address here.  Find it from I2C Scanner
#define BACKLIGHT_PIN     3
#define En_pin  2
#define Rw_pin  1
#define Rs_pin  0
#define D4_pin  4
#define D5_pin  5
#define D6_pin  6
#define D7_pin  7

int n = 1;

LiquidCrystal_I2C  lcd(I2C_ADDR,En_pin,Rw_pin,Rs_pin,D4_pin,D5_pin,D6_pin,D7_pin);
TinyGPS gps;//Declaramos el objeto gps
SoftwareSerial serialgps(4,3);//Declaramos el pin 4 Tx y 3 Rx

const int PIRPin= 2;
//Declaramos la variables para la obtención de datos
int year;
byte month, day, hour, minute, second, hundredths;
unsigned long chars;
unsigned short sentences, failed_checksum;

void setup()
{

lcd.begin(20,4);//Iniciamos el puerto serie
lcd.setBacklightPin(BACKLIGHT_PIN,POSITIVE);
lcd.setBacklight(HIGH);
lcd.home (); // go home

pinMode(PIRPin, INPUT);

serialgps.begin(9600);//Iniciamos el puerto serie del gps
//Imprimimos:
lcd.setCursor(0,0);
lcd.print("Buscando GPS");
}

void loop()
{
int value= digitalRead(PIRPin);
if (value == HIGH)

 
{  
  

while(serialgps.available()) 
{
int c = serialgps.read(); 
if(gps.encode(c)) 
{

gps.crack_datetime(&year,&month,&day,&hour,&minute,&second,&hundredths);
lcd.setCursor(0,0);
lcd.print(" Hora: "); 


  if (hour <= 9)
  {
    lcd.print("0"); lcd.print(hour, DEC);
  }
  else
  {
    lcd.print(hour, DEC);
  }
  lcd.print(":");
  if (minute <=9)
  {
    lcd.print("0"); lcd.print(minute, DEC);
  }
  else
  {
    lcd.print(minute, DEC); 
  }
  lcd.print(":");
  if (second <= 9) 
  {
    lcd.print("0"); lcd.print(second, DEC);
  } 
  else 
  {
    lcd.print(second, DEC);
  }
  lcd.print(";");
  lcd.print(hundredths, DEC);
  } 
  else 
  {
    lcd.print(hundredths, DEC);
  }


}
}
}

I think we have a little translation error around "lecture". "Lectura" in Spanish could be translated as "reading" so maybe that would be a better word to use?

Your system only even looks at the GPS when the value is high. That's wrong. The GPS data is always coming in the serial port. You should always handle it. Otherwise the buffer can fill up and new data is discarded - the buffer is full of old data from just after the value went low.

When you get a valid GPS sentence, you need to know the time that ocurred, with millis(). The GPS library may do that for you. Then when you get a high-to-low transition, take the last GPS time and add the elapsed time. The last GPS fix may have been 10 minutes ago and you will still get a very accurate time reading.

And by hour, I guess you actually mean time ie (HH:MM:SS.sss).
If you require a complete timestamp with your readings (as appears it appears from your code), you could also use the GPS clock simply to synchronize the Arduino internal time, and then use that [Arduino clock] for your measurements instead of relying on sporadic readings from the GPS to coincide with your photocell beam break events.

6v6gt:
And by hour, I guess you actually mean time ie (HH:MM:SS.sss).
If you require a complete timestamp with your readings (as appears it appears from your code), you could also use the GPS clock simply to synchronize the Arduino internal time, and then use that [Arduino clock] for your measurements instead of relying on sporadic readings from the GPS to coincide with your photocell beam break events.

Sounds great, but, how can I do this without a RTC clock?

politxillo:
Sounds great, but, how can I do this without a RTC clock?

Using Arduino Time library

politxillo:
Hi, i've seen you replied to my post about the GPS clock,
i've tried to make a simple clock with the arduino without the RTC, it works but when hundredths rise above 100, the counter jumps on seconds, and the clock crashes, could you take a look to the code please? thanks!

#include <LCD.h>

#include <LiquidCrystal_I2C.h>
#include <SoftwareSerial.h>//incluimos SoftwareSerial

#define I2C_ADDR    0x3F // <<----- Add your address here.  Find it from I2C Scanner
#define BACKLIGHT_PIN     3
#define En_pin  2
#define Rw_pin  1
#define Rs_pin  0
#define D4_pin  4
#define D5_pin  5
#define D6_pin  6
#define D7_pin  7

LiquidCrystal_I2C  lcd(I2C_ADDR,En_pin,Rw_pin,Rs_pin,D4_pin,D5_pin,D6_pin,D7_pin);
SoftwareSerial serialgps(4,3);//Declaramos el pin 4 Tx y 3 Rx

unsigned long timeNow = 0;
unsigned long timeLast = 0;

//Time start Settings:

int startingHour = 12; // set your starting hour here, not below at int hour. This ensures accurate daily correction of time

int hundredths = 0;
int seconds = 0;
int minutes = 59;
int hours = startingHour;

void setup() {

lcd.begin(20,4);//Iniciamos el puerto serie
lcd.setBacklightPin(BACKLIGHT_PIN,POSITIVE);
lcd.setBacklight(HIGH);
lcd.home ();

void loop() {
 // put your main code here, to run repeatedly:

timeNow = millis()/10;
hundredths = timeNow;

if (hundredths >= 100) {
 hundredths = 0;
 seconds = seconds + 1;
}

if (seconds == 60) {
 seconds=0;
 minutes = minutes + 1;
}

if (minutes == 60){
 minutes = 0;
 hours = hours + 1;
}

lcd.setCursor(0,0);
lcd.print(hours);
lcd.print(":");
lcd.print(minutes);
lcd.print(":");
lcd.print(seconds);
lcd.print(":");
lcd.print(hundredths);

}

Don't do it in pm, so that other have a change to know what is happening.

Why don't you use the Time library that I show you?

It have all the function

hour(); // The hour now (0-23)
minute(); // The minute now (0-59)
second(); // The second now (0-59)
day(); // The day now (1-31)
weekday(); // Day of the week, Sunday is day 1
month(); // The month now (1-12)
year(); // The full four digit year: (2009,
// 2010 etc)

What GPS module and what Arduino are you using?

how can I do this without a RTC clock?

Most GPS modules have an internal RTC. They will usually report the time, even if it does not have a GPS fix on the satellites. You can use that time if it is sent to the Arduino.

However, you will have to use a different GPS library. My NeoGPS library reports the time if the GPS sends it, even if the GPS device is not locked on to satellites. NeoGPS is smaller and faster than other libraries.

Also, SoftwareSerial blocks interrupts for long periods of time, so I would suggest switching to another library I maintain, NeoSWSerial. If you can move the GPS to pins 8 & 9, that would be even better. Then you could use AltSoftSerial, which is even more efficient and reliable.

Your sketch looks like a stopwatch. Is that right? Should it display the time when the photocell beam is broken, and hold that time until the beam is not broken? An LCD does not update very quickly, so it would not make sense to have a running clock with with blurry hundredths.

If that's true, here's a version of your sketch that uses NeoGPS and NeoSWSerial:

#include <NeoSWSerial.h>
#include "NMEAGPS.h"

#include <Wire.h>
#include <LCD.h>
#include <LiquidCrystal_I2C.h>

#define I2C_ADDR    0x3F // <<----- Add your address here.  Find it from I2C Scanner
#define BACKLIGHT_PIN     3
#define En_pin  2
#define Rw_pin  1
#define Rs_pin  0
#define D4_pin  4
#define D5_pin  5
#define D6_pin  6
#define D7_pin  7

int n = 1;

LiquidCrystal_I2C  lcd(I2C_ADDR,En_pin,Rw_pin,Rs_pin,D4_pin,D5_pin,D6_pin,D7_pin);
NMEAGPS gps;//Declaramos el objeto gps
NeoSWSerial serialgps(4,3);//Declaramos el pin 4 Tx y 3 Rx

const int PIRPin= 2;
//Declaramos la variables para la obtención de datos
gps_fix  fix;             // The most recent GPS fields
uint32_t lastValidTime;   // The last millis() when GPS sent a TIME field
bool     GPStimeIncremented = false;
int      lastValue = LOW; // The previous state of the photocell pin

void setup()
{
  lcd.begin(20,4);//Iniciamos el puerto serie
  lcd.setBacklightPin(BACKLIGHT_PIN,POSITIVE);
  lcd.setBacklight(HIGH);
  lcd.home (); // go home

  pinMode(PIRPin, INPUT);

  serialgps.begin(9600);//Iniciamos el puerto serie del gps
  //Imprimimos:
  lcd.setCursor(0,0);
  lcd.print( F("Buscando GPS") );
}

void loop()
{
  // Always read the GPS information.  This will keep
  //   the fix.dateTime always correct.
  
  while (gps.available( serialgps )) {
    fix    = gps.read();

    if (fix.valid.time) {
      // TIME field had current time
      lastValidTime = millis();

    } else {
      // TIME field was empty, just add to the old time.
      uint32_t currentMS = millis();

      uint32_t dt = currentMS - lastValidTime;
      if (dt >= 999) {
        lastValidTime  += 1000;
        if (!GPStimeIncremented) {
          // Add one second to the old time
          fix.dateTime   += (NeoGPS::clock_t) 1;
          fix.dateTime_cs = 0;
        }

      } else if (dt >= 333) {
        // Add one-third of a second to the old time (3Hz)
        lastValidTime   += 333;
        fix.dateTime_cs += 33;
      }
    }
    GPStimeIncremented = false;
  }

  //  See if the beam was broken
  int value= digitalRead(PIRPin);

  //  Did it *change* from LOW to HIGH?
  if ((lastValue == LOW) && (value == HIGH)) {  

    // Round the current millis() delta to the nearest hundredth
    uint32_t dt         = millis() - lastValidTime;
    uint8_t  hundredths = (dt + 5) / 10 + fix.dateTime_cs;

    // Just in case the hundredths gets too big, increment the seconds.
    //   There is probably a new GPS time about to come in
    while (hundredths >= 100) {
      hundredths -= 100;
      fix.dateTime += (NeoGPS::clock_t) 1;
      GPStimeIncremented = true;
    }

    lcd.setCursor(0,0);
    lcd.print( F(" Hora: ") ); 

    if (fix.dateTime.hours <= 9)
      lcd.print('0');
    lcd.print(fix.dateTime.hours, DEC);

    lcd.print(':');

    if (fix.dateTime.minutes <=9)
      lcd.print('0');
    lcd.print(fix.dateTime.minutes, DEC);

    lcd.print(':');

    if (fix.dateTime.seconds <= 9) 
      lcd.print('0');
    lcd.print(fix.dateTime.seconds, DEC);

    lcd.print(';');
    if (hundredths <= 9) 
      lcd.print('0');
    lcd.print(hundredths, DEC);
    
    // The displayed time will not change until the pin goes LOW and
    //    then goes HIGH again.
  }

  //  Remember the pin state for next time, so we know when it *changes*.
  lastValue = value;
}

Notes:

1) If the GPS does not report a time, it will just increment the previous time by your GPS update interval (333ms period is 3Hz frequency). Are you sure your update frequency is 3.33Hz? The numbers in the code would have to change if you really have an update period of 300ms.

2) The displayed time is calculated as an offset from the previous GPS time, in hundredths. When the GPS device does not have a fix and the time is not reported (no RTC in the GPS device), the flag GPStimeIncremented makes sure that an old GPS time does not get incremented more than once per second. If you never have a good GPS time, it will start from 00:00:00.00.

3) The displayed time will not change until the beam is cleared (not broken) and then broken again.

4) NeoGPS has its own time functions, so you don't need the extra Time library.

Cheers,
/dev