UDP NTP Client - NIST Internet Time Service changes

The IP that is used in the UDP NTP Client is going to be abandoned Dec 17th.

I found out about it by chance, I happened to stumble across this page: NIST Internet Time Service. The nice thing about the page is it give a long list of other IP addresses that can be used and their current status.

Merry Christmas!
Jason

this message can just in time :wink:

Ja, I found using the NTP time server was already giving me some hic-cups recently and looked to use another IP.
Maybe there needs to be some information regarding this in the documentation for the NTP client.
Maybe a hint on how or where to look for suitable NTP servers.

Paul.

rockwallaby:
Ja, I found using the NTP time server was already giving me some hic-cups recently and looked to use another IP.
Maybe there needs to be some information regarding this in the documentation for the NTP client.
Maybe a hint on how or where to look for suitable NTP servers.

Presumably it needs to do a real domain lookup instead of hard-coding the IP address. The DnsWebClient (http://arduino.cc/en/Tutorial/DnsWebClient) contains code that uses the name lookup service for TCP, but I didn't see any support for doing a DNS query via UDP.

It is rather unfriendly that libraries like this use static IP addresses. Using the domain name service allows the NIST (or other organizations) to use round robin scheduling to distribute the load among servers.

You can do a dns query for pool.ntp.org to get a current NTP ip. Here is the code I used to get that ip address. It is based on the DHCPAddressPrinter sketch.

/*
  DHCP-based IP printer
 
 This sketch uses the DHCP extensions to the Ethernet library
 to get an IP address via DHCP and print the address obtained.
 using an Arduino Wiznet Ethernet shield. 
 
 Circuit:
 * Ethernet shield attached to pins 10, 11, 12, 13
 
 created 12 April 2011
 by Tom Igoe
 
 */

#include <SPI.h>
#include <Ethernet.h>
#include <Dns.h>

// Enter a MAC address for your controller below.
// Newer Ethernet shields have a MAC address printed on a sticker on the shield
byte mac[] = { 0x00, 0xAA, 0xBB, 0xCC, 0xDE, 0x02 };

// Initialize the Ethernet client library
// with the IP address and port of the server 
// that you want to connect to (port 80 is default for HTTP):
EthernetClient client;

void setup() {
  // start the serial library:
  Serial.begin(9600);

  pinMode(4,OUTPUT);
  digitalWrite(4,HIGH);

  // start the Ethernet connection:
  if (Ethernet.begin(mac) == 0) {
    Serial.println("Failed to configure Ethernet using DHCP");
    // no point in carrying on, so do nothing forevermore:
    for(;;)
      ;
  }
  // print your local IP address:
  Serial.print("My IP address: ");
  for (byte thisByte = 0; thisByte < 4; thisByte++) {
    // print the value of each byte of the IP address:
    Serial.print(Ethernet.localIP()[thisByte], DEC);
    Serial.print("."); 
  }
  Serial.println();
  IPAddress testIP;

  DNSClient dns;
  dns.begin(Ethernet.dnsServerIP());
  dns.getHostByName("pool.ntp.org",testIP);
  Serial.print("NTP IP from the pool: ");
  Serial.println(testIP);
  
}

void loop() {
}

Thanks SurferDude,
I will look at putting that code into my project.

Will this patch be included before the next release for people, otherwise there could potentially be a flood of questions?

Paul

I modified the code to include error checking on the dns resolution. It should look like this:

  // you probably want this IP global
  IPAddress ntpIP;

  DNSClient dns;
  dns.begin(Ethernet.dnsServerIP());

  if(dns.getHostByName("pool.ntp.org",ntpIP)) {
    Serial.print("NTP IP from the pool: ");
    Serial.println(ntpIP);
  }
  else Serial.println("dns lookup failed");

edit: Renamed IP variable to ntpIP.

This is the UdpNtpClient example modified for dhcp and dns NTP server. Let me know if it works for you.

/*
 Udp NTP Client with DHCP and dns acquired NTP server IP
 
 Get the time from a Network Time Protocol (NTP) time server
 Demonstrates use of UDP sendPacket and ReceivePacket 
 For more on NTP time servers and the messages needed to communicate with them, 
 see http://en.wikipedia.org/wiki/Network_Time_Protocol
 
 created 4 Sep 2010 
 by Michael Margolis
 modified 17 Sep 2010
 by Tom Igoe
 modified 19 Dec 2012
 by Tim Dicus
 
 This code is in the public domain.
 */
#include <SPI.h>         
#include <Ethernet.h>
#include <EthernetUdp.h>
#include <Dns.h>

// Enter a MAC address for your controller below.
// Newer Ethernet shields have a MAC address printed on a sticker on the shield
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };

unsigned int localPort = 8888;      // local port to listen for UDP packets

IPAddress timeServer; // pool.ntp.org NTP server

const int NTP_PACKET_SIZE= 48; // NTP time stamp is in the first 48 bytes of the message

byte packetBuffer[ NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets 

// A UDP instance to let us send and receive packets over UDP
EthernetUDP Udp;

char weekday[7][4] = {"THU","FRI","SAT","SUN","MON","TUE","WED"};

void setup() 
{
  Serial.begin(9600);

  // disable SD SPI while starting w5100
  pinMode(4,OUTPUT);
  digitalWrite(4,HIGH);
  
  Serial.print(F("Starting w5100..."));
  if(!Ethernet.begin(mac))
  {
    Serial.println(F("failed"));
    while(1);    
  }
  else {
   Serial.println(F("ok"));
   Serial.print(F("ip = "));
   Serial.println(Ethernet.localIP());
  }
  
  Udp.begin(localPort);

  DNSClient dns;
  dns.begin(Ethernet.dnsServerIP());
  
  if(dns.getHostByName("pool.ntp.org",timeServer)) {
    Serial.print(F("NTP server ip :"));
    Serial.println(timeServer);
  }
  else Serial.print(F("dns lookup failed"));

  Serial.println(F("Ready"));
}

void loop()
{
  sendNTPpacket(timeServer); // send an NTP packet to a time server

    // wait to see if a reply is available
  delay(1000);  
  if ( Udp.parsePacket() ) {  
    // We've received a packet, read the data from it
    Udp.read(packetBuffer,NTP_PACKET_SIZE);  // read the packet into the buffer

    //the timestamp starts at byte 40 of the received packet and is four bytes,
    // or two words, long. First, esxtract the two words:

    unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
    unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);  
    // combine the four bytes (two words) into a long integer
    // this is NTP time (seconds since Jan 1 1900):
    Serial.print(packetBuffer[40],DEC);
    Serial.print(" ");
    Serial.print(packetBuffer[41],DEC);
    Serial.print(" ");
    Serial.print(packetBuffer[42],DEC);
    Serial.print(" ");
    Serial.println(packetBuffer[43],DEC);
    
    unsigned long secsSince1900 = highWord << 16 | lowWord;  
    Serial.print(F("Seconds since Jan 1 1900 = " ));
    Serial.println(secsSince1900);               

    // now convert NTP time into everyday time:
    Serial.print(F("Unix time = "));
    // Unix time starts on Jan 1 1970. In seconds, that's 2208988800:
    const unsigned long seventyYears = 2208988800UL;     
    // subtract seventy years:
    unsigned long epoch = secsSince1900 - seventyYears;  
    // print Unix time:
    Serial.println(epoch);                               

    unsigned long dayCount = epoch / 86400UL;
    Serial.print(F("DayCount = "));
    Serial.println(dayCount);

    Serial.print(F("Day of week = "));
    Serial.println(weekday[dayCount%7UL]);    

    // print the hour, minute and second:
    Serial.print(F("The UTC time is "));       // UTC is the time at Greenwich Meridian (GMT)
    Serial.print((epoch  % 86400L) / 3600); // print the hour (86400 equals secs per day)
    Serial.print(':');  
    if ( ((epoch % 3600) / 60) < 10 ) {
      // In the first 10 minutes of each hour, we'll want a leading '0'
      Serial.print('0');
    }
    Serial.print((epoch  % 3600) / 60); // print the minute (3600 equals secs per minute)
    Serial.print(':'); 
    if ( (epoch % 60) < 10 ) {
      // In the first 10 seconds of each minute, we'll want a leading '0'
      Serial.print('0');
    }
    Serial.println(epoch %60); // print the second
  }

  // wait another 599 seconds (one second delay was above)  10 minutes
  for(int y=0;y<599;y++)
  {
    // count off the minutes
    if(y%60 == 0) Serial.println(y/60);
    delay(1000); 
  }
  Serial.println();
}

