-
Notifications
You must be signed in to change notification settings - Fork 13.3k
UdpNtpClient rewritten in a clearer, more pedantic fashion. #2008
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
50 changes: 50 additions & 0 deletions
50
libraries/Ethernet/examples/udpntpclient_pedantic/RFC1305.h
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
#ifndef RFC1305_H | ||
#define RFC1305_H | ||
/* | ||
* see https://www.eecis.udel.edu/~mills/database/rfc/rfc1305/rfc1305c.pdf | ||
* https://tools.ietf.org/html/rfc1305 | ||
*/ | ||
|
||
#pragma pack(1) | ||
struct sRFC1305 { | ||
// NOTE all fields are BIG-ENDIAN so must be swapped on little endian machines | ||
uint8_t MODE:3; | ||
uint8_t VN:3; | ||
uint8_t LI:2; | ||
uint8_t stratum; | ||
uint8_t poll; | ||
uint8_t precision; | ||
int16_t rootdelay_main; | ||
uint16_t rootdelay_fraction; | ||
int16_t rootdispersion_main; | ||
uint16_t rootdispersion_fraction; | ||
uint8_t identifier[4]; | ||
// 64 bit timestamps contain 32 bit whole part + 32 bit fractional part | ||
uint32_t referencetimestamp_main; | ||
uint32_t referencetimestamp_fraction; | ||
uint32_t origintimestamp_main; | ||
uint32_t origintimestamp_fraction; | ||
uint32_t receivetimestamp_main; | ||
uint32_t receivetimestamp_fraction; | ||
uint32_t transmittimestamp_main; | ||
uint32_t transmittimestamp_fraction; | ||
}; | ||
#pragma pack(0) | ||
|
||
#define LI_NOWARNING 0 | ||
#define LI_61_SEC 1 | ||
#define LI_59_SEC 2 | ||
#define LI_ALARM 3 | ||
|
||
#define VERN 4 | ||
|
||
#define MODE_SYMMETRIC_ACTIVE 1 | ||
#define MODE_SYMMETRIC_PASSIVE 2 | ||
#define MODE_CLIENT 3 | ||
#define MODE_SERVER 4 | ||
#define MODE_BROADCAST 5 | ||
|
||
#define ENDIAN_SWAP_32(l) ((l>>24) |((l>>16)<<8)&0xff00 | ((l>>8)<<16)&0xff0000 | (l << 24)) | ||
#define ENDIAN_SWAP_16(l) ((l>>8) | (l << 8)) | ||
#endif | ||
|
160 changes: 160 additions & 0 deletions
160
libraries/Ethernet/examples/udpntpclient_pedantic/UdpNtpClient_Pedantic.ino
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,160 @@ | ||
/* | ||
|
||
Udp NTP Client | ||
|
||
Get the time from a Network Time Protocol (NTP) time server | ||
Demonstrates use of UDP sendPacket and ReceivePacket | ||
For more on NTP time servers and the messages needed to communicate with them, | ||
see http://en.wikipedia.org/wiki/Network_Time_Protocol | ||
|
||
created 4 Sep 2010 | ||
by Michael Margolis | ||
modified 9 Apr 2012 | ||
by Tom Igoe | ||
|
||
This code is in the public domain. | ||
|
||
Modified by David Henry to show where all the 'magic numbers' come from. | ||
You need to read the RFC-1305 spec to understand https://tools.ietf.org/html/rfc1305 | ||
[email protected] | ||
|
||
*/ | ||
|
||
#include <SPI.h> | ||
#include <Ethernet.h> | ||
#include <EthernetUdp.h> | ||
#include "RFC1305.h" | ||
|
||
// 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 | ||
|
||
char timeServer[] = "time.nist.gov"; // time.nist.gov NTP server | ||
|
||
#define NTP_PACKET_SIZE sizeof(struct sRFC1305) | ||
|
||
struct sRFC1305 packetBuffer; //buffer to hold incoming and outgoing packets | ||
|
||
// A UDP instance to let us send and receive packets over UDP | ||
EthernetUDP Udp; | ||
|
||
void setup() | ||
{ | ||
// Open serial communications and wait for port to open: | ||
Serial.begin(9600); | ||
while (!Serial) { | ||
; // wait for serial port to connect. Needed for Leonardo only | ||
} | ||
//Serial.println(NTP_PACKET_SIZE); // just for debugging | ||
//Serial.println(ENDIAN_SWAP_32(0x11223344),HEX); | ||
//Serial.println(ENDIAN_SWAP_16(0xAABB),HEX); | ||
// start Ethernet and UDP | ||
if (Ethernet.begin(mac) == 0) { | ||
Serial.println("Failed to configure Ethernet using DHCP"); | ||
// no point in carrying on, so do nothing forevermore: | ||
for (;;) | ||
; | ||
} | ||
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((byte *)&packetBuffer, NTP_PACKET_SIZE); // read the packet into the buffer | ||
#if 0 // just for debugging | ||
Serial.println(ENDIAN_SWAP_16(packetBuffer.rootdelay_main),HEX); | ||
Serial.println(ENDIAN_SWAP_16(packetBuffer.rootdelay_fraction),HEX); | ||
Serial.println(ENDIAN_SWAP_16(packetBuffer.rootdispersion_main),HEX); | ||
Serial.println(ENDIAN_SWAP_16(packetBuffer.rootdispersion_fraction),HEX); | ||
Serial.println(ENDIAN_SWAP_32(packetBuffer.referencetimestamp_main),HEX); | ||
Serial.println(ENDIAN_SWAP_32(packetBuffer.referencetimestamp_fraction),HEX); | ||
Serial.println(ENDIAN_SWAP_32(packetBuffer.origintimestamp_main),HEX); | ||
Serial.println(ENDIAN_SWAP_32(packetBuffer.origintimestamp_fraction),HEX); | ||
Serial.println(ENDIAN_SWAP_32(packetBuffer.receivetimestamp_main),HEX); | ||
Serial.println(ENDIAN_SWAP_32(packetBuffer.receivetimestamp_fraction),HEX); | ||
Serial.println(ENDIAN_SWAP_32(packetBuffer.transmittimestamp_main),HEX); | ||
Serial.println(ENDIAN_SWAP_32(packetBuffer.transmittimestamp_fraction),HEX); | ||
#endif | ||
Serial.print("Delay "); | ||
Serial.print(ENDIAN_SWAP_16(packetBuffer.rootdelay_main));Serial.print(".");Serial.println(ENDIAN_SWAP_16(packetBuffer.rootdelay_fraction)); | ||
Serial.print("Seconds since Jan 1 1900 = " ); | ||
unsigned long secsSince1900 = ENDIAN_SWAP_32(packetBuffer.transmittimestamp_main); | ||
Serial.print(secsSince1900);Serial.print(".");Serial.println(ENDIAN_SWAP_32(packetBuffer.transmittimestamp_fraction)); | ||
|
||
// 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); | ||
|
||
#define SECS_PER_MINUTE 60 | ||
#define SECS_PER_HOUR 3600 | ||
#define SECS_PER_DAY 86400L | ||
|
||
// print the hour, minute and second: | ||
Serial.print("The UTC time is "); // UTC is the time at Greenwich Meridian (GMT) | ||
Serial.print((epoch % SECS_PER_DAY) / SECS_PER_HOUR); | ||
Serial.print(':'); | ||
if ( ((epoch % SECS_PER_HOUR) / SECS_PER_MINUTE) < 10 ) { | ||
// In the first 10 minutes of each hour, we'll want a leading '0' | ||
Serial.print('0'); | ||
} | ||
Serial.print((epoch % SECS_PER_HOUR) / SECS_PER_MINUTE); | ||
Serial.print(':'); | ||
if ( (epoch % SECS_PER_MINUTE) < 10 ) { | ||
// In the first 10 seconds of each minute, we'll want a leading '0' | ||
Serial.print('0'); | ||
} | ||
Serial.println(epoch % SECS_PER_MINUTE); // 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(char* address) | ||
{ | ||
// set all bytes in the buffer to 0 | ||
memset((char *)&packetBuffer, 0, NTP_PACKET_SIZE); | ||
// Initialize values needed to form NTP request | ||
// (see URL above for details on the packets) | ||
packetBuffer.LI = LI_ALARM; | ||
packetBuffer.VN = VERN; | ||
packetBuffer.MODE = MODE_CLIENT; | ||
packetBuffer.stratum = 0; | ||
packetBuffer.poll = 6; | ||
packetBuffer.precision = -20; // ? copied from original UdnNtpClient code | ||
packetBuffer.identifier[0] = '1'; // I've no idea where this ID comes from | ||
packetBuffer.identifier[1] = 'N'; | ||
packetBuffer.identifier[2] = '1'; | ||
packetBuffer.identifier[3] = '4'; | ||
// Serial.println(*(uint8_t *)&packetBuffer,HEX); | ||
// 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((byte *)&packetBuffer, NTP_PACKET_SIZE); | ||
Udp.endPacket(); | ||
} | ||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
This is essentially the same as the original, classic, example. However instead of treating the NTP message as a mysterious array of bytes it is referred to by its offical name and structure. | ||
The concept of Big-Endian and Little_Endian data is introduced, which may be new to many readers. | ||
Actually there is a good historical reason for the dominance of BigEndian in internet protocols. In the early days of networking Sun computers were the major force and they had BigEndian processors. Later on Intel entered the field with LittleEndian processors and the rest is history. I wonder how many CPU cycles are wasted every second converting between the two? | ||
Treating byte arrays as structures encourages good programming style as 'magic numbers' are replaced by meaningful names. |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ethernet? I feel adding an example which uses WiFi would be more relevant to the scope of this project.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The original client was in the Ethernet library and then copied to the Wifi library. Just like I updated the Ethernet version I could just go ahead and apply the same to the Wifi version.