GPS Time Sycned PPS Interrupt has occasion problem

I have an Adafruit Ultimate GPS hooked up to an UNO. The intention of the project is to blink the LED On and Off at given intervals, starting at the same time every day. So if two of these were built and and setup to run miles apart, the lights would still blink in synchronization as the timing is controlled by the PPS pin on the GPS.

The example picture I have attached shows an oscilloscope output of the PPS pin on the GPS (red) and the LED pin (blue). The HIGH cycle should be 4 secs and the LOW should be 2.

For some reason I can’t figure out, every once in a while the timing cycle either picks up or drops a second on a given wave. It then corrects itself a few cycles later. Can anyone help me figure out a good solution stop this error from happening?

Could this be a bad connection between the arduino and the GPS PPS pin?

I’ll put the code in the next post

#include <Adafruit_GPS.h>
#include <SoftwareSerial.h>
#include <Time.h>
#include <DS1307RTC.h>
#include <LiquidCrystal.h>
#include <Wire.h>




SoftwareSerial mySerial(8, 7);
LiquidCrystal lcd(0);

Adafruit_GPS GPS(&mySerial);


volatile unsigned long beginTime = 0;    //result of the beginTime function which determines UNIX start time
volatile unsigned long countTime = 0;    //result of the countTime function which determines next ledAstate change
volatile unsigned long gpsindexTime = 0;

volatile unsigned long currentindexTime = 0;    //used for pps_interrupt function
volatile unsigned long previousindexTime = 0;    //used for pps_interrupt function
volatile unsigned int driftindexTime = 0;    //used for pps_interrupt function

volatile unsigned long currentMillis = 0;    //used for pps_interrupt function
volatile unsigned long previousMillis = 0;    //used for pps_interrupt function
volatile unsigned int driftMillis = 0;    //used for pps_interrupt function

volatile unsigned long currentledMillis = 0;    //used for pps_interrupt function
volatile unsigned long previousledMillis = 0;    //used for pps_interrupt function
volatile unsigned int driftledMillis = 0;    //used for pps_interrupt function

volatile unsigned long ppsIndex = 0;
volatile unsigned long lastppsIndex = 0;

//Used for blinking the light in ledFunction.  Will be for driving a MOSFET in final version.
volatile byte ledAstate = HIGH;
volatile byte ledAprevious = HIGH;
const byte ledApin = 13;

// Offset hours from gps time (UTC)
const int offset = -6;  //timezone where I am

const int onTime = 4;   //time in seconds the MOSFET will be "ON".  User input between 0-10 in final version.
const int offTime = 2;   //time in seconds the MOSFET will be "OFF".  User input between 0-10 in final version.
const int beginHour = 7;    //24 hour reference (7 = 7am) for calculating beging UNIX time to feed the beginTime function.  User input final version.

time_t prevDisplay = 0; // when the digital clock was displayed

// Set GPSECHO to 'false' to turn off echoing the GPS data to the Serial console
// Set to 'true' if you want to debug and listen to the raw GPS sentences. 
#define GPSECHO  false

// this keeps track of whether we're using the interrupt
// off by default!
boolean usingInterrupt = false;
void useInterrupt(boolean); // Func prototype keeps Arduino 0023 happy

void pps_interrupt() {
  
  ppsIndex++;
  
  if (ledAstate != ledAprevious)  {    
    digitalWrite(ledApin, ledAstate);
    previousledMillis = currentledMillis;
    currentledMillis = millis();
    driftledMillis = (currentledMillis - previousledMillis);
    ledAprevious = ledAstate;
  }
//  driftindexTime = (currentindexTime - previousindexTime);
//  previousindexTime = currentindexTime;
  previousMillis = currentMillis;
  currentMillis = millis();
  driftMillis = (currentMillis - previousMillis);
  
  }

void setup()  
{  
  Serial.begin(115200);
  // set up the LCD's number of rows and columns:
  lcd.begin(20, 4);
  lcd.setBacklight(HIGH);  

  pinMode(ledApin, OUTPUT);
  digitalWrite(ledApin, HIGH);

  pinMode(2, INPUT);
  attachInterrupt(0, pps_interrupt, RISING);

  setSyncProvider(RTC.get);   // the function to get the time from the RTC
  setSyncInterval(60);
    if (timeStatus() != timeSet)    
     lcd.print("Unable to sync with RTC");
    else   
     lcd.print("System synched to RTC");     
  delay(2000);
  
  lcd.clear();   
  lcd.print("GPS Interruption:");
  lcd.setCursor(0, 1);
  lcd.print("Time:     :  :  ");
  
  GPS.begin(9600);
  
  
  GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCONLY);  
  GPS.sendCommand(PMTK_SET_NMEA_UPDATE_5HZ);   // 1 Hz update rate   
  GPS.sendCommand(PGCMD_ANTENNA);  
  useInterrupt(true);

  tmElements_t tm;  
  delay(1000);
  
  mySerial.println(PMTK_Q_RELEASE);
}


