GPS based local time/UTC clock using Velleman Arduino

I am trying to make a GPS clock that outputs local time (Pacific for me) as well as UTC. I am an amateur radio operator and I use both times.

The base is mainly Velleman Arduino components. Main board is a ATmega328 UNO VMA100. Real Time Clock is a DS1302 VMA301. GPS is a U-Blox Neo-7M VMA430. The output screen is a 20 character x 2 line LCD purchased off Ebay since the standard Velleman is only 16 characters.

I bought my components from Fry's (except the screen) so that tells you how long I have been planning this, but life happens.

The desired output is formatted thusly:

A11:00:00TH10-10-24 (local time, also has P reading)
u18:00:00TH10-10-24 (UTC)

This is an example, of course.

I have been copy-and-pasting segments of code on and off for a while and finally have something that might work, but I want to have a second opinion.

//GPS Arduino clock
//Arduino board: Velleman ATmega328 UNO VMA100
#include <Arduino.h>
//GPS module Velleman U-Blox NEO-7M VMA430
#include <VMA430_GPS.h>
//real time clock Velleman DS1302 VMA301
#include <DS1302.h>
#include <WProgram.h>
#include <stdint.h>
#include <stdio.h>
#include <SoftwareSerial.h>
//initialize 20 character LCD display, generic 20char x 2 row
#include <LiquidCrystal.h>
#define TIME_SYNC_INTERVAL 180
#define CYCLES_PER_RTC_SYNC 20 
#define SerialGPS Serial1 
const int timeZone = -8;
#define largeA 0x77
#define largeP 0x73
#define dash 0x40
#define blank 0x00
#define DP 0x80
//initialize GPS
VMA430_GPS gps;
time_t prevDisplay = 0;
int cyclesUntilSync = 0;
time_t dstStart = 0;
time_t dstEnd = 0;
bool gpsLocked false;
int currentYear = 0;
void setup()
{
initDisplay();
setDisplayBrightness(MIN_BRIGHTNESS+3);
Serial.begin(9600);
SerialGPS.begin(9600);
//manualTimeSet();
setSyncProvider(timeSync);
setSyncInterval(TIME_SYNC_INTERVAL);
}
// http://quadpoint.org/projects/arduino-ds1302
namespace {
// Set the appropriate digital I/O pin connections. These are the pin
// assignments for the Arduino as well for as the DS1302 chip.
const int kCePin = 5; //chip enable
const int kIoPin = 6; //input\output
const int kSclkPin = 7; //serial clock
//create DS1302 object
DS1302 rtc(kCePin, kIoPin, kSclkPin);
String dayAsString(const Time::Day Day) {
switch (day) {
case Time::kSunday: return "SU";
case Time::kMonday: return "MO";
case Time::kTuesday: return "TU";
case Time::kWednesday: return "WE";
case Time::kThursday: return "TH";
case Time::kFriday: return "FR";
case Time::kSaturday: return "SA";
}
return "(un)";
}
void printTime() {
//get current time and date from chip
Time t = rtc.time();
//name day of week
const String day = dayAsString(t.day);
char buf[20];
//format time and date and insert into temp buffer
segment(buf, sizeof(buf), "$02d:%02d:%02d" %s %04d-%02d-%02d,
t.hr, t.min, t.sec, day.c_str(), t.mon, t.date, t.yr);
//print to serial
Serial.println(buf);
}
}
void setup() {
Serial.begin(9600);
//initialize new chip, turn off write protect, clear clock halt flag
rtc.writeProtect(false);
rtc.halt(false);
// Make a new time object to set the date and time.
  // Monday, January 1, 2024, 1:00:00
  Time t(2024, 1, 1, 1, 0, 00, Time::kMonday);
//set time and date on chip
rtc.time(t);
}
//loop and print time per one second
void loop() {
printTime();
delay(1000);
}
void loop()
{
while (SerialGPS.available())
{
int c=SerialGPS.read();
gps.encode(c);
}
if (timeStatus()!= timeNotSet)
{
if (now() != prevDisplay)
{
prevDisplay = now();
updateDST();
displayLocalTime();
displayUTC();
printTime(prevDisplay);
}
}
}
}
}
//GPS intialize, may have to be changed to ublox for VMA430
time_t timeSync()
{
tmElements_t tm;
time_t t = 0;
int yr;
unsigned long fixAge;
gpsLocked = false;
gps.crack_datetime(&yr, &tm.Month, &tm.Day,
&tm.Hour, &tm.Minute, &tm.Second,
NULL, &fixAge); 
if (fixAge==TinyGPS::GPS_INVALID_FIX_TIME)
{
Serial.println("\nNo GPS fix, using RTC");
t = RTC.get();
}
else if (fixAge > 2000)
{
Serial.println("\nGPS offline, using RTC");
t = RTC.get();
}
else
{
gpsLocked = true;
tm.Year = yr-1970;
Serial.print("\nGPS time OK, ");
Serial.print(gps.satellites());
Serial.print(" sats. ");
if (cyclesUntilSync <=0)
{
if (RTC.write(tm))
Serial.print("RTC updated.");
else Serial.print("RTC update failed.");
cyclesUntilSync = CYCLES_PER_RTC_SYNC;
}
Serial.println();
t = makeTime(tm);
}
cyclesUntilSync --;
return t; // GPS online but no satellite fix
}
//DST initializing
void manualTimeSet()
{
tmElements_t tm;
tm.Year = 2020 - 1970;
tm.Month = 11;
tm.Day = 6;
tm.Hour = 5;
tm.Minute = 59;
tm.Second = 30;
RTC.write(tm);
#define DST_START_WEEK 2
#define DST_START_MONTH 3
#define DST_START_HOUR 9
#define DST_END_WEEK 1
#define DST_END_MONTH 11
#define DST_END_HOUR 9
time_t timeChange(int hr, int wk, int mo, int yr)
{
tmElements_t tm;
tm.Year = yr - 1970;
tm.Month = mo;
tm.Day = 1;
tm.Hour = hr;
tm.Minute = 0;
tm.Second = 0;
time_t t = makeTime(tm);
int daysTillSunday = (8 - weekday(t)) % 7;
t += (daysTillSunday + (wk-1)*7)*SECS_PER_DAY; 
return t;
}
void printDST()
{
Serial.print("DST starts at ");
printTime(dstStart);
Serial.print("DST ends at ");
printTime(dstEnd);
}
void initDST(int yr)
{
dstStart = timeChange(DST_START_HOUR, DST_START_WEEK, DST_START_MONTH, yr);
dstEnd
= timeChange(DST_END_HOUR,
DST_END_WEEK,
DST_END_MONTH,
yr);
}
void updateDST()
{
int yr = year();
if (yr != currentYear)
{
initDST(yr);
}
}
bool isDST(time_t t)
{
return ((t >= dstStart) && (t < dstEnd));
} 
time_t localTime()
{
time_t t = now();
if (isDST(t)) t += SECS_PER_HOUR;
t += (timeZone * SECS_PER_HOUR);
return t;
} 
}

