Arduino Nixie GPS Clock: Part 1


serial_gps_modulesCheap ublox NEO-6 Serial GPS

Recently I thought it would be a great idea to build a clock! Who doesn’t love a clock? Preferably one of those lovely old vintage glowing nixie clocks but more on that later. I wanted it to be an NTP internet clock. What’s the point otherwise, right? Or if not NTP at least have some method of magically extracting the time from the ether.

My first port of call for these projects of late has been the Raspberry Pi. I began to realise that a fully blown computer, all be it an inexpensive little fully blown computer, is extreme overkill for just a clock. Some would say even a micro controller is overkill but anyway… I ditched the Raspberry Pi and decided to dip my toe into even less expensive Arduino waters. I bought a very cheap ATMega328p complete with Arduino UNO Bootloader, 16Mhz crystal, USB to serial cable and various other junk. Turns out it would have probably been as cheap to buy a Pi.

Adding ‘the internet’ to a micro controller looked expensive ( I later found this little thing that may have done the job : http://www.adafruit.com/products/2282 ), foolishly overlooking MSF radio decoders and vaguely recalling something about GPS transmitting the time, or something. I hit on a NEO-6 Serial GPS Module for less than a tenner on ebay which seemed ridiculously cheap for GPS receiver. Ignoring the potential problem of getting a GPS fix indoors, how I would go about connecting this thing, coding for it or generally knowing what the heck I was doing. I bought it and plunged headlong into an Arduino GPS clock project.

/* dumps GPS gumpf  to serial monitor */
#include "SoftwareSerial.h"

SoftwareSerial mySerial(8, 4); // RX, TX
byte byteGPS;

void setup() 
{                
 	Serial.begin(9600);  
 	mySerial.begin(9600);
}

void loop() 
{
	if (mySerial.available()) 
	{
		byteGPS = mySerial.read();
		Serial.print(char(byteGPS));
	}
}

Turns out serial is pretty simple to connect and read. I gave it 5 volts, connected the GPS TX to the microcontroller’s RX and GPS RX to the TX. Before long I had this thing churning out all manner of GPS gumpf.


Incoming GumpfPS

And look here’s the time and the date!


Date and time spotted

Interestingly it looks like the time was being sent even without a full GPS fix. In the GPRMC sentence V (void) was set and not A (active) which means there was no true GPS signal, I think. I’m not sure if this is good news or not. Does it mean I won’t have to hang out of the window waving this thing in the air to get the GPS time? Or is this ‘V time’ inaccurate, a quirk of the hardware, the output from some internal RTC that’s been keeping time since the last true GPS signal?

Sometime later, and with me hanging it out of the window, a proper fix was established.


Full GPS fix

Next, I attempted to get the micro controller to extract a usable time and date. The plan… to read the data into a buffer, look for the GPRMC sentence and simply pull out the first 6 characters. Here’s some simple code. It doesn’t use the Arduino time library, sync or do anything clever. It just, rather crudely, parses and extracts hours, minutes and seconds.

byte byteGPS;
char gpsbuffer[80] = "";
char gprmc[6] = "GPRMC";
byte bufferindex=0;
byte match=0;
byte dateoffset=0;
byte commacount=0;

byte gpshour,gpsmin,gpssec,gpsday,gpsmonth;
int gpsyear;
bool validtime, validdate;

void parseBuffer()
{

 /*
 A good string looks something like this : 
 
 GPRMC,123519.00,A,1234.567,N,12345.678,E,123.4,123.4,230314,123.4,W*6A
 
 Time is the 1st 6 characters after 'GPRMC,' date is the 9th field
 */
  

validtime=false;
validdate=false;

  // retrieve the time from the buffer

  // first character a comma? then there's nothing to retrieve
  if (gpsbuffer[6]!=',')
  {
  gpshour=(gpsbuffer[6]-48)*10;
  gpshour+=(gpsbuffer[7]-48);

  gpsmin=(gpsbuffer[8]-48)*10;
  gpsmin+=(gpsbuffer[9]-48);

  gpssec=(gpsbuffer[10]-48)*10;
  gpssec+=(gpsbuffer[11]-48);  
  
  // Lets find the date
  // count fields to date
  dateoffset=0;
  commacount=0;

  for(int i=0;i<60;i++)
  {
   dateoffset++; // offset to date
   if (gpsbuffer[i]==44) commacount++; // a comma?
   if (commacount==9) i=61; // 9 commas? Break out
  }
  
  gpsday=(gpsbuffer[dateoffset]-48)*10;
  gpsday+=(gpsbuffer[dateoffset+1]-48);
  
  gpsmonth=(gpsbuffer[dateoffset+2]-48)*10;
  gpsmonth+=(gpsbuffer[dateoffset+3]-48);
  
  gpsyear=(gpsbuffer[dateoffset+4]-48)*10;
  gpsyear+=(gpsbuffer[dateoffset+5]-48);
  gpsyear+=2000;

  if((gpshour>=0)&&(gpshour<=23))
  {
         if((gpsmin>=0)&&(gpsmin<=59))
         {  
          validtime=true; 
         } 
  }

  if((gpsday>=1)&&(gpsday<=31))
  {
         if((gpsmonth>=1)&&(gpsmonth<=12))
         {  
         	if((gpsyear>=2015))
         	{  
          		validdate=true; 
         	}
         }
  } 


  }
    
}

void readGPS()
{

	if (Serial.available()) 
	{
	       byteGPS = Serial.read();
	       gpsbuffer[bufferindex]=byteGPS;
	       bufferindex++;
	       bufferindex%=72;
	  
	       if ((byteGPS==42) || (byteGPS==36) || (byteGPS==10) || (byteGPS==13) )
	       {         
	       	bufferindex=0;
	       }       
	} 
    
  // a potential full buffer?
	if (bufferindex==0)  
	{
	
	    // Check for GPRMC sentence
	    match=0;
	    for (int j=0; j<5; j++) 
	    {
	      if (gpsbuffer[j] == gprmc[j]) match++;
	    }
	
	    if (match==5) parseBuffer();
	      
	    //clear buffer
	    for(int i=0;i<=71;i++)
	    {
	        gpsbuffer[i]=33;
	    }
	}

}

So far so good. However, it seems that GPS sends UTC/GMT time only. I needed my clock to show DST/Summer time, or not. The only way to do this is to work out, from the date, if an extra hour needs to be added. I live in the UK, British Summer Time begins on the last Sunday of March and ends on the last Sunday of October. After lots of head scratching I came up with this code to determine BST. Officially, BST starts at 1am but for simplicity I've left it at midnight. It returns either 1, yes BST or 0, nope. We can just add this to our extracted hours later.

/*
Determine BST from passed date
*/
boolean BST(int day, int month, int dow)
{
// dow : 0 = sunday, 1 = mon etc...
// doesn't do the > 1am change. bst changes at midnight here
	
if ((month > 3) && (month < 10))  return true; 
  
if ((month < 3) || (month > 10))  return false; 

if (month == 3) && ( (day - dow) >= 25) return true;
  
if (month == 10) && ( (day - dow) >= 25) return true;

return false;

}

And there we have it, accurate (I hope) time pulled out of thin air. Next, fun with high voltage nixie tubes as I try to avoid electrocution and get something displayed.

4 thoughts on “Arduino Nixie GPS Clock: Part 1

  1. Greetings

    i was reading your code above and i came across this line : byte byteGPS=-1

    i would like to know what it means and its function

    thanks…..

    1. Hi. It looks like a stupid mistake on my part. The variable doesn’t really need initialising and trying to init a byte to -1 seems stupid as a byte is unsigned, maybe it would set it to 255. Anyway, not required. I’ll change the code. Thanks.

    1. It uses the old DateTime library to pull out day of week from GPS provided date.

      The GPS signal is poor indoors. The final clock uses an onboard RTC and DateTime library for the majority of the time, only occasionally setting it to accurate GPS time every 8 hours or so if it has a signal.

Leave a Reply to admin Cancel reply

Your email address will not be published. Required fields are marked *