Синхронизация локальных часов используя NTP в миллисекундах

Мне интересно получить локальные часы на модуле ESP32 для синхронизации с сетевыми часами с помощью NTP. Я могу читать данные и время здесь. Но я заинтересован в синхронизации со вторым уровнем milli.

    #include <WiFi.h>
    #include <WiFiUdp.h>

    char ssid[] = "*************"; //  your network SSID (name)
    char pass[] = "********"; // your network password

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

    IPAddress timeServerIP; // time.nist.gov NTP server address
    const char* ntpServerName = "time.nist.gov";

    const int NTP_PACKET_SIZE = 48; // NTP time stamp is in the first 48 bytes 
    byte packetBuffer[ NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing 
    // A UDP instance to let us send and receive packets over UDP
    WiFiUDP udp;

    void setup() {
      Serial.begin(115200);
      Serial.println();
      Serial.println();
      // We start by connecting to a WiFi network
      Serial.print("Connecting to ");
      Serial.println(ssid);
      WiFi.begin(ssid, pass);
      while (WiFi.status() != WL_CONNECTED) {
        delay(500);
        Serial.print(".");
      }
      Serial.println("");
      Serial.println("WiFi connected");
      Serial.println("IP address: ");
      Serial.println(WiFi.localIP());
      Serial.println("Starting UDP");
      udp.begin(localPort);
      Serial.print("Local port: ");
    }

    void loop() {
      //get a random server from the pool
      WiFi.hostByName(ntpServerName, timeServerIP);
      sendNTPpacket(timeServerIP);
      // send an NTP packet to a time server
      // wait to see if a reply is available
      delay(1000);
      int cb = udp.parsePacket();
      if (!cb) {
        Serial.println("no packet yet");
      } else {
        Serial.print("packet received, length=");
        Serial.println(cb);
        // We've received a packet, read the data from it
        udp.read(packetBuffer, NTP_PACKET_SIZE);
        // read the packet into the buf
        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);
        unsigned long mshighWord = word(packetBuffer[44], packetBuffer[45]);
        unsigned long mslowWord = word(packetBuffer[46], packetBuffer[47]);
        unsigned long mssecs = mshighWord << 16 | mslowWord;
        Serial.print("Frac Seconds = " );
        Serial.println(mssecs);
        // 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);
        // print the hour, minute and second:
        Serial.print("The UTC time is "); // UTC is the time at 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
        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
      }
      // wait ten seconds before asking for the time again
      delay(10000);
    }

    // send an NTP request to the time server at the given address
    unsigned long sendNTPpacket(IPAddress& address) {
      Serial.println("sending NTP packet...");
      // 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();

, 👍1

Обсуждение

его не ясно точно, как дробная часть поля секунд считывается из пакета NTP UDP. Здесь у меня есть код, который читает 4 байта после секунд без знака длинное mshighWord = word(packetBuffer[44], packetBuffer[45]); unsigned long mslowWord = слово(packetBuffer[46], packetBuffer[47]); unsigned long mssecs = mshighWord << 16 | mslowWord; Serial.print("Frac Seconds =" ); Serial.println(mssecs);, @Venkat

Кодирование достаточно сложно, и еще сложнее, если вы ставите просто как предложение. Если у вас есть дополнительная информация, вы можете отредактировать свой вопрос и поместить его туда, чтобы его было легче читать и легче замечать., @dhimaspw

привет дхимаспв...понял ... новичок здесь. Не был уверен, как добавить код в поле комментария, @Venkat


1 ответ


3

Формат временной метки NTP не использует миллисекунды. Все отметки времени находятся в секунды, в формате 32,32 с фиксированной точкой. Это означает, что поле дробей выражено в единицах 2-32 с, или около 233 пикосекунд. Вы можете преобразовать это в миллисекунды, умножив на 1000 и разделив на 232. Например:

uint32_t frac  = (uint32_t) packetBuffer[44] << 24
               | (uint32_t) packetBuffer[45] << 16
               | (uint32_t) packetBuffer[46] <<  8
               | (uint32_t) packetBuffer[47] <<  0;
uint16_t mssec = ((uint64_t) frac * 1000) >> 32;

Если вы хотите избежать 64-разрядной арифметики, вы можете вместо этого разделить на 128, затем умножить на 125, а затем разделить на 225:

uint16_t mssec = ((frac >> 7) * 125) >> 25;

Это должно дать тот же результат, за исключением потенциально большей ошибки округления. Обратите внимание, что, если вас интересует округление, вы попытаетесь округлить до ближайшего, а не до нуля:

uint16_t mssec = ((frac >> 7) * 125 + (1UL << 24)) >> 25;
,