simplest gps clock

I have searched and found loads of projects with clocks using a gps module, but some have RTC chips too, and some seem very complicated.

What I want to try is ( in its simplest form where the clock can be mounted by a window or wherever there is gps signal ) have a Arduino 328 chip, running a simple 4 digit LED clock.

The display will be latched using TPIC6B595 shift reg drivers. There are no multiplexing delays in the loop, the display is refreshed once per minute.

The general timing of the minute could be a simple " blink without delay" millis check to see if the count has reached 60 seconds.

A cheap gps module that churns out NMEA strings , via ttl connections to the Arduino chip UART from which I could mask out the hours and minutes digits.

If the signal lock is high, and there is a change in the ten minutes digit, it must update the display counter of the Arduino chip.

( I suppose I could get fancy and check the error first to correct the millis counter to fine tune the error )

The next step would be to have the gps module in the window, sending the time every 10 minutes via a rf link to the clock, or clocks across the room, but I just want to start somewhere.

If I wanted to start really simple, I could just mask out the minutes and hours numbers from the NMEA string, updated every second, and send them to the LED displays via a lookup table, and not bother with a timer at all in the micro?

Has anyone played with this?

I've done parsing of a NEMA message. In my case I wanted Lat, Lon, and Altitude. Perhaps my code can be used as a model.

#include <NewSoftSerial.h>

//  Fault flags
extern const unsigned int GPS_fault;
extern unsigned int Faults;

NewSoftSerial GPS(2, -1);

// Parse GPS message
byte GPS_byteCount = 0;
unsigned char GPS_buff[100];
unsigned long GPS_time = 0;
String GPS_lat = "";
String GPS_long = "";
String GPS_altitude = "";
unsigned long GPS_lastgood = 0;
unsigned long GPS_lasttest = 0;


void GPS_setup(void)
{
  GPS.begin(4800);
}


void GPS_loop(void)
{
  // If any characters are available from the GPS, send them to the parser
  while (GPS.available())
  {
    GPS_parse_char(GPS.read());
  }

  // Check for GPS status
  if ((millis() - GPS_lastgood > 3000))
    Faults |= GPS_fault;   
}

void GPS_log(Print &dest)
{
  // Log GPS data
  dest.print(GPS_time, DEC);
  dest.print(',');
  dest.print(GPS_lat);
  dest.print(',');
  dest.print(GPS_long);      
  dest.print(',');
  dest.print(GPS_altitude);
  dest.print(',');
  dest.print((millis() - GPS_lastgood)/1000);
}


int GPS_parse_char(byte inputChar)
{
  switch (inputChar)
  {
  case '\r':
    return GPS_check_buffer();

  case '

:
    GPS_byteCount = 0;  // Start of message

default:
    GPS_buff[GPS_byteCount++] = inputChar;
    if (GPS_byteCount == 100)
    {
      GPS_byteCount = 0;  // Start of message
      return -3;  //  Buffer overrun
    }
    return 0; // Not a full message
  }
}

