To make it work, you connect up a GPS module of your choice to the RX and TX lines (experiments with SoftwareSerial on other lines were a failure) and the PPS pin to digital pin 2. You'll also need to connect up an AdaFruit RGB LCD shield, or replace LiquidTWI2 with LiquidCrystal and wire the display parallel-style.
This sketch is time-zone and DST aware, so you may need to edit the "summer" and "winter" timezone rule declarations. Depending on your GPS module, you may also need to change the GPS_BAUD or change the interrupt from RISING to FALLING.
    
 
    
  
 
 
#include <Wire.h>
#include <LiquidTWI2.h>
//#include <SoftwareSerial.h>
#include <TinyGPS.h>
#include <Time.h>
#include <Timezone.h>
#define PPS_PIN 2
#define PPS_INT 0
#define RX_PIN 4
#define TX_PIN 3
#define GPS_BAUD 4800
#define LCD_I2C_ADDR 0x20 // for adafruit shield or backpack
LiquidTWI2 display(LCD_I2C_ADDR, 0, 0);
//SoftwareSerial gps_port(RX_PIN, TX_PIN);
#define gps_port Serial
TinyGPS gps;
time_t prevTime = 0; // when the digital clock was displayed
unsigned int prevTenths = 99; // not 0-9
boolean complained = false;
unsigned long last_pps_millis;
/*
For this to work, an extension must be made to the Arduino Time library. This
method's intent is to designate the precise start of a second. It does this by
replacing the prevMillis value saved in the library with the current value of
millis(), but preserving any "owed" updates.
void syncSecond() {
  unsigned long now_millis = millis();
  while (((int)(now_millis - prevMillis)) > 500) { // 500 so we sync to the *nearest* second
    // we're owed at least one update
    now_millis -= 1000;
  }
  prevMillis = now_millis;
}
*/
void pps_interrupt() {
  last_pps_millis = millis();
  syncSecond();
}
void setup() {
  gps_port.begin(GPS_BAUD);
  pinMode(PPS_PIN, INPUT);
  attachInterrupt(PPS_INT, pps_interrupt, RISING);
  display.setMCPType(LTI_TYPE_MCP23017);
  display.begin(16, 2);
  setSyncProvider(gpsTimeSync);
  display.setBacklight(WHITE);
  display.print("GPS clock");
  delay(2000);
  display.clear();  
}
TimeChangeRule summer = { "PDT", Second, Sun, Mar, 2, -7*60 };
TimeChangeRule winter = { "PST", First, Sun, Nov, 2, -8*60 };
Timezone zone(winter, summer);
time_t gpsTimeSync() {
  unsigned long fix_age = 0;
  gps.get_datetime(NULL, NULL, &fix_age);
  if (fix_age < 2000) {
    unsigned int tenths = ((millis() - last_pps_millis) / 100) % 10;
    tmElements_t tm;
    int year;
    gps.crack_datetime(&year, &tm.Month, &tm.Day, &tm.Hour, &tm.Minute, &tm.Second, NULL, NULL);
    tm.Year = year - 1970;
    time_t out = makeTime(tm);
    if (tenths >= 5) out++; // round to the nearest second given our PPS discipline
    return out;
  }
  return 0;
}
void updateDisplay(time_t Now, unsigned int tenths) {
  tmElements_t tm;
  char buf[16];
  breakTime(Now, tm);
  display.setCursor(0, 0);
  sprintf(buf, " %02d:%02d:%02d.%1d %s   ", hourFormat12(Now), tm.Minute, tm.Second, tenths, isPM(Now)?"PM":"AM");
  display.print(buf);
  display.setCursor(0, 1);
  sprintf(buf, "  %2d-%s-%04d   ", tm.Day, monthShortStr(tm.Month), tmYearToCalendar(tm.Year));
  display.print(buf);
}
void loop() {
  while(gps_port.available()) {
    gps.encode(gps_port.read());
  }
  time_t Now = zone.toLocal(now());
  if (timeStatus() != timeNotSet && timeStatus() != timeNeedsSync) {
    unsigned int tenths = ((millis() - last_pps_millis) / 100) % 10;
    if (Now != prevTime || prevTenths != tenths) {
      prevTime = Now;
      prevTenths = tenths;
      complained = false;
      display.setBacklight(GREEN);
      updateDisplay(Now, tenths);
    }
  } else {
    if (!complained) {
      complained = true;
      display.setBacklight(RED);
      display.clear();
      display.print("Waiting for sync");
    }
  }
}
No comments:
Post a Comment
Note: Only a member of this blog may post a comment.