|
| 1 | + |
| 2 | +#include <time.h> // Note: this is the standard c time library, not Time.h |
| 3 | + |
| 4 | +// Get the time from NTP |
| 5 | +// This code is based heavily on: |
| 6 | +// https://docs.arduino.cc/tutorials/mkr-nb-1500/mkr-nb-library-examples#mkr-nb-gprs-udp-ntp-client |
| 7 | +// Many thanks to: Michael Margolis, Tom Igoe, Arturo Guadalupi, et al |
| 8 | + |
| 9 | +// NTP Server |
| 10 | +//const char* ntpServer = "africa.pool.ntp.org"; // The Network Time Protocol Server |
| 11 | +//const char* ntpServer = "asia.pool.ntp.org"; // The Network Time Protocol Server |
| 12 | +//const char* ntpServer = "europe.pool.ntp.org"; // The Network Time Protocol Server |
| 13 | +const char* ntpServer = "north-america.pool.ntp.org"; // The Network Time Protocol Server |
| 14 | +//const char* ntpServer = "oceania.pool.ntp.org"; // The Network Time Protocol Server |
| 15 | +//const char* ntpServer = "south-america.pool.ntp.org"; // The Network Time Protocol Server |
| 16 | + |
| 17 | +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= |
| 18 | + |
| 19 | +bool getNTPTime(uint8_t *y, uint8_t *mo, uint8_t *d, uint8_t *h, uint8_t *min, uint8_t *s) |
| 20 | +{ |
| 21 | + int serverPort = 123; //NTP requests are to port 123 |
| 22 | + |
| 23 | + // Set up the packetBuffer for the NTP request |
| 24 | + const int NTP_PACKET_SIZE = 48; // NTP time stamp is in the first 48 bytes of the message |
| 25 | + byte packetBuffer[NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets |
| 26 | + |
| 27 | + // set all bytes in the buffer to 0 |
| 28 | + memset(packetBuffer, 0, NTP_PACKET_SIZE); |
| 29 | + |
| 30 | + // Initialize values needed to form NTP request |
| 31 | + packetBuffer[0] = 0b11100011; // LI, Version, Mode |
| 32 | + packetBuffer[1] = 0; // Stratum, or type of clock |
| 33 | + packetBuffer[2] = 6; // Polling Interval |
| 34 | + packetBuffer[3] = 0xEC; // Peer Clock Precision |
| 35 | + // 8 bytes of zero for Root Delay & Root Dispersion |
| 36 | + packetBuffer[12] = 49; |
| 37 | + packetBuffer[13] = 0x4E; |
| 38 | + packetBuffer[14] = 49; |
| 39 | + packetBuffer[15] = 52; |
| 40 | + |
| 41 | + //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= |
| 42 | + |
| 43 | + //Open a socket to the NTP server |
| 44 | + |
| 45 | + int socketNum = mySARA.socketOpen(SARA_R5_UDP); |
| 46 | + if (socketNum == -1) |
| 47 | + { |
| 48 | + Serial.println(F("getNTPTime: socketOpen failed!")); |
| 49 | + return (false); |
| 50 | + } |
| 51 | + |
| 52 | + Serial.print(F("getNTPTime: using socket ")); |
| 53 | + Serial.println(socketNum); |
| 54 | + |
| 55 | + //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= |
| 56 | + |
| 57 | + // Send the request - NTP uses UDP |
| 58 | + |
| 59 | + if (mySARA.socketWriteUDP(socketNum, ntpServer, serverPort, (const char *)&packetBuffer, NTP_PACKET_SIZE) != SARA_R5_SUCCESS) // Send the request |
| 60 | + { |
| 61 | + Serial.println(F("getNTPTime: socketWrite failed!")); |
| 62 | + mySARA.socketClose(socketNum); // Be nice. Close the socket |
| 63 | + return (false); |
| 64 | + } |
| 65 | + |
| 66 | + //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= |
| 67 | + |
| 68 | + // Wait up to 10 seconds for the response |
| 69 | + unsigned long requestTime = millis(); |
| 70 | + |
| 71 | + while (millis() < (requestTime + 10000)) |
| 72 | + { |
| 73 | + // We could use the Socket Read Callback to get the data, but, just for giggles, |
| 74 | + // and to prove it works, let's poll the arrival of the data manually... |
| 75 | + int avail = 0; |
| 76 | + if (mySARA.socketReadAvailableUDP(socketNum, &avail) != SARA_R5_SUCCESS) |
| 77 | + { |
| 78 | + Serial.println(F("getNTPTime: socketReadAvailable failed!")); |
| 79 | + mySARA.socketClose(socketNum); // Be nice. Close the socket |
| 80 | + return (false); |
| 81 | + } |
| 82 | + |
| 83 | + if (avail >= NTP_PACKET_SIZE) // Is enough data available? |
| 84 | + { |
| 85 | + if (avail > NTP_PACKET_SIZE) // Too much data? |
| 86 | + { |
| 87 | + Serial.print(F("getNTPTime: too much data received! Length: ")); |
| 88 | + Serial.print(avail); |
| 89 | + Serial.print(F(". Reading ")); |
| 90 | + Serial.print(NTP_PACKET_SIZE); |
| 91 | + Serial.println(F(" bytes...")); |
| 92 | + } |
| 93 | + |
| 94 | + if (mySARA.socketReadUDP(socketNum, NTP_PACKET_SIZE, (char *)&packetBuffer) != SARA_R5_SUCCESS) |
| 95 | + { |
| 96 | + Serial.println(F("getNTPTime: socketRead failed!")); |
| 97 | + mySARA.socketClose(socketNum); // Be nice. Close the socket |
| 98 | + return (false); |
| 99 | + } |
| 100 | + |
| 101 | + // Extract the time from the reply |
| 102 | + |
| 103 | + Serial.print(F("getNTPTime: received ")); |
| 104 | + Serial.print(avail); |
| 105 | + Serial.println(F(" bytes. Extracting the time...")); |
| 106 | + |
| 107 | + //the timestamp starts at byte 40 of the received packet and is four bytes, |
| 108 | + // or two words, long. First, esxtract the two words: |
| 109 | + unsigned long highWord = word(packetBuffer[40], packetBuffer[41]); |
| 110 | + unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]); |
| 111 | + |
| 112 | + // combine the four bytes (two words) into a long integer |
| 113 | + // this is NTP time (seconds since Jan 1 1900): |
| 114 | + unsigned long secsSince1900 = highWord << 16 | lowWord; |
| 115 | + |
| 116 | + Serial.print(F("getNTPTime: seconds since Jan 1 1900 = ")); |
| 117 | + Serial.println(secsSince1900); |
| 118 | + |
| 119 | + // now convert NTP time into everyday time: |
| 120 | + Serial.print(F("getNTPTime: Unix time = ")); |
| 121 | + |
| 122 | + // Unix time starts on Jan 1 1970. In seconds, that's 2208988800: |
| 123 | + const unsigned long seventyYears = 2208988800UL; |
| 124 | + |
| 125 | + // subtract seventy years: |
| 126 | + unsigned long epoch = secsSince1900 - seventyYears; |
| 127 | + |
| 128 | + // print Unix time: |
| 129 | + Serial.println(epoch); |
| 130 | + |
| 131 | + // Instead of calculating the year, month, day, etc. manually, let's use time_t and tm to do it for us! |
| 132 | + time_t dateTime = epoch; |
| 133 | + tm *theTime = gmtime(&dateTime); |
| 134 | + |
| 135 | + // Load the time into y, mo, d, h, min, s |
| 136 | + *y = theTime->tm_year - 100; // tm_year is years since 1900. Convert to years since 2000. |
| 137 | + *mo = theTime->tm_mon + 1; //tm_mon starts at zero. Add 1 for January. |
| 138 | + *d = theTime->tm_mday; |
| 139 | + *h = theTime->tm_hour; |
| 140 | + *min = theTime->tm_min; |
| 141 | + *s = theTime->tm_sec; |
| 142 | + |
| 143 | + // Finish off by printing the time |
| 144 | + Serial.print(F("getNTPTime: YY/MM/DD HH:MM:SS : ")); |
| 145 | + if (*y < 10) Serial.print(F("0")); |
| 146 | + Serial.print(*y); |
| 147 | + Serial.print(F("/")); |
| 148 | + if (*mo < 10) Serial.print(F("0")); |
| 149 | + Serial.print(*mo); |
| 150 | + Serial.print(F("/")); |
| 151 | + if (*d < 10) Serial.print(F("0")); |
| 152 | + Serial.print(*d); |
| 153 | + Serial.print(F(" ")); |
| 154 | + if (*h < 10) Serial.print(F("0")); |
| 155 | + Serial.print(*h); |
| 156 | + Serial.print(F(":")); |
| 157 | + if (*min < 10) Serial.print(F("0")); |
| 158 | + Serial.print(*min); |
| 159 | + Serial.print(F(":")); |
| 160 | + if (*s < 10) Serial.print(F("0")); |
| 161 | + Serial.println(*s); |
| 162 | + |
| 163 | + mySARA.socketClose(socketNum); // Be nice. Close the socket |
| 164 | + return (true); // We are done! |
| 165 | + } |
| 166 | + |
| 167 | + delay(100); // Wait before trying again |
| 168 | + } |
| 169 | + |
| 170 | + //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= |
| 171 | + |
| 172 | + Serial.println(F("getNTPTime: no NTP data received!")); |
| 173 | + mySARA.socketClose(socketNum); // Be nice. Close the socket |
| 174 | + return (false); |
| 175 | +} |
0 commit comments