GPS time-date

Sometimes the obvious just slaps you up in the face like a cold, wet fish.

I have an old Parallax GPS module from the days of 2007. It outputs 4800 BAUD. The idea is to build one GPS controlled clock so that the time/date would always be accurate. In this clock, I would put a transmitter to relay the serial TTL via radio to other Arduino'ish devices that needed correct time. So far, so good.

I breadboarded the GPS module, applied power, used an FTDI to connect to my PC and a piece of software on the PC called VisualGPSView. http://www.visualgps.net/VisualGPSView/default.htm I was amazed that even with this old GPS and it's patch antenna that I could get signals from the satellites down in the basement of my house; that is through the plywood floor, the mat and rug, the ceiling, the attic insulation, the attic floor, the plywood roof, and the tar-paper and asphalt roof shingles. But getting a "lock" took a long time and was problematic although it did occur. And so this thing has been on the bench for nearly a week before I got around to a sketch to take the place of VisualGPSView and parse out just the time and date.

I used the Adafruit library GitHub - adafruit/Adafruit_GPS: An interrupt-based GPS Arduino library for no-parsing-required use and started with her parse script. I stripped out lots of stuff, it is hardly recognizable.

/*
Originally based on Adafruit example sketch: "parsing"
Utilizes the Adafruit_GPS library https://github.com/adafruit/Adafruit-GPS-Library
20131115: GPS_Parsing_Test
  Heavily mucked by M. Ray Burnette to simply use (most) any dumb serial TTL GPS for time-date
  Tested with ancient #28146 Parallax module
  Arduino 1.0.5:

  Program:    9090 bytes
  (.text + .data + .bootloader)

  Data:        754 bytes
  (.data + .bss + .noinit)
*/

#include "Adafruit_GPS.h"
#include <SoftwareSerial.h>
#include <Streaming.h>

//   Connect the GPS TX (transmit) pin to Digital 3
SoftwareSerial mySerial(3, 2);

Adafruit_GPS GPS(&mySerial);

// Set GPSDEBUG 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. 
// Could be as simple as checking a digital pin to-Gnd state
#define GPSDEBUG  false  // should be a check of a digital pin to Gnd

uint8_t last_seconds ;
uint8_t TimeZoneHour = -5 ;  // needs to be a pin-to-ground adjustment for DST
uint8_t LocalHour ;

void setup()  
{
  // connect at 115200 so we can read the GPS fast enough and echo without dropping chars
  Serial.begin(115200);
  Serial.println("GPS time-date:");
  // Some GPS are 4800 and some are 9600 or faster
  GPS.begin(4800);
  delay(1000);
  // Add a DST check of a digital pin for DayLightSavingTime or
  // Use multiple digital pins to set any TimeZone
}


void loop()
{
    // read data from the GPS
    char c = GPS.read();
    if (GPSDEBUG)  // Raw character echo to monitor
      if (c) Serial.print(c);

  // if a sentence is received, check the checksum, parse it...
  if (GPS.newNMEAreceived()) {
    if (!GPS.parse(GPS.lastNMEA()))   // this also sets the newNMEAreceived() flag to false
      return;  // return to top of loop() if sentence does not verify correctly or completely
  }

  // BIG if() active only once per second since this sketch is time-date focused
  if (last_seconds != GPS.seconds) {    last_seconds = GPS.seconds;
    LocalHour = GPS.hour + TimeZoneHour;
    Serial.print("\nTime: ") ;
    Serial << ((LocalHour<10)?"0":"") ; 
    Serial.print(LocalHour, DEC); 
    Serial << (':') ;
    Serial << ((GPS.minute<10)?"0":"") ;
    Serial.print (GPS.minute, DEC); 
    Serial << (':') ;
    Serial << ((GPS.seconds<10)?"0":"") ;
    Serial.print(GPS.seconds, DEC) ;

    Serial.print("\nDate: ");
    Serial << ((GPS.day<10)?"0":"") ;
    Serial.print(GPS.day, DEC) ;              //DD
    Serial << "/" << ((GPS.month<10)?"0":"") ;
    Serial.print(GPS.month, DEC) ;            //MM
    Serial << "/20" ;
    Serial.println(GPS.year, DEC) ;           //YYYY

    if (GPS.fix && GPSDEBUG) {
      Serial.print("Location: ");
      Serial.print(GPS.latitude, 4); Serial.print(GPS.lat);
      Serial.print(", "); 
      Serial.print(GPS.longitude, 4); Serial.println(GPS.lon);
      // Serial.print("Speed (knots): "); Serial.println(GPS.speed);
      // Serial.print("Angle: "); Serial.println(GPS.angle);
      Serial.print("Altitude: "); Serial.println(GPS.altitude);
      Serial.print("Satellites: "); Serial.println((int)GPS.satellites);
    }
  } // BIG if
} // loop