// Interrupt is called once a millisecond, looks for any new GPS data, and stores it
SIGNAL(TIMER0_COMPA_vect) {
  char c = GPS.read();
  // if you want to debug, this is a good time to do it!
#ifdef UDR0
  if (GPSECHO)
    if (c) UDR0 = c;  
    // writing direct to UDR0 is much much faster than Serial.print 
    // but only one character can be written at a time. 
#endif
}

void useInterrupt(boolean v) {
  if (v) {
    // Timer0 is already used for millis() - we'll just interrupt somewhere
    // in the middle and call the "Compare A" function above
    OCR0A = 0xAF;
    TIMSK0 |= _BV(OCIE0A);
    usingInterrupt = true;
  } else {
    // do not call the interrupt function COMPA anymore
    TIMSK0 &= ~_BV(OCIE0A);
    usingInterrupt = false;
  }
}
uint32_t timer = millis();

void loop()                     
{
  ledFunction();
  
  if (! usingInterrupt) {
    // read data from the GPS in the 'main loop'
    char c = GPS.read();
    
  }
  
  // if a sentence is received, we can check the checksum, parse it...
  if (GPS.newNMEAreceived()) {
    
  
    if (!GPS.parse(GPS.lastNMEA()))   // this also sets the newNMEAreceived() flag to false
      return;  // we can fail to parse a sentence in which case we should just wait for another
  }
        

  // if millis() or timer wraps around, we'll just reset it
  if (timer > millis())  timer = millis();

  // every 10 pules fromt he PPS pin, synch to GPS Time
  if (ppsIndex > 10) {     
    tmElements_t tm;
    tm.Second = (GPS.seconds);
    tm.Hour = (GPS.hour);
    tm.Minute = (GPS.minute);
    tm.Day = (GPS.day);
    tm.Month = (GPS.month);
    tm.Year = (GPS.year - 2018);   
    
    RTC.set((makeTime(tm) + offset * SECS_PER_HOUR));
    setTime(makeTime(tm));
    adjustTime(offset * SECS_PER_HOUR);
    ppsIndex = 0;
  }
  if (millis() - timer > 1000) { 
    timer = millis(); // reset the timer
      lcdDisplay();
//    digitalClockDisplay();
  }
  
  beginFunction();
  countFunction();
}

void ledFunction()  {
  
  if ((now() + 1) == countTime) {

    ledAstate = HIGH;
  }
  if ((now() + 1) == (countTime - offTime)) {

    ledAstate = LOW;
  }

}

void beginFunction() {
  //Function determines UNIX start point for 7am of current day
  //and returns value as beginTime  
  tmElements_t tm;
  tm.Second = 0;
  tm.Hour = beginHour;
  tm.Minute = 0;
  tm.Day = day();
  tm.Month = month();
  tm.Year = year() - 1970;
  beginTime = makeTime(tm);
}
void countFunction() {
  //Function counts one cycle past now() to obtain countTime variable to feed the LED Function
  countTime = beginTime;
  while (countTime <= now())  {
    countTime = (countTime + onTime + offTime);
  }

}

void lcdDisplay() { 
  
  lcd.setCursor(19, 0);
  lcd.print(ledAstate);

  lcd.setCursor(8, 1);
  if (hour() < 10) {
    lcd.print("0 ");
    lcd.setCursor(9, 1);
    lcd.print(hour());
  }
  else {
    lcd.setCursor(8, 1);
    lcd.print(hour());
  }

  lcd.setCursor(11, 1);
  if (minute() < 10) {
    lcd.print("0 ");
    lcd.setCursor(12, 1);
    lcd.print(minute());
  }
  else {
    lcd.setCursor(11, 1);
    lcd.print(minute());
  }

  lcd.setCursor(14, 1);
  if (second() < 10) {
    lcd.print("0 ");
    lcd.setCursor(15, 1);
    lcd.print(second());
  }
  else {
    lcd.setCursor(14, 1);
    lcd.print(second());
  }

  lcd.setCursor(0, 2);
  lcd.print("      ms");
  lcd.setCursor(1, 2);
  lcd.print(driftledMillis);
  lcd.setCursor(0, 3);
  lcd.print("      ms");
  lcd.setCursor(1, 3);
  lcd.print(driftMillis);
  lcd.setCursor(10, 3);
  lcd.print("Synch    ");
  
  lcd.setCursor(17, 3);
  lcd.print(ppsIndex);
}
void digitalClockDisplay() {
  // digital clock display of the time
  Serial.print("\nTime: ");
  Serial.print(GPS.hour, DEC); Serial.print(':');
  Serial.print(GPS.minute, DEC); Serial.print(':');
  Serial.print(GPS.seconds, DEC); Serial.print('.');
  Serial.println(GPS.milliseconds);
  Serial.print("Date: ");
  Serial.print(GPS.day, DEC); Serial.print('/');
  Serial.print(GPS.month, DEC); Serial.print("/20");
  Serial.println(GPS.year, DEC);


}

void printDigits(int digits) {
  // utility function for digital clock display: prints preceding colon and leading 0
  Serial.print(":");
  if (digits < 10)
    Serial.print('0');
  Serial.print(digits);
}