Tuesday, August 20, 2019

Arduino Eithernet Shield 2 NTP Time

I'm trying to get an accurate time on an Arduino. I have an Ethernet Shield 2 attached to an Arduino Mega. There's also a couple current sensors and a barcode scanner. The main reason for this post is for those people that happen to have the Ethernet Shield 2 on any Arduino, and want to get the time. Every single example on the internet or in the Arduino IDE examples doesn't work, except for this one:

https://www.arduino.cc/en/Tutorial/UdpNtpClient

So as of the time of reading this, that tutorial will most likely be out of data, but let's say for the next 6 months it'll be the place to go.

So, here's a little insight into what I'm doing and my decisions along with some code at the end:

1. I have an Arduino. That means no clock.
2. I have an Ethernet Shield 2. That means there are an abundance of clocks on the internet.
3. I am not hooking up a clock to the Arduino. That means I need to use the Arduino to keep time.
4. So I'm going to do this by polling a clock on the internet, keeping that time around and adding the time since boot to that time. It isn't a clock you'd want to use as your morning alarm clock, but it'll be accurate enough for what I need.

So I hacked together two functions from that tutorial:

unsigned long getCurrentTime() {
  unsigned long result = 0;
  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, extract 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):
    unsigned long secsSince1900 = highWord << 16 | lowWord;
    Serial.print("Seconds since Jan 1 1900 = ");
    Serial.println(secsSince1900);

    // now convert NTP time into everyday time:
    Serial.print("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);
    result = epoch;
  }
  else {
    result = 2099414000; // Picked a date so we can test when not connected to the internet.
  }

  return result;
}

String timeToString(unsigned long value) {
    String result = "";
    unsigned long epoch = value;
    // print the hour, minute and second:
    result += String((epoch  % 86400L) / 3600); // print the hour (86400 equals secs per day)
    result += String('-');
    if (((epoch % 3600) / 60) < 10) {
      // In the first 10 minutes of each hour, we'll want a leading '0'
      result += String('0');
    }
    result += String((epoch  % 3600) / 60); // print the minute (3600 equals secs per minute)
    result += String('-');
    if ((epoch % 60) < 10) {
      // In the first 10 seconds of each minute, we'll want a leading '0'
      result += String('0');
    }
    result += String(epoch % 60); // print the second

    return result;
}

unsigned long startTime;

void setup() {
   // ...
   startTime = getCurrentTime();
}

And anytime you want the current time viola!

timeToString(startTime + millis() / 1000)

2 comments:

Malcolm Burrows said...
This comment has been removed by the author.
Malcolm Burrows said...

This looks like a marbel in the world of embedded systems, I had no idea people can do so much using arduino. Feel free to swing by : iPhone Error 4013

Post a Comment