friendev EtherDune TCP/IP library
UDPClientDemo_NTPClient.ino
Go to the documentation of this file.
1 // EtherDune UDP Client demo: NTP client
2 // Author: Javier Peletier <jm@friendev.com>
3 // Summary: Demonstrates how to build a simple UDP client application
4 // that syncs with an NTP server.
5 //
6 // Copyright (c) 2015 All Rights Reserved, http://friendev.com
7 //
8 // Credits: Some time calculation code inspired by Paul Stoffregen's Time.cpp
9 // https://github.com/PaulStoffregen/Time/blob/master/Time.cpp
10 //
11 // This source is subject to the GPLv2 license.
12 // Please see the License.txt file for more information.
13 // All other rights reserved.
14 //
15 // THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
16 // KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
17 // IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
18 // PARTICULAR PURPOSE.
19 
36 #include <ACross.h>
37 #include <Checksum.h>
38 #include <TCPSocket.h>
39 #include <UDPSocket.h>
40 
41 #include <inet.h>
42 #include <ENC28J60.h>
43 #include <FlowScanner.h>
44 #include <HTTPClient.h>
45 
46 #define AC_LOGLEVEL 6
47 #include <ACLog.h>
48 ACROSS_MODULE("UDPClient");
49 
50 
51 static const uint8_t CS_PIN = 10; //Put here what pin you are using for your ENC28J60's chip select
52 static MACAddress_P mymac = { 0x66, 0x72, 0x69, 0x65, 0x6e, 0x64 };
53 static IPAddress_P gatewayIP = { 192, 168, 1, 1 };
54 static IPAddress_P myIP = { 192, 168, 1, 33 };
55 static IPAddress_P netmask = { 255, 255, 255, 0 };
56 
57 static IPAddress_P NTPServerIP = { 206, 246, 122, 250 }; // nist1-pa.ustiming.org. alternatives: http://tf.nist.gov/tf-cgi/servers.cgi
58 
59 
60 // NTP message format struct definitions
61 //
62 /*
63 For this simple demo it wouldn't have been necessary to be so formal
64 defining the structs, etc, but it is a good example of how
65 Socket::write() works, allowing you to just build the struct
66 and put it on the wire.
67 
68 See RFC1305 https://tools.ietf.org/html/rfc1305
69 */
70 
71 // NTP Short timestamp
72 struct NTPShort
73 {
74  nint16_t seconds;
75  nint16_t fraction;
76 
77 };
78 
79 // NTP Timestamp definition
80 struct NTPTimestamp
81 {
82  nint32_t seconds;
83  nint32_t fraction;
84 
85  void zero()
86  {
87  seconds.zero();
88  fraction.zero();
89  }
90 };
91 
92 // Structure of an NTP message according to the RFC.
93 struct NTPMessage
94 {
95  uint8_t mode : 3;
96  uint8_t versionNumber : 3;
97  uint8_t leapIndicator : 2;
98  uint8_t stratum;
99  uint8_t pollingInterval;
100  uint8_t precision;
101  NTPShort rootDelay;
102  NTPShort rootDispersion;
103  union
104  {
105  uint32_t referenceId;
106  char referenceStr[4];
107  };
108  NTPTimestamp reference;
109  NTPTimestamp origin;
110  NTPTimestamp receive;
111  NTPTimestamp transmit;
112 
113 };
114 
115 
116 class NTPClient : UDPSocket
117 {
118  static const int timeZone = 1; //Time offset from GMT
119  static const uint16_t NTP_UPDATE_INTERVAL_TICKS = NTICKS(10 * 60 * 1000); //sync every 10 minutes with NTP server
120 
121 
122  static const unsigned long secs1900_1970 = (17UL * 366UL + (70UL - 17UL) * 365UL) * 24UL * 60UL * 60UL; // Seconds between 1970 and 1800. 17 leap years.
123 
124 
125 private:
126  uint32_t millis_offset;
127  uint32_t epoch;
128  uint16_t updateTimer;
129 
130  bool isLeapYear(uint16_t y)
131  {
132  return !((1970 + y) % 4) && (((1970 + y) % 100) || !((1970 + y) % 400));
133  }
134 
135 public:
136 
137  uint16_t year;
138  uint8_t month;
139  uint8_t weekDay;
140  uint8_t day;
141  uint8_t hour;
142  uint8_t minute;
143  uint8_t second;
144 
145 
146  void start()
147  {
148  remotePort = 123; //NTP UDP port
149  remoteIP = NTPServerIP;
150  updateTimer = 0;
151  epoch = 0;
152 
153  }
154 
155  void syncTime()
156  {
157  NTPMessage msg;
158 
159  msg.leapIndicator = 3;
160  msg.versionNumber = 3;
161  msg.mode = 3;
162  msg.stratum = 0;
163  msg.pollingInterval = 17;
164  msg.precision = 0xFA;
165  msg.rootDelay.fraction.zero();
166  msg.rootDelay.seconds.zero();
167  msg.rootDispersion.seconds = 1;
168  msg.rootDispersion.fraction = 0;
169  msg.referenceId = 0;
170  msg.reference.zero();
171  msg.reference.fraction = 0;
172  msg.origin.zero();
173  msg.receive.zero();
174  msg.transmit.zero();
175 
176  write(msg);
177  send();
178  }
179 
180 
181  void onReceive(uint16_t len)
182  {
183  ACASSERT(len == 48, "Incorrect frame length %d", len);
184 
185  millis_offset = millis();
186 
187  NTPMessage& msg = *(NTPMessage*)packet.udpData;
188 
189  epoch = msg.transmit.seconds - secs1900_1970;
190 
191  }
192 
193  void getTime()
194  {
195  //inspired by Paul Stoffregen's Time.cpp https://github.com/PaulStoffregen/Time/blob/master/Time.cpp
196  const uint8_t daysInMonth[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
197 
198  uint32_t ep = epoch + (millis() - millis_offset) / 1000;
199 
200  second = ep % 60;
201  ep /= 60;
202  minute = ep % 60;
203  ep /= 60;
204  hour = ((ep % 24) + timeZone) % 24;
205  ep /= 24;
206  weekDay = ((ep + 4) % 7);
207  year = 0;
208  uint32_t days = 0;
209 
210  while ((uint32_t)(days += (isLeapYear(year) ? 366 : 365)) <= ep)
211  {
212  year++;
213  }
214 
215  days -= isLeapYear(year) ? 366 : 365;
216  ep -= days;
217  days = 0;
218  month = 0;
219 
220  uint16_t d = ep;
221  uint8_t dm;
222 
223  for (uint8_t m = 0; m < 12; m++)
224  {
225  dm = (m == 1) ? (isLeapYear(year) ? 29 : 28) : daysInMonth[m];
226 
227  if (d > dm)
228  {
229  d -= dm;
230  }
231  else
232  {
233  month = m + 1;
234  day = d + 1;
235  break;
236  }
237  }
238 
239  year += 1970;
240 
241  }
242 
243  bool inSync()
244  {
245  return epoch != 0;
246  }
247 
248  void tick()
249  {
250 
251  if (updateTimer == 0 || !inSync())
252  {
253  updateTimer = NTP_UPDATE_INTERVAL_TICKS;
254  syncTime();
255  }
256  else
257  {
258  updateTimer--;
259  }
260 
261 
262  UDPSocket::tick();
263  }
264 
265 
266 }ntp;
267 
268 
269 uint32_t timer;
270 
271 void setup()
272 {
273 
274  Serial.begin(115200);
275  ACross::init();
276 #ifdef ACROSS_ARDUINO
277  ACross::printf_serial_init();
278 #endif
279 
280  printf(PSTR("NTP/UDP Client EtherDune sample\n"));
281  Serial.print(F("Free RAM: ")); Serial.println(ACross::getFreeRam());
282  Serial.println(F("Press any key to start..."));
283 
284  while (!Serial.available());
285 
286 
287  net::localIP = myIP;
291 
292 
293  if (!net::begin(CS_PIN))
294  ACERROR("failed to start EtherDune");
295 
296  ACINFO("waiting for link...");
297 
298  while (!net::isLinkUp());
299 
300  ACINFO("link is up");
301 
302  ntp.start();
303 
304  timer = millis();
305 }
306 
307 
308 
309 void loop()
310 {
311  net::loop();
312 
313 
314  if ((int32_t)(millis() - timer) >= 0)
315  {
316  timer += 1000;
317 
318  if (ntp.inSync())
319  {
320 
321  ntp.getTime();
322 
323  printf_P(PSTR("Current date/time: %.4d-%.2d-%.2d %.2d:%.2d:%.2d\n"), ntp.year, ntp.month, ntp.day, ntp.hour, ntp.minute, ntp.second);
324 
325  }
326  else
327  {
328  Serial.println(F("Waiting for time sync..."));
329  }
330 
331  }
332 
333 }
334 
335 
336 /// \endcond
#define MACAddress_P
Definition: inet.h:242
void zero()
sets the variable to 0.
Definition: inet.h:156
virtual void onReceive(uint16_t len)
Called when a datagram has arrived.
Definition: UDPSocket.cpp:28
#define IPAddress_P
helper macro to store an IP address in PROGMEM
Definition: inet.h:170
Implements the UDP protocol.
Definition: UDPSocket.h:34
uint16_t write(uint16_t len, const byte *data)
In the case of TCP, writes the given data buffer to the socket.
Definition: Socket.cpp:54
static IPAddress_P gatewayIP
static bool begin(uint8_t cspin)
Initializes EtherDune and the underlying hardware
void tick()
This is a timer function that is called every NETWORK_TIMER_RESOLUTION milliseconds on every service...
Definition: UDPSocket.cpp:99
bool send()
Sends the packet currently in the outgoing buffer
Definition: UDPSocket.cpp:45
static IPAddress netmask
Subnet mask.
static IPAddress gatewayIP
IP address of the gateway in this network.
ACROSS_MODULE("ARP")
static const uint8_t CS_PIN
Represents a network byte order 32 bit integer.
Definition: inet.h:109
static MACAddress_P mymac
Represents a network byte order 16 bit integer.
Definition: inet.h:59
static bool isLinkUp()
Determines whether the network link is ready
Definition: ENC28J60.cpp:306
void setup()
static MACAddress localMAC
Ethernet MAC address.
static IPAddress_P myIP
Base data structures for Internet communication.
static void loop()
Gives processing time to EtherDune so that it can check for incoming packets or send queued packets...
static IPAddress localIP
IP address of this application.
static IPAddress_P netmask
void loop()