441 lines
13 KiB
C
441 lines
13 KiB
C
/*
|
|
* FreeRTOS+TCP V2.0.3
|
|
* 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://aws.amazon.com/freertos
|
|
* http://www.FreeRTOS.org
|
|
*/
|
|
|
|
/*
|
|
* 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
|
|
}
|
|
}
|
|
/*-----------------------------------------------------------*/
|