Notes About Working with Various Arduino & Netduino Microcontroller Boards

Friday, November 30, 2012

Setting the Chronodot via NTP with an Arduino Uno

Here is some code for the Arduino Uno with an Ethernet Shield that gets the current time from a network time server and sets the date and time of the Chronodot to match. 

The Chronodot's SDA is connected to Arduino Uno pin A4 and SCL to A5. Using 3.3V power, the SCL and SDA lines have 10k pull-up resistors. 

I have used an Uno to set the Chronodot's time and date because I can't get the Arduino Time library to work with the Arduino Due.  The error messages indicate a problem with multiple definitions of time_t. (I am using Arduino IDE 1.5.1 r2.).

The code for getting the time from an NTP server is derived from code by Michael Margolis and Tom Igoe.

#include <Wire.h>
#include <SPI.h>         
#include <Ethernet.h>
#include <EthernetUdp.h>
#include <Time.h>

// Enter MAC address for your Ethernet Shield
byte mac[] = { 0x90, 0xAD, 0xA2, 0xDA, 0x0D, 0x0b, 0xF6 };
// local port to listen for UDP packets
unsigned int localPort = 8888;      
// See http://tf.nist.gov/tf-cgi/servers.cgi for address list
IPAddress timeServer(96,47,67,105); 
// NTP time stamp is in the first 48 bytes of the message
const int NTP_PACKET_SIZE = 48;     
//buffer to hold incoming and outgoing packets
byte packetBuffer[NTP_PACKET_SIZE]; 
// A UDP instance to let us send and receive packets over UDP
EthernetUDP Udp;

// Holds seconds since midnight 1/1/1970
unsigned long epoch; 

// Holds formatted result from sprintf()
char buffer[25]; 

void setup()
{
  Wire.begin();
  Serial.begin(9600);
  // clear /EOSC bit
  // Sometimes necessary to ensure that the clock
  // keeps running on just battery power. Once set,
  // it shouldn't need to be reset but it's a good
  // idea to make sure.
  Wire.beginTransmission(0x68); // address DS3231
  Wire.write(0x0E);             // select register
  Wire.write(0b00011100);       // write register bitmap, bit 7 is /EOSC
  Wire.endTransmission();
    Serial.println("Starting Ethernet...");
  if (Ethernet.begin(mac) == 0) {
    Serial.println("Failed to configure Ethernet using DHCP");
    // no point in carrying on, so do nothing forevermore:
    while(1) { ; };
  }
  Serial.println("Starting UDP...");
  Udp.begin(localPort);
  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 -
    // read the packet into the buffer
    Udp.read(packetBuffer,NTP_PACKET_SIZE);  
    // 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 4 bytes (2 words) into a long integer.
    // This is NTP time (seconds since Jan 1 1900):
    unsigned long secsSince1900 = highWord << 16 | lowWord;  
    // Unix time starts on Jan 1 1970. In seconds, that's 2208988800:
    const unsigned long seventyYears = 2208988800UL;     
    // Subtract 70 years:
    epoch = secsSince1900 - seventyYears;
    // Subtract 6 hrs for Central Time - change as neeed
    epoch = epoch - 60 * 60 * 6;    
    setDate();
    setTime();
    getDateTime();
  }      
}

void loop() { }

// Convert binary coded decimal to decimal
byte bcdToDec(byte val)
{
  return ( (val/16*10) + (val%16) );
}

// Convert decimal to binary coded decimal
byte decToBcd(byte val)
{
  return ( (val/10*16) + (val%10) );
}

void getDateTime() {
  // Send request to receive data starting at register 0
  Wire.beginTransmission(0x68); // 0x68 is DS3231 device address
  Wire.write(0);                // start at register 0
  Wire.endTransmission();
  Wire.requestFrom(0x68, 7);    // request 7 bytes (seconds, minutes, hours, day, 
                                // date, month, year)
  while(Wire.available())
  { 
    int seconds = bcdToDec(Wire.read()); 
    int minutes = bcdToDec(Wire.read()); 
    int hours = bcdToDec(Wire.read());   
    int day = bcdToDec(Wire.read());    // Not used in example
    int date = bcdToDec(Wire.read());
    int month = bcdToDec(Wire.read());
    int year = bcdToDec(Wire.read());
    // Format code %02d = 2-digit base-10 number with leading 0 
    // if needed for padding
    sprintf(buffer, "%02d/%02d/%02d %02d:%02d:%02d", month, date, year, hours, 
      minutes, seconds);        
    Serial.println(buffer);   
  }
}

void setDate()
{
  Wire.beginTransmission(0x68);
  Wire.write(3);
  Wire.write(decToBcd(weekday(epoch)));
  Wire.write(decToBcd(day(epoch)));
  Wire.write(decToBcd(month(epoch)));
  Wire.write(decToBcd(year(epoch)-2000));
  Wire.endTransmission();
}

void setTime()
{
   Wire.beginTransmission(0x68);
   Wire.write(0);
   Wire.write(decToBcd(second(epoch)));
   Wire.write(decToBcd(minute(epoch)));
   // Adjust offset from GMT as needed
   Wire.write(decToBcd(hour(epoch)));
   Wire.endTransmission();
}

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

No comments:

Post a Comment