Edit your code like this and try:

#include <Arduino.h>
#include <VMA430_GPS.h>
#include <DS1302.h>
#include <LiquidCrystal.h>
#include <TimeLib.h>  // For time-handling functions
#include <SoftwareSerial.h>

// Initialize GPS and RTC modules
VMA430_GPS gps;
DS1302 rtc(5, 6, 7);  // Pins for the DS1302
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);  // Adjust pins accordingly

const int timeZone = -8;  // Pacific Standard Time (PST)
bool gpsLocked = false;
time_t prevDisplay = 0;
int cyclesUntilSync = 20;
time_t dstStart = 0;
time_t dstEnd = 0;
int currentYear = 0;

void setup() {
  lcd.begin(20, 2);  // 20x2 LCD initialization
  Serial.begin(9600);  // For serial debugging
  gps.begin(9600);  // Initialize GPS at 9600 baud
  rtc.writeProtect(false);  // Allow RTC writes
  rtc.halt(false);  // Start the RTC
  
  // Set an initial time (for example purposes)
  Time t(2024, 1, 1, 1, 0, 0, Time::kMonday);
  rtc.time(t);  // Set the RTC time
  
  setSyncProvider(timeSync);  // Use GPS/RTC for syncing
  setSyncInterval(180);  // Sync interval
}

void loop() {
  // Process GPS data
  while (SerialGPS.available()) {
    int c = SerialGPS.read();
    gps.encode(c);
  }

  if (timeStatus() != timeNotSet && now() != prevDisplay) {
    prevDisplay = now();
    updateDST();  // Update DST based on the current date

    // Display local and UTC times
    displayLocalTime();
    displayUTC();
    delay(1000);  // Update every second
  }
}

void displayLocalTime() {
  time_t localT = localTime();
  char buf[20];
  sprintf(buf, "A%02d:%02d:%02d %02d-%02d-%02d", hour(localT), minute(localT), second(localT), day(localT), month(localT), year(localT));
  lcd.setCursor(0, 0);  // First row
  lcd.print(buf);
}

void displayUTC() {
  time_t utcT = now();
  char buf[20];
  sprintf(buf, "u%02d:%02d:%02d %02d-%02d-%02d", hour(utcT), minute(utcT), second(utcT), day(utcT), month(utcT), year(utcT));
  lcd.setCursor(0, 1);  // Second row
  lcd.print(buf);
}

time_t timeSync() {
  tmElements_t tm;
  unsigned long fixAge;
  int yr;
  
  gpsLocked = false;
  gps.crack_datetime(&yr, &tm.Month, &tm.Day, &tm.Hour, &tm.Minute, &tm.Second, NULL, &fixAge);
  
  if (fixAge == TinyGPS::GPS_INVALID_FIX_TIME) {
    Serial.println("No GPS fix, using RTC");
    return rtc.get();
  } else if (fixAge > 2000) {
    Serial.println("GPS offline, using RTC");
    return rtc.get();
  } else {
    gpsLocked = true;
    tm.Year = yr - 1970;
    time_t t = makeTime(tm);
    return t;
  }
}

// Time adjustment for DST
time_t localTime() {
  time_t t = now();
  if (isDST(t)) t += SECS_PER_HOUR;
  return t + timeZone * SECS_PER_HOUR;
}

Great, thanks! As noted, the original code is a frankenstein of copypasta, so I will definitely be reconciling my code block with your recommendations.

Phreaking Cow

Opening multiple threads for the same project is against forum rules.
Previous thread (click).
Leo..

That thread was intended to be my introduction to the forum. When I join a new to me forum, I always introduce myself and state why I have joined and what I hope to accomplish. I was looking for advice on what subforum would be the best to ask specific questions about my particular project. I was given suggestions, so came to this particular subforum with the particulars of my project, looking for specific technical assistance. I have zero intention of "spamming" or "trash posting". I hope this clears it up.

Phreaking Cow

I noticed that in the routine:

