GPS clock almost working right

I have built a GPS clock plus temperature display, using the UP501D gps module.

I have the display seperate to the gps unit, and send the numbers to be displayed via a cable using VirtualWire ( running slowly to avoid cable capacitance problems ) The waveform at the display end is identical to the gps unit end so thats fine.

At switch on, no data is sent to the display until the first valid sentance is received.

To avoid the occaisional spurious data from the gps being displayed, I read the minutes and hours every second, and every time the minute changes, I add a minute to make " nextminute" and " nexthour" to compare with the following minutes data.

If they match, the freerunning clock is updated, and its millis timing reset to zero.

Initially I lose an extra minute at switch on, but it takes a while for the gps to lock on anyway at power on.

If the GPS loses lock, the free running clock carries on updating the display every minute using its 16 MHz xtal ( its accurate to within a few seconds a day, so I havn't bothered with a RTC , the gps stays locked on virtually all the time )

At the display end, I have a micro receiving the VW data, and sending it serially to the display digits which are latched.

To avoid showing the wrong time all day if the data stops, I have a timer that blanks the display after 130 seconds of no data received.

My only problem is that once in a blue moon, the display blanks , which shows a lack of data.

The LED stays on ( not flashing ) which shows a valid sentance is being received... and if it did lose lock, the freerun clock should be transmitting every minute anyway..

Unfortunately both times it has happened, I didnt leave it on to see if it would come back on after a minute, and it hasn't happened in the last day or two. I am waiting with the scope ready, but a watched pot never boils ....

Can anyone see in my code below, if I have any stupid mistakes, or situations that could make it hang ?

// GPS CLOCK BOTTOM END APR 7   
    
    #include <NewSoftSerial.h>
    #include <TinyGPS.h>
    #include <VirtualWire.h>
    #define ledPin 8  //  flashes LED
    int sensorPin = 3; //  analog pin 0 reads tmp35 output
    unsigned long clockmillis = 0;
    unsigned long rxmillis = 0;
    unsigned long interval = 60000;
    unsigned long currentMillis;
    unsigned long currentclockMillis;
    int clockmins;
    int clockhours;
    #define RXPIN 14 //   
    #define TXPIN 15 // 
    #define GPSBAUD 9600 // baud rate of our UP501D GPS module. Change for your GPS module if different
    char msg [6]; 
    TinyGPS gps;
    NewSoftSerial uart_gps(RXPIN, TXPIN);
    void getgps(TinyGPS &gps);
    int temptens;
    int tempunits;
    int hourtens;
    int hourunits;
    int mintens;
    int minunits;
    int rxhours;
    int rxmins;
    int ledState = LOW;
    long previousMillis = 0; 
    long flashinterval = 1200; 
    int searching;
    // ************************************* SET UP ***********************************************
    void setup()    {
      uart_gps.begin(GPSBAUD); // setup sketch for data output speed of GPS module
      // Serial.begin(115200);
      interval = 60000;
      pinMode  (ledPin, OUTPUT );
      digitalWrite(ledPin, LOW);
      searching = HIGH; 
      vw_set_tx_pin(16);    //  Tx module connections and speed
      vw_set_ptt_pin(10);   // powers Tx and LED not used this project
      vw_setup(1000);
    }   //   end of setup
    //  ===================================GET GPS==========================
     
    void getgps(TinyGPS &gps)    // The getgps function will get the values we want.
    {
      int year,a,t;
      byte month, day, hour, minute, second, hundredths;
      gps.crack_datetime(&year,&month,&day,&hour,&minute,&second,&hundredths);
      hour=hour+2; // my zone is GMT +2
      if (hour>23) { 
        hour = hour - 24;      
      }
      int nexthour = rxhours;
      int nextmins = (rxmins + 1) ;
      if (nextmins > 59 ){ 
        nextmins = 0; 
        (  nexthour + 1 );
        if ( nexthour > 23 ){ 
          nexthour = 0; 
        }
      }  
      rxhours = ( int ) hour;
      rxmins = (int) minute;
      if ( rxmins == nextmins ){ 
        if (rxhours == nexthour) { //  IF TIME IS AS EXPECTED FROM PREV READ
          searching = LOW;
          clockmins = rxmins;  
          clockhours = rxhours;   
          clockmillis = millis () ;                    // reset freerun clock counter millis
          showtime (); 
        }     
      }      
    }   //   end of get gps
    
    //********************************************************************************************
    void loop()
    {
        //******************************* flashing the led *************************************************                                
    
         
      currentMillis = millis();   //  flash the led til GPS aquires
      if(currentMillis - previousMillis > flashinterval) {
        previousMillis = currentMillis;   
        if (ledState == LOW)
        {
          ledState = HIGH;
        }
        else
        {
          ledState = LOW;
        }
      }
     
      digitalWrite ( ledPin, ledState );
      //********************************NOW CHECK THE GPS RECEIVER  ************************************************
      
      if(uart_gps.available())     // While there is data on the RX pin...
      {  
        int c = uart_gps.read();    // load the data into a variable...
        if(gps.encode(c))      // if there is a new valid sentence...
        {   
    
          ledState = HIGH;  //  LEAVES THE LED ON when valid sentance aquired by resetting the ledflash timer
          previousMillis = currentMillis; 
          getgps(gps);         // then grab the data, 
        }// end if GPS encode
      } // end if uuart avail
      //  ------------------------------------------------   FREE RUNNING BACKUP CLOCK  ------------------
    
      currentclockMillis = millis();      
      if(currentclockMillis - clockmillis > interval) {  // if new minute
        clockmillis = currentclockMillis;
        clockmins ++;       
        if ( clockmins >59 ) { 
          clockmins = 0; 
          clockhours ++; 
          if (clockhours >23 ) { 
            clockhours = 0 ; 
          }   
        }
        if ( searching == LOW ){
          showtime ();  
        }  
    
      }// end of if new minute    
          
    }  // end of loop
    
    //*********************************GEN DISPLAY NUMBERS AND SEND TO DISPLAY UNIT ****************************************
    void showtime ()  {  
     
      checktemp ();
      int t,a;
      byte hour, minute;
      if (clockhours<10)
      {
        hourtens = 0;
        hourunits = clockhours; // Serial.print("less than ten hours  == "); Serial.println(hourunits);
      }    
      if (clockhours>=10)  {
        t=clockhours/10;
        a=int(t);
        hourtens = a;   // Serial.print(" hourstens  =  "); Serial.println(hourtens);
        t=clockhours%10;
        hourunits = t;    // Serial.print("  hoursunits  == "); Serial.println(hourunits);       
      }      
      if (clockmins<10)
      {
        mintens = 0;
        minunits = clockmins;  // Serial.print("less than ten mins  == "); Serial.println(minunits);  
      } 
      if (clockmins>=10)
      {
        t=clockmins/10;
        a=int(t);
        mintens = a;    //Serial.print(" mintens >10  ="); Serial.println(mintens);
        t=clockmins%10;
        minunits = t;    //  Serial.print("  minunits>10  == "); Serial.println(minunits);       
      }
      msg [0]  = hourtens;
      msg [1]  = hourunits;
      msg [2]  = mintens;
      msg [3]  = minunits;
      msg [4] = temptens;
      msg [5] = tempunits;
      vw_send((uint8_t *)msg, 6);     // send the character out
      vw_wait_tx();                             // Wait until the whole message is gone
   
    }
    //================================================CHECKING TEMPERATURE=======================
    void  checktemp ()  {
      int reading = analogRead(sensorPin);  
      float voltage = reading * 5.0;
      voltage /= 1024.0; 
      int temperatureC = (voltage ) * 100 ;                                             
      int x,y;
      if (temperatureC<10)
      {
        temptens = 0;
        tempunits = temperatureC; 
      }    
      if (temperatureC>=10)  {
        x=temperatureC/10;
        y=int(x);
        temptens = y;   //
        x=temperatureC%10;
        tempunits = x;    //
    
      }
    }  // end checktemp
    
    // =================================================================================

Ah ! I have just noticed that I defined

long previousMillis = 0;

instead of the normal unsigned long, so perhaps this was overrunning when the unit had been on a long time ( it gets set to millis () at one point ) ??

Even so, that would take 1/2 of 49+ days.

If that's not it tho, it's too subtle for me.

True..

I will change it anyway, and perhaps I will let the clock wait for 5 minutes before blanking, or even put another free running clock in the display micro to take over if nothing is coming in...not ideal as it might show the wrong time if the gps unit one hangs up for a week :frowning:

I imagine a lot of local commerce would make a lot of noise if GPS was to go down for a week in your area.

LOL, I meant if my gps receiving box hung up !

Oh :grin:

Well I managed to catch it while in the fault condition, and saw that the VW data was going out OK, but when I scoped the input pin on the display micro, it came back on. Could be poor connection on the IC socket ?
I cleaned the socket with some switch cleaner and also doubled the baud rate both ends ( up from 1000 to 2000 )

Switched on ( on Friday 13th ) and nothing ! I left it a couple of minutes to verify the minute, but smelled burning :frowning: the 5v regulator and the Atmega burnt my fingers.

I had plugged the micro in one pin offset, and it is my last chip !

Being the optimist, when it had cooled down enough to touch, I plugged in the chip correctly, and voila ! its working, what a lovely chip , no wonder the Arduino is so succesful.

I often plug the chips in backwards in the middle of the night, and they can take it without damage.

So now I must wait to see if the intermittent fault shows up again. It might have been a clash with the data rate too low ??

I will see. I have also corrected the unsigned long millis declaration........

Ouch!
Being 180 degrees off is not bad, GND pin goes to GND pin, VCC goes to AREF, AREF goes to VCC.
One pin off - could be bad news, with GND pin on VCC.

I havn't even checked to see what was connected where , with pin 1 plugged into 2 on the socket etc, perhaps I have blown some ports that I am not using, but its still running , the 5v reg has a thermal limiter that probably limited the damage, but I couldn't touch the chip for about a minute !

I have done the lotto tonight just in case, Friday the 13th might stay lucky for me !

I made a GPS clock, well with only a simple LCD display, that keeps track of the drift of the Uno clock.
With some calibrating and stable temperatures you can get 1ppm...

I think I heard about your clock, I tried to monitor the clock drift and vary the interval to compensate, but I am not experienced enough and it all went pear shaped :slight_smile:

So I checked the gps receiver for drop out ( I have an LED showing valid sentance received ) and it never flashes, so my freerunning clock I just let run if the gps were to drop out.
I tested it by lifting the battery supply to the gps, the LED flashed to show no data received , and 6 hours later, the clock was within a second of the computer clock.

I am not expecting more than a minute of gps signal loss, and when it comes back it resets the freerunning clock time and millis clock anyway, so it is perfectly in sync again.

The signal through the cable is perfect ( its the 3rd wire of a 5 metre core mains cable that carries the 12v for the LEDs etc ) at the faster 2000 rate, so I am going to take it much faster, I dont know what governs the choice of baud rate if the waveforms are good, and I am only sending 6 bytes once a minute ????

virtualwire has an upper limit tho, due to being intended for wireless and adding start & stop bytes for syncing & manchester encoding etc.

True Bob, I am going to leave this running to see if the tweaks have solved it, its just such a simple sketch that I can't believe it has a bug :slight_smile:

I really dont need more than 2000 for any reason. The VW library is so stable in all my other sketches, I am sure its not the problem.

Boffin1:
I often plug the chips in backwards in the middle of the night, and they can take it without damage.

Yours is a charmed life.

I wasnt charmed when I checked the Lotto last night though :slight_smile:

Thats a great little clock you made there !

I have another display board that I have to hack/re-configure to go with it.
It's for a friend.
The final version will just be hh:mm

The GPS I'm using has its own clock that it outputs without a valid sat lock. I have that white LED that blinks if the status is 'A'. If I haven't had it on for a few days I can tell versus WWV that it's drifted, and there's data without a confirmation blink, and then once it locks and comes right the display will skip a second and it's right.
Is it your aim to keep tighter time (less drift), with millis(), absent sat lock (to be in the presence of the dreaded 'V')?

Mine is much the same, but I have written my code to blink the LED until there is a new valid sentance, then stay solid while locked. It just seems more logical to me.

The millis clock is fine as it doesnt drift more than a second over 5 hours ( I unplugged the gps backup battery to test this )
This is fine as I dont expect it to lose lock for more than a minute.
It corrects on aquisition of valid sentance again.

We don't have WWV here in Africa , we havn't even got fast ADSL yet ( mine downloads at 340K and they call it fast :slight_smile: )

To test the drift I sync the PC clock to time.windows before and at the end of measurements.

I have only seen the GPS lose signal once in 6 months of playing with these. In fact it is extremely difficult to make it lose lock, even with the antenna facing down, and an aluminium pot over it.

I use VirtualWire through a "real wire" as I might want to use an RF link on some similar projects ( plus its versatile with choosing the serial pin )

OK, the problem seems to be at the display and data receiving end, see sketch below.

The data is coming in OK but it sometimes blanks, so it is thinking that it hasn't had data for 125 seconds.

It does come back on later.

I think I have seen a delay (50) after sending the shiftout data, but it seems to have disappeared along the line somewhere, could this be the trouble?

Any ideas out there ?

//     GPS clock receiver using twin wire and virtualwire
    
    #include <VirtualWire.h
    
    #define latchPin 19  // rck
    #define clockPin 18  // sck
    #define dataPin 16   // ser in
    
    unsigned long prevmillis =0;
    
    int hourunits;
    int hourtens;
    int minunits;
    int mintens;
     int temptens;
    int tempunits;
  
    const byte digitTable [10] = {
   B11111110, B10110000, B11101101, B11111001, B10110011,  //  first bit for switching on degree C letter
   B11011011, B11011111, B11110000, B11111111, B11111011};
    
    void setup()
    { Serial.begin(9600);	// Debugging only
     
      pinMode ( latchPin, OUTPUT);
      pinMode ( clockPin, OUTPUT);
      pinMode ( dataPin, OUTPUT);
    
       digitalWrite(latchPin, LOW); 
      
    shiftOut(dataPin, clockPin, LSBFIRST, B00000000);  // clear display
    shiftOut(dataPin, clockPin, LSBFIRST, B00000000); 
    shiftOut(dataPin, clockPin, LSBFIRST, B00000000); 
    shiftOut(dataPin, clockPin, LSBFIRST, B00000000); 
    shiftOut(dataPin, clockPin, LSBFIRST, B00000000); 
    shiftOut(dataPin, clockPin, LSBFIRST, B00000000); 
    
      digitalWrite(latchPin, HIGH); 
      
    
      vw_set_rx_pin(9);        // set Rx
      vw_setup(2000);	 // Bits per sec
      vw_rx_start();    
    }
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    
    void loop () 
    { 
      //        CHECK FOR INCOMING MESSAGE   
      uint8_t buf[VW_MAX_MESSAGE_LEN];
      uint8_t buflen = VW_MAX_MESSAGE_LEN;
      if (vw_get_message(buf, &buflen)) // if data received update displays
      { 
         hourunits = buf[0];
         hourtens = buf[1];
         minunits = buf [2];
         mintens = buf [3];
         temptens = buf [4];
         tempunits = buf [5];
          
      show ();  // runs the display function
      
     prevmillis = millis ();  //  resets the timeout blanking timer
      }//  end of if message received 
      
      if ( millis () - prevmillis >= 125000 )   //  if no update received within 125 seconds, clears the display
      { 
      digitalWrite(latchPin, LOW); 
           shiftOut(dataPin, clockPin, LSBFIRST, B00000000);  // clear display
           shiftOut(dataPin, clockPin, LSBFIRST, B00000000); 
           shiftOut(dataPin, clockPin, LSBFIRST, B00000000); 
           shiftOut(dataPin, clockPin, LSBFIRST, B00000000); 
           shiftOut(dataPin, clockPin, LSBFIRST, B00000000); 
           shiftOut(dataPin, clockPin, LSBFIRST, B00000000); 
       digitalWrite(latchPin, HIGH);  
      }  
        Serial.println(prevmillis, BIN);
    }  //  end of loop   
 
 
    
    void show ()
    { 
      
      int  HU = digitTable [hourunits];
      int  HT = digitTable [hourtens];
      int  MU = digitTable [minunits];
      int  MT = digitTable [mintens];
      int  TU = digitTable [tempunits];
      int  TT = digitTable [temptens];
      
      digitalWrite(latchPin, LOW); 
     
         shiftOut(dataPin, clockPin, LSBFIRST, TT);
         shiftOut(dataPin, clockPin, LSBFIRST, TU); 
      shiftOut(dataPin, clockPin, LSBFIRST, HU);   
      shiftOut(dataPin, clockPin, LSBFIRST, HT);
      shiftOut(dataPin, clockPin, LSBFIRST, MU); 
      shiftOut(dataPin, clockPin, LSBFIRST, MT);
       
      digitalWrite(latchPin, HIGH); 
         Serial.println(" sending data "); 
   
    }  // end of showT function

Adding a 50 mS delay after the VW send command seems to have sorted it out, I cannot remember when I removed it !

No that wasnt it either !

I am now puttting the free running timer in the display end, to take over if no gps received..