int GPS_check_buffer()
{
  GPS_buff[GPS_byteCount] = '\0';

unsigned char *buffp = GPS_buff;

// Check the message type
  for (int i=0; i<7; i++)
  {
    if (buffp[i] != "$GPGGA,"[i])
      return 0;
  }

// Verify the checksum before doing any more
  if (GPS_buff[GPS_byteCount-3] != '*')  // Checksum marker
    return -1;  // Bad message format

byte checksum = 0;
  for (int i=1; i<GPS_byteCount-3; i++)  // Skip '


 and '*'
    checksum ^= GPS_buff[i];
  //  Test the result
  checksum -= hexval(GPS_buff[GPS_byteCount-2]) << 4;
  checksum -= hexval(GPS_buff[GPS_byteCount-1]);

  if (checksum != 0) return -2; // Checksum failure

  //  parse the time field
  buffp += 7;  // Skip over $GPGGA,
  GPS_time = 0;
  while (hexval(*buffp) != -1)  // for each digit
  {
    GPS_time *= 10;
    GPS_time += hexval(*buffp);
    buffp++;
  }

  if (*buffp++ != '.') // Should be a decimal point
    return -1;

  while (*buffp == '0')  // Skip zeros
    buffp++;

  if (*buffp++ != ',') // Should be a comma
    return -1;

  // Parse latitude
  GPS_lat = "";
  for (int i=0; i<20; i++)
  {
    if (*buffp == ',')
      break;
    GPS_lat += *buffp++;
    if (i == 1)
      GPS_lat += ' ';  // Separate degrees from minutes
  }
  buffp++;  // skip the comma

  if (*buffp == 'S')
    GPS_lat = "-" + GPS_lat;
  else
    if (*buffp != 'N')
      return -1;

  buffp++;  // Skip the N or S

  if (*buffp++ != ',') // Should be a comma
    return -1;


  // Parse longitude
  GPS_long = "";
  for (int i=0; i<20; i++)
  {
    if (*buffp == ',')
      break;
    GPS_long += *buffp++;
    if (i == 2)
      GPS_long += ' ';  // Separate degrees from minutes
  }
  buffp++;  // skip the comma

  if (*buffp == 'W')
    GPS_long = "-" + GPS_long;
  else
    if (*buffp != 'E')
      return -1;

  buffp++;  // Skip the E or W

  if (*buffp++ != ',') // Should be a comma
    return -1;

  if (*buffp++ == '0')
  {
    Faults |= GPS_fault;
    return -3; // No view of sattelites
  }

  if (*buffp++ != ',') // Should be a comma
    return -1;

  GPS_lastgood = millis();

  while (*buffp++ != ',') ; // Skip number of satellites
  while (*buffp++ != ',') ; // Skip horizontal dillution
  GPS_altitude = "";
  while (*buffp != ',')
    GPS_altitude += *buffp++;

  //Serial.println((char *)GPS_buff);
  Faults &= ~GPS_fault;
  return 1;  // Good fix
}


int hexval(char digit)
{
  if (digit >= '0' && digit <= '9')
    return digit - '0';

  if (digit >= 'A' && digit <= 'F')
    return digit - 'A' + 10;

  if (digit >= 'a' && digit <= 'f')
    return digit - 'a' + 10;

  return -1;
}

I've played with a GPS module before, using this code:

char data[300];
char GPSsignature[7] = "$GPRMC"; 
int comma[13];
int ledPin = 13;                  // LED test pin
int GPSbyte;
int count;
int correctData;

void setup() {
  pinMode(ledPin, OUTPUT);       // Initialize LED pin
  Serial.begin(4800);  
}

void loop() {
  digitalWrite(ledPin, HIGH);

  if (Serial.available() > 0){
    GPSbyte=Serial.read();         // Read a byte of the serial port
    
    data[count]=GPSbyte;        // If there is serial port data, it is put in the array
    count++;                      

    if (GPSbyte==13){            // If the received byte is = to 13, end of transmission
      digitalWrite(ledPin, LOW); 
      count=0;
      correctData=1;

      for (int i=1;i<7;i++){     // Verifies if the received command starts with $GPR
        if (data[i]!=GPSsignature[i-1])
          correctData=0;
      }

      if(correctData){               // If yes, countinue and process the data
        for (int i=0;i<300;i++){
          if (data[i]==','){    // check for the position of the  "," separator
            comma[count]=i;
            count++;
          }
          if (data[i]=='*'){    // ... and the "*"
            comma[12]=i;
            count++;
          }
        }
          count = 0;

          Serial.println("---------------");
          for (int i=0;i<8;i++){
            switch(i){
            case 0 : Serial.print("Time in UTC (HhMmSs): "); break;
            case 1 : Serial.print("Status (A=OK,V=!OK): "); break;
            case 2 : Serial.print("Latitude: "); break;
            case 3 : Serial.print("Direction (N/S): "); break;
            case 4 : Serial.print("Longitude: "); break;
            case 5 : Serial.print("Direction (E/W): "); break;
            case 6 : Serial.print("Velocity in knots: "); break;
            case 7 : Serial.print("Heading in degrees: "); break;
            case 8 : Serial.print("Date UTC (DdMmAa): "); break;
            }

           for (int j=comma[i];j<(comma[i+1]-1);j++){
             Serial.print(data[j+1]); 
           }
            Serial.println("");
          }
        }    
      }
    }
  else delay(100);
}

