Internet Clock

Posted: August 26, 2012 in Arduino, Software development

This experiment uses the an Ethernet shield, real-time clock and LCD modules. The Arduino will get the current time from NTP on an hourly basis. The NTP return value is translated to Unix (POSIX) time. This value is adjusted to for the time-zone  and used to set the real-time clock. Unfortunately, the time zone offset is hard-coded. Eventually, this value can also be obtained from the Internet. The LCD will display the time and date, every second. The hardware includes a DS1307 clock module, which is connected to analog ports 4 and 5. The Ethernet shield uses DHCP to obtain an IP address. It will send an NTP UDP request whenever the current hour is not equal to the previous hour. Finally, the clock output is sent to both the serial interface and the LCD. The clock and Internet functions form the basis for many types of automation.  The Ethernet shield and the LCD can conflict. It was necessary to move the LCD connections, until they no longer caused a conflict with the Ethernet.

Components:


/*
* Internet Clock using NTP and LCD display module
* William Beckett (c) 2012
* RightAResearch.com
*
*/

#include <LiquidCrystal.h>
#include <Time.h>
#include <Wire.h>
#include <DS1307RTC.h>  // a basic DS1307 library that returns time as a time_t
#include <SPI.h>
#include <Ethernet.h>
#include <EthernetUDP.h>

// Connections:
// changed based on Ethernet conflicts
// rs (LCD pin 4) to Arduino pin 9
// rw (LCD pin 5) to Arduino pin 8
// enable (LCD pin 6) to Arduino pin 7
// LCD pin 15 to Arduino pin 6
// LCD pins d4, d5, d6, d7 to Arduino pins 5, 4, 3, 2
LiquidCrystal lcd(9, 8, 7, 5, 4, 3, 2);

int backLight = 6;    // pin 6 will control the backlight

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };  // MAC address to use

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

IPAddress timeServer(192, 43, 244, 18); // time.nist.gov 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/outgoing packets

const  long timeZoneOffset = -14400L;
boolean pm=false;

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

int lasthour=0;
int currenthour=-1;

void setup()
{
pinMode(backLight, OUTPUT);
digitalWrite(backLight,HIGH); // turn backlight on. Replace ‘HIGH’ with ‘LOW’ to turn it off.
lcd.begin(16,4);              // columns, rows.
lcd.clear();                  // start with a blank screen
lcd.setCursor(0,0);           // set cursor to column 0, row 0 (the first row)
lcd.print(“RightAResearch.com”);
lcd.setCursor(0,1);           // set cursor to column 0, row 1
lcd.print(” “);

// if you have a 4 row LCD, uncomment these lines to write to the bottom rows
// and change the lcd.begin() statement above.
lcd.setCursor(0,2);         // set cursor to column 0, row 2
lcd.print(” “);
lcd.setCursor(0,3);         // set cursor to column 0, row 3
lcd.print(” “);

Serial.begin(9600);
setSyncProvider(RTC.get);   // the function to get the time from the RTC

if (Ethernet.begin(mac) == 0) {
Serial.println(“Failed to configure Ethernet using DHCP”);
}

}

void loop()
{

if (lasthour != currenthour)
getSetTime();             // get NTP time

digitalClockDisplay();
currenthour=hour();
delay(1000);
}

void getSetTime()
{
lcd.setCursor (0,1);
lcd.print (“Getting NTP Time”);
Udp.begin(localPort);
time_t t = getUNIXTime();

if(t >0)
{
RTC.set(t);   // set the RTC and the system time to the received value
setTime(t);
}

lcd.setCursor (0,1);
lcd.print (“Set NTP Time      “);
lasthour=hour();
currenthour=hour();

}

void digitalClockDisplay(){
// digital clock display of the time

int h=hour();
if (h> 12) {
h -= 12;
pm=true;
}
else if (h ==12) {
if (!pm)
pm=true;
else
pm=false;
}
lcd.setCursor (0,1);
lcd.print (”                “);
Serial.print(h);
printDigits(minute());
printDigits(second());
Serial.print(” “);
Serial.print(day());
Serial.print(” “);
Serial.print(month());
Serial.print(” “);
Serial.print(year());
Serial.println();
lcd.setCursor (3,2);
lcdPrintDigits (h);
lcd.print (“:”);
lcdPrintDigits (minute());
lcd.print (“:”);
lcdPrintDigits (second());
if (pm)
lcd.print (” pm”);
else
lcd.print (” am”);
lcd.setCursor (0,3);
lcd.print (dayShortStr (weekday()));
lcd.print (” “);
lcd.print (monthShortStr(month()));
lcd.print (” “);
lcd.print (day());
lcd.print (“, “);
lcd.print (year());
}

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);
}

void lcdPrintDigits(int digits){
// utility function for digital clock display: prints
// preceding colon and leading 0

if(digits < 10)
lcd.print(‘0′);

lcd.print(digits);
}

unsigned long getUNIXTime()
{

unsigned long epoch=0;
sendNTPpacket(timeServer); // send an NTP packet to a time server
// wait to see if a reply is available
delay(1000);
if ( Udp.parsePacket() ) {
Udp.read(packetBuffer,NTP_PACKET_SIZE);  // read packet into buffer

//the timestamp starts at byte 40, convert four bytes into a long integer
unsigned long hi = word(packetBuffer[40], packetBuffer[41]);
unsigned long low = word(packetBuffer[42], packetBuffer[43]);
unsigned long secsSince1900 = hi << 16 | low;  // this is NTP time
// (seconds since Jan 1 1900)

Serial.print(“Seconds since Jan 1 1900 = ” );
Serial.println(secsSince1900);

Serial.print(“Unix time = “);
// Unix time starts on Jan 1 1970
const unsigned long seventyYears = 2208988800UL;
epoch = secsSince1900 – seventyYears + timeZoneOffset;  // subtract 70 years
Serial.println(epoch);                               // print Unix time

// print the hour, minute and second:
// UTC is the time at Greenwich Meridian (GMT)
Serial.print(“The UTC time is “);
// print the hour (86400 equals secs per day)
Serial.print((epoch  % 86400L) / 3600);
Serial.print(‘:’);
if ( ((epoch % 3600) / 60) < 10 ) {
// Add leading zero for the first 10 minutes of each hour
Serial.print(‘0′);
}
// print the minute (3600 equals secs per minute)
Serial.print((epoch  % 3600) / 60);
Serial.print(‘:’);
if ( (epoch % 60) < 10 ) {
// Add leading zero for the first 10 seconds of each minute
Serial.print(‘0′);
}
Serial.println(epoch %60); // print the second
}

return epoch;
}

// send an NTP request to the time server at the given address
unsigned long sendNTPpacket(IPAddress& address)
{

memset(packetBuffer, 0, NTP_PACKET_SIZE);  // set all bytes in the buffer to 0

// Initialize values needed to form NTP request
packetBuffer[0] = B11100011;   // LI, Version, Mode
packetBuffer[1] = 0;     // Stratum
packetBuffer[2] = 6;     // Max Interval between messages in seconds
packetBuffer[3] = 0xEC;  // Clock Precision
// bytes 4 – 11 are for Root Delay and Dispersion and were set to 0 by memset
packetBuffer[12]  = 49;  // four byte reference ID identifying
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();
}

 

About these ads

Comments are closed.