If you’ve got an Arduino and an ethernet connection, you can use one of the many public Network Time Protocol (NTP) servers to find out the current time. This can remove the need for adding a realtime clock module to your project, as you can check the time in your initial setup and then re-check (and reset) at regular intervals to account for any vagueness in the inbuilt millis() based timekeeping. As it’s all I’ve got, my comments here relate to using the EtherCard library with an ENC28J60-based shield – if you’re using an Arduino Ethernet, for example, you’ll be using a different library (but hey, at least the principle stays the same).

An NTP server tells you the time in UTC and from there, you need simply add on any desired offset for your local timezone and you’re sorted. Well, almost; on an Arduino, time is relative to 1st January 1970, but NTP time is relative to 1st January 1900, so you need to remember to deduct 70 years from the answer returned by the NTP server. It’s no biggie, it’d just be confusing if you didn’t know why it was happening, but luckily others have been there before us. As an example;

SecondsDate / Time
UTC from NTP server in seconds since 1/1/1900354922244720/06/2012 23:07:25
Deduct seventy years (don’t forget the leap years!)-2208988800
Add on British Summer Time offset (+1 hour)3600
Equals a local time of134023724721/06/2012 00:07:25

To see how we can accomplish this in code, I’ve combined Andrew Lindsay’s example referenced above with an example from the Time library, sprinkled in a function from this gist and used Jack Christensen‘s TimeZone library to calculate the local time taking the relevant timezone offset into account (rather than have to laboriously hand code something to see if we’re in British Summer Time or not). The upshot is that our Arduino now has the correct time …

Arduino time set from NTP server

The magic happens in the getNtpTime() function and the output from that is used to set the system clock Time library’s internal counter on line 35. The call to debug() then prints out a quick message, but more importantly, shows the current system time.

To remain polite to all the people hosting NTP servers, convention says that one should keep a list of a few and spread your request traffic amongst them and Andy’s code gives a good example of how to do this – I’ve just used a fixed IP here for the sake of brevity.  Remember, this is just an example – to get fancy, add your own code to reset the clock once a day, maybe. If you come up with any other ideas about how it could be used or improved, add a comment.

#include <EtherCard.h>      // https://github.com/jcw/ethercard
#include <Time.h>           // http://www.arduino.cc/playground/Code/Time
#include <Timezone.h>       // https://github.com/JChristensen/Timezone

// EtherCard
byte mymac[] = { 0x74,0x69,0x69,0x2D,0x30,0x31 };
char website[] PROGMEM = "blog.cuyahoga.co.uk";
byte Ethernet::buffer[700];

// TimeZone : United Kingdom (London, Belfast)
TimeChangeRule BST = {"BST", Last, Sun, Mar, 1, 60};        //British Summer Time
TimeChangeRule GMT = {"GMT", Last, Sun, Oct, 2, 0};         //Standard Time
Timezone UK(BST, GMT);

void setup () {
  Serial.begin(57600);
  Serial.println("\n[NTP test]");

  if (ether.begin(sizeof Ethernet::buffer, mymac, 10) == 0) {
    Serial.println( "Failed to access Ethernet controller");
  }
  if (!ether.dhcpSetup()) {
    Serial.println("DHCP failed");
  }

  ether.printIp("Allocated IP: ", ether.myip);
  ether.printIp("Gateway IP  : ", ether.gwip);  
  ether.printIp("DNS IP      : ", ether.dnsip);  

  if (!ether.dnsLookup(website)) {
    Serial.println("DNS failed");
  }
  ether.printIp("Lookup IP   : ", ether.hisip);

  setTime(getNtpTime());

  debug("Quick time check...");
}

void loop () {
  ether.packetLoop(ether.packetReceive());

}

void debug(char* message) {
  printTime();
  Serial.print(" : ");
  Serial.println(message);
}

void printTime() {
  Serial.print(day());
  Serial.print("/");
  Serial.print(month());
  Serial.print("/");
  Serial.print(year()); 
  Serial.print(" ");
  Serial.print(hour());
  printDigits(minute());
  printDigits(second());
}

void printDigits(int digits){
  // utility function for digital clock display: prints preceding colon and leading 0
  Serial.print(":");
  if(digits < 10)
    Serial.print('0');   
  Serial.print(digits);
}

uint8_t ntpServer[4] = { 130, 159, 196, 117 }; 
uint8_t ntpMyPort = 123; 
unsigned long getNtpTime() {   
  unsigned long timeFromNTP;   
  const unsigned long seventy_years = 2208988800UL;      

  ether.ntpRequest(ntpServer, ntpMyPort);   
  while(true) {       
    word length = ether.packetReceive();       
    ether.packetLoop(length);       
    if(length > 0 && ether.ntpProcessAnswer(&timeFromNTP, ntpMyPort)) {
      Serial.print("Time from NTP: ");
      Serial.println(timeFromNTP);
      return UK.toLocal(timeFromNTP - seventy_years);
    }
  }
  return 0;
}