It takes in all the data from the GPS via the serial port, then extrancs the data and prints out each individual part. If you wanted, you could ignore the lat,lon,altitude,n/s,e/w bits and just look at the time, which will not be too hard.
I managed to find a decent GPS module for £20, but I cannot find it in the shop I bought it from, so no links... It had good accuracy though, quick to find sattelites, and worked indoors!

Onions.

Thanks guys, that helps a lot.

There are some cheap GPS modules here, I will get one and try it out if I get some time this weekend, I must make sure it has ttl connections though.

I will first try the simplest displaying the hhmm barebones version, and post any results

I did this, but a bit more elaborately. I needed a time standard for my entire house. I have several devices (thermostats, timers, etc) around the house that needed the time, and originally, I was getting the time over the internet from NIS. This wasn't as good an idea as I thought it would be. Seems the NIS servers sometimes don't respond and sometimes the internet doesn't work (duh !). So I got a GPS module, hooked an arduino to it with an ethernet shield and had a LAN based clock that was synced to the satellites. This was cool, but I still had some problems with the ethernet. So, I built an XBee network and modified the satellite clock to work over it as well.

Now, I have a clock that is synced with the satellites and transmits the time over the LAN on request and also transmits over the XBee network to sync the various devices around the house. Oh, it also has a serial LCD display that shows the time on the front of it.

However, I almost never see the display, it is in an attic room that also houses my router, phone and TV distribution stuff as well as my comic book collection (total geek I am). The code is online at my blog (see signature line) or get to it directly Desert Home: House Clock

Feel free to snag anything you want.

Thats a cool house/lab you have there !
I looked at your code and have to admit it makes me realise how little I know yet about programming ! but I am learning all the time.

I am sure with all the sketches you guys have supplied I will find a way to get the time from the NMEA string.

Sending to the LED displays I am an old hat with now :slight_smile:

Use a TLC5916 or STP16C596 instead of the TPIC6B59. The outputs are current-controlled so you can avoid all the resistors.

Thats a far better chip,, but I still have a few hundred of the TPIC595 ones to use up first .

Few HUNDRED ??? Wow, you believe in stocking up....

Regarding code, don't take mine as an example of how it should be. Even though it works pretty well, I keep finding things I should have done differently. I don't know about other folk, but sometimes I'm a-feared of showing my code because people will laugh at it. They haven't yet, but I'm sure it's coming.

shudder

I use a lot of them in my business, so I choose them for the hobby experimantal stuff too.

I asked a good friend who sells components for a quote on them a few months ago, and his supplier in Hong Kong thought he was ordering them, and sent them in the next container, so I am sort of committed to buying these chips until they are used up !

Re your code, thats fine its a great starting point, I often start with a section of code from someones example, and mess around experimenting with it to help me learn...... I have been playing with the Arduino for a year now, and have made dozens of projects, and I feel I probably know 1% of it all now !

Its 3 am , passing through the office, brain still asleep, wondering how I am going to Serial.print("Velocity in knots: "); when I have the gps module connected to the serial port ???

read about NewSoftSerial when you wake up.

Ah, now that rings a bell, thanks

Back on the project again, finally got the cheapy GPS receiver module, hooked it up on a bit of stripboard and used NewSerailSoft and the sketch from Onions ( thank you ) and its working fine from the 3v3 supply from the Arduino board.

The time comes on correct immediately , even while its waiting for the satellite fixes ? Theres no back up battery, but thats fine. I guess it only needs the first satellite for the time.

