mirror of
https://github.com/esp8266/Arduino.git
synced 2025-04-19 23:22:16 +03:00
UdpNtpClient rewritten in a clearer, more pedantic fashion. (#2008)
* Create readme.txt * Add files via upload * Update RFC1305.h Added guard lines
This commit is contained in:
parent
0f3d0e86f7
commit
974b9ae2fa
50
libraries/Ethernet/examples/udpntpclient_pedantic/RFC1305.h
Normal file
50
libraries/Ethernet/examples/udpntpclient_pedantic/RFC1305.h
Normal file
@ -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
|
||||||
|
|
@ -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
|
||||||
|
mgadriver@gmail.com
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -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.
|
Loading…
x
Reference in New Issue
Block a user