So, here is what I did not know until today: It is NOT necessary to have a GPS lock to get the time and date. From my experiments, you need very little quality satellite data for the time and date to populate. It is really quiet amazing to me. What I did was just feed the Adafruit GPS object with the entire GPS module stream and allow the object to populate the 6 values that I wanted. This seems to happen in just a few seconds and stays reasonably stable no matter how I orient the patch antenna. Edit: Almanac (definitely) and Ephemeris (maybe) data from at least one satellite does appear to be required;
( GPS signals - Wikipedia )
so depending on the age of your GPS receiver, the time-date may require upwards to 15 minutes; however, I tried a GPS PMB-648 that had not been used in weeks and got the correct time&date in under 20 seconds (as they say, your results may vary!)
How GPS Works

Sample sketch output:

GPS time-date:			<== from program setup()

Time: 11:45:58
Date: 00/00/200			<== variables not initialized

Time: 11:45:59
Date: 15/11/2013		<== object variables all populated

Time: 11:46:00
Date: 15/11/2013

Time: 11:46:01
Date: 15/11/2013

Time: 11:46:02
Date: 15/11/2013

Time: 11:46:03
Date: 15/11/2013

Time: 11:46:04
Date: 15/11/2013

Time: 11:46:05
Date: 15/11/2013

Time: 11:46:06
Date: 15/11/2013

Time: 11:46:07
Date: 15/11/2013

Time: 11:46:08
Date: 15/11/2013

Time: 11:46:09
Date: 15/11/2013

Time: 11:46:10
Date: 15/11/2013

Time: 11:46:11
Date: 15/11/2013

Time: 11:46:12
Date: 15/11/2013