// send an NTP request to the time server at the given address 
unsigned long sendNTPpacket(IPAddress& address)
{
  // set all bytes in the buffer to 0
  memset(packetBuffer, 0, NTP_PACKET_SIZE); 
  // Initialize values needed to form NTP request
  // (see URL above for details on the packets)
  packetBuffer[0] = 0b11100011;   // LI, Version, Mode
  packetBuffer[1] = 0;     // Stratum, or type of clock
  packetBuffer[2] = 6;     // Polling Interval
  packetBuffer[3] = 0xEC;  // Peer Clock Precision
  // 8 bytes of zero for Root Delay & Root Dispersion
  packetBuffer[12]  = 49; 
  packetBuffer[13]  = 0x4E;
  packetBuffer[14]  = 49;
  packetBuffer[15]  = 52;

  // all NTP fields have been given values, now
  // you can send a packet requesting a timestamp: 		   
  Udp.beginPacket(address, 123); //NTP requests are to port 123
  Udp.write(packetBuffer,NTP_PACKET_SIZE);
  Udp.endPacket(); 
}

edit: Corrected time delay loop again. It counts off the minutes and checks time every 10 minutes (was 1 minute). I was on to the second after 10 minutes.

SurferTim:
edit: Corrected time delay loop. It checks time every minute.

From an internet perspective, you really should not be polling the NTP server every minute. If every device on the internet does this, it will eventually mean people will stop offering NTP servers, and the infrastructure will be clogged with all of these packets to the remaining hosts offering the service.

Also, if you pay for your internet as a metered service, having your device continually polling the NTP server will mean you will get pushed over your limit and either it will slow down your connection, it will start charging you more, or cut off service all together (I've seen all 3 in cell phone contracts).

Poll once when you start up, and then keep local time, and poll every so often (every few hours perhaps) to adjust the time. In terms of keeping local time, perhaps getting one of the real time clocks, and use that. Use NTP occasionally to update the clock.

Also, having the delays in the code means your device can't do anything else while it is waiting for a response, unless you are using interrupts. I do wish the Arduino library provided a standard way of doing blink without delay that all of the library functions used.

Hi Michael. I agree. I used that only as a test. This should really be used with a RTC, and checked every hour or so. The real purpose was to show how close I could come to the correct time just using delay(). The millis routine would probably be even better (more accurate?). It appears the original code may have checked even more often that this. :frowning:

If this works for most, I will put this code in the wiki with a longer delay and a warning to that effect.

Also, having the delays in the code means your device can't do anything else while it is waiting for a response, unless you are using interrupts.

Not quite true. That is why I divide that 59 second time up into 1 second delays. A switch-case on the loop counter works well for "time slicing".

edit: I didn't like the one minute thing. I changed it to check every 10 minutes, and count off the minutes since the last check.

When I try to access this url http://arduino.cc/en/Tutorial/UdpNtpClient, I get a 403 Forbidden error.
Is that just me? temporary messed apache setup ? I don't know where I should post this problem.
I have the same message while trying to access documents in the http://arduino.cc/en/Learning/ sections.

It appears the playground is having technical difficulties. I can't access anything there either. Hopefully they will get it fixed soon.

yes, temporary problem :slight_smile: