/* | |
* FreeRTOS+TCP V2.0.1 | |
* Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. | |
* | |
* Permission is hereby granted, free of charge, to any person obtaining a copy of | |
* this software and associated documentation files (the "Software"), to deal in | |
* the Software without restriction, including without limitation the rights to | |
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of | |
* the Software, and to permit persons to whom the Software is furnished to do so, | |
* subject to the following conditions: | |
* | |
* The above copyright notice and this permission notice shall be included in all | |
* copies or substantial portions of the Software. | |
* | |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS | |
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR | |
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER | |
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | |
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
* | |
* http://www.FreeRTOS.org | |
* http://aws.amazon.com/freertos | |
* | |
* 1 tab == 4 spaces! | |
*/ | |
/* | |
* NTPDemo.c | |
* | |
* An example of how to lookup a domain using DNS | |
* And also how to send and receive UDP messages to get the NTP time | |
* | |
*/ | |
/* Standard includes. */ | |
#include <stdint.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <time.h> | |
/* FreeRTOS includes. */ | |
#include "FreeRTOS.h" | |
#include "task.h" | |
#include "semphr.h" | |
/* FreeRTOS+TCP includes. */ | |
#include "FreeRTOS_IP.h" | |
#include "FreeRTOS_Sockets.h" | |
#include "FreeRTOS_DNS.h" | |
#include "FreeRTOS_Stream_Buffer.h" | |
/* Use the date & time functions from +FAT. */ | |
#include "ff_time.h" | |
#include "NTPDemo.h" | |
#include "ntpClient.h" | |
#include "date_and_time.h" | |
enum EStatus { | |
EStatusLookup, | |
EStatusAsking, | |
EStatusPause, | |
EStatusFailed, | |
}; | |
static struct SNtpPacket xNTPPacket; | |
#if( ipconfigUSE_CALLBACKS == 0 ) | |
static char cRecvBuffer[ sizeof( struct SNtpPacket ) + 64 ]; | |
#endif | |
static enum EStatus xStatus = EStatusLookup; | |
static const char *pcTimeServers[] = { | |
"0.asia.pool.ntp.org", | |
"0.europe.pool.ntp.org", | |
"0.id.pool.ntp.org", | |
"0.south-america.pool.ntp.org", | |
"0.oceania.pool.ntp.org", | |
"0.north-america.pool.ntp.org" | |
}; | |
static SemaphoreHandle_t xNTPWakeupSem = NULL; | |
static uint32_t ulIPAddressFound; | |
static Socket_t xUDPSocket = NULL; | |
static TaskHandle_t xNTPTaskhandle = NULL; | |
static TickType_t uxSendTime; | |
static void prvNTPTask( void *pvParameters ); | |
static void vSignalTask( void ) | |
{ | |
#if( ipconfigUSE_CALLBACKS == 0 ) | |
if( xUDPSocket != NULL ) | |
{ | |
/* Send a signal to the socket so that the | |
FreeRTOS_recvfrom will get interrupted. */ | |
FreeRTOS_SignalSocket( xUDPSocket ); | |
} | |
else | |
#endif | |
if( xNTPWakeupSem != NULL ) | |
{ | |
xSemaphoreGive( xNTPWakeupSem ); | |
} | |
} | |
void vStartNTPTask( uint16_t usTaskStackSize, UBaseType_t uxTaskPriority ) | |
{ | |
/* The only public function in this module: start a task to contact | |
some NTP server. */ | |
if( xNTPTaskhandle != NULL ) | |
{ | |
switch( xStatus ) | |
{ | |
case EStatusPause: | |
xStatus = EStatusAsking; | |
vSignalTask(); | |
break; | |
case EStatusLookup: | |
FreeRTOS_printf( ( "NTP looking up server\n" ) ); | |
break; | |
case EStatusAsking: | |
FreeRTOS_printf( ( "NTP still asking\n" ) ); | |
break; | |
case EStatusFailed: | |
FreeRTOS_printf( ( "NTP failed somehow\n" ) ); | |
ulIPAddressFound = 0ul; | |
xStatus = EStatusLookup; | |
vSignalTask(); | |
break; | |
} | |
} | |
else | |
{ | |
xUDPSocket = FreeRTOS_socket( FREERTOS_AF_INET, FREERTOS_SOCK_DGRAM, FREERTOS_IPPROTO_UDP ); | |
if( xUDPSocket != NULL ) | |
{ | |
struct freertos_sockaddr xAddress; | |
#if( ipconfigUSE_CALLBACKS != 0 ) | |
BaseType_t xReceiveTimeOut = pdMS_TO_TICKS( 0 ); | |
#else | |
BaseType_t xReceiveTimeOut = pdMS_TO_TICKS( 5000 ); | |
#endif | |
xAddress.sin_addr = 0ul; | |
xAddress.sin_port = FreeRTOS_htons( NTP_PORT ); | |
FreeRTOS_bind( xUDPSocket, &xAddress, sizeof( xAddress ) ); | |
FreeRTOS_setsockopt( xUDPSocket, 0, FREERTOS_SO_RCVTIMEO, &xReceiveTimeOut, sizeof( xReceiveTimeOut ) ); | |
xTaskCreate( prvNTPTask, /* The function that implements the task. */ | |
( const char * ) "NTP client", /* Just a text name for the task to aid debugging. */ | |
usTaskStackSize, /* The stack size is defined in FreeRTOSIPConfig.h. */ | |
NULL, /* The task parameter, not used in this case. */ | |
uxTaskPriority, /* The priority assigned to the task is defined in FreeRTOSConfig.h. */ | |
&xNTPTaskhandle ); /* The task handle. */ | |
} | |
else | |
{ | |
FreeRTOS_printf( ( "Creating socket failed\n" ) ); | |
} | |
} | |
} | |
/*-----------------------------------------------------------*/ | |
static void vDNS_callback( const char *pcName, void *pvSearchID, uint32_t ulIPAddress ) | |
{ | |
char pcBuf[16]; | |
/* The DNS lookup has a result, or it has reached the time-out. */ | |
FreeRTOS_inet_ntoa( ulIPAddress, pcBuf ); | |
FreeRTOS_printf( ( "IP address of %s found: %s\n", pcName, pcBuf ) ); | |
if( ulIPAddressFound == 0ul ) | |
{ | |
ulIPAddressFound = ulIPAddress; | |
} | |
/* For testing: in case DNS doen't respond, still try some NTP server | |
with a known IP-address. */ | |
if( ulIPAddressFound == 0ul ) | |
{ | |
ulIPAddressFound = FreeRTOS_inet_addr_quick( 184, 105, 182, 7 ); | |
/* ulIPAddressFound = FreeRTOS_inet_addr_quick( 103, 242, 70, 4 ); */ | |
} | |
xStatus = EStatusAsking; | |
vSignalTask(); | |
} | |
/*-----------------------------------------------------------*/ | |
static void prvSwapFields( struct SNtpPacket *pxPacket) | |
{ | |
/* NTP messages are big-endian */ | |
pxPacket->rootDelay = FreeRTOS_htonl( pxPacket->rootDelay ); | |
pxPacket->rootDispersion = FreeRTOS_htonl( pxPacket->rootDispersion ); | |
pxPacket->referenceTimestamp.seconds = FreeRTOS_htonl( pxPacket->referenceTimestamp.seconds ); | |
pxPacket->referenceTimestamp.fraction = FreeRTOS_htonl( pxPacket->referenceTimestamp.fraction ); | |
pxPacket->originateTimestamp.seconds = FreeRTOS_htonl( pxPacket->originateTimestamp.seconds ); | |
pxPacket->originateTimestamp.fraction = FreeRTOS_htonl( pxPacket->originateTimestamp.fraction ); | |
pxPacket->receiveTimestamp.seconds = FreeRTOS_htonl( pxPacket->receiveTimestamp.seconds ); | |
pxPacket->receiveTimestamp.fraction = FreeRTOS_htonl( pxPacket->receiveTimestamp.fraction ); | |
pxPacket->transmitTimestamp.seconds = FreeRTOS_htonl( pxPacket->transmitTimestamp.seconds ); | |
pxPacket->transmitTimestamp.fraction = FreeRTOS_htonl( pxPacket->transmitTimestamp.fraction ); | |
} | |
/*-----------------------------------------------------------*/ | |
static void prvNTPPacketInit( ) | |
{ | |
memset (&xNTPPacket, '\0', sizeof( xNTPPacket ) ); | |
xNTPPacket.flags = 0xDB; /* value 0xDB : mode 3 (client), version 3, leap indicator unknown 3 */ | |
xNTPPacket.poll = 10; /* 10 means 1 << 10 = 1024 seconds */ | |
xNTPPacket.precision = 0xFA; /* = 250 = 0.015625 seconds */ | |
xNTPPacket.rootDelay = 0x5D2E; /* 0x5D2E = 23854 or (23854/65535)= 0.3640 sec */ | |
xNTPPacket.rootDispersion = 0x0008CAC8; /* 0x0008CAC8 = 8.7912 seconds */ | |
/* use the recorded NTP time */ | |
time_t uxSecs = FreeRTOS_time( NULL );/* apTime may be NULL, returns seconds */ | |
xNTPPacket.referenceTimestamp.seconds = uxSecs; /* Current time */ | |
xNTPPacket.transmitTimestamp.seconds = uxSecs + 3; | |
/* Transform the contents of the fields from native to big endian. */ | |
prvSwapFields( &xNTPPacket ); | |
} | |
/*-----------------------------------------------------------*/ | |
static void prvReadTime( struct SNtpPacket * pxPacket ) | |
{ | |
FF_TimeStruct_t xTimeStruct; | |
time_t uxPreviousSeconds; | |
time_t uxPreviousMS; | |
time_t uxCurrentSeconds; | |
time_t uxCurrentMS; | |
const char *pcTimeUnit; | |
int32_t ilDiff; | |
TickType_t uxTravelTime; | |
uxTravelTime = xTaskGetTickCount() - uxSendTime; | |
/* Transform the contents of the fields from big to native endian. */ | |
prvSwapFields( pxPacket ); | |
uxCurrentSeconds = pxPacket->receiveTimestamp.seconds - TIME1970; | |
uxCurrentMS = pxPacket->receiveTimestamp.fraction / 4294967; | |
uxCurrentSeconds += uxCurrentMS / 1000; | |
uxCurrentMS = uxCurrentMS % 1000; | |
// Get the last time recorded | |
uxPreviousSeconds = FreeRTOS_get_secs_msec( &uxPreviousMS ); | |
// Set the new time with precision in msec. */ | |
FreeRTOS_set_secs_msec( &uxCurrentSeconds, &uxCurrentMS ); | |
if( uxCurrentSeconds >= uxPreviousSeconds ) | |
{ | |
ilDiff = ( int32_t ) ( uxCurrentSeconds - uxPreviousSeconds ); | |
} | |
else | |
{ | |
ilDiff = 0 - ( int32_t ) ( uxPreviousSeconds - uxCurrentSeconds ); | |
} | |
if( ( ilDiff < -5 ) || ( ilDiff > 5 ) ) | |
{ | |
/* More than 5 seconds difference. */ | |
pcTimeUnit = "sec"; | |
} | |
else | |
{ | |
/* Less than or equal to 5 second difference. */ | |
pcTimeUnit = "ms"; | |
uint32_t ulLowest = ( uxCurrentSeconds <= uxPreviousSeconds ) ? uxCurrentSeconds : uxPreviousSeconds; | |
int32_t iCurMS = 1000 * ( uxCurrentSeconds - ulLowest ) + uxCurrentMS; | |
int32_t iPrevMS = 1000 * ( uxPreviousSeconds - ulLowest ) + uxPreviousMS; | |
ilDiff = iCurMS - iPrevMS; | |
} | |
uxCurrentSeconds -= iTimeZone; | |
FreeRTOS_gmtime_r( &uxCurrentSeconds, &xTimeStruct ); | |
/* | |
378.067 [NTP client] NTP time: 9/11/2015 16:11:19.559 Diff -20 ms (289 ms) | |
379.441 [NTP client] NTP time: 9/11/2015 16:11:20.933 Diff 0 ms (263 ms) | |
*/ | |
FreeRTOS_printf( ("NTP time: %d/%d/%02d %2d:%02d:%02d.%03u Diff %d %s (%lu ms)\n", | |
xTimeStruct.tm_mday, | |
xTimeStruct.tm_mon + 1, | |
xTimeStruct.tm_year + 1900, | |
xTimeStruct.tm_hour, | |
xTimeStruct.tm_min, | |
xTimeStruct.tm_sec, | |
( unsigned )uxCurrentMS, | |
( unsigned )ilDiff, | |
pcTimeUnit, | |
uxTravelTime ) ); | |
/* Remove compiler warnings in case FreeRTOS_printf() is not used. */ | |
( void ) pcTimeUnit; | |
( void ) uxTravelTime; | |
} | |
/*-----------------------------------------------------------*/ | |
#if( ipconfigUSE_CALLBACKS != 0 ) | |
static BaseType_t xOnUDPReceive( Socket_t xSocket, void * pvData, size_t xLength, | |
const struct freertos_sockaddr *pxFrom, const struct freertos_sockaddr *pxDest ) | |
{ | |
if( xLength >= sizeof( xNTPPacket ) ) | |
{ | |
prvReadTime( ( struct SNtpPacket *)pvData ); | |
if( xStatus != EStatusPause ) | |
{ | |
xStatus = EStatusPause; | |
} | |
} | |
vSignalTask(); | |
/* Tell the driver not to store the RX data */ | |
return 1; | |
} | |
/*-----------------------------------------------------------*/ | |
#endif /* ipconfigUSE_CALLBACKS != 0 */ | |
static void prvNTPTask( void *pvParameters ) | |
{ | |
BaseType_t xServerIndex = 3; | |
struct freertos_sockaddr xAddress; | |
#if( ipconfigUSE_CALLBACKS != 0 ) | |
F_TCP_UDP_Handler_t xHandler; | |
#endif /* ipconfigUSE_CALLBACKS != 0 */ | |
xStatus = EStatusLookup; | |
#if( ipconfigSOCKET_HAS_USER_SEMAPHORE != 0 ) || ( ipconfigUSE_CALLBACKS != 0 ) | |
{ | |
xNTPWakeupSem = xSemaphoreCreateBinary(); | |
} | |
#endif | |
#if( ipconfigUSE_CALLBACKS != 0 ) | |
{ | |
memset( &xHandler, '\0', sizeof( xHandler ) ); | |
xHandler.pxOnUDPReceive = xOnUDPReceive; | |
FreeRTOS_setsockopt( xUDPSocket, 0, FREERTOS_SO_UDP_RECV_HANDLER, ( void * ) &xHandler, sizeof( xHandler ) ); | |
} | |
#endif | |
#if( ipconfigSOCKET_HAS_USER_SEMAPHORE != 0 ) | |
{ | |
FreeRTOS_setsockopt( xUDPSocket, 0, FREERTOS_SO_SET_SEMAPHORE, ( void * ) &xNTPWakeupSem, sizeof( xNTPWakeupSem ) ); | |
} | |
#endif | |
for( ; ; ) | |
{ | |
switch( xStatus ) | |
{ | |
case EStatusLookup: | |
if( ( ulIPAddressFound == 0ul ) || ( ulIPAddressFound == ~0ul ) ) | |
{ | |
if( ++xServerIndex == sizeof( pcTimeServers ) / sizeof( pcTimeServers[ 0 ] ) ) | |
{ | |
xServerIndex = 0; | |
} | |
FreeRTOS_printf( ( "Looking up server '%s'\n", pcTimeServers[ xServerIndex ] ) ); | |
FreeRTOS_gethostbyname_a( pcTimeServers[ xServerIndex ], vDNS_callback, (void *)NULL, 1200 ); | |
} | |
else | |
{ | |
xStatus = EStatusAsking; | |
} | |
break; | |
case EStatusAsking: | |
{ | |
char pcBuf[16]; | |
prvNTPPacketInit( ); | |
xAddress.sin_addr = ulIPAddressFound; | |
xAddress.sin_port = FreeRTOS_htons( NTP_PORT ); | |
FreeRTOS_inet_ntoa( xAddress.sin_addr, pcBuf ); | |
FreeRTOS_printf( ( "Sending UDP message to %s:%u\n", | |
pcBuf, | |
FreeRTOS_ntohs( xAddress.sin_port ) ) ); | |
uxSendTime = xTaskGetTickCount( ); | |
FreeRTOS_sendto( xUDPSocket, ( void * )&xNTPPacket, sizeof( xNTPPacket ), 0, &xAddress, sizeof( xAddress ) ); | |
} | |
break; | |
case EStatusPause: | |
break; | |
case EStatusFailed: | |
break; | |
} | |
#if( ipconfigUSE_CALLBACKS != 0 ) | |
{ | |
xSemaphoreTake( xNTPWakeupSem, 5000 ); | |
} | |
#else | |
{ | |
uint32_t xAddressSize; | |
BaseType_t xReturned; | |
xAddressSize = sizeof( xAddress ); | |
xReturned = FreeRTOS_recvfrom( xUDPSocket, ( void * ) cRecvBuffer, sizeof( cRecvBuffer ), 0, &xAddress, &xAddressSize ); | |
switch( xReturned ) | |
{ | |
case 0: | |
case -pdFREERTOS_ERRNO_EAGAIN: | |
case -pdFREERTOS_ERRNO_EINTR: | |
break; | |
default: | |
if( xReturned < sizeof( xNTPPacket ) ) | |
{ | |
FreeRTOS_printf( ( "FreeRTOS_recvfrom: returns %ld\n", xReturned ) ); | |
} | |
else | |
{ | |
prvReadTime( ( struct SNtpPacket *)cRecvBuffer ); | |
if( xStatus != EStatusPause ) | |
{ | |
xStatus = EStatusPause; | |
} | |
} | |
break; | |
} | |
} | |
#endif | |
} | |
} | |
/*-----------------------------------------------------------*/ |