When Time.h gets sync from GPS, board resets if time was manually set before

I have a sketch that is essentially a clock. At first, I had it set up so that the sketch initially set the time using setTime(XXXXXXXXXX) to 12AM on a certain day (the day doesn't matter since the clock does not display the date, I just chose a day at 12AM and put that epoch time value in in place of the Xs.)

After that, there are two buttons the user can use to set the time. I programmed it so that one does "setTime(now()+3600)", the other does "setTime(now()+60)". Basically, one advances the time by an hour, one by a minute, just like a normal clock.

I realized that the Arduino does not keep accurate time by itself, so I added a GPS module, and set the sync provider as GPS.

This generally works well, but since a GPS lock is not always available when you first give power to the device, I still allowed the buttons on the back to advance the time. My idea was that if someone manually set the time before the GPS lock was obtained, then I would set a variable indicating that had happened, and then when it did finally get a GPS lock, it would take into account how far off the current time was from UTC, and take that into account every time it re-synced to the GPS. (So, essentially, I assume that if you set the time yourself, that time is correct, and the GPS sync would just keep that time accurate, because I would offset the GPS set time by however far off it was from UTC when it first got a GPS lock.)

Problem is, if you manually set the time using the buttons, whenever the GPS lock is obtained, it seems like the whole board resets, so I can't do any of the calculations I just talked about, because everything starts over from the beginning.

The puzzling thing is that the board only resets like this if you manually set the time before getting a GPS lock. If you just leave it alone, it gets a GPS lock after a minute or two, factors in the default timezone offset I programmed it with (eastern time), and all is good.

Even if you then advance the time manually after it gets that first GPS lock, it still doesn't reset the next time it syncs with GPS. (I made it so that advancing the time after getting GPS lock both advances the time and adjusts the timezone offset forward by however much you advanced the time, so the next time it syncs, it doesn't go back to the old time.

I'm aware that opening new serial connections can reset the board, but I'm not handling the serial connection any different whether you set the time before GPS lock happens or not. The GPS is set to transmit and receive on pins A4 and A5 using the SoftwareSerial library, and the serial connection begins right at the start of the sketch. (The buttons to advance time are connected to A0 and A1 if it matters.)

Any idea why adjusting the time manually before a GPS lock is obtained seems to make the Arduino reset when it gets a lock? This is an Uno board BTW. Thanks.

Any idea why adjusting the time manually before a GPS lock is obtained seems to make the Arduino reset when it gets a lock?

Without seeing your code? Not a clue.

As PaulS says, it is hard to debug code we can't see.

Please use code tags.

Read this before posting a programming question

Sorry, I was posting somewhere before where I did not have access to the code. I thought perhaps there would be a known issue with this sort of thing. Here is the code.

#include <Time.h>
#include <PWM.h>
#include <SoftwareSerial.h>
#include <TinyGPS.h>

TinyGPS gps;

SoftwareSerial Serial_GPS(A4, A5);

float currentledlevel = 0;
float lastledlevel = 0;
float brightmultiplier = 0;
float brightmultbuff = 1;
float fadeonoff = 0;
float lastfadeonoff = 0;
int blinktimer = 0;
int countdownminute = 0;
int countdownsecond = 0;
int totalcountdownseconds = 0;
int totalcurrentseconds = 0;
int currenthour = 25;
int currenthourGPS = 25;
int currenthourDSTadjust = 25;
int currenthourset = 25;
int currentsecond = 61;
int initialset = 0;
int blinksecond = 0;
int houradvancedelay = 0;
int minuteadvancedelay = 0;
int hourglitchdelay = 0;
int minuteglitchdelay = 0;
int setstatus = 0;
//0 = no time set
//1 = time set only by GPS
//2 = time altered only manually
//3 = time set by GPS then altered manually
//4 = time altered manually then set by GPS
//5 = time altered manually, set by GPS, then altered manually again
float TZoffset = 0;
int DST = 0;
int alteredtolockcompensation = 0;
int initialGPSlockedTZDSTcomp = 0;
int initialGPSlockedDSTeval = 0;
float GPStotalseconds;//total seconds elapsed that day according to GPS time
float settotalseconds;//total seconds elapsed that day according to set time



void setup()  {
  pinMode (0, OUTPUT);
  digitalWrite(0, LOW);
  pinMode (1, OUTPUT);
  digitalWrite(1, LOW);
  pinMode (2, OUTPUT);
  digitalWrite(2, LOW);
  pinMode (3, OUTPUT);
  digitalWrite(3, LOW);
  pinMode (4, OUTPUT);
  digitalWrite(4, LOW);
  pinMode (5, OUTPUT);
  digitalWrite(5, LOW);
  pinMode (6, OUTPUT);
  digitalWrite(6, LOW);
  pinMode (7, OUTPUT);
  digitalWrite(7, LOW);
  pinMode (7, OUTPUT);
  digitalWrite(7, LOW);
  pinMode (8, OUTPUT);
  digitalWrite(8, LOW);
  pinMode (9, OUTPUT);
  digitalWrite(9, LOW);
  pinMode (10, OUTPUT);
  digitalWrite(10, LOW);
  pinMode (11, OUTPUT);
  digitalWrite(11, LOW);
  pinMode (12, OUTPUT);
  digitalWrite(12, LOW);
  pinMode (13, OUTPUT);
  digitalWrite(13, LOW);

  pinMode (A0, INPUT_PULLUP);
  pinMode (A1, INPUT_PULLUP);
  pinMode (A2, INPUT);  
  InitTimersSafe();
  SetPinFrequency(9, 131);
  SetPinFrequency(10, 131);
  setTime(1452470400);
  currenthour=hourFormat12();
  
  delay(5000);
  Serial_GPS.begin(9600);
  //Serial.begin(9600);
  setSyncProvider(gpsTimeSync);
  setSyncInterval(60); 
  
}

void loop(){
 
  while (Serial_GPS.available()) 
  {
    gps.encode(Serial_GPS.read());   
  }

if (initialGPSlockedTZDSTcomp == 0 && setstatus == 1)
    {
     DSTdatetest();//Function that tests if the date/time that was set by the GPS is within daylight savings time
     initialGPSlockedTZDSTcomp = 1;//Set this to 1 so this test is only run once, when the time is initially set
    }
    
if (setstatus == 1 || setstatus == 3 || setstatus == 4 || setstatus == 5)
{
  
  if (currenthourDSTadjust != hour()) //Checks if hour has changed. 
    {
    if (month() == 3 && day() > 7 && day() < 15 && weekday() == 1 && hour () == 2)//Checks for moment of spring DST start
      {
        DST = 1;
        if (setstatus == 3 || setstatus == 4 || setstatus == 5)
          {
           TZoffset = TZoffset-3600;
          }
      }
    if (month() == 11 && day() < 8 && weekday() == 1 && hour () == 2)//Checks for moment of fall DST end
      {
        DST = 0;
        if (setstatus == 3 || setstatus == 4 || setstatus == 5)
          {
           TZoffset = TZoffset+3600;
          }        
      }
      
      if (setstatus == 1 && DST == 0)             
          {
           TZoffset = -18000; 
          }
      if (setstatus == 1 && DST == 1)             
          {
           TZoffset = -14400; 
          }     
      currenthourDSTadjust = hour();
    }
}    
      
  
if (digitalRead(A0) == LOW && houradvancedelay == 0)
  {
   if (setstatus == 0)//sets set status to appropriate status based on what it was before and the fact that now time is being altered manually
      {
       setstatus = 2;
      }
   if (setstatus == 1)
      {
       setstatus = 3;
      }
   if (setstatus == 4)
      {
       setstatus = 5;
      }
   setTime(now()+3600);     
   blinksecond = 0;
   initialset = 1;
   houradvancedelay = houradvancedelay + 1;
   hourglitchdelay = 0;
   if (setstatus == 3 || setstatus == 5)//If status shows GPS time has already been set and now a manual adjustment is being made, the TZoffset must also be adjusted by this much so that this adjustment will be accounted for in future GPS locks
      {
       TZoffset = TZoffset + 3600;
      }    
  }
if (digitalRead(A0) == LOW && houradvancedelay >= 1 && houradvancedelay < 1000)//All of this just makes a delay between hour advances so the time doesn't rapidly fly forward when you push the hour advance button. It also makes it so that it doesn't recognize a small, momentarily lapse in button contact as a separate press of the button. This is what the glitch delay is.
  {
   houradvancedelay = houradvancedelay + 1;
  }  
if (digitalRead(A0) == LOW && houradvancedelay >= 1000)
  {
   houradvancedelay = 0;
  }
if (digitalRead(A0) == HIGH && hourglitchdelay < 10)
  {
   hourglitchdelay = hourglitchdelay + 1; 
  }
if (digitalRead(A0) == HIGH && hourglitchdelay >= 10)   
  { 
   houradvancedelay = 0;  
  }
  
  
  
if (digitalRead(A1) == LOW && minuteadvancedelay == 0)
  {
   if (setstatus == 0)//sets set status to appropriate status based on what it was before and the fact that now time is being altered manually
      {
       setstatus = 2;
      }
   if (setstatus == 1)
      {
       setstatus = 3;
      }
   if (setstatus == 4)
      {
       setstatus = 5;
      }     

   setTime(now()+60);
   blinksecond = 0;   
   initialset = 1;
   minuteadvancedelay = minuteadvancedelay + 1;
   minuteglitchdelay = 0;
   if (setstatus == 3 || setstatus == 5)//If status shows GPS time has already been set and now a manual adjustment is being made, the TZoffset must also be adjusted by this much so that this adjustment will be accounted for in future GPS locks
      {
       TZoffset = TZoffset + 60;
      }    
  }
if (digitalRead(A1) == LOW && minuteadvancedelay >= 1 && minuteadvancedelay < 1000)//All of this just makes a delay between minute advances so the time doesn't rapidly fly forward when you push the minute advance button. It also makes it so that it doesn't recognize a small, momentarily lapse in button contact as a separate press of the button. This is what the glitch delay is.
  {
   minuteadvancedelay = minuteadvancedelay + 1;
  }  
if (digitalRead(A1) == LOW && minuteadvancedelay >= 1000)
  {
   minuteadvancedelay = 0;
  }   
if (digitalRead(A1) == HIGH && minuteglitchdelay < 10)
  {
   minuteglitchdelay = minuteglitchdelay + 1; 
  }
if (digitalRead(A1) == HIGH && minuteglitchdelay >= 10)   
  { 
   minuteadvancedelay = 0;  
  } 
  
      
      if (currenthour != hour()) //Checks if hour has changed. Sets hour pins appropriately     
          {
           hourpinset();
           currenthour = hour(); //Sets currenthour back to the actual hour.
          }        
    
    
    //Everything in the loop below this is just functions that calculate some display brightness settings in the clock. One of them reads an ambient light sensor that inputs to pin A2

    outputproperPWMbrightness(); 
        
          
    calculateactiveLEDintensity();
    
  
    adjustbrightness();
       

 
}//End of loop

And here are the GPS time setting functions. This is directly after the end of the last block of code. It would not all fit in one post.

time_t gpsTimeSync() {
  //  returns time if avail from gps, else returns 0
  unsigned long fix_age = 0 ;   
  unsigned long date, time;
  gps.get_datetime(&date, &time, &fix_age);
  // Ignore if fix age is invalid or stale
  if (fix_age == TinyGPS::GPS_INVALID_AGE || fix_age > 1000) {
    return 0;
  }
  // Ignore if no valid date or time yet received, such as when GPGGA is received before a GPRMC, because there's no date in a GPGGA
  if (date == TinyGPS::GPS_INVALID_DATE || time == TinyGPS::GPS_INVALID_TIME) {
    return 0;
  }
  return gpsTimeToArduinoTime(); // return time only if updated recently by gps 
}

time_t gpsTimeToArduinoTime() {
  // returns time_t from gps date and time with the given offset value
  tmElements_t tm;
  int year;
  gps.crack_datetime(&year, &tm.Month, &tm.Day, &tm.Hour, &tm.Minute, &tm.Second, NULL, NULL);
  tm.Year = year - 1970;

    if( setstatus == 0)//Sets the status of timeset. See the variable declarations at the top for what these mean
    {
     setstatus = 1;
    }
    else if( setstatus == 2)
    {
     setstatus = 4;
    }
    
    if(setstatus == 1)
    {
     if(DST == 0)
       {
        TZoffset = -18000;
       }
     else if(DST == 1)
       {
        TZoffset = -14400;
       }
    }
    
    if( setstatus == 4 && alteredtolockcompensation == 0)
    {
      
           GPStotalseconds = tm.Hour*3600;
           GPStotalseconds = GPStotalseconds + (tm.Minute*60);
           GPStotalseconds = GPStotalseconds + tm.Second;
           settotalseconds = hour()*3600;
           settotalseconds = settotalseconds + (minute()*60);
           settotalseconds = settotalseconds + second();
           TZoffset = settotalseconds - GPStotalseconds;
           alteredtolockcompensation = 1;//Set this to 1 so the compensation only happens once     
    }    
    
  time_t time = makeTime(tm);    
  return time + (TZoffset);
}

Ess_El_Emm:
This generally works well, but since a GPS lock is not always available when you first give power to the device, I still allowed the buttons on the back to advance the time.

How long had the device in power-off mode?
Which GPS module do you use?

Many modern GPS modules provide internal RTC functions as well as a small rechargable battery with the module mounted on PCB, which will keep RTC time accurate for at least 14 days in power-off mode.

So if you power-on modern GPS modules, you most likely will get a correct time within one second, without having a GPS location fix.

It's the adafruit flora GPS. I don't see an obvious battery mounted on the board. If there is one that is not apparent, would taking out the test where it checks for a valid date/time just take whatever the GPS is sending out even if it doesn't have an actual lock yet?

In either case, I still want this to work how I describe, because I want it to function even indoors where there may not be good GPS reception.

I just tried a capacitor between the reset and ground pins, as I have read this can prevent the board from resetting when a serial connection is opened... Doesn't seem to work. However, I was pretty sure that wouldn't happen with a software serial connection anyway. Not to mention the fact that I don't think I'm opening one when this happens.

There must be something within the code I don't realize. Maybe some memory location gets corrupted that I'm not realizing when the time is manually set and then GPS data is read in? Seems odd though, because the time is already "manually" set right new the beginning of the sketch.

Ess_El_Emm:
It's the adafruit flora GPS. I don't see an obvious battery mounted on the board.

No, there is no battery with the Adafruit Flora GPS.

But the description clearly tells you:

RTC battery-compatible - sew a battery on to create a atomic-precision real time clock

So you just have to connect a small coin battery to enable the battery backup for RTC clock function of the module, and the module will keep accurate time during power-off (until the backup battery is empty after several year, perhaps). As soon as you power-on the module, it will start to send accurate time, even if there is no GPS location fix present.

Of course: At first the module needs to receive an accurate GPS time after attaching the battery, before you can power-down the module and it can keep the time accurate.

I'd rather just rely strictly on the GPS fix, as it requires no battery. I still don't understand why my code causes this reset. I used the memoryfree library to check my free memory, it's showing 1141(bytes?)

Ess_El_Emm:
I'd rather just rely strictly on the GPS fix, as it requires no battery. I still don't understand why my code causes this reset.

Your code is calling this without previously checking if you have a GPS fix:

setSyncProvider(gpsTimeSync);

As soon as you call this setSyncProvider() function, you will sync your time using the gpsTimeSync() function.

So what will happen? I don't know as I don't any of the libraries you are using.

But if you are trying to sync your time from a GPS device which does not provide a valid time because it has no valid time, I'd not expect to get a correct time before the GPS module gets a valid time from the GPS satellites.

If you need a valid time from power-on, you will need a RTC.

Ess_El_Emm:
I'd rather just rely strictly on the GPS fix, as it requires no battery.

A GPS unit that has a battery can get a fix faster because, knowing the time, it can use an almanac to estimate satellite locations. That creates an easier search which speeds up the fix. Without it you can have a cold fix with every boot, which can take up to 15 minutes worst case to get a satellite lock.

I added a CR2032 battery to the pins on the GPS module marked battery. It does seem to get a GPS lock and set the time much faster now. Maybe fast enough that I won't need to worry about a scenario where the time is set before a GPS lock can be obtained. Still really bugs me that I don't understand why the board is resetting.

I moved the setsyncprovider command down to the while Serial_GPS available area. If it's down there, it doesn't seem to ever sync, so that's not good.

One question about having the battery in there. Would it be re-charged by the GPS module? In other words, is the battery only being drained when the GPS module is not powered up, and then does it re-charge when power is applied to the GPS module again?