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.