My Clock is Losing Time...

So I know we need a RTC to keep time correctly but can it be done using just code and NTP? Also if not any idea on what I need to build one?

I have included the code for my clock as I would really appreciate some feedback. My clock seemed to be ok it got an IP via DHCP then got the time via NTP but over time it ends up behind about an hour... So not very good...

Any help would be very appreciated.

/*
 * NIXI CLOCK - DHCP & NTP
 *
 * NIXI CODE REMOVED
 *
 */

// Includes
#include <Time.h>
#include <Ethernet.h>
#include <EthernetUdp.h>
#include <SPI.h>

/****************************************************************************************************/

// *** Ethernet MAC Address
static byte mac[] = { 0x90, 0xA2, 0xDA, 0x00, 0x9B, 0xC1 }; 
// *** NTP Server Address
static IPAddress timeServer(10, 100, 150, 1);
// *** Time Zone
const int timeZone = -7;  // Pacific Daylight Time (USA)
// *** DST Offset Toggle
const int timeDST = 1;  // 1 = DST ON / 0 = DST OFF
// *** Misc Settings
const int NTP_PACKET_SIZE = 48; // NTP Packet Size
byte packetBuffer[NTP_PACKET_SIZE]; // NTP Buffer
time_t prevDisplay = 0; // NTP Status
const int ntpsync = 7200; // NTP Sync Interval

/****************************************************************************************************/

// DHCP Buffer
char ipbuf[21];

// DHCP Array
union IPAddressConverter {
  uint32_t ipInteger;
  uint8_t ipArray[4];
};
unsigned long dlastnow; // DHCP Renew
const int dhcplease = 7200; // DHCP Lease Time - 2 Hours
EthernetClient client; // Create Ethernet Instance
EthernetUDP Udp; // Create UDP Instance

/****************************************************************************************************/

void setup() 
{
  // ** Boot Serial Interface
  Serial.begin(9600); // Create Serial Interface Monitor
  // ** Boot Ethernet Interface
  Ethernet.begin(mac); // Assign MAC to NIC
  // ** Wait for Settings to Apply
  delay(500);
  Udp.begin(8888);   // Set UDP Port to 8888
  setSyncProvider(getNtpTime); // Fetch NTP Data
  setSyncInterval(ntpsync); // NTP Sync Timer
  setTime(hour() + timeDST,minute(),second(),day(),month(),year()); // Store NTP Data
}

void loop()
{  
  if (timeStatus() != timeNotSet) // Initialize on Time Change
  {
    if (now() != prevDisplay)
    {
      prevDisplay = now();
      //NixiTubeDisplay();
      SerialClockDisplay();
    }
    if (hour() == 16 && minute() == 20)   // 4:20 PM GREEN!
    {
      // Output Effect Here
    } else if (hour() == 16 && minute() == 21) {
      // Output Effect Here
    }
    if (hour() == 03 && minute() == 00)   // 3:00 AM RED!
    {
      // Output Effect Here
    } else if (hour() == 04 && minute() == 00) {
      // Output Effect Here
    }
    if (now() - dlastnow > dhcplease) dhcprenew(); // Initialize DHCP
  }
}

// Print Data to Serial Interface

void SerialClockDisplay(){
  Serial.print("Time / Date: ");
  printDigits(hour());
  Serial.print(":");
  printDigits(minute());
  Serial.print(":");
  printDigits(second());
  Serial.print("  ");
  printDigits(day());
  Serial.print(".");
  printDigits(month());
  Serial.print(".");
  Serial.println(year());
  Serial.println(ipbuf);
}

// Print Data to Nixi Tube Display

void NixiTubeDisplay(){
  
// Nixi Code Here

}

void printDigits(int digits)
{
  if(digits < 10) Serial.print('0');
  Serial.print(digits);
}

time_t getNtpTime()
{
  while (Udp.parsePacket() > 0) ;
  sendNTPpacket(timeServer);
  uint32_t beginWait = millis();
  while (millis() - beginWait < 1500)
  {
    int size = Udp.parsePacket();
    if (size >= NTP_PACKET_SIZE)
    {
      Udp.read(packetBuffer, NTP_PACKET_SIZE);
      unsigned long secsSince1900;

      secsSince1900 =  (unsigned long)packetBuffer[40] << 24;
      secsSince1900 |= (unsigned long)packetBuffer[41] << 16;
      secsSince1900 |= (unsigned long)packetBuffer[42] << 8;
      secsSince1900 |= (unsigned long)packetBuffer[43];
      return secsSince1900 - 2208988800UL + timeZone * SECS_PER_HOUR;
    }
  }
  return 0; 
}

// NTP Request Transmission
void sendNTPpacket(IPAddress &address)
{
  memset(packetBuffer, 0, NTP_PACKET_SIZE); // NTP Packet Buffer
  
  // NTP Packet Payload
  packetBuffer[0] = 0b11100011;
  packetBuffer[1] = 0;
  packetBuffer[2] = 6;
  packetBuffer[3] = 0xEC;
  packetBuffer[12]  = 49;
  packetBuffer[13]  = 0x4E;
  packetBuffer[14]  = 49;
  packetBuffer[15]  = 52;
  
  // Send Payload to NTP Server Port 123
  Udp.beginPacket(address, 123);
  Udp.write(packetBuffer, NTP_PACKET_SIZE);
  Udp.endPacket();
}
void dhcprenew()
{
  Ethernet.maintain();
  IPAddressConverter ipAddress;
  ipAddress.ipInteger = Ethernet.localIP();
  sprintf(ipbuf, "IP: %d.%d.%d.%d", ipAddress.ipArray[0], ipAddress.ipArray[1], ipAddress.ipArray[2], ipAddress.ipArray[3]);
  dlastnow=now();
}

I use NTP to get the current internet time both at start up and everyday at 6:27AM for an update.

I use the millis() time that I extracted time using NTP plus current millis() to calculate the current time whenever needed.

My system's time is never more than one or two seconds early or late as evidenced by the system time for the update that is recorded in my logs each day.

Cheers

catweazle NZ

I apologize but how would I apply this to my code exactly? This is one of my first projects using time and NTP.

On another note the time is right for a while. It seems fine for about an hour or so.. I was wondering if maybe I made a mistake in my programming?

Pyrex:

// *** Time Zone

const int timeZone = -7;  // Pacific Daylight Time (USA)
// *** DST Offset Toggle
const int timeDST = 1;  // 1 = DST ON / 0 = DST OFF

It looks like the problem is that you are compensating for Daylight Saving Time twice instead of just once.
You are using: -7 + 1 = -6
I think you want: -8 + 1 = -7

You are right... I am actually in Mountain Standard time which is -7 but we use Daylight savings time so recently our clocks jumped ahead an hour. I attempted to compensate by adding a toggle variable:

// *** DST Offset Toggle
const int timeDST = 1;  // 1 = DST ON / 0 = DST OFF

Which was then used to add or simply not add an hour to the set time function:

setTime(hour() + timeDST,minute(),second(),day(),month(),year()); // Store NTP Data

I know someone made a library for this but I could not seem to grasp how it worked so I tried and clearly failed to fix it myself. If someone could please help me maybe add a more automated version that works correctly into my code I would appreciate it very much.

Thank You

So, does you time error increase gradually, or does it jump to being an hour wrong ?

I am unsure of when it happens but last night I disabled the DST setting and simply set my timezone to -6 and it's been OK since however, I need a clean way to allow the clock to detect when we are in daylight savings time and adjust for it on it's own. I already can fetch the date from the NTP so I can use that to help but need some help coding it.