I dont know if I have found why it was cheap, but it places my house about 100 Km up the coast , every time !

I couldnt give a hoot about the position for my clock project, and will find out if they have a whole bucket of these cheap :slight_smile:

Hmm, while it runs fine for 10 minutes, reading out the time etc, it freezes after 10 minutes or so.
Am I filling some buffer or something?

Heres the test sketch :-

#include <NewSoftSerial.h>

NewSoftSerial GPS1(14, 15);


char data[300];
char GPSsignature[7] = "$GPRMC"; 
int comma[13];
int ledPin = 13;                  // LED test pin
int GPSbyte;
int count;
int correctData;

void setup() {
  pinMode(ledPin, OUTPUT);       // Initialize LED pin
  Serial.begin(115200);  
   GPS1.begin(9600);
  
}

void loop() {
  digitalWrite(ledPin, HIGH);

  if (GPS1.available() > 0){
    GPSbyte=GPS1.read();         // Read a byte of the serial port
    
    data[count]=GPSbyte;        // If there is serial port data, it is put in the array
    count++;                      

    if (GPSbyte==13){            // If the received byte is = to 13, end of transmission
      digitalWrite(ledPin, LOW); 
      count=0;
      correctData=1;

      for (int i=1;i<7;i++){     // Verifies if the received command starts with $GPR
        if (data[i]!=GPSsignature[i-1])
          correctData=0;
      }

      if(correctData){               // If yes, countinue and process the data
        for (int i=0;i<300;i++){
          if (data[i]==','){    // check for the position of the  "," separator
            comma[count]=i;
            count++;
          }
          if (data[i]=='*'){    // ... and the "*"
            comma[12]=i;
            count++;
          }
        }
          count = 0;

          Serial.println("---------------");
          for (int i=0;i<9;i++){
            switch(i){
            case 0 : Serial.print("Time in UTC (HhMmSs): "); break;
            case 1 : Serial.print("Status (A=OK,V=!OK): "); break;
            case 2 : Serial.print("Latitude: "); break;
            case 3 : Serial.print("Direction (N/S): "); break;
            case 4 : Serial.print("Longitude: "); break;
            case 5 : Serial.print("Direction (E/W): "); break;
            case 6 : Serial.print("Velocity in knots: "); break;
            case 7 : Serial.print("Heading in degrees: "); break;
            case 8 : Serial.print("Date UTC (DdMmAa): "); break;
            }

           for (int j=comma[i];j<(comma[i+1]-1);j++){
             Serial.print(data[j+1]); 
           }
            Serial.println("");
          }
        }    
      }
    }
  else delay(100);
}

I notice two things that gave me trouble initially. First you don't check the buffer size to be sure you don't run over the end. I had this happen because sometimes the GPS sentences were screwed up. I don't know why or how or anything, just that they were. This would mean that the CR might get missed and the buffer would run over.

Second, you don't check the checksum for the sentence. Due to the problem above, I would have sentences that I thought were correct, but weren't and that would lead to times that were waaay off.

Basically, don't trust the GPS to be perfect, check the sentences and be sure the stuff coming back is good before you do something with it.

  else delay(100);

Why? When GPS data does arrive, you might have to wait 99 milliseconds to read it. What does that accomplish?

Blinking the LED when there is data from the GPS, instead of just turning it one on every pass through loop() would give you a clue where the problem is. So would printing out what you get from the GPS each time.

Thanks guys, I realise that the code I copied was more for initial testing, and I have now switched to the TinyGps library, and not only has it kept going for the last couple of hours, but it has also located my house correctly.
( I was looking forward to a weekend up the coast where the previous software placed me, right next to our favourite seafood restaurant XD )
I will leave this running, but it looks pretty stable, considering the gps module is indoors nowhere near a window.

It has had 54 failed checksums in 2 hours, checking evey 5 seconds, which is more than enough for keeping a clock accurate