SenseBox – Time

Getting Time via NTP

My future sensor data log file will need a time stamp. So I have to use the Arduino time library. Because the SenseBox already connected to internet via the WiFi I will use NTP to setup and sync the  local timer.

NTP Request Test Code

I using an older Arduino Ethernet board and library to play around to test basic NTP and Time functions. This example will connect to internet via Ethernet and request time from NTP server, syncing the internal clock.

Most important function in the following sketch is the NTP code for communicating with NTP server you will find in the time_t getTime() function. Some explanation you will also find on this web site.

// Using An Arduino Ethernet Shield To Get Date and Time
// Using mostly sketch from https://www.circuitbasics.com/using-an-arduino-ethernet-shield-for-timekeeping/
// Thomas Gohle 31.05.2020
// CC BY-SA 3.0 https://creativecommons.org/licenses/by-sa/3.0/

// ========================================
// Libaries

#include     // for communication with Ethernet Shield
#include     // for update/display of time
#include     // for communication with NTP Server via UDP

// ========================================
// global variables
// variable to hold Ethernet shield MAC address
byte mac[] = {0x90, 0xA3, 0xDA, 0x00, 0x00, 0x00 }; // see your Ethernet Shield, should be printed there...

// I never got working DHCP and failback statik working in a propper way, so I use only static for tests
IPAddress ip(192,168,xxx,xxx);      // IP adress of your arduino Ethernet shield
IPAddress subnet(255,255,255,0);    // subnet and 
IPAddress gateway(192,168,xxx,xxx); // gateway are necessary for NTP

// define IPAddress object that will contain the NTP server IP address
// use an NTP server from https://tf.nist.gov/tf-cgi/servers.cgi
IPAddress timeSrvr(129,6,15,30);

// define Ethernet UDP object and local port 8888
EthernetUDP ethernet_UDP;
unsigned int localPort = 8888;

// variable to store previous displayed time
time_t prevDisplay = 0;

// array to hold incoming/outgoing NTP messages
// NTP time message is 48 bytes long
byte messageBuffer[48];

// ========================================
// Setup
//    1.  Arduino Serial interface
//    2.a Ethernet shield MAC address [part of Ethernet Library]
//    2.b Ethernet shield IP address [part of Ethernet Library]
//    3.  Start UDP
//    4.  setSyncProvider() [part of Time Library]

void setup()
{
  // 1. start Arduino Serial with baud rate 9600
  Serial.begin(9600);
  delay(500);
  Serial.println("Sample Program to get Date and Time via NTP");

  // 2.
  Ethernet.begin(mac, ip, subnet, gateway);
  Serial.print("Ethernet Shield IP(set manual): "); //just for debugging...
  Serial.println(Ethernet.localIP());

  // 3. Start UDP, part of Ethernet Library
  ethernet_UDP.begin(localPort);
  Serial.println("Ethernet UDP Start....");

  // pass function getTime() to Time Library to update current time
  // [part of Time Library]
  setSyncProvider(getTime);
}

// ========================================
// into the loop...
// Just display current time via serial Monitor

void loop()
{
  if (timeStatus() != timeNotSet) {   // check if the time is successfully updated
    if (now() != prevDisplay) {       // update the display only if time has changed
      prevDisplay = now();
      digitalClockDisplay();          // display the current date and time
    }
  }
}

// ========================================
// function to display the time in an easy to read format
// using the Time Library functions

void digitalClockDisplay() {

  Serial.print(hour());
  printDigits(minute());
  printDigits(second());
  Serial.print(" ");
  Serial.print(day());
  Serial.print(" ");
  Serial.print(month());
  Serial.print(" ");
  Serial.print(year());
  Serial.println();
}

//helper function for digitalClockDisplay()

void printDigits(int digits) {
  // add colon character and a leading zero if number < 10 
  Serial.print(":");
  if (digits < 10) Serial.print('0'); Serial.print(digits); } 

  // ======================================== 
  // NTP code for communicating with NTP server 
  // function to request current time from NTP server
  time_t getTime() {
  while (ethernet_UDP.parsePacket() > 0) ; // discard packets remaining to be parsed
  Serial.println("Transmit NTP Request message");

  // send packet to request time from NTP server
  sendRequest(timeSrvr);

  // wait for response
  uint32_t beginWait = millis();

  while (millis() - beginWait < 1500) { int size = ethernet_UDP.parsePacket(); if (size >= 48) {
      Serial.println("Receiving NTP Response");

      // read data and save to messageBuffer
      ethernet_UDP.read(messageBuffer, 48);

      // NTP time received will be the seconds elapsed since 1 January 1900
      unsigned long secsSince1900;

      // convert to an unsigned long integer the reference timestamp found at byte 40 to 43
      secsSince1900 =  (unsigned long)messageBuffer[40] << 24;
      secsSince1900 |= (unsigned long)messageBuffer[41] << 16;
      secsSince1900 |= (unsigned long)messageBuffer[42] << 8;
      secsSince1900 |= (unsigned long)messageBuffer[43];

      // returns UTC time
      return secsSince1900 - 2208988800UL;
    }
  }

  // error if no response
  Serial.println("Error: No Response.");
  return 0;
}

// helper function for getTime()
// this function sends a request packet 48 bytes long

void sendRequest(IPAddress &address)
{
  // set all bytes in messageBuffer to 0
  memset(messageBuffer, 0, 48);

  // create the NTP request message
  // see RFC5905, 9.1. Peer Process Variables
  // https://tools.ietf.org/html/rfc5905#page-31
  // don't change
  
  messageBuffer[0] = 0b11100011;  // LI, Version, Mode
  messageBuffer[1] = 0;           // Stratum, or type of clock
  messageBuffer[2] = 6;           // Polling Interval
  messageBuffer[3] = 0xEC;        // Peer Clock Precision
  
  // array index 4 to 11 is left unchanged - 8 bytes of zero for Root Delay & Root Dispersion
  messageBuffer[12]  = 49;
  messageBuffer[13]  = 0x4E;
  messageBuffer[14]  = 49;
  messageBuffer[15]  = 52;

  // send messageBuffer to NTP server via UDP at port 123
  ethernet_UDP.beginPacket(address, 123);
  ethernet_UDP.write(messageBuffer, 48);
  ethernet_UDP.endPacket();
}


This will result in the following output at serial monitor, you see time between NTP requests is 5min.

18:36:55.723 -> Sample Program to get Date and Time via NTP
18:36:56.021 -> Ethernet Shield IP(set manual): 192.168.xxx.xxx
18:36:56.087 -> Ethernet UDP Start....
18:36:56.120 -> Transmit NTP Request message
18:36:56.186 -> Receiving NTP Response
18:36:56.219 -> 16:36:56 31 5 2020
...
18:41:55.665 -> 16:41:55 31 5 2020
18:41:56.659 -> Transmit NTP Request message
18:41:56.758 -> Receiving NTP Response
18:41:56.791 -> 6:28:16 7 2 2036
...

NTP Request for SenseBox

tbd…

Update Display to show time

tbd…