347 views|0 replies

34

Posts

4

Resources
The OP
 

[Digi-Key Follow me Issue 4] Advanced Task: Synchronize time from NTP server and get time [Copy link]

 This post was last edited by zygalaxy on 2024-2-23 19:18

Advanced task: synchronize time from NTP server (pay attention to the parsing of data exchange format), obtain time and send it to display screen (serial port) for display.

Matching devices: W5500-EVB-Pico , Adafruit Sharp Memory Display Breakout

1. NTP protocol

NTP (Network Time Protocol) is an application layer protocol based on UDP for computer time synchronization. NTP uses Coordinated Universal Time (UTC) to synchronize computer clock times with extremely high accuracy, such as as low as 1 millisecond in a local area network (LAN) and within tens of milliseconds on the Internet.

2. Working Principle of NTP

NTP obtains accurate time sources from atomic clocks, observatories, satellites or the Internet, and stratifies servers at different levels to synchronize time. According to the distance from the external UTC time source, NTP classifies servers into different layers (Stratum). The top layer is Stratum-1 with external UTC access, and Stratum-2 will obtain time from Stratum-1, and so on. The maximum number of layers is 15. Therefore, the larger the number of layers, the lower the time accuracy. Layer 16 means that it is not synchronized.

The system clock synchronization process is as follows:

  1. The NTP client sends an NTP request message to the NTP server at time T1. The request message carries the timestamp T1 when it leaves the NTP client.
  2. The NTP request message arrives at the NTP server, and the time of the NTP server is now T2.
  3. After processing, the NTP server sends an NTP reply message at time T3. The reply message carries the timestamp T1 when it leaves the NTP client, the timestamp T2 when it arrives at the NTP server, and the timestamp T3 when it leaves the NTP server.
  4. The NTP client receives the response message at time T4.

Through the above NTP message interaction, the NTP client obtains four time parameters, namely T1, T2, T3, and T4. Since the clocks of the NTP client and the NTP server are completely accurate, we can use the following formula to calculate the time difference between the NTP client and the NTP server, that is, the time that the NTP client needs to adjust.

First, calculate the time Delay required for the NTP message to be sent from the NTP client to the NTP server. Delay = [(T4 - T1) - (T3 - T2)] / 2

Take T4 as an example. At this time, when the message sent by the NTP server is received by the NTP client, the server time is T3 + Delay. Then the time difference Offset can be calculated by the following formula: T4 + Offset = T3 + Delay

After the formula is rearranged, Offset = T3 + Delay - T4 = T3 + [ ( T4 - T1 ) - ( T3 - T2 ) ] / 2 - T4 = [ ( T2- T1 ) + ( T3 - T4 ) ] / 2.

The NTP client adjusts its own clock based on the calculated Offset to achieve clock synchronization with the NTP server.

3. NTP message format

The specific implementation code is as follows:


#include <SPI.h>
#include <Ethernet.h>
#include <EthernetUdp.h>
// Screen
#include "PDLS_EXT3_Basic_Global.h"

// SDK
// #include <Arduino.h>
#include "hV_HAL_Peripherals.h"

// Include application, user and local libraries
// #include <SPI.h>

// Configuration

Screen_EPD_EXT3 myScreen(eScreen_EPD_EXT3_370, boardRaspberryPiPico_RP2040);

// Enter a MAC address for your controller below.
// Newer Ethernet shields have a MAC address printed on a sticker on the shield
byte mac[] = {
  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED
};

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

const char timeServer[] = "time.nist.gov";  // 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 and outgoing packets

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

void setup() {
  // You can use Ethernet.init(pin) to configure the CS pin
  //Ethernet.init(10);  // Most Arduino shields
  //Ethernet.init(5);   // MKR ETH Shield
  //Ethernet.init(0);   // Teensy 2.0
  //Ethernet.init(20);  // Teensy++ 2.0
  //Ethernet.init(15);  // ESP8266 with Adafruit FeatherWing Ethernet
  //Ethernet.init(33);  // ESP32 with Adafruit FeatherWing Ethernet

  // Open serial communications and wait for port to open:
  Serial.begin(9600);
  while (!Serial) {
    ;  // wait for serial port to connect. Needed for native USB port only
  }
  myScreen.begin();
  Serial.println(formatString("%s %ix%i", myScreen.WhoAmI().c_str(), myScreen.screenSizeX(), myScreen.screenSizeY()));
  myScreen.clear();
  // start Ethernet and UDP
  if (Ethernet.begin(mac) == 0) {
    Serial.println("Failed to configure Ethernet using DHCP");
    // Check for Ethernet hardware present
    if (Ethernet.hardwareStatus() == EthernetNoHardware) {
      Serial.println("Ethernet shield was not found.  Sorry, can't run without hardware. :(");
    } else if (Ethernet.linkStatus() == LinkOFF) {
      Serial.println("Ethernet cable is not connected.");
    }
    // no point in carrying on, so do nothing forevermore:
    while (true) {
      delay(1);
    }
  }
  Udp.begin(localPort);
}

void loop() {
  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 + 3600 * 8;
    // print Unix time:
    Serial.println(epoch);


    myScreen.setOrientation(7);

    // #if (USE_FONT_MODE == USE_FONT_TERMINAL)
    myScreen.selectFont(Font_Terminal12x16);
    // print the hour, minute and second:
    Serial.print("The UTC time is ");       // UTC is the time at Greenwich Meridian (GMT)
    Serial.print((epoch % 86400L) / 3600);  // print the hour (86400 equals secs per day)
    Serial.print(':');
    if (((epoch % 3600) / 60) < 10) {
      // In the first 10 minutes of each hour, we'll want a leading '0'
      Serial.print('0');
    }
    Serial.print((epoch % 3600) / 60);  // print the minute (3600 equals secs per minute)
    Serial.print(':');
    if ((epoch % 60) < 10) {
      // In the first 10 seconds of each minute, we'll want a leading '0'
      Serial.print('0');
    }
    Serial.println(epoch % 60);  // print the second
    myScreen.gText(0, 0, formatString("The UTC time is %02i:%02i:%02i", (epoch % 86400L) / 3600, (epoch % 3600) / 60, epoch % 60));
    myScreen.flush();
  }
  // wait ten seconds before asking for the time again
  delay(20000);
  Ethernet.maintain();
}

// send an NTP request to the time server at the given address
void sendNTPpacket(const char* 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();
}

Because Beijing time is East 8, this line of code needs to add 8 hours to the time

Upload the program to pico and observe the serial port output as follows

At the same time, the synchronization time is displayed on the ink screen

Since the refresh speed of the ink screen is slow, I changed the synchronization time to 20 seconds

Summarize:

The NTP protocol can ensure that the clocks of various devices in the network remain synchronized, avoiding problems such as data transmission errors and log record confusion caused by inconsistent device clocks. It plays a very important role in daily life.

This post is from DigiKey Technology Zone
 
 

Just looking around
Find a datasheet?

EEWorld Datasheet Technical Support

EEWorld
subscription
account

EEWorld
service
account

Automotive
development
circle

Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号
快速回复 返回顶部 Return list