user42574: I am interested in getting a local clock on an ESP32 module synch to
a network clock using NTP. I am able to read data and time here. But I am interested in synching to the milli second level.
    #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();


user7508: The NTP timestamp format doesn't use milliseconds. All timestamps are in
seconds, in a 32.32 fixed point format. This means that the
fraction field is in units of 2−32 s, or about
233 picoseconds. You can convert that to milliseconds by
multiplying by 1000 and dividing by 232. For example:
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;

If you want to avoid 64-bit arithmetic, you can instead divide by 128,
then multiply by 125, then divide by 222:
uint16_t mssec = ((frac >> 7) * 125) >> 22;

That should give the same result except for a potentially larger
rounding error. Note that, if you care about rounding, you would try to
round to nearest rather than towards zero:
uint16_t mssec = ((frac >> 7) * 125 + (1UL << 21)) >> 22;


42574: <p>I am interested in getting a local clock on an ESP32 module synch to
a network clock using NTP. I am able to read data and time here. But I am interested in synching to the milli second level.</p>
<pre class="lang-cpp prettyprint-override"><code>    #include &lt;WiFi.h&gt;
    #include &lt;WiFiUdp.h&gt;

    char ssid[] = &quot;*************&quot;; //  your network SSID (name)
    char pass[] = &quot;********&quot;; // 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 = &quot;time.nist.gov&quot;;

    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(&quot;Connecting to &quot;);
      Serial.println(ssid);
      WiFi.begin(ssid, pass);
      while (WiFi.status() != WL_CONNECTED) {
        delay(500);
        Serial.print(&quot;.&quot;);
      }
      Serial.println(&quot;&quot;);
      Serial.println(&quot;WiFi connected&quot;);
      Serial.println(&quot;IP address: &quot;);
      Serial.println(WiFi.localIP());
      Serial.println(&quot;Starting UDP&quot;);
      udp.begin(localPort);
      Serial.print(&quot;Local port: &quot;);
    }

    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(&quot;no packet yet&quot;);
      } else {
        Serial.print(&quot;packet received, length=&quot;);
        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 &lt;&lt; 16 | lowWord;
        Serial.print(&quot;Seconds since Jan 1 1900 = &quot; );
        Serial.println(secsSince1900);
        unsigned long mshighWord = word(packetBuffer[44], packetBuffer[45]);
        unsigned long mslowWord = word(packetBuffer[46], packetBuffer[47]);
        unsigned long mssecs = mshighWord &lt;&lt; 16 | mslowWord;
        Serial.print(&quot;Frac Seconds = &quot; );
        Serial.println(mssecs);
        // now convert NTP time into everyday time:
        Serial.print(&quot;Unix time = &quot;);
        // 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(&quot;The UTC time is &quot;); // 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) &lt; 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) &lt; 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&amp; address) {
      Serial.println(&quot;sending NTP packet...&quot;);
      // 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 &amp; 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();
</code></pre>
 7508: <p>The NTP timestamp format doesn't use milliseconds. All timestamps are in
<strong>seconds</strong>, in a 32.32 <a href="https://en.wikipedia.org/wiki/Fixed-point_arithmetic" rel="nofollow noreferrer">fixed point</a> format. This means that the
fraction field is in units of 2<sup>−32</sup> s, or about
233 picoseconds. You can convert that to milliseconds by
multiplying by 1000 and dividing by 2<sup>32</sup>. For example:</p>

<pre class="lang-c++ prettyprint-override"><code>uint32_t frac  = (uint32_t) packetBuffer[44] &lt;&lt; 24
               | (uint32_t) packetBuffer[45] &lt;&lt; 16
               | (uint32_t) packetBuffer[46] &lt;&lt;  8
               | (uint32_t) packetBuffer[47] &lt;&lt;  0;
uint16_t mssec = ((uint64_t) frac * 1000) &gt;&gt; 32;
</code></pre>
<p>If you want to avoid 64-bit arithmetic, you can instead divide by 128,
then multiply by 125, then divide by 2<sup>22</sup>:</p>
<pre class="lang-c++ prettyprint-override"><code>uint16_t mssec = ((frac &gt;&gt; 7) * 125) &gt;&gt; 22;
</code></pre>
<p>That should give the same result except for a potentially larger
rounding error. Note that, if you care about rounding, you would try to
round to nearest rather than towards zero:</p>
<pre class="lang-c++ prettyprint-override"><code>uint16_t mssec = ((frac &gt;&gt; 7) * 125 + (1UL &lt;&lt; 21)) &gt;&gt; 22;
</code></pre>
