]> git.sur5r.net Git - freertos/blob - FreeRTOS-Plus/Source/FreeRTOS-Plus-TCP/protocols/NTP/NTPDemo.c
Update to MIT licensed FreeRTOS V10.0.0 - see https://www.freertos.org/History.txt
[freertos] / FreeRTOS-Plus / Source / FreeRTOS-Plus-TCP / protocols / NTP / NTPDemo.c
1 /*\r
2  * FreeRTOS+TCP V2.0.0\r
3  * Copyright (C) 2017 Amazon.com, Inc. or its affiliates.  All Rights Reserved.\r
4  *\r
5  * Permission is hereby granted, free of charge, to any person obtaining a copy of\r
6  * this software and associated documentation files (the "Software"), to deal in\r
7  * the Software without restriction, including without limitation the rights to\r
8  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\r
9  * the Software, and to permit persons to whom the Software is furnished to do so,\r
10  * subject to the following conditions:\r
11  *\r
12  * The above copyright notice and this permission notice shall be included in all\r
13  * copies or substantial portions of the Software. If you wish to use our Amazon\r
14  * FreeRTOS name, please do so in a fair use way that does not cause confusion.\r
15  *\r
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\r
18  * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\r
19  * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r
20  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
21  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
22  *\r
23  * http://www.FreeRTOS.org\r
24  * http://aws.amazon.com/freertos\r
25  *\r
26  * 1 tab == 4 spaces!\r
27  */\r
28 \r
29 /*\r
30  * NTPDemo.c\r
31  *\r
32  * An example of how to lookup a domain using DNS\r
33  * And also how to send and receive UDP messages to get the NTP time\r
34  *\r
35  */\r
36 \r
37 /* Standard includes. */\r
38 #include <stdint.h>\r
39 #include <stdio.h>\r
40 #include <stdlib.h>\r
41 #include <time.h>\r
42 \r
43 /* FreeRTOS includes. */\r
44 #include "FreeRTOS.h"\r
45 #include "task.h"\r
46 #include "semphr.h"\r
47 \r
48 /* FreeRTOS+TCP includes. */\r
49 #include "FreeRTOS_IP.h"\r
50 #include "FreeRTOS_Sockets.h"\r
51 #include "FreeRTOS_DNS.h"\r
52 #include "FreeRTOS_Stream_Buffer.h"\r
53 \r
54 /* Use the date & time functions from +FAT. */\r
55 #include "ff_time.h"\r
56 \r
57 #include "NTPDemo.h"\r
58 #include "ntpClient.h"\r
59 \r
60 #include "date_and_time.h"\r
61 \r
62 enum EStatus {\r
63         EStatusLookup,\r
64         EStatusAsking,\r
65         EStatusPause,\r
66         EStatusFailed,\r
67 };\r
68 \r
69 static struct SNtpPacket xNTPPacket;\r
70 \r
71 #if( ipconfigUSE_CALLBACKS == 0 )\r
72         static char cRecvBuffer[ sizeof( struct SNtpPacket ) + 64 ];\r
73 #endif\r
74 \r
75 static enum EStatus xStatus = EStatusLookup;\r
76 \r
77 static const char *pcTimeServers[] = {\r
78         "0.asia.pool.ntp.org",\r
79         "0.europe.pool.ntp.org",\r
80         "0.id.pool.ntp.org",\r
81         "0.south-america.pool.ntp.org",\r
82         "0.oceania.pool.ntp.org",\r
83         "0.north-america.pool.ntp.org"\r
84 };\r
85 \r
86 static SemaphoreHandle_t xNTPWakeupSem = NULL;\r
87 static uint32_t ulIPAddressFound;\r
88 static Socket_t xUDPSocket = NULL;\r
89 static TaskHandle_t xNTPTaskhandle = NULL;\r
90 static TickType_t uxSendTime;\r
91 \r
92 static void prvNTPTask( void *pvParameters );\r
93 \r
94 static void vSignalTask( void )\r
95 {\r
96         #if( ipconfigUSE_CALLBACKS == 0 )\r
97         if( xUDPSocket != NULL )\r
98         {\r
99                 /* Send a signal to the socket so that the\r
100                 FreeRTOS_recvfrom will get interrupted. */\r
101                 FreeRTOS_SignalSocket( xUDPSocket );\r
102         }\r
103         else\r
104         #endif\r
105         if( xNTPWakeupSem != NULL )\r
106         {\r
107                 xSemaphoreGive( xNTPWakeupSem );\r
108         }\r
109 }\r
110 \r
111 void vStartNTPTask( uint16_t usTaskStackSize, UBaseType_t uxTaskPriority )\r
112 {\r
113         /* The only public function in this module: start a task to contact\r
114         some NTP server. */\r
115 \r
116         if( xNTPTaskhandle != NULL )\r
117         {\r
118                 switch( xStatus )\r
119                 {\r
120                 case EStatusPause:\r
121                         xStatus = EStatusAsking;\r
122                         vSignalTask();\r
123                         break;\r
124                 case EStatusLookup:\r
125                         FreeRTOS_printf( ( "NTP looking up server\n" ) );\r
126                         break;\r
127                 case EStatusAsking:\r
128                         FreeRTOS_printf( ( "NTP still asking\n" ) );\r
129                         break;\r
130                 case EStatusFailed:\r
131                         FreeRTOS_printf( ( "NTP failed somehow\n" ) );\r
132                         ulIPAddressFound = 0ul;\r
133                         xStatus = EStatusLookup;\r
134                         vSignalTask();\r
135                         break;\r
136                 }\r
137         }\r
138         else\r
139         {\r
140                 xUDPSocket = FreeRTOS_socket( FREERTOS_AF_INET, FREERTOS_SOCK_DGRAM, FREERTOS_IPPROTO_UDP );\r
141                 if( xUDPSocket != NULL )\r
142                 {\r
143                 struct freertos_sockaddr xAddress;\r
144                 #if( ipconfigUSE_CALLBACKS != 0 )\r
145                         BaseType_t xReceiveTimeOut = pdMS_TO_TICKS( 0 );\r
146                 #else\r
147                         BaseType_t xReceiveTimeOut = pdMS_TO_TICKS( 5000 );\r
148                 #endif\r
149 \r
150                         xAddress.sin_addr = 0ul;\r
151                         xAddress.sin_port = FreeRTOS_htons( NTP_PORT );\r
152 \r
153                         FreeRTOS_bind( xUDPSocket, &xAddress, sizeof( xAddress ) );\r
154                         FreeRTOS_setsockopt( xUDPSocket, 0, FREERTOS_SO_RCVTIMEO, &xReceiveTimeOut, sizeof( xReceiveTimeOut ) );\r
155                         xTaskCreate(    prvNTPTask,                                             /* The function that implements the task. */\r
156                                                         ( const char * ) "NTP client",  /* Just a text name for the task to aid debugging. */\r
157                                                         usTaskStackSize,                                /* The stack size is defined in FreeRTOSIPConfig.h. */\r
158                                                         NULL,                                                   /* The task parameter, not used in this case. */\r
159                                                         uxTaskPriority,                                 /* The priority assigned to the task is defined in FreeRTOSConfig.h. */\r
160                                                         &xNTPTaskhandle );                              /* The task handle. */\r
161                 }\r
162                 else\r
163                 {\r
164                         FreeRTOS_printf( ( "Creating socket failed\n" ) );\r
165                 }\r
166         }\r
167 }\r
168 /*-----------------------------------------------------------*/\r
169 \r
170 static void vDNS_callback( const char *pcName, void *pvSearchID, uint32_t ulIPAddress )\r
171 {\r
172 char pcBuf[16];\r
173 \r
174         /* The DNS lookup has a result, or it has reached the time-out. */\r
175         FreeRTOS_inet_ntoa( ulIPAddress, pcBuf );\r
176         FreeRTOS_printf( ( "IP address of %s found: %s\n", pcName, pcBuf ) );\r
177         if( ulIPAddressFound == 0ul )\r
178         {\r
179                 ulIPAddressFound = ulIPAddress;\r
180         }\r
181         /* For testing: in case DNS doen't respond, still try some NTP server\r
182         with a known IP-address. */\r
183         if( ulIPAddressFound == 0ul )\r
184         {\r
185                 ulIPAddressFound = FreeRTOS_inet_addr_quick( 184, 105, 182, 7 );\r
186 /*              ulIPAddressFound = FreeRTOS_inet_addr_quick( 103, 242,  70, 4 );        */\r
187         }\r
188         xStatus = EStatusAsking;\r
189 \r
190         vSignalTask();\r
191 }\r
192 /*-----------------------------------------------------------*/\r
193 \r
194 static void prvSwapFields( struct SNtpPacket *pxPacket)\r
195 {\r
196         /* NTP messages are big-endian */\r
197         pxPacket->rootDelay = FreeRTOS_htonl( pxPacket->rootDelay );\r
198         pxPacket->rootDispersion = FreeRTOS_htonl( pxPacket->rootDispersion );\r
199 \r
200         pxPacket->referenceTimestamp.seconds = FreeRTOS_htonl( pxPacket->referenceTimestamp.seconds );\r
201         pxPacket->referenceTimestamp.fraction = FreeRTOS_htonl( pxPacket->referenceTimestamp.fraction );\r
202 \r
203         pxPacket->originateTimestamp.seconds = FreeRTOS_htonl( pxPacket->originateTimestamp.seconds );\r
204         pxPacket->originateTimestamp.fraction = FreeRTOS_htonl( pxPacket->originateTimestamp.fraction );\r
205 \r
206         pxPacket->receiveTimestamp.seconds = FreeRTOS_htonl( pxPacket->receiveTimestamp.seconds );\r
207         pxPacket->receiveTimestamp.fraction = FreeRTOS_htonl( pxPacket->receiveTimestamp.fraction );\r
208 \r
209         pxPacket->transmitTimestamp.seconds = FreeRTOS_htonl( pxPacket->transmitTimestamp.seconds );\r
210         pxPacket->transmitTimestamp.fraction = FreeRTOS_htonl( pxPacket->transmitTimestamp.fraction );\r
211 }\r
212 /*-----------------------------------------------------------*/\r
213 \r
214 static void prvNTPPacketInit( )\r
215 {\r
216         memset (&xNTPPacket, '\0', sizeof( xNTPPacket ) );\r
217 \r
218         xNTPPacket.flags = 0xDB;                                /* value 0xDB : mode 3 (client), version 3, leap indicator unknown 3 */\r
219         xNTPPacket.poll = 10;                                   /* 10 means 1 << 10 = 1024 seconds */\r
220         xNTPPacket.precision = 0xFA;                    /* = 250 = 0.015625 seconds */\r
221         xNTPPacket.rootDelay = 0x5D2E;                  /* 0x5D2E = 23854 or (23854/65535)= 0.3640 sec */\r
222         xNTPPacket.rootDispersion = 0x0008CAC8; /* 0x0008CAC8 = 8.7912  seconds */\r
223 \r
224         /* use the recorded NTP time */\r
225         time_t uxSecs = FreeRTOS_time( NULL );/* apTime may be NULL, returns seconds */\r
226 \r
227         xNTPPacket.referenceTimestamp.seconds = uxSecs; /* Current time */\r
228         xNTPPacket.transmitTimestamp.seconds = uxSecs + 3;\r
229 \r
230         /* Transform the contents of the fields from native to big endian. */\r
231         prvSwapFields( &xNTPPacket );\r
232 }\r
233 /*-----------------------------------------------------------*/\r
234 \r
235 static void prvReadTime( struct SNtpPacket * pxPacket )\r
236 {\r
237         FF_TimeStruct_t xTimeStruct;\r
238         time_t uxPreviousSeconds;\r
239         time_t uxPreviousMS;\r
240 \r
241         time_t uxCurrentSeconds;\r
242         time_t uxCurrentMS;\r
243 \r
244         const char *pcTimeUnit;\r
245         int32_t ilDiff;\r
246         TickType_t uxTravelTime;\r
247 \r
248         uxTravelTime = xTaskGetTickCount() - uxSendTime;\r
249 \r
250         /* Transform the contents of the fields from big to native endian. */\r
251         prvSwapFields( pxPacket );\r
252 \r
253         uxCurrentSeconds = pxPacket->receiveTimestamp.seconds - TIME1970;\r
254         uxCurrentMS = pxPacket->receiveTimestamp.fraction / 4294967;\r
255         uxCurrentSeconds += uxCurrentMS / 1000;\r
256         uxCurrentMS = uxCurrentMS % 1000;\r
257 \r
258         // Get the last time recorded\r
259         uxPreviousSeconds = FreeRTOS_get_secs_msec( &uxPreviousMS );\r
260 \r
261         // Set the new time with precision in msec. */\r
262         FreeRTOS_set_secs_msec( &uxCurrentSeconds, &uxCurrentMS );\r
263 \r
264         if( uxCurrentSeconds >= uxPreviousSeconds )\r
265         {\r
266                 ilDiff = ( int32_t ) ( uxCurrentSeconds - uxPreviousSeconds );\r
267         }\r
268         else\r
269         {\r
270                 ilDiff = 0 - ( int32_t ) ( uxPreviousSeconds - uxCurrentSeconds );\r
271         }\r
272 \r
273         if( ( ilDiff < -5 ) || ( ilDiff > 5 ) )\r
274         {\r
275                 /* More than 5 seconds difference. */\r
276                 pcTimeUnit = "sec";\r
277         }\r
278         else\r
279         {\r
280                 /* Less than or equal to 5 second difference. */\r
281                 pcTimeUnit = "ms";\r
282                 uint32_t ulLowest = ( uxCurrentSeconds <= uxPreviousSeconds ) ? uxCurrentSeconds : uxPreviousSeconds;\r
283                 int32_t iCurMS = 1000 * ( uxCurrentSeconds - ulLowest ) + uxCurrentMS;\r
284                 int32_t iPrevMS = 1000 * ( uxPreviousSeconds - ulLowest ) + uxPreviousMS;\r
285                 ilDiff = iCurMS - iPrevMS;\r
286         }\r
287         uxCurrentSeconds -= iTimeZone;\r
288 \r
289         FreeRTOS_gmtime_r( &uxCurrentSeconds, &xTimeStruct );\r
290 \r
291         /*\r
292                 378.067 [NTP client] NTP time: 9/11/2015 16:11:19.559 Diff -20 ms (289 ms)\r
293                 379.441 [NTP client] NTP time: 9/11/2015 16:11:20.933 Diff 0 ms (263 ms)\r
294         */\r
295 \r
296         FreeRTOS_printf( ("NTP time: %d/%d/%02d %2d:%02d:%02d.%03u Diff %d %s (%lu ms)\n",\r
297                 xTimeStruct.tm_mday,\r
298                 xTimeStruct.tm_mon + 1,\r
299                 xTimeStruct.tm_year + 1900,\r
300                 xTimeStruct.tm_hour,\r
301                 xTimeStruct.tm_min,\r
302                 xTimeStruct.tm_sec,\r
303                 ( unsigned )uxCurrentMS,\r
304                 ( unsigned )ilDiff,\r
305                 pcTimeUnit,\r
306                 uxTravelTime ) );\r
307 \r
308         /* Remove compiler warnings in case FreeRTOS_printf() is not used. */\r
309         ( void ) pcTimeUnit;\r
310         ( void ) uxTravelTime;\r
311 }\r
312 /*-----------------------------------------------------------*/\r
313 \r
314 #if( ipconfigUSE_CALLBACKS != 0 )\r
315 \r
316         static BaseType_t xOnUDPReceive( Socket_t xSocket, void * pvData, size_t xLength,\r
317                 const struct freertos_sockaddr *pxFrom, const struct freertos_sockaddr *pxDest )\r
318         {\r
319                 if( xLength >= sizeof( xNTPPacket ) )\r
320                 {\r
321                         prvReadTime( ( struct SNtpPacket *)pvData );\r
322                         if( xStatus != EStatusPause )\r
323                         {\r
324                                 xStatus = EStatusPause;\r
325                         }\r
326                 }\r
327                 vSignalTask();\r
328                 /* Tell the driver not to store the RX data */\r
329                 return 1;\r
330         }\r
331         /*-----------------------------------------------------------*/\r
332 \r
333 #endif  /* ipconfigUSE_CALLBACKS != 0 */\r
334 \r
335 static void prvNTPTask( void *pvParameters )\r
336 {\r
337 BaseType_t xServerIndex = 3;\r
338 struct freertos_sockaddr xAddress;\r
339 #if( ipconfigUSE_CALLBACKS != 0 )\r
340         F_TCP_UDP_Handler_t xHandler;\r
341 #endif /* ipconfigUSE_CALLBACKS != 0 */\r
342 \r
343         xStatus = EStatusLookup;\r
344         #if( ipconfigSOCKET_HAS_USER_SEMAPHORE != 0 ) || ( ipconfigUSE_CALLBACKS != 0 )\r
345         {\r
346                 xNTPWakeupSem = xSemaphoreCreateBinary();\r
347         }\r
348         #endif\r
349 \r
350         #if( ipconfigUSE_CALLBACKS != 0 )\r
351         {\r
352                 memset( &xHandler, '\0', sizeof( xHandler ) );\r
353                 xHandler.pxOnUDPReceive = xOnUDPReceive;\r
354                 FreeRTOS_setsockopt( xUDPSocket, 0, FREERTOS_SO_UDP_RECV_HANDLER, ( void * ) &xHandler, sizeof( xHandler ) );\r
355         }\r
356         #endif\r
357         #if( ipconfigSOCKET_HAS_USER_SEMAPHORE != 0 )\r
358         {\r
359                 FreeRTOS_setsockopt( xUDPSocket, 0, FREERTOS_SO_SET_SEMAPHORE, ( void * ) &xNTPWakeupSem, sizeof( xNTPWakeupSem ) );\r
360         }\r
361         #endif\r
362         for( ; ; )\r
363         {\r
364                 switch( xStatus )\r
365                 {\r
366                 case EStatusLookup:\r
367                         if( ( ulIPAddressFound == 0ul ) || ( ulIPAddressFound == ~0ul ) )\r
368                         {\r
369                                 if( ++xServerIndex == sizeof( pcTimeServers ) / sizeof( pcTimeServers[ 0 ] ) )\r
370                                 {\r
371                                         xServerIndex = 0;\r
372                                 }\r
373                                 FreeRTOS_printf( ( "Looking up server '%s'\n", pcTimeServers[ xServerIndex ] ) );\r
374                                 FreeRTOS_gethostbyname_a( pcTimeServers[ xServerIndex ], vDNS_callback, (void *)NULL, 1200 );\r
375                         }\r
376                         else\r
377                         {\r
378                                 xStatus = EStatusAsking;\r
379                         }\r
380                         break;\r
381 \r
382                 case EStatusAsking:\r
383                         {\r
384                         char pcBuf[16];\r
385 \r
386                                 prvNTPPacketInit( );\r
387                                 xAddress.sin_addr = ulIPAddressFound;\r
388                                 xAddress.sin_port = FreeRTOS_htons( NTP_PORT );\r
389 \r
390                                 FreeRTOS_inet_ntoa( xAddress.sin_addr, pcBuf );\r
391                                 FreeRTOS_printf( ( "Sending UDP message to %s:%u\n",\r
392                                         pcBuf,\r
393                                         FreeRTOS_ntohs( xAddress.sin_port ) ) );\r
394 \r
395                                 uxSendTime = xTaskGetTickCount( );\r
396                                 FreeRTOS_sendto( xUDPSocket, ( void * )&xNTPPacket, sizeof( xNTPPacket ), 0, &xAddress, sizeof( xAddress ) );\r
397                         }\r
398                         break;\r
399 \r
400                 case EStatusPause:\r
401                         break;\r
402 \r
403                 case EStatusFailed:\r
404                         break;\r
405                 }\r
406 \r
407                 #if( ipconfigUSE_CALLBACKS != 0 )\r
408                 {\r
409                         xSemaphoreTake( xNTPWakeupSem, 5000 );\r
410                 }\r
411                 #else\r
412                 {\r
413                 uint32_t xAddressSize;\r
414                 BaseType_t xReturned;\r
415 \r
416                         xAddressSize = sizeof( xAddress );\r
417                         xReturned = FreeRTOS_recvfrom( xUDPSocket, ( void * ) cRecvBuffer, sizeof( cRecvBuffer ), 0, &xAddress, &xAddressSize );\r
418                         switch( xReturned )\r
419                         {\r
420                         case 0:\r
421                         case -pdFREERTOS_ERRNO_EAGAIN:\r
422                         case -pdFREERTOS_ERRNO_EINTR:\r
423                                 break;\r
424                         default:\r
425                                 if( xReturned < sizeof( xNTPPacket ) )\r
426                                 {\r
427                                         FreeRTOS_printf( ( "FreeRTOS_recvfrom: returns %ld\n", xReturned ) );\r
428                                 }\r
429                                 else\r
430                                 {\r
431                                         prvReadTime( ( struct SNtpPacket *)cRecvBuffer );\r
432                                         if( xStatus != EStatusPause )\r
433                                         {\r
434                                                 xStatus = EStatusPause;\r
435                                         }\r
436                                 }\r
437                                 break;\r
438                         }\r
439                 }\r
440                 #endif\r
441         }\r
442 }\r
443 /*-----------------------------------------------------------*/\r