void displayLocalTime() {
  time_t localT = localTime();
  char buf[20];
  sprintf(buf, "A%02d:%02d:%02d %02d-%02d-%02d", hour(localT), minute(localT), second(localT), day(localT), month(localT), year(localT));
  lcd.setCursor(0, 0);  // First row
  lcd.print(buf);

that there is no allowance made for the day of week. In my original code in between second and day there is a day of week, rendered as two letters.

Take a note of the following routine from my original code:

String dayAsString(const Time::Day Day) {
switch (day) {
case Time::kSunday: return "SU";
case Time::kMonday: return "MO";
case Time::kTuesday: return "TU";
case Time::kWednesday: return "WE";
case Time::kThursday: return "TH";
case Time::kFriday: return "FR";
case Time::kSaturday: return "SA";

This routine denoted the string usage for the day of the week. Please take a second look at my desired output as noted in OP. I am not sure if placing a return as "dayofweek(localT)" and the equivalent in the routine for the UTC printout would be sufficient, or if "dayAsString(localT)" would be necessary with a reference to the "String dayAsString(const Time::Day Day)" routine.

Again, thank you @aliarifat794 for bothering to write up a better program than the galaxysourced mess I had. I was a little scared to try to compile it because I know that compilers can give weird error messages and I would be stuck.

OK, I have inserted the coding for day of week string. I am worried that the function "const String day" may be inserted in the wrong place and that the definition of "dayAsString" as "(t.day)" may need updating. Again, all feedback welcome.

#include <Arduino.h>
#include <VMA430_GPS.h>
#include <DS1302.h>
#include <LiquidCrystal.h>
#include <TimeLib.h>  // For time-handling functions
#include <SoftwareSerial.h>

// Initialize GPS and RTC modules
VMA430_GPS gps;
DS1302 rtc(5, 6, 7);  // Pins for the DS1302
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);  // Adjust pins accordingly

const int timeZone = -8;  // Pacific Standard Time (PST)
bool gpsLocked = false;
time_t prevDisplay = 0;
int cyclesUntilSync = 20;
time_t dstStart = 0;
time_t dstEnd = 0;
int currentYear = 0;

void setup() {
  lcd.begin(20, 2);  // 20x2 LCD initialization
  Serial.begin(9600);  // For serial debugging
  gps.begin(9600);  // Initialize GPS at 9600 baud
  rtc.writeProtect(false);  // Allow RTC writes
  rtc.halt(false);  // Start the RTC
  
  // Set an initial time (for example purposes)
  Time t(2024, 1, 1, 1, 0, 0, Time::kMonday);
  rtc.time(t);  // Set the RTC time
String dayAsString(const Time::Day Day) {
switch (day) {
case Time::kSunday: return "SU";
case Time::kMonday: return "MO";
case Time::kTuesday: return "TU";
case Time::kWednesday: return "WE";
case Time::kThursday: return "TH";
case Time::kFriday: return "FR";
case Time::kSaturday: return "SA";
}
return "(un)";
}
const String day = dayAsString(t.day);
char buf[20];
  
  setSyncProvider(timeSync);  // Use GPS/RTC for syncing
  setSyncInterval(180);  // Sync interval
}

void loop() {
  // Process GPS data
  while (SerialGPS.available()) {
    int c = SerialGPS.read();
    gps.encode(c);
  }

  if (timeStatus() != timeNotSet && now() != prevDisplay) {
    prevDisplay = now();
    updateDST();  // Update DST based on the current date

    // Display local and UTC times
    displayLocalTime();
    displayUTC();
    delay(1000);  // Update every second
  }
}

void displayLocalTime() {
  time_t localT = localTime();
  char buf[20];
  sprintf(buf, "A%02d:%02d:%02d%s%02d-%02d-%02d", hour(localT), minute(localT), second(localT), day.c_str(liocalT), day(localT), month(localT), year(localT));
  lcd.setCursor(0, 0);  // First row
  lcd.print(buf);
}

void displayUTC() {
  time_t utcT = now();
  char buf[20];
  sprintf(buf, "u%02d:%02d:%02d%s%02d-%02d-%02d", hour(utcT), minute(utcT), second(utcT), day.c_str(utcT), day(utcT), month(utcT), year(utcT));
  lcd.setCursor(0, 1);  // Second row
  lcd.print(buf);
}

time_t timeSync() {
  tmElements_t tm;
  unsigned long fixAge;
  int yr;
  
  gpsLocked = false;
  gps.crack_datetime(&yr, &tm.Month, &tm.Day, &tm.Hour, &tm.Minute, &tm.Second, NULL, &fixAge);
  
  if (fixAge == TinyGPS::GPS_INVALID_FIX_TIME) {
    Serial.println("No GPS fix, using RTC");
    return rtc.get();
  } else if (fixAge > 2000) {
    Serial.println("GPS offline, using RTC");
    return rtc.get();
  } else {
    gpsLocked = true;
    tm.Year = yr - 1970;
    time_t t = makeTime(tm);
    return t;
  }
}

// Time adjustment for DST
time_t localTime() {
  time_t t = now();
  if (isDST(t)) t += SECS_PER_HOUR;
  return t + timeZone * SECS_PER_HOUR;
}

You're right, @Delta_G. I wasn't paying attention. Try this:

#include <Arduino.h>
#include <VMA430_GPS.h>
#include <DS1302.h>
#include <LiquidCrystal.h>
#include <TimeLib.h>  // For time-handling functions
#include <SoftwareSerial.h>

// Initialize GPS and RTC modules
VMA430_GPS gps;
DS1302 rtc(5, 6, 7);  // Pins for the DS1302
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);  // Adjust pins accordingly

const int timeZone = -8;  // Pacific Standard Time (PST)
bool gpsLocked = false;
time_t prevDisplay = 0;
int cyclesUntilSync = 20;
time_t dstStart = 0;
time_t dstEnd = 0;
int currentYear = 0;

void setup() {
  lcd.begin(20, 2);  // 20x2 LCD initialization
  Serial.begin(9600);  // For serial debugging
  gps.begin(9600);  // Initialize GPS at 9600 baud
  rtc.writeProtect(false);  // Allow RTC writes
  rtc.halt(false);  // Start the RTC
  
  // Set an initial time (for example purposes)
  Time t(2024, 1, 1, 1, 0, 0, Time::kMonday);
  rtc.time(t);  // Set the RTC time
  setSyncProvider(timeSync);  // Use GPS/RTC for syncing
  setSyncInterval(180);  // Sync interval
}

void loop() {
  // Process GPS data
  while (SerialGPS.available()) {
    int c = SerialGPS.read();
    gps.encode(c);
  }

  if (timeStatus() != timeNotSet && now() != prevDisplay) {
    prevDisplay = now();
    updateDST();  // Update DST based on the current date

    // Display local and UTC times
    displayLocalTime();
    displayUTC();
    delay(1000);  // Update every second
  }
}

String dayAsString(const Time::Day Day) {
switch (day) {
case Time::kSunday: return "SU";
case Time::kMonday: return "MO";
case Time::kTuesday: return "TU";
case Time::kWednesday: return "WE";
case Time::kThursday: return "TH";
case Time::kFriday: return "FR";
case Time::kSaturday: return "SA";
return "(un)";
const String day = dayAsString(t.day);
char buf[20];
}

void displayLocalTime() {
  time_t localT = localTime();
  char buf[20];
  sprintf(buf, "A%02d:%02d:%02d%s%02d-%02d-%02d", hour(localT), minute(localT), second(localT), day.c_str(liocalT), day(localT), month(localT), year(localT));
  lcd.setCursor(0, 0);  // First row
  lcd.print(buf);
}

void displayUTC() {
  time_t utcT = now();
  char buf[20];
  sprintf(buf, "u%02d:%02d:%02d%s%02d-%02d-%02d", hour(utcT), minute(utcT), second(utcT), day.c_str(utcT), day(utcT), month(utcT), year(utcT));
  lcd.setCursor(0, 1);  // Second row
  lcd.print(buf);
}

time_t timeSync() {
  tmElements_t tm;
  unsigned long fixAge;
  int yr;
  
  gpsLocked = false;
  gps.crack_datetime(&yr, &tm.Month, &tm.Day, &tm.Hour, &tm.Minute, &tm.Second, NULL, &fixAge);
  
  if (fixAge == TinyGPS::GPS_INVALID_FIX_TIME) {
    Serial.println("No GPS fix, using RTC");
    return rtc.get();
  } else if (fixAge > 2000) {
    Serial.println("GPS offline, using RTC");
    return rtc.get();
  } else {
    gpsLocked = true;
    tm.Year = yr - 1970;
    time_t t = makeTime(tm);
    return t;
  }
}

// Time adjustment for DST
time_t localTime() {
  time_t t = now();
  if (isDST(t)) t += SECS_PER_HOUR;
  return t + timeZone * SECS_PER_HOUR;
}

I haven't tried to compile/run it yet. I was just trying to make sure all my bases were covered before trying to do so. I will try to do so today.

Well, it says it can't find any of the compiled resources referenced in "#include", nor can it find my board.

Edit: Selecting Arduino Uno solves the board problem, but it can't find VMA430_GPS.h. I can't find that module anywhere online, just a "vma430_code.txt" file. I'm new to Arduino so I'm probably missing something obvious.

It helps to correctly install the Arduino IDE first. :roll_eyes:

Error list:

Arduino: 1.8.19 (Linux), Board: "Arduino Uno"

Warning: platform.txt from core 'Arduino AVR Boards' contains deprecated compiler.path={runtime.tools.avr-gcc.path}/bin/, automatically converted to compiler.path=/usr/bin/. Consider upgrading this core.
clock:10:12: error: no matching function for call to ‘VMA430_GPS::VMA430_GPS()’
VMA430_GPS gps;
^
In file included from /home/brycow/Arduino/clock/clock.ino:3:0:
/home/brycow/Arduino/libraries/VMA430_GPS_Module-2.0.0/src/VMA430_GPS.h:79:3: note: candidate: VMA430_GPS::VMA430_GPS(HardwareSerial*)
VMA430_GPS(HardwareSerial );
^
/home/brycow/Arduino/libraries/VMA430_GPS_Module-2.0.0/src/VMA430_GPS.h:79:3: note: candidate expects 1 argument, 0 provided
/home/brycow/Arduino/libraries/VMA430_GPS_Module-2.0.0/src/VMA430_GPS.h:77:3: note: candidate: VMA430_GPS::VMA430_GPS(SoftwareSerial
)
VMA430_GPS(SoftwareSerial *);
^
/home/brycow/Arduino/libraries/VMA430_GPS_Module-2.0.0/src/VMA430_GPS.h:77:3: note: candidate expects 1 argument, 0 provided
/home/brycow/Arduino/libraries/VMA430_GPS_Module-2.0.0/src/VMA430_GPS.h:74:7: note: candidate: constexpr VMA430_GPS::VMA430_GPS(const VMA430_GPS&)
class VMA430_GPS {
^
/home/brycow/Arduino/libraries/VMA430_GPS_Module-2.0.0/src/VMA430_GPS.h:74:7: note: candidate expects 1 argument, 0 provided
/home/brycow/Arduino/libraries/VMA430_GPS_Module-2.0.0/src/VMA430_GPS.h:74:7: note: candidate: constexpr VMA430_GPS::VMA430_GPS(VMA430_GPS&&)
/home/brycow/Arduino/libraries/VMA430_GPS_Module-2.0.0/src/VMA430_GPS.h:74:7: note: candidate expects 1 argument, 0 provided
/home/brycow/Arduino/clock/clock.ino: In function ‘void setup()’:
clock:32:19: error: ‘timeSync’ was not declared in this scope
setSyncProvider(timeSync); // Use GPS/RTC for syncing
^
/home/brycow/Arduino/clock/clock.ino: In function ‘void loop()’:
clock:38:10: error: ‘SerialGPS’ was not declared in this scope
while (SerialGPS.available()) {
^
clock:40:9: error: ‘class VMA430_GPS’ has no member named ‘encode’
gps.encode(c);
^
clock:45:15: error: ‘updateDST’ was not declared in this scope
updateDST(); // Update DST based on the current date
^
clock:48:22: error: ‘displayLocalTime’ was not declared in this scope
displayLocalTime();
^
clock:49:16: error: ‘displayUTC’ was not declared in this scope
displayUTC();
^
/home/brycow/Arduino/clock/clock.ino: In function ‘String dayAsString(Time::Day)’:
clock:55:12: error: switch quantity not an integer
switch (day) {
^
clock:63:32: error: ‘t’ was not declared in this scope
const String day = dayAsString(t.day);
^
clock:67:25: error: a function-definition is not allowed here before ‘{’ token
void displayLocalTime() {
^
clock:75:19: error: a function-definition is not allowed here before ‘{’ token
void displayUTC() {
^
clock:83:19: error: a function-definition is not allowed here before ‘{’ token
time_t timeSync() {
^
clock:110:1: error: expected ‘}’ at end of input
}
^
exit status 1
no matching function for call to ‘VMA430_GPS::VMA430_GPS()’

This report would have more information with
"Show verbose output during compilation"
option enabled in File -> Preferences.

It appears to me as if many of the errors are due to undefined parameters in the parentheses, as well as misplaced function definitions. Since this is based on @aliarifat794's off the cuff program, I figure these errors are accidental. I'm gonna have to haul out my book on C programming and make sure I know what the heck I'm doing before anything else.

There are multiple problems with the code.

Software serial is used for the VMA430, but there is no constructor for a SoftwareSerial instance.

The constructor for the VMA430 is wrong, it needs a reference to either a hardware serial or software serial object.

The code mixes SerialGPS and gps when calling SoftwareSerial funtions.

The lcd and rtc are sharing pin 5.

Once those are fixed, the compiler might give some meaningful error messages.

Most of the undefined functions are caused by a missing closing bracket for the dayAsString() function, causing all subsequent functions to be inside that function.

The closing bracket problem has been fixed.

I was planning on reassigning the data output pins away from the PWM pins anyway, I will make sure to do that.

Revised code:


#include <Arduino.h>
#include <VMA430_GPS.h>
#include <DS1302.h>
#include <LiquidCrystal.h>
#include <TimeLib.h>  // For time-handling functions
#include <SoftwareSerial.h>

// Initialize GPS and RTC modules
VMA430_GPS gps(2,3)
DS1302 rtc(5, 6, 7);  // Pins for the DS1302
LiquidCrystal lcd(8, 9, 10, 11, 12);  // Pins for LCD, may need revising

const int timeZone = -8;  // Pacific Standard Time (PST)
bool gpsLocked = false;
time_t prevDisplay = 0;
int cyclesUntilSync = 20;
time_t dstStart = 0;
time_t dstEnd = 0;
int currentYear = 0;

void setup() {
  lcd.begin(20, 2);  // 20x2 LCD initialization
  Serial.begin(9600);  // For serial debugging
  gps.begin(9600);  // Initialize GPS at 9600 baud
  rtc.writeProtect(false);  // Allow RTC writes
  rtc.halt(false);  // Start the RTC
  
  // Set an initial time (for example purposes)
  Time t(2024, 1, 1, 1, 0, 0, Time::kMonday);
  rtc.time(t);  // Set the RTC time
  setSyncProvider(timeSync);  // Use GPS/RTC for syncing
  setSyncInterval(180);  // Sync interval
}

void loop() {
  // Process GPS data
  while (gps.available()) {
    int c = gps.read();
    gps.encode(c);
  }

  if (timeStatus() != timeNotSet && now() != prevDisplay) {
    prevDisplay = now();
    updateDST();  // Update DST based on the current date

    // Display local and UTC times
    displayLocalTime();
    displayUTC();
    delay(1000);  // Update every second
  }
}

String dayAsString(const Time::Day) {
switch (day) 
case Time::kSunday: return "SU";
case Time::kMonday: return "MO";
case Time::kTuesday: return "TU";
case Time::kWednesday: return "WE";
case Time::kThursday: return "TH";
case Time::kFriday: return "FR";
case Time::kSaturday: return "SA";
}

void displayLocalTime() {
  time_t localT = localTime();
  char buf[20];
  sprintf(buf, "A%02d:%02d:%02d%s%02d-%02d-%02d", hour(localT), minute(localT), second(localT), day.c_str(liocalT), day(localT), month(localT), year(localT));
  lcd.setCursor(0, 0);  // First row
  lcd.print(buf);
}

void displayUTC() {
  time_t utcT = now();
  char buf[20];
  sprintf(buf, "u%02d:%02d:%02d%s%02d-%02d-%02d", hour(utcT), minute(utcT), second(utcT), day.c_str(utcT), day(utcT), month(utcT), year(utcT));
  lcd.setCursor(0, 1);  // Second row
  lcd.print(buf);
}

time_t timeSync() {
  tmElements_t tm;
  unsigned long fixAge;
  int yr;
  
  gpsLocked = false;
  gps.crack_datetime(&yr, &tm.Month, &tm.Day, &tm.Hour, &tm.Minute, &tm.Second, NULL, &fixAge);
  
  if (fixAge == TinyGPS::GPS_INVALID_FIX_TIME) {
    Serial.println("No GPS fix, using RTC");
    return rtc.get();
  } else if (fixAge > 2000) {
    Serial.println("GPS offline, using RTC");
    return rtc.get();
  } else {
    gpsLocked = true;
    tm.Year = yr - 1970;
    time_t t = makeTime(tm);
    return t;
  }
}

// Time adjustment for DST
time_t localTime() {
  time_t t = now();
  if (updateDST(t)) t += SECS_PER_HOUR;
  return t + timeZone * SECS_PER_HOUR;
}

This gives me the following errors:

return rtc.get();
        ^

clock:94:12: error: ‘rtc’ was not declared in this scope
return rtc.get();
^
/home/brycow/Arduino/clock/clock.ino: In function ‘time_t localTime()’:
clock:106:18: error: ‘updateDST’ was not declared in this scope
if (updateDST(t)) t += SECS_PER_HOUR;
^
exit status 1
no matching function for call to ‘VMA430_GPS::VMA430_GPS(int, int)’

I got rid of the oddball "isDST" signifier only to have what I thought was a correct one also bounced back.

I am also not sure why it doesn't recognize "rtc.get" as defined.

I also thought I had the mixed signifiers with regards to the VMA430 fixed, but I guess not.

I went through and fixed the "dayAsString" bracket problem as well as the pin problem with the LCD.

Your code is admittedly a lot of copy/past from various sketches, and you have ended up with a lot of things that do not fit together.

rtc.get() would work with the DS1307RTC or DS3232RTC libraries, but you have a DS1302 with a different library.

  // Process GPS data
  while (gps.available()) {
    int c = gps.read();
    gps.encode(c);
  }

This looks like code that would be used with TinyGPS, but you are not using that library. The VMA430 library you are using handles reading the serial data from the GPS internally, so there would be no need for such code.

I think its time that you started a new sketch, and slowly build up your code one piece at a time. The VMA430 library has an example sketch that prints the time to the serial monitor. Start with that code, then slowly add the other parts you need, such as reading/setting the time for the DS1302. After you get that much working, then add the code to display on the LCD. Trying to do the entire code all at one time, from bits and pieces of multiple sketches, is generally a disaster and very frustrating.

I realized what @david_2018 was talking about with the need for SoftwareSerial to be defined, so doing so resulted in the following code:


#include <Arduino.h>
#include <VMA430_GPS.h>
#include <DS1302.h>
#include <LiquidCrystal.h>
#include <TimeLib.h>  // For time-handling functions
#include <SoftwareSerial.h>

const byte rxPin = 2;
const byte txPin = 3;
// Set up a new SoftwareSerial object

SoftwareSerial VMA430_GPS gps (2, 3);
// Initialize GPS and RTC modules
VMA430_GPS gps (2, 3)
DS1302 rtc (5, 6, 7);  // Pins for the DS1302
LiquidCrystal lcd (8, 9, 10, 11, 12);  // Pins for LCD, may need revising

const int timeZone = -8;  // Pacific Standard Time (PST)
bool gpsLocked = false;
time_t prevDisplay = 0;
int cyclesUntilSync = 20;
time_t dstStart = 0;
time_t dstEnd = 0;
int currentYear = 0;

void setup() {
  lcd.begin(20, 2);  // 20x2 LCD initialization
  Serial.begin(9600);  // For serial debugging
  gps.begin(9600);  // Initialize GPS at 9600 baud
  rtc.writeProtect(false);  // Allow RTC writes
  rtc.halt(false);  // Start the RTC
  
  // Set an initial time (for example purposes)
  Time t(2024, 1, 1, 1, 0, 0, Time::kMonday);
  rtc.time(t);  // Set the RTC time
  setSyncProvider(timeSync);  // Use GPS/RTC for syncing
  setSyncInterval(180);  // Sync interval
}

void loop() {
  // Process GPS data
  while (gps.available()) {
    int c = gps.read();
    gps.encode(c);
  }

  if (timeStatus() != timeNotSet && now() != prevDisplay) {
    prevDisplay = now();
    updateDST();  // Update DST based on the current date

    // Display local and UTC times
    displayLocalTime();
    displayUTC();
    delay(1000);  // Update every second
  }
}

String dayAsString(const Time::Day) {
switch (day) 
case Time::kSunday: return "SU";
case Time::kMonday: return "MO";
case Time::kTuesday: return "TU";
case Time::kWednesday: return "WE";
case Time::kThursday: return "TH";
case Time::kFriday: return "FR";
case Time::kSaturday: return "SA";
}

void displayLocalTime() {
  time_t localT = localTime();
  char buf[20];
  sprintf(buf, "A%02d:%02d:%02d%s%02d-%02d-%02d", hour(localT), minute(localT), second(localT), day.c_str(liocalT), day(localT), month(localT), year(localT));
  lcd.setCursor(0, 0);  // First row
  lcd.print(buf);
}

void displayUTC() {
  time_t utcT = now();
  char buf[20];
  sprintf(buf, "u%02d:%02d:%02d%s%02d-%02d-%02d", hour(utcT), minute(utcT), second(utcT), day.c_str(utcT), day(utcT), month(utcT), year(utcT));
  lcd.setCursor(0, 1);  // Second row
  lcd.print(buf);
}

time_t timeSync() {
  tmElements_t tm;
  unsigned long fixAge;
  int yr;
  
  gpsLocked = false;
  gps.crack_datetime(&yr, &tm.Month, &tm.Day, &tm.Hour, &tm.Minute, &tm.Second, NULL, &fixAge);
  
  if (fixAge == TinyGPS::GPS_INVALID_FIX_TIME) {
    Serial.println("No GPS fix, using RTC");
    return rtc.get();
  } else if (fixAge > 2000) {
    Serial.println("GPS offline, using RTC");
    return rtc.get();
  } else {
    gpsLocked = true;
    tm.Year = yr - 1970;
    time_t t = makeTime(tm);
    return t;
  }
}

// Time adjustment for DST
time_t localTime() {
  time_t t = now();
  if (updateDST(t)) t += SECS_PER_HOUR;
  return t + timeZone * SECS_PER_HOUR;
}

Which then resulted in the following errors:

             ^

clock:96:12: error: ‘rtc’ was not declared in this scope
return rtc.get();
^
clock:99:12: error: ‘rtc’ was not declared in this scope
return rtc.get();
^
/home/brycow/Arduino/clock/clock.ino: In function ‘time_t localTime()’:
clock:111:18: error: ‘updateDST’ was not declared in this scope
if (updateDST(t)) t += SECS_PER_HOUR;
^
exit status 1
expected initializer before ‘gps’

It's still complaining about "rtc" and "updateDST" not being defined, as well as "gps" not being initialized correctly. I'm stumped.

SoftwareSerial VMA430_GPS gps (2, 3);
// Initialize GPS and RTC modules
VMA430_GPS gps (2, 3)

you are missing ; at the line VMA430_GPS(2, 3) should be VMA430_GPS(2, 3);

I've been trying to interpret the errors and alter the code accordingly only to realize that there's a couple essential commands missing. I will have to crack open my C++ book.

Code:

#include <Arduino.h>
#include <VMA430_GPS.h>
#include <DS1302.h>
#include <LiquidCrystal.h>
#include <TimeLib.h>  // For time-handling functions
#include <SoftwareSerial.h>

const byte rxPin = 0;
const byte txPin = 1;
// Initialize GPS and RTC modules
const VMA430_GPS gps (SoftwareSerial 0, 1);
const DS1302 rtc (2, 3, 4);  // Pins for the DS1302
const LiquidCrystal lcd (5, 6, 7, 8, 9, 10, 11);  // Pins for LCD, may need revising

const int timeZone = -8;  // Pacific Standard Time (PST)
bool gpsLocked = false;
time_t prevDisplay = 0;
int cyclesUntilSync = 20;
time_t dstStart = 0;
time_t dstEnd = 0;
int currentYear = 0;

void setup() {
  lcd.begin(20, 2);  // 20x2 LCD initialization
  Serial.begin(9600);  // For serial debugging
  gps.begin(9600);  // Initialize GPS at 9600 baud
  rtc.writeProtect(false);  // Allow RTC writes
  rtc.halt(false);  // Start the RTC
  
  // Set an initial time (for example purposes)
  Time t(2024, 1, 1, 1, 0, 0, Time::kMonday);
  rtc.time(t);  // Set the RTC time
  setSyncProvider(timeSync);  // Use GPS/RTC for syncing
  setSyncInterval(180);  // Sync interval
}

void loop() {
  // Process GPS data
  while (gps.available()) {
    int c = gps.read();
    gps.encode(c);
  }

  if (timeStatus() != timeNotSet && now() != prevDisplay) {
    prevDisplay = now();
    updateDST();  // Update DST based on the current date

    // Display local and UTC times
    displayLocalTime();
    displayUTC();
    delay(1000);  // Update every second
  }
}

void setup () {
String dayAsString(const Time::Day) 
switch (day) 
case Time::kSunday: return "SU";
case Time::kMonday: return "MO";
case Time::kTuesday: return "TU";
case Time::kWednesday: return "WE";
case Time::kThursday: return "TH";
case Time::kFriday: return "FR";
case Time::kSaturday: return "SA";
}

void displayLocalTime() {
  time_t localT = localTime();
  char buf[20];
  sprintf(buf, "A%02d:%02d:%02d%s%02d-%02d-%02d", hour(localT), minute(localT), second(localT), dayAsString(localT), day(localT), month(localT), year(localT));
  lcd.setCursor(0, 0);  // First row
  lcd.print(buf);
}

void displayUTC() {
  time_t utcT = now();
  char buf[20];
  sprintf(buf, "u%02d:%02d:%02d%s%02d-%02d-%02d", hour(utcT), minute(utcT), second(utcT), dayAsString(utcT), day(utcT), month(utcT), year(utcT));
  lcd.setCursor(0, 1);  // Second row
  lcd.print(buf);
}

time_t timeSync() {
  tmElements_t tm;
  unsigned long fixAge;
  int yr;
  
  gpsLocked = false;
  gps.crack_datetime(&yr, &tm.Month, &tm.Day, &tm.Hour, &tm.dayAsString, &tm.Minute, &tm.Second, NULL, &fixAge);
  
  if (fixAge == gps::GPS_INVALID_FIX_TIME) {
    Serial.println("No GPS fix, using RTC");
    return rtc ();
  } else if (fixAge > 2000) {
    Serial.println("GPS offline, using RTC");
    return rtc ();
  } else {
    gpsLocked = true;
    tm.Year = yr - 1970;
    time_t t = makeTime(tm);
    return t;
  }
}

// Time adjustment for DST
time_t localTime() {
  time_t t = now();
  if updateDST t += SECS_PER_HOUR;
  return t + timeZone * SECS_PER_HOUR;
}

Errors:

Arduino: 1.8.19 (Linux), Board: "Arduino Uno"

Warning: platform.txt from core 'Arduino AVR Boards' contains deprecated compiler.path={runtime.tools.avr-gcc.path}/bin/, automatically converted to compiler.path=/usr/bin/. Consider upgrading this core.
clock:12:38: error: expected ‘,’ or ‘...’ before numeric constant
const VMA430_GPS gps (SoftwareSerial 0, 1);
^
/home/brycow/Arduino/clock/clock.ino: In function ‘void setup()’:
/home/brycow/Arduino/clock/clock.ino:25:18: warning: passing ‘const LiquidCrystal’ as ‘this’ argument discards qualifiers [-fpermissive]
lcd.begin(20, 2); // 20x2 LCD initialization
^
In file included from /home/brycow/Arduino/clock/clock.ino:5:0:
/home/brycow/Arduino/libraries/LiquidCrystal-1.0.7/src/LiquidCrystal.h:62:8: note: in call to ‘void LiquidCrystal::begin(uint8_t, uint8_t, uint8_t)’
void begin(uint8_t cols, uint8_t rows, uint8_t charsize = LCD_5x8DOTS);
^
clock:27:7: error: request for member ‘begin’ in ‘gps’, which is of non-class type ‘const VMA430_GPS(SoftwareSerial)’
gps.begin(9600); // Initialize GPS at 9600 baud
^
/home/brycow/Arduino/clock/clock.ino:28:25: warning: passing ‘const DS1302’ as ‘this’ argument discards qualifiers [-fpermissive]
rtc.writeProtect(false); // Allow RTC writes
^
In file included from /home/brycow/Arduino/clock/clock.ino:4:0:
/home/brycow/Arduino/libraries/arduino-ds1302-master/DS1302.h:78:8: note: in call to ‘void DS1302::writeProtect(bool)’
void writeProtect(bool enable);
^
/home/brycow/Arduino/clock/clock.ino:29:17: warning: passing ‘const DS1302’ as ‘this’ argument discards qualifiers [-fpermissive]
rtc.halt(false); // Start the RTC
^
In file included from /home/brycow/Arduino/clock/clock.ino:4:0:
/home/brycow/Arduino/libraries/arduino-ds1302-master/DS1302.h:93:8: note: in call to ‘void DS1302::halt(bool)’
void halt(bool value);
^
/home/brycow/Arduino/clock/clock.ino:33:13: warning: passing ‘const DS1302’ as ‘this’ argument discards qualifiers [-fpermissive]
rtc.time(t); // Set the RTC time
^
In file included from /home/brycow/Arduino/clock/clock.ino:4:0:
/home/brycow/Arduino/libraries/arduino-ds1302-master/DS1302.h:109:8: note: in call to ‘void DS1302::time(Time)’
void time(Time t);
^
/home/brycow/Arduino/clock/clock.ino: In function ‘void loop()’:
clock:40:14: error: request for member ‘available’ in ‘gps’, which is of non-class type ‘const VMA430_GPS(SoftwareSerial)’
while (gps.available()) {
^
clock:41:17: error: request for member ‘read’ in ‘gps’, which is of non-class type ‘const VMA430_GPS(SoftwareSerial)’
int c = gps.read();
^
clock:42:9: error: request for member ‘encode’ in ‘gps’, which is of non-class type ‘const VMA430_GPS(SoftwareSerial)’
gps.encode(c);
^
clock:47:15: error: ‘updateDST’ was not declared in this scope
updateDST(); // Update DST based on the current date
^
/home/brycow/Arduino/clock/clock.ino: In function ‘void setup()’:
clock:56:6: error: redefinition of ‘void setup()’
void setup () {
^
/home/brycow/Arduino/clock/clock.ino:24:6: note: ‘void setup()’ previously defined here
void setup() {
^
clock:58:1: error: expected initializer before ‘switch’
switch (day)
^
clock:60:1: error: case label ‘kMonday’ not within a switch statement
case Time::kMonday: return "MO";
^
/home/brycow/Arduino/clock/clock.ino:60:28: warning: return-statement with a value, in function returning 'void' [-fpermissive]
case Time::kMonday: return "MO";
^
clock:61:1: error: case label ‘kTuesday’ not within a switch statement
case Time::kTuesday: return "TU";
^
/home/brycow/Arduino/clock/clock.ino:61:29: warning: return-statement with a value, in function returning 'void' [-fpermissive]
case Time::kTuesday: return "TU";
^
clock:62:1: error: case label ‘kWednesday’ not within a switch statement
case Time::kWednesday: return "WE";
^
/home/brycow/Arduino/clock/clock.ino:62:31: warning: return-statement with a value, in function returning 'void' [-fpermissive]
case Time::kWednesday: return "WE";
^
clock:63:1: error: case label ‘kThursday’ not within a switch statement
case Time::kThursday: return "TH";
^
/home/brycow/Arduino/clock/clock.ino:63:30: warning: return-statement with a value, in function returning 'void' [-fpermissive]
case Time::kThursday: return "TH";
^
clock:64:1: error: case label ‘kFriday’ not within a switch statement
case Time::kFriday: return "FR";
^
/home/brycow/Arduino/clock/clock.ino:64:28: warning: return-statement with a value, in function returning 'void' [-fpermissive]
case Time::kFriday: return "FR";
^
clock:65:1: error: case label ‘kSaturday’ not within a switch statement
case Time::kSaturday: return "SA";
^
/home/brycow/Arduino/clock/clock.ino:65:30: warning: return-statement with a value, in function returning 'void' [-fpermissive]
case Time::kSaturday: return "SA";
^
/home/brycow/Arduino/clock/clock.ino: In function ‘void displayLocalTime()’:
clock:71:115: error: ‘dayAsString’ was not declared in this scope
sprintf(buf, "A%02d:%02d:%02d%s%02d-%02d-%02d", hour(localT), minute(localT), second(localT), dayAsString(localT), day(localT), month(localT), year(localT));
^
/home/brycow/Arduino/clock/clock.ino:72:21: warning: passing ‘const LiquidCrystal’ as ‘this’ argument discards qualifiers [-fpermissive]
lcd.setCursor(0, 0); // First row
^
In file included from /home/brycow/Arduino/clock/clock.ino:5:0:
/home/brycow/Arduino/libraries/LiquidCrystal-1.0.7/src/LiquidCrystal.h:82:8: note: in call to ‘void LiquidCrystal::setCursor(uint8_t, uint8_t)’
void setCursor(uint8_t, uint8_t);
^
/home/brycow/Arduino/clock/clock.ino:73:16: warning: passing ‘const LiquidCrystal’ as ‘this’ argument discards qualifiers [-fpermissive]
lcd.print(buf);
^
In file included from /home/brycow/.arduino15/packages/arduino/hardware/avr/1.8.6/cores/arduino/Stream.h:26:0,
from /home/brycow/.arduino15/packages/arduino/hardware/avr/1.8.6/cores/arduino/HardwareSerial.h:29,
from /home/brycow/.arduino15/packages/arduino/hardware/avr/1.8.6/cores/arduino/Arduino.h:233,
from /home/brycow/Arduino/clock/clock.ino:2:
/home/brycow/.arduino15/packages/arduino/hardware/avr/1.8.6/cores/arduino/Print.h:67:12: note: in call to ‘size_t Print::print(const char*)’
size_t print(const char[]);
^
/home/brycow/Arduino/clock/clock.ino: In function ‘void displayUTC()’:
clock:79:107: error: ‘dayAsString’ was not declared in this scope
sprintf(buf, "u%02d:%02d:%02d%s%02d-%02d-%02d", hour(utcT), minute(utcT), second(utcT), dayAsString(utcT), day(utcT), month(utcT), year(utcT));
^
/home/brycow/Arduino/clock/clock.ino:80:21: warning: passing ‘const LiquidCrystal’ as ‘this’ argument discards qualifiers [-fpermissive]
lcd.setCursor(0, 1); // Second row
^
In file included from /home/brycow/Arduino/clock/clock.ino:5:0:
/home/brycow/Arduino/libraries/LiquidCrystal-1.0.7/src/LiquidCrystal.h:82:8: note: in call to ‘void LiquidCrystal::setCursor(uint8_t, uint8_t)’
void setCursor(uint8_t, uint8_t);
^
/home/brycow/Arduino/clock/clock.ino:81:16: warning: passing ‘const LiquidCrystal’ as ‘this’ argument discards qualifiers [-fpermissive]
lcd.print(buf);
^
In file included from /home/brycow/.arduino15/packages/arduino/hardware/avr/1.8.6/cores/arduino/Stream.h:26:0,
from /home/brycow/.arduino15/packages/arduino/hardware/avr/1.8.6/cores/arduino/HardwareSerial.h:29,
from /home/brycow/.arduino15/packages/arduino/hardware/avr/1.8.6/cores/arduino/Arduino.h:233,
from /home/brycow/Arduino/clock/clock.ino:2:
/home/brycow/.arduino15/packages/arduino/hardware/avr/1.8.6/cores/arduino/Print.h:67:12: note: in call to ‘size_t Print::print(const char*)’
size_t print(const char[]);
^
/home/brycow/Arduino/clock/clock.ino: In function ‘time_t timeSync()’:
clock:90:7: error: request for member ‘crack_datetime’ in ‘gps’, which is of non-class type ‘const VMA430_GPS(SoftwareSerial)’
gps.crack_datetime(&yr, &tm.Month, &tm.Day, &tm.Hour, &tm.dayAsString, &tm.Minute, &tm.Second, NULL, &fixAge);
^
clock:90:61: error: ‘struct tmElements_t’ has no member named ‘dayAsString’
gps.crack_datetime(&yr, &tm.Month, &tm.Day, &tm.Hour, &tm.dayAsString, &tm.Minute, &tm.Second, NULL, &fixAge);
^
clock:92:17: error: ‘gps’ is not a class, namespace, or enumeration
if (fixAge == gps::GPS_INVALID_FIX_TIME) {
^
clock:94:17: error: no match for call to ‘(const DS1302) ()’
return rtc ();
^
clock:97:17: error: no match for call to ‘(const DS1302) ()’
return rtc ();
^
/home/brycow/Arduino/clock/clock.ino: In function ‘time_t localTime()’:
clock:109:6: error: expected ‘(’ before ‘updateDST’
if updateDST t += SECS_PER_HOUR;
^
exit status 1
expected ‘,’ or ‘...’ before numeric constant

=========
ugh...