UTC (NMEA) to local time sketch

This is a basic sketch that will convert UTC (NMEA) to local time and display it on an LCD. I hope to add a menu option later that will let you change the time zone instead of changing it in the code. The conversion code part was developed by Jinseok Jeon (JeonLab.wordpress.com). I look forward to seeing any improvements or additions by others.

Enjoy!

Craig Clark

/*
  
  By: Craig Clark N5XNQ  Jan 2014
  
  This sketch takes the UTC time from the NMEA "RMC" sentence and converts it 
  to your local time. You will need to change the TimeZone number to match the 
  time zone you are in.
  I hope later to add a menu that will allow you to change the time zone without
  having to chage it in the program. This sketch is a good basic start for those
  that wish to learn about time converting.   
  
  UTC to local time conversion by: Jinseok Jeon (JeonLab.wordpress.com)
  Date: September 2013
*/  

#include <SoftwareSerial.h>
#include <LiquidCrystal.h>

// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

SoftwareSerial gps =  SoftwareSerial(8, 9); // GPS TX ---> Arduino Pin 8


//*******CHANGE TIME ZONE HERE************
const int TimeZone = -6; 

int DSTbegin[] = { //DST 2013 - 2025 in Canada and US
  310, 309, 308, 313, 312, 311, 310, 308, 314, 313, 312, 310, 309};
int DSTend[] = { //DST 2013 - 2025 in Canada and US
  1103, 1102, 1101, 1106, 1105, 1104, 1103, 1101, 1107, 1106, 1105, 1103, 1102};
int DaysAMonth[] = { //number of days a month
  31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
  
int gpsYear;
int gpsMonth;
int gpsDay = 0;
int gpsHour;
int gpsMin;
byte gpsSec;


void setup() {
  
  gps.begin(4800); // GPS baud rate
  
  // Print a message to the LCD.
  lcd.begin(20, 4); // Using a 20 X 4 VFD
  lcd.clear(); // Clear the VFD
  lcd.setCursor(1, 0);
  lcd.print("N5XNQ local Time"); 
  lcd.setCursor(4, 1);
  lcd.print("Version 2.00");
  delay(2000); // Hold for 2 seconds
  lcd.clear(); 
  lcd.setCursor(3, 1);
  lcd.print("Current Time");
}

void loop() 
{
  PrintGps();
  
  while  (gps.available() > 0) 
  {
    if (char(gps.read()) == 'R' && char(gps.read()) == 'M' && char(gps.read()) == 'C') 
    {
      gpsTime(gps.parseInt());
      
      gpsDate(gps.parseInt());
      if (gpsYear%4 == 0) DaysAMonth[1] = 29; //leap year check

      //Time zone adjustment
      gpsHour += TimeZone;
      
      //DST adjustment
      if (gpsMonth*100+gpsDay >= DSTbegin[gpsYear-13] && 
        gpsMonth*100+gpsDay < DSTend[gpsYear-13]) gpsHour += 1;
      if (gpsHour < 0)
      {
        gpsHour += 24;
        gpsDay -= 1;
        if (gpsDay < 1)
        {
          if (gpsMonth == 1)
          {
            gpsMonth = 12;
            gpsYear -= 1;
          }
          else
          {
            gpsMonth -= 1;
          }
          gpsDay = DaysAMonth[gpsMonth-1];
        } 
      }
      if (gpsHour >= 24)
      {
        gpsHour -= 24;
        gpsDay += 1;
        if (gpsDay > DaysAMonth[gpsMonth-1])
        {
          gpsDay = 1;
          gpsMonth += 1;
          if (gpsMonth > 12) gpsYear += 1;
        }
      }

      
    }
  
  }
}

void gpsTime(long UTC)
{
  gpsHour = int(UTC/10000);
  gpsMin = int(UTC/100%100);
  gpsSec = UTC % 100;
  
}

void gpsDate(long dateRead)
{
  gpsDay = int(dateRead/10000);
  gpsMonth = int(dateRead%10000/100);
  gpsYear = dateRead%100; //last 2 digits, e.g. 2013-> 13
}

void PrintGps (){
  lcd.setCursor(5, 2);
 if (gpsHour < 10) {
     lcd.print("0");
    }  
  lcd.print(gpsHour); 
  lcd.setCursor(7, 2);  
  lcd.print(":");
  lcd.setCursor(8, 2);
   if (gpsMin < 10) {
     lcd.print("0");
    }  
  lcd.print(gpsMin); 
  lcd.setCursor(10, 2);    
  lcd.print(":");
  lcd.setCursor(11, 2);
    if (gpsSec < 10) {
     lcd.print("0");
    } 
  lcd.print(gpsSec);
}

Nice. Do a few pics and put it in the Gallery.

I did it a bit differently, but hardcoded my timezone, too. It is a clock and my house does not straddle 2 zones!
http://forum.arduino.cc/index.php?topic=199216.0
Note, I only use Recommended Minimum and I also bypass any check on location lock... If the recommended sentence passes a checksum, I use it. Hacked the Adafruit lib.

Ray

< ... >
    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
} //    loop

Thanks for the post Ray. I have more features that I would like to add but, the checksum is something I need to take a look at. I will also take a picture of the hardware and post it in the gallery.

Craig

Hello,

I want to thank you all for the shared code.
I used a lot of this, since I only recently started programming the Arduino stuff.

I used an old TomTom serial GPS-mouse for my clock.
This unit gives bogus data after power-on. (in $GPRMC time starts counting at 00:00:00, and the Date is 18-08-02 when there is no fix, or 22-08-02 when there is a fix.)
Only after a while the data becomes real, so I check for these weird numbers before I start updating my display.

I want to share my code with you as well, so here it is: https://drive.google.com/file/d/0B0OBe102ekzobWFYZkozZWhoVjQ/edit?usp=sharing

Un saludo,

Satbeginner