And here is raw GPS sentences printed with DEBUG on (time/date inter-spaced with GPS input to sketch:

Time: 11:06:14
Date: 00/00/0
No Fix...Satellites: 0
$GPGSA,A,1,,,,,,,,,,,,,,,*1E
$GPRMC,160614.295,V,3355.2535,N,08402.2105,W,,,151113,,,N*61
$GPGGA,160615.295,3355.2535,N,08402.2105,W,0,00,,305.9,M,-31.0,M,,0000*43

Time: 11:06:15
Date: 11/15/13
No Fix...Satellites: 0
$GPGSA,A,1,,,,,,,,,,,,,,,*1E
$GPRMC,160615.295,V,3355.2535,N,08402.2105,W,,,151113,,,N*60
$GPGGA,160616.295,3355.2535,N,08402.2105,W,0,00,,305.9,M,-31.0,M,,0000*40

Time: 11:06:16
Date: 11/15/13
No Fix...Satellites: 0
$GPGSA,A,1,,,,,,,,,,,,,,,*1E
$GPGSV,3,1,12,02,89,171,25,12,64,322,,04,43,046,,10,35,099,*7F
$GPGSV,3,2,12,24,33,226,,25,23,316,,05,22,160,,29,12,277,*71
$GPGSV,3,3,12,17,12,077,,31,-9,339,,14,-10,299,,28,-10,133,*6C
$GPRMC,160616.295,V,3355.2535,N,08402.2105,W,,,151113,,,N*63
$GPGGA,160617.295,3355.2535,N,08402.2105,W,0,00,,305.9,M,-31.0,M,,0000*41

Time: 11:06:17
Date: 11/15/13
No Fix...Satellites: 0
$GPGSA,A,1,,,,,,,,,,,,,,,*1E
$GPRMC,160617.295,V,3355.2535,N,08402.2105,W,,,151113,,,N*62
$GPGGA,160618.295,3355.2535,N,08402.2105,W,0,00,,305.9,M,-31.0,M,,0000*4E

Time: 11:06:18
Date: 11/15/13
No Fix...Satellites: 0
$GPGSA,A,1,,,,,,,,,,,,,,,*1E
$GPRMC,160618.295,V,3355.2535,N,08402.2105,W,,,151113,,,N*6D
$GPGGA,160619.295,3355.2535,N,08402.2105,W,0,00,,305.9,M,-31.0,M,,0000*4F

Time: 11:06:19
Date: 11/15/13
No Fix...Satellites: 0
$GPGSA,A,1,,,,,,,,,,,,,,,*1E
$GPRMC,160619.295,V,3355.2535,N,08402.2105,W,,,151113,,,N*6C
$GPGGA,160620.295,3355.2535,N,08402.2105,W,0,00,,305.9,M,-31.0,M,,0000*45

Time: 11:06:20
Date: 11/15/13
No Fix...Satellites: 0
$GPGSA,A,1,,,,,,,,,,,,,,,*1E
$GPRMC,160620.295,V,3355.2535,N,08402.2105,W,,,151113,,,N*66
$GPGGA,160621.295,3355.2535,N,08402.2105,W,0,00,,305.9,M,-31.0,M,,0000*44

Time: 11:06:21
Date: 11/15/13
No Fix...Satellites: 0
$GPGSA,A,1,,,,,,,,,,,,,,,*1E
$GPGSV,3,1,12,02,89,171,24,12,64,322,,04,43,046,,10,35,099,*7E
$GPGSV,3,2,12,24,33,226,,25,23,316,,05,22,160,,29,12,277,*71
$GPGSV,3,3,12,17,12,077,,31,-9,339,,14,-10,299,,28,-10,133,*6C
$GPRMC,160621.295,V,3355.2535,N,08402.2105,W,,,151113,,,N*67
$GPGGA,160622.295,3355.2535,N,08402.2105,W,0,00,,305.9,M,-31.0,M,,0000*47

Time: 11:06:22
Date: 11/15/13
No Fix...Satellites: 0
$GPGSA,A,1,,,,,,,,,,,,,,,*1E
$GPRMC,160622.295,V,3355.2535,N,08402.2105,W,,,151113,,,N*64
$GPGGA,160623.295,3355.2535,N,08402.2105,W,0,00,,305.9,M,-31.0,M,,0000*46

That GPS clock is just a little formatting on the serial OLED display away from being completed!

Ray

GPS_TimeDate.zip (5.75 KB)

UPDATE: GPS time

I received my 2 GPS modules from Hong Kong and they both work well. The GPS component is: NEO-6 and the host board is Chinese etched and the SMT components (few) are hand-soldered. While I consider the lack of oven-reflow a negative, the units function correctly and the host board is mostly 5V to 3.3V interfacing for the NEO-6. The price of $16 is the real winner here, IMO. I would not hesitate to reorder same and I'm now considering building a few of these clocks for friends/family as gifts. Using backlight LCD instead of OLED cuts the cost by approximately $20 (qty 1) and I am using a Chinese 328P Pro-Mini ($3.25) as the clone. Note: I developed everything on a 'real' UNO before moving to the Pro-Mini clone.

I am actually very pleased with the NEO-6. While my older PMB-648 SiRF module could receive adequate enough in my basement to recover the time/date from the $GP sentences, the NEO-6 actually performed a GPS lock and it seems very stable.

I assume that the last power-up of my modules were at assembly in Hong Kong. I timed both upon powering, and I received accurate time-date in 3 minutes and 20 seconds. Full GPS satellite lock occurred in about 7 minutes from my basement. Hot restarts seem to instantly pull the time while warm restarts (after about 2 minutes of being off) takes approximately 2 seconds or so.
I do not recommend this vendor over others, but I did purchase the test units from this site (as reference only):
http://www.ebay.com/itm/181219728986
Interesting, my units shipped from Hong Kong and were received in Atlanta in only 10 days. Very commendable.

Ray

Thanks for the info.

great idea, thanks for sharing

Previously, I have used the Parallax (6+ years old) and the and the RYG25AI high performance GPS antenna with the u-blox NEO-6M engine - all one module:
http://www.ebay.com/itm/181219728986

Note: I came back from Thanksgiving vacation in Washington, D.C. to my lab yesterday and powered-on the module from above and the time and date were displayed correctly immediately after the 2 second credit screen; the module has been without power since last Tuesday morning. Awesome!

While vacationing, the NEO-6V2 with an external antenna arrived:
http://www.ebay.com/itm/390647042336

I disconnected the 3 wires to the earlier module and connected to the new module, then powered on the Arduino (Flatduino) and the LCD and the GPS. The system "came alive" with the correct time+date after 7 minutes and 30 seconds. Excellent. The module is showing a GPS lock with the 1-PPS LED active. Again, this is from my basement lab in the center of my house, no horizon view, through the basement ceiling, the subfloor and carpet of the living room, the sheet-rock ceiling of the living room, a separate attic plywood floor, the plywood ceiling, and tar paper and asphalt singles. For a sub-$20 module, I am impressed.

At present, I have moved from the OLED display to a black on yellow-green LCD which costs approximately $3 from Hong Kong. My Flatduino is essentially a naked Atmega328P-PU spread eagle on a protoboard for a cost of $3 and I paid under $17 shipped for the GPS... total bill, about $23 for a self-setting, real-time display, clock/calendar.

I have not cleaned-up the code as it has migrated from Adafruit, to OLED, to LCD, so it is not optimized at all.

Full code is in ZIP of my next post!

/*
Roughly based on Adafruit example sketch: "parsing"
Utilizes the Adafruit_GPS library https://github.com/adafruit/Adafruit-GPS-Library
20131115: GPS_Parsing_Test
  Heavily mucked by M. Ray Burnette to simply use (most) any dumb serial TTL GPS for time-date
  Tested with ancient #28146 Parallax module
  Arduino 1.0.5:

*/

#include "Adafruit_GPS.h"
#include "Utilities.h"
#include <SoftwareSerial.h>
#include <LiquidCrystal.h>
//#include <AXE133Y.h>
#include <Streaming.h>

//   Connect the GPS TX (transmit) pin to Digital 3 input
SoftwareSerial mySerial(9, 8) ;  // Rx Tx
Adafruit_GPS GPS(&mySerial) ;

// Set GPSDEBUG 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. 
// Could be as simple as checking a digital pin to-Gnd state
#define GPSDEBUG  false  // should be a check of a digital pin to Gnd

uint8_t last_seconds ;
uint8_t TimeZoneHour = 5 ;  // needs to be a pin-to-ground adjustment for DST
uint8_t LocalHour ;
uint8_t LocalMinute ;
uint8_t LocalSecond ;
uint8_t LocalMonth ;
uint8_t LocalDay ;
uint8_t LocalYear ;
byte nRow;                          // line count      (0-5 for NOKIA LCD)
byte nColumn;                       // character count (0-11 for NOKIA LCD)
boolean PM ;
int OLED_Tx    =  5;         // Serial out for OLED
char* PROGMEM BlankLine[]  = {"                "};

// AXE133Y OLED =  AXE133Y(OLED_Tx);
LiquidCrystal lcd(7, 6, 2, 3, 4, 5);  // BEWARE: does not match Arduino Examples

void setup()  
{
  lcd.begin(16, 2) ;
  Serial.begin(115200) ; // connect at 115200 debug output to console
  GPS.begin(9600); // Some GPS are 4800 and some are 9600 or faster
  if(GPSDEBUG) Serial.println("GPS time-date:") ;
  lcd.setCursor(0, 0); lcd << BlankLine[0] ;       // clear lower row
  lcd.setCursor(0, 1); lcd << BlankLine[0] ;       // clear upper row
  delay(500) ;
  lcd.setCursor(0, 0) ;
  lcd.print("by M.R. Burnette") ;
  lcd.setCursor(0, 1) ;
  lcd.print("(c) GPS timedate") ;
  delay(2000) ;  // credits
  lcd.setCursor(0, 0); lcd << BlankLine[0] ;       // clear lower row
  lcd.setCursor(0, 1); lcd << BlankLine[0] ;       // clear upper row
  // Add a DayLightSavingTime or
  // Use multiple digital pins to set any TimeZone
}


void loop()
{
    // read data from the GPS
    char c = GPS.read();
    if (GPSDEBUG)  // Raw character echo to monitor
      if (c) Serial.print(c);

  // if a sentence is received, check the checksum, parse it...
  if (GPS.newNMEAreceived()) {
    if (!GPS.parse(GPS.lastNMEA()))   // this also sets the newNMEAreceived() flag to false
      return;  // return to top of loop() if sentence does not verify correctly or completely
  }

  PM = false ;
  // BIG if() active only once per second since this sketch is time-date focused
  if (last_seconds != GPS.seconds) {
    last_seconds =  GPS.seconds ;
    LocalHour    =  GPS.hour ;
    LocalMinute  =  GPS.minute ;
    LocalSecond  =  GPS.seconds ;
    LocalMonth   =  GPS.month ;
    LocalDay     =  GPS.day ;
    LocalYear    =  GPS.year ;

    if(LocalHour >= TimeZoneHour) {
        LocalHour = LocalHour - TimeZoneHour ;
      } else {
        // Correct for period when London is a day ahead
        LocalHour = (LocalHour + 24) - TimeZoneHour ;
        LocalDay  = LocalDay - 1 ;
        if( LocalDay == 0)
        {
          LocalDay   = DaysInMonth[ LocalMonth - 1] ;  // 0 - 11
          LocalMonth = LocalMonth - 1 ;  // January in London still December Westward
          if( LocalMonth == 0)           // GPS months are 01 through 12
          {
            LocalMonth = 12 ;
            LocalDay   = DaysInMonth[ 11 ] ;  // lastday of December
          }
         // Need to deal with LocalMonth = 2 and leapyear (simple... not exhaustive)
          if( LocalMonth == 2 && (LocalYear % 4) ) LocalDay = LocalDay + 1 ;
        }
      }

    if(LocalHour >= 12)
    {
      PM = true ;
      if(LocalHour > 12) LocalHour = LocalHour - 12 ;
    }

    lcd.setCursor(0, 0);

    if(PM) {
      lcd.print("PM    ") ;
    } else {
      lcd.print("AM    ") ;
    }
    
    //lcd.print("GPS   ");
    lcd.setCursor(6, 0) ;
    if(LocalHour < 10) lcd.print(" ") ;
    lcd.print(LocalHour) ;
    lcd.setCursor(8, 0) ;
    lcd.print(":") ;

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

    lcd.setCursor(11, 0) ;
    lcd.print(":") ;
    lcd.setCursor(12, 0) ;

    if(LocalSecond < 10) {
      lcd.print("0") ;
      lcd.print(LocalSecond) ;  // 12, 13
    } else {
      lcd.print(LocalSecond) ;
    }

    if(PM) {
      lcd.print(" P ") ;     // positions 14, 15 + 1 off screen
    } else {
      lcd.print(" A ") ;
    }

    // The DOY and Month and Date info is shown every 10 seconds
    if((LocalSecond % 10) == 0) {
      lcd.setCursor(0, 1);
      lcd.print(DayOfWeek[dow(LocalYear, LocalMonth, LocalDay)]) ; // positions 0 - 2
      lcd.print("   "); // postition 3 - 5
      lcd.setCursor(6, 1) ;
      lcd.print(NameOfMonth[LocalMonth - 1]) ; // 6 - 8 lower line
      lcd.setCursor(9, 1) ;
      lcd.print(" ") ;
      lcd.setCursor(10, 1) ;
      if(LocalDay < 10) {
        lcd.print(" ") ;  // 10
        lcd.print(LocalDay) ;
      } else {
        lcd.print(LocalDay) ; // 10, 11
      }
      lcd.setCursor(12, 1) ;
      lcd.print(" '") ;  // 12
      lcd.setCursor(14, 1) ;
      lcd.print(LocalYear) ;  // 13, 14
      lcd.print(" ") ; // 15
    }

    if(GPSDEBUG) {
      Serial.print("\nTime: ") ;
      Serial << ((LocalHour<10)?"0":"") ;
      Serial.print(LocalHour, DEC);
      Serial << (":") ;
      Serial << ((GPS.minute<10)?"0":"") ;
      Serial.print (GPS.minute, DEC); 
      Serial << (":") ;
      Serial << ((GPS.seconds<10)?"0":"") ;
      Serial.print(GPS.seconds, DEC) ;
      Serial.print("\nDate: ");
      Serial << ((GPS.day<10)?"0":"") ;
      Serial.print(GPS.day, DEC) ;              //DD
      Serial << "/" << ((GPS.month<10)?"0":"") ;
      Serial.print(GPS.month, DEC) ;            //MM
      Serial << "/20" ;
      Serial.println(GPS.year, DEC) ;           //YYYY
    }
  } // BIG if
} // loop

ADDED:
This Flatduino is running at 8MHz on the internal RC oscillator. More than adequate for this purpose.

I decided the OLED needed to be replaced with a parallel LCD since I am building a couple of these as Christmas presents and wanted to keep the cost down. I also wanted to show the room temperature either in Fahrenheit or Celsius. I also wanted to support both 9600 and 4800 default BAUD GPS modules. So I made some changes. Beside the GPS, the mega328P-PU, a pot for contrast, a resistor for backlight current limiting, there is just a 10K NTC thermistor and a 10K 1% resistor for the thermometer. There is no crystals since the uC internal RC oscillator runs the uC at 8MHz and accurate enough for receiving the GPS aerial data. Total parts cost, under $25. HoHoHo

Some notes:

/*

                            MiniPro backpack        328P Board-duino
                           lcd(7, 6, 2, 3, 4, 5)       <ditto>
   * LCD RS     pin to     digital pin 7                  "
   * LCD Enable pin to     digital pin 6                  "
   * LCD D7     pin to     digital pin 5                  "
   * LCD D6     pin to     digital pin 4                  "
   * LCD D5     pin to     digital pin 3                  "
   * LCD D4     pin to     digital pin 2                  "
   * LCD R/W pin to ground
   * 10K variable resistor (contrast):
       * ends to +5V and ground
       * wiper to LCD VO pin (pin 3)
                                      +-\/-+
Reset                           PC6  1|    |28  PC5 (AI 5)
Rx                        (D 0) PD0  2|    |27  PC4 (AI 4)
Tx                        (D 1) PD1  3|    |26  PC3 (AI 3)      Digital - Daylight Savings Time (Lo = True)
LCD D4                    (D 2) PD2  4|    |25  PC2 (AI 2)
LCD D5               PWM+ (D 3) PD3  5|    |24  PC1 (AI 1)
LCD D6                    (D 4) PD4  6|    |23  PC0 (AI 0)      Junction of 10K NTC Thermistor & Resistor 10K
+5                              VCC  7|    |22  GND             GND
GND                             GND  8|    |21  AREF            +5 and bypass cap
XTL1                            PB6  9|    |20  AVCC            +5
XTL2                            PB7 10|    |19  PB5 (D 13)      LED 1-PPS
LCD D7               PWM+ (D 5) PD5 11|    |18  PB4 (D 12)      F/C MODE (hi = F lo = C)
LCD E                PWM+ (D 6) PD6 12|    |17  PB3 (D 11) PWM  DEBUG (hi = OFF lo = on)
LCD RS                    (D 7) PD7 13|    |16  PB2 (D 10) PWM  BAUD (hi = 9600 lo = 4800)
(Tx reserved)             (D 8) PB0 14|    |15  PB1 (D  9) PWM  GPS serial 9600 input
                                      +----+
 
    The 2 x 16 LCD display is now mapped as follows:
          0000000000111111
          0123456789012345
 0        68F   12:59:59 P    (DST shown as .P or .A)
 1        Wed   DEC 25 '13
 
    The following state-pins on the uC will control feature modes:
   -------------------------------------------------------------------
  328  Log  Arduino
  Pin  Port RefName    Signal usage for GPS time-date
  ___  ___  _______    _______________________________________________
   15  PB1 (D  9)      GPS serial input @BAUD
   16  PB2 (D 10)      BAUD (hi = 9600 lo = 4800)
   17  PB3 (D 11)      DEBUG (hi = OFF lo = on)
   18  PB4 (D 12)      F/C MODE (hi = F lo = C)
   19  PB5 (D 13)      LED Cycle On-Off at 1 second
   23  PC0 (A  0)      Junction of 10K NTC Thermistor & Resistor 10K
   26  PC3 (A  3)      Digital - Daylight Savings Time (Lo = True)
 */

downloadfile.zip (8.07 KB)

The holiday season is seriously upon us and my wife suggested that I build our daughter a GPS clock for a Christmas present. So, I set about doing it.

Instead of the FLAT-duino that I had used previously, I decided to use a 5V Pro Mini. I love to use these with LCD displays, because I use industrial Velcro and stick the Arduino to the back of the parallel LCD - kind of an Arduino backpack. I changed the standard LCD library pin out just a little do the 4 data bits lined up nearly with the Pro Mini board.

I modified the boot-up credits on the LCD to personalize the clock for the daughter and son in law. The second line says " Christmas 2013". Other than that, the code is the same as posted previously.

I attached some pics. The wife is doing the crafty stuff and staining and varnishing a wooden box to use as a clock cabinet. A Wii rechargeable battery pack makes the perfect inexpensive power supply when used with a 5V USB wall wart. I stuck the switches on a protoboard along with the 10K LCD contrast pot and a 500 Ohm ( w/ 100 Ohm safety resistor in series) for a brightness control.

Ray

Pix of the final project

"498" shows the various modules mounted inside the wooden box. The LCD is fitted inside the cutout and secured with a seam of hot-melt glue. The switches-controls board is mounted on the lower left to a dowel that is screwed into the box bottom panel. The top and right side of the board are affixed with hot-melt glue. The glued popsicle sticks create a top mount to provide a wider area for glue than could be achieved without the ridge. Switches operate On/Off, Daylight Saving Time, and the Fahrenheit / Celsius option. The BIG red button is Reset, the small pot is for LCD contrast, and the larger pot is for the LCD backlight brightness (100 Ohm + 500 Ohm rheostat.)

"499" shows the front right side of the enclosure with the thermistor (10K NTC) poking out of a LED panel mount adapter - ideal for this use. A smearing of hot-melt glue on the inside holds everything steady and the thermistor was adjusted so that the body of the sensor is not in contact with the plastic housing in which it is mounted; contact is only where the wires go through the plastic holes. By having the thermistor recessed, the temperature readings will be more "averaged" based on the air and near enclosure interface.

The GPS module is screwed with brass screws to the top lid - patch antenna "up" although these things just seem to work in any position. Since I do not go for a GPS "lock", the clock signal appears on the LCD in just seconds of power On.

Ray

With a PPS pin output from GPS you can get time accurate to about 100ns!

Nice write up. Thanks for the links to the GPS sources also.

I'm using Jack Christensen's timezone library to determine when daylight saving time hits, and update the GMT offset:

That would save you a switch at the expense of some flash.

It would be even cooler to use the GPS to determine the GMT offset depending upon the location of the GPS. That's only useful if the clock plans to change location from time to time.

-transfinite

I'm using Jack Christensen's timezone library to determine when daylight saving time hits, and update the GMT offset:
GitHub - JChristensen/Timezone: Arduino library to facilitate time zone conversions and automatic daylight saving (summer) time adjustments.
That would save you a switch at the expense of some flash.
It would be even cooler to use the GPS to determine the GMT offset depending upon the location of the GPS. That's only useful if the clock plans to change location from time to time

I saw Jack's library in my pre-project research. Excellent work. But, I kind of wanted to hack my own just for fun. Libraries "cloak" so much. Then, there is the issue of the U.S. Federal idiots in D.C. screwing with DST dates! A physical switch is a middle-finger to these fools.

I was looking at maybe using Lat/Lon to calculate the GMT offset but that would require a GPS lock and I am running the code now lock-free, only need 1 satellite and the almanac in the GPS module.

PPS pin output from GPS you can get time accurate to about 100ns!

Yes, the cheap module has PPS but it only works after a GPS lock. Bummer. During the recent storms here last week, I lost lock in my basement lab, but still the clock functioned... I guessing the satellite most closely overhead provided the strongest signal. I do have the ability to ground a pin and echo the GPS raw sentences over serial, so I may investigate this more during another stormy day.

Ray

Reworked the main module using the Adafruit_ILI9340.h and Adafruit_GFX.h libraries with an inexpensive 240x320 GLCD from China; similar to 2.2 18-bit color TFT LCD display with microSD card breakout [EYESPI Connector] : ID 1480 : $24.95 : Adafruit Industries, Unique & fun DIY electronics and kits

Also corrected my stupid mistake in the date/time calculation for timezone offset from UTC. This needs to be backported to all of the forward code.

    if(LocalHour >= TimeZoneHour)
    {
      LocalHour = LocalHour - TimeZoneHour ;
    }
    else 
    {
      // Correct for period when London is a day ahead
      LocalHour = (LocalHour + 24) - TimeZoneHour ;
      LocalDay  = LocalDay - 1 ;
      if( LocalDay == 0)  // backward a month
      {
          LocalMonth = LocalMonth - 1 ;
          LocalDay   = DaysInMonth[ LocalMonth - 1 ] ; // index 0 based
      }
      // The above may need correcting...
      // January in London still December Westward
      if( LocalMonth == 0)           // GPS months are 01 through 12
      {
        LocalMonth = 12 ;
        LocalDay   = DaysInMonth[ 11 ] ;  // lastday of December
      }
      else if( (LocalMonth == 2) && (LocalYear % 4 == 0) && (LocalDay == 28) )
      {
        // Need to deal with LocalMonth = 2 and leapyear (simple... not exhaustive)
        LocalDay = LocalDay + 1 ;
      }
    }

I continue to be very impressed with the performance of the $16 (delivered) u-blox 6 flavor GPS units. I have 3 clocks built now with this design and they work anywhere in the house including the center of the basement. I do not always get "lock' but that is not important for the time/date recovery.

Ray

GPS_GLCD_uBlox_BIG.zip (10.1 KB)

Great post!

Pictures, code, great text

....... And it is a PILB!
(Project I'd Love to Build)

Thanks.