UTC/GMT time using UDP / NTP

edited February 2017 in Library Questions

Extracting data from ntp servers is very easy using an ESP8266 and/or Arduino ethernet or wifi. Since I absolutely love Processing for it's awesome libraries, flexibility, and easy to use IDE I decided to give it a spin using the following as a reference: https://www.arduino.cc/en/Tutorial/UdpNtpClient. Since Processing's support for the 'long' data type is mostly useless parsing up the received packet got pretty messy. The program is doing exactly what I want but before going any further I'd like to know if there is any better way to parse up the received packet. Thank you for looking.

// import UDP library
import hypermedia.net.*;

UDP udp;  // define the UDP object

byte[] packetBuff = new byte[48];

void setup() 
{
  size(175, 100);
  // create a new datagram connection on port 8888
  // and wait for an incoming message
  udp = new UDP( this, 8888 );
  udp.listen( true );

  for (int i = 0; i < 48; i += 1) packetBuff[i] = 0; // fill packetBuff with 0's 

  packetBuff[0]   = byte(227);   // LI, Version, Mode
  packetBuff[1]   = 0;           // Stratum, or type of clock
  packetBuff[2]   = 6;           // Polling Interval
  packetBuff[3]   = byte(236);   // Peer Clock Precision
  // 8 bytes of zero for Root Delay & Root Dispersion
  packetBuff[12]  = 49;
  packetBuff[13]  = 78;
  packetBuff[14]  = 49;
  packetBuff[15]  = 52;
  // all NTP fields have been given values, now
  // you can send a packet requesting a timestamp:
}

//process events
void draw() {
}


void mouseClicked() 
{ 
  String ip   = "time.nist.gov";      // the destination IP 
  //String ip     = "us.pool.ntp.org";    // the destination IP  
  int port      = 123;                  // the destination port 
  // send the buffer
  udp.send(packetBuff, ip, port ); 
  background(random(256), random(256), random(256));
}


void receive( byte[] data )  // <-- default handler
{   

  for (int i = 0; i < 48; i += 1)
  {
    int k = data[i] & 0xFF;
    if (i >= 40 && i < 44) println("data[" + i + "]" + " = " + k);
  }

  int ab = data[40] & 0xFF; // data[40 to 43] hi byte to low byte
  int ac = data[41] & 0xFF; // is seconds since January 1 1900 (UNIX time)
  int ad = data[42] & 0xFF;
  int ae = data[43] & 0xFF;

  int af = ((ab << 24)+(ac << 16)+(ad << 8)+(ae));
  af -= 1104494400; // subtract 2208988800 seconds for 70 years in two chunks
  af -= 1104494400; // af = signed number that represents seconds since January 1 1970

  // convert to UTC/GMT time
  int ag = Integer.remainderUnsigned(af, 86400); // get modulus using seconds in 24 hours
  ag /= 3600; // extract hours
  String ah = Integer.toUnsignedString(af); // thank you Java 8 Integer class :)
  println("Seconds since January 1 1970 = " + ah);

  print("UTC/GMT time = " + ag + ":"); // hours

  // In the first 10 minutes of each hour print a leading '0'
  if (((af % 3600) / 60) < 10)  print('0');

  //print the minute (3600 equals secs per hour)
  println((af  % 3600) / 60);
  println();
}

Answers

  • So as it turns out Processing does support the use of the long data type (My Bad Mistake!!) Gottfried Haider pointed me in the right direction... longs need a postfix of a lower or upper case L in order to be used in declarations and functions.

    https://github.com/processing/processing/issues/4878

    Can someone please mark this post as answered...?

  • Answer ✓

    I changed your receive function to this other way shown below. Not sure if the negative offsets artificially introduced are due to working with int data types. It will be great to have a better way to do the conversion. Can you comment about that negative value?

    Kf

    import java.nio.ByteBuffer;
    import java.util.Date;
    import java.text.SimpleDateFormat;
    
    
    void receive( byte[] data )  // <-- default handler
    {   
      // NOTICE ByteBuffer.wrap(data, 40, 4).getInt() is same value as your variable "af" in your code
      ByteBuffer bb =ByteBuffer.wrap(data, 40, 4);
      //2*1104494400   is the offset to get correct time
      //Factor of 1000 to convert seconds to msecs
      Date date = new Date( (long)(ByteBuffer.wrap(data, 40, 4).getInt()-2*1104494400)* 1000);
      SimpleDateFormat sdf = new SimpleDateFormat("EEEE,MMMM d,yyyy h:mm,a");
      System.out.println(sdf.format(date));
    
    }
    
  • edited February 2017

    Here is my revised code...thank you Gottfried Haider!!

    // import UDP library
    import hypermedia.net.*;
    
    UDP udp;  // define the UDP object
    
    byte[] packetBuff = new byte[48];
    
    void setup() 
    {
      size(175, 100);
      // create a new datagram connection on port 8888
      // and wait for an incoming message
      udp = new UDP( this, 8888 );
      udp.listen( true );
    
      // fill packetBuff with 0's 
      for (int i = 0; i < 48; i += 1) packetBuff[i] = 0; 
    
      // load packet with required info
      packetBuff[0]   = byte(227);   // LI, Version, Mode
      packetBuff[1]   = 0;           // Stratum, or type of clock
      packetBuff[2]   = 6;           // Polling Interval
      packetBuff[3]   = byte(236);   // Peer Clock Precision
      // 8 bytes of zero for Root Delay & Root Dispersion
      packetBuff[12]  = 49;
      packetBuff[13]  = 78;
      packetBuff[14]  = 49;
      packetBuff[15]  = 52;
      // all NTP fields have been given values, now
      // you can send a packet requesting a timestamp:
    }
    
    //process events
    void draw() {
    }
    
    
    void mouseClicked() 
    { 
      String ip   = "time.nist.gov";       // the destination IP 
      //String ip     = "us.pool.ntp.org"; // this IP also works  
      int port      = 123;                 // the destination port 
      // send the buffer
      udp.send(packetBuff, ip, port ); 
      background(random(256), random(256), random(256));
    }
    
    
    // This automagically gets called when a packet is received
    void receive( byte[] data )  
    {   
    
      for (int i = 0; i < 48; i += 1)
      {
        int k = data[i] & 0xFF;
        if (i >= 40 && i < 44) println("data[" + i + "]" + " = " + k);
      }
    
      long ab = data[40] & 0xFF; // data[40 to 43] hi byte to low byte
      long ac = data[41] & 0xFF; // is seconds since January 1 1900 
      long ad = data[42] & 0xFF; // UNIX time
      long ae = data[43] & 0xFF;
    
      long af = ((ab << 24)+(ac << 16)+(ad << 8)+(ae));
      af -= 2208988800L; // subtract 2208988800 seconds for 70 years
      long ag = ((af % 86400) / 3600); // extract UTC/GMT hours
    
      println("Seconds since January 1 1970 = " + af);
    
      //print UTC time in 24 hour format
      print("UTC time = " + ag + ":"); // hours
    
      // In the first 10 minutes of each hour print a leading '0'
      if (((af % 3600) / 60) < 10)  print('0');
    
      //print the minute (3600 equals secs per hour)
      println((af  % 3600) / 60);
      println();
    }
    
  • edited February 2017

    @kfrajer ...thank you for your reply but after finding out how to use the long datatype correctly I got rid of the int's and now everything is ok. So, yes the int's were causing the negative offsets. Hopefully Processing's docs and/or reference page will get updated to show the proper way to use the long datatype in declarations and functions.

Sign In or Register to comment.