A time interface class. This class replicates the normal time functions, but goes a couple of steps further. mbed library 82 and prior has a defective gmtime function. Also, this class enables access to setting the time, and adjusting the accuracy of the RTC.
Dependents: CI-data-logger-server WattEye X10Svr SSDP_Server
Revision 21:f3818e2e0370, committed 2017-11-21
- Comitter:
- WiredHome
- Date:
- Tue Nov 21 17:04:12 2017 +0000
- Parent:
- 20:5ca2c94d46b8
- Child:
- 22:72ce9bd14c5e
- Commit message:
- Updates for OS5 and integrate NTPClient for network time sync.
Changed in this revision
--- a/NTPClient.lib Mon Nov 20 17:09:48 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -http://mbed.org/users/WiredHome/code/NTPClient/#0a9c48cb2b10
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/NTPClient/NTPClient.cpp Tue Nov 21 17:04:12 2017 +0000 @@ -0,0 +1,354 @@ +/* NTPClient.cpp */ +/* Copyright (C) 2012 mbed.org, MIT License + * + * 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. + */ +#include "mbed.h" //time() and set_time() + +#include "EthernetInterface.h" +#include "UDPSocket.h" +#include "Socket.h" +#include "NTPClient.h" + + +//#define DEBUG "NTPc" + +#if (defined(DEBUG)) +#include <cstdio> +#define INFO(x, ...) std::printf("[INF %s %4d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__); +#define WARN(x, ...) std::printf("[WRN %s %4d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__); +#define ERR(x, ...) std::printf("[ERR %s %4d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__); +static void HexDump(const char * title, void * pT, int count) +{ + int i; + uint8_t * p = (uint8_t *)pT; + char buf[100] = "0000: "; + + if (*title) + INFO("%s", title); + for (i=0; i<count; ) { + sprintf(buf + strlen(buf), "%02X ", *(p+i)); + if ((++i & 0x0F) == 0x00) { + INFO("%s", buf); + if (i < count) + sprintf(buf, "%04X: ", i); + else + buf[0] = '\0'; + } + } + if (strlen(buf)) + INFO("%s", buf); +} +#else +//Disable debug +#define INFO(x, ...) +#define WARN(x, ...) +#define ERR(x, ...) +#define HexDump(a,b,c) +#endif + + +#define NTP_PORT 123 +#define NTP_CLIENT_PORT 0 //Random port +#define NTP_TIMESTAMP_DELTA 2208988800ull //Diff btw a UNIX timestamp (Starting Jan, 1st 1970) and a NTP timestamp (Starting Jan, 1st 1900) + +#if 0 && MBED_MAJOR_VERSION == 5 +#define htonl(x) ((((x) & 0x000000ffUL) << 24) | \ + (((x) & 0x0000ff00UL) << 8) | \ + (((x) & 0x00ff0000UL) >> 8) | \ + (((x) & 0xff000000UL) >> 24)) +#define ntohl(x) htonl(x) +#endif + +NTPClient::NTPClient(EthernetInterface * _net) : m_sock() +{ + net = _net; +} + + +NTPResult NTPClient::setTime(const char* host, uint16_t port, uint32_t timeout) +{ +#ifdef DEBUG + time_t ctTime; + ctTime = time(NULL); + INFO("Time is currently (UTC): %s", ctime(&ctTime)); +#endif + + // + // MBED OS 5 + // +#if MBED_MAJOR_VERSION == 5 + + struct NTPPacket pkt; + + SocketAddress nist; + int ret_gethostbyname = net->gethostbyname(host, &nist); + INFO("gethostbyname(%s) returned %d", host, ret_gethostbyname); + if (ret_gethostbyname < 0) { + return NTP_DNS; // Network error on DNS lookup + } + + nist.set_port(port); + INFO("set_port(%d)", port); + + time_t tQueryTime = time(NULL); + // + //Prepare NTP Packet for the query: + // + pkt.li = 0; //Leap Indicator : No warning + pkt.vn = 4; //Version Number : 4 + pkt.mode = 3; //Client mode + pkt.stratum = 0; //Not relevant here + pkt.poll = 0; //Not significant as well + pkt.precision = 0; //Neither this one is + pkt.rootDelay = 0; //Or this one + pkt.rootDispersion = 0; //Or that one + pkt.refId = 0; //... + pkt.refTm_s = 0; + pkt.origTm_s = 0; + pkt.rxTm_s = 0; + pkt.txTm_s = NTP_TIMESTAMP_DELTA + tQueryTime; + pkt.refTm_f = pkt.origTm_f = pkt.rxTm_f = pkt.txTm_f = 0; + + INFO(" ctime: %s", ctime(&tQueryTime)); + + //WARN: We are in LE format, network byte order is BE + INFO(" pkt.txTm_s %08X, %u, time to send to server", pkt.txTm_s, pkt.txTm_s); + pkt.txTm_s = htonl(pkt.txTm_s); + INFO(" pkt.txTm_s %08X, %u, time to send to server", pkt.txTm_s, pkt.txTm_s); + HexDump("sending", &pkt, sizeof(NTPPacket)); + + UDPSocket sock; + nsapi_error_t ret = sock.open(net); + INFO("sock.open(...) returned %d", ret); + + sock.set_timeout(timeout); + + int ret_send = sock.sendto(nist, (void *)&pkt, sizeof(NTPPacket)); + INFO("sock.sendto(...) returned %d", ret_send); + + SocketAddress source; + source.set_ip_address(nist.get_ip_address()); + const int n = sock.recvfrom(&source, (void *)&pkt, sizeof(NTPPacket)); + uint32_t destTimeStamp = NTP_TIMESTAMP_DELTA + time(NULL); + INFO("recvfrom(...) returned %d", n); + + if (pkt.stratum == 0) { //Kiss of death message : Not good ! + ERR("Kissed to death!"); + m_sock.close(); + return NTP_PRTCL; + } + + HexDump("received", &pkt, sizeof(NTPPacket)); + + //Correct Endianness + #if 1 + pkt.refTm_s = ntohl( pkt.refTm_s ); + pkt.refTm_f = ntohl( pkt.refTm_f ); + pkt.origTm_s = ntohl( pkt.origTm_s ); + pkt.origTm_f = ntohl( pkt.origTm_f ); + pkt.rxTm_s = ntohl( pkt.rxTm_s ); + pkt.rxTm_f = ntohl( pkt.rxTm_f ); + pkt.txTm_s = ntohl( pkt.txTm_s ); + pkt.txTm_f = ntohl( pkt.txTm_f ); + #endif + + #ifdef DEBUG + const char *ModeList[] = { + "reserved", "symmetric active", "symmetric passive", "client", + "server", "broadcast", "reserved for NTP ctrl", "reserved for priv use" + }; + INFO(" pkt.li (Leap Ind) %d", pkt.li); + INFO(" pkt.vn (Vers #) %d", pkt.vn); + INFO(" pkt.mode %d, mode %s", pkt.mode, ModeList[pkt.mode]); + INFO(" pkt.stratum %d, 0=kiss-o'-death, 1=prim, 2=secd", pkt.stratum); + INFO(" pkt.poll %d", pkt.poll); + INFO(" pkt.precision %d", pkt.precision); + INFO(" pkt.rootDelay %d", pkt.rootDelay); + INFO(" pkt.rootDispersion %d", pkt.rootDispersion); + INFO(" pkt.refId %08X, %u", pkt.refId, pkt.refId); + INFO(" pkt.refTm_s %08X, %u, ref time (last set)", pkt.refTm_s, pkt.refTm_s); + INFO(" pkt.origTm_s %08X, %u, time sent from client", pkt.origTm_s, pkt.origTm_s); + INFO(" pkt.rxTm_s %08X, %u, time rcvd at server", pkt.rxTm_s, pkt.rxTm_s); + INFO(" pkt.txTm_s %08X, %u, time sent from server", pkt.txTm_s, pkt.txTm_s); + INFO(" pkt.refTm_f %08X, %u, fraction", pkt.refTm_f, pkt.refTm_f); + #endif + + ret = sock.close(); + INFO("sock.close() returned %d", ret); + + if (n == sizeof(NTPPacket)) { + int64_t t; + #ifdef DEBUG + uint32_t T1, T2, T3, T4; + T1 = pkt.origTm_s; + T2 = pkt.rxTm_s; + T3 = pkt.txTm_s; + T4 = destTimeStamp; + + uint32_t d = (T4 - T1) - (T3 - T2); + INFO("d = %d = (%d - %d) - (%d - %d)", d, T4,T1,T3,T2); + INFO("d = %d = ( %d ) - ( %d )", d, (T4-T1), (T3-T2)); + t = ((T2 - T1) + (T3 - T4))/2; + INFO("t = %lld = ((%d - %d) + (%d - %d)) / 2;", t, T2,T1,T3,T4); + INFO("t = %lld = (( %d ) + ( %d )) / 2", t, (T2-T1), (T3-T4)); + #endif + + // Modification by David Smart + // The setTime function was computing the offset incorrectly as the value was promoted to 64-bit. + // The side effect was that a negative offset ended up as a very large positive (e.g. jump from + // 2016 to 2084). This change revises that computation. + t = (((int64_t)pkt.rxTm_s - pkt.origTm_s) + ((int64_t)pkt.txTm_s - destTimeStamp))/2; + set_time( time(NULL) + t ); + } else { + ERR("bad return from recvfrom() %d", n); + if (n < 0) { + // Network error + return NTP_CONN; + } else { + // No or partial data returned + return NTP_PRTCL; + } + } + +#else // MBED OS 2 + + //Create & bind socket + INFO("Binding socket"); + m_sock.bind(0); //Bind to a random port + + // + // MBED OS 2 + // + m_sock.set_blocking(false, timeout); //Set not blocking + + struct NTPPacket pkt; + + //Now ping the server and wait for response + INFO("Ping"); + //Prepare NTP Packet: + pkt.li = 0; //Leap Indicator : No warning + pkt.vn = 4; //Version Number : 4 + pkt.mode = 3; //Client mode + pkt.stratum = 0; //Not relevant here + pkt.poll = 0; //Not significant as well + pkt.precision = 0; //Neither this one is + + pkt.rootDelay = 0; //Or this one + pkt.rootDispersion = 0; //Or that one + pkt.refId = 0; //... + + pkt.refTm_s = 0; + pkt.origTm_s = 0; + pkt.rxTm_s = 0; + pkt.txTm_s = htonl( NTP_TIMESTAMP_DELTA + time(NULL) ); //WARN: We are in LE format, network byte order is BE + INFO("pkt.txTm_s = %u", ntohl(pkt.txTm_s) ); + pkt.refTm_f = pkt.origTm_f = pkt.rxTm_f = pkt.txTm_f = 0; + + HexDump("NTP Post", (uint8_t *)&pkt, sizeof(NTPPacket)); + + Endpoint outEndpoint; + INFO("outEndpoint instantiated"); + if( outEndpoint.set_address(host, port) < 0) { + m_sock.close(); + return NTP_DNS; + } + INFO("outEndpoint: %s:%d", outEndpoint.get_address(), outEndpoint.get_port()); + //Set timeout, non-blocking and wait using select + int ret = m_sock.sendTo( outEndpoint, (char*)&pkt, sizeof(NTPPacket) ); + if (ret < 0 ) { + ERR("Could not send packet"); + m_sock.close(); + return NTP_CONN; + } + + //Read response + Endpoint inEndpoint; + INFO(" inEndpoint instantiated: %s.", inEndpoint.get_address()); + // Set the inEndpoint address property + inEndpoint.set_address(outEndpoint.get_address(), 0); + INFO(" inEndpoint: %s", inEndpoint.get_address()); + + INFO("Pong"); + int loopLimit = 20; // semi-randomly selected so it doesn't hang forever here... + do { + ret = m_sock.receiveFrom( inEndpoint, (char*)&pkt, sizeof(NTPPacket) ); + if(ret < 0) { + ERR("Could not receive packet"); + m_sock.close(); + return NTP_CONN; + } + INFO("."); + loopLimit--; + } while( strcmp(outEndpoint.get_address(), inEndpoint.get_address()) != 0 && loopLimit > 0); + + if(ret < (int)sizeof(NTPPacket)) { //TODO: Accept chunks + ERR("Receive packet size does not match"); + m_sock.close(); + return NTP_PRTCL; + } + + if( pkt.stratum == 0) { //Kiss of death message : Not good ! + ERR("Kissed to death!"); + m_sock.close(); + return NTP_PRTCL; + } + + HexDump("NTP Info", (uint8_t *)&pkt, sizeof(NTPPacket)); + + //Correct Endianness + pkt.refTm_s = ntohl( pkt.refTm_s ); + pkt.refTm_f = ntohl( pkt.refTm_f ); + pkt.origTm_s = ntohl( pkt.origTm_s ); + pkt.origTm_f = ntohl( pkt.origTm_f ); + pkt.rxTm_s = ntohl( pkt.rxTm_s ); + pkt.rxTm_f = ntohl( pkt.rxTm_f ); + pkt.txTm_s = ntohl( pkt.txTm_s ); + pkt.txTm_f = ntohl( pkt.txTm_f ); + + //Compute offset, see RFC 4330 p.13 + uint32_t destTm_s = (NTP_TIMESTAMP_DELTA + time(NULL)); + INFO("destTm_s = %u", destTm_s); + INFO("pkt.txTm_s = %u", pkt.txTm_s ); + + // Modification by David Smart + // The setTime function was computing the offset incorrectly as the value was promoted to 64-bit. + // The side effect was that a negative offset ended up as a very large positive (e.g. jump from + // 2016 to 2084). This change revises that computation. + int64_t offset = ( ((int64_t)pkt.rxTm_s - pkt.origTm_s ) + ((int64_t) pkt.txTm_s - destTm_s ) ) / 2; //Avoid overflow + + // delay is not needed, this was for diagnostic purposes only. + //int64_t delay = ((int64_t) destTm_s - pkt.origTm_s) - ((int64_t) pkt.txTm_s - pkt.rxTm_s); + INFO("txTm_s @%u", pkt.txTm_s); + INFO("origTm_s @%u", pkt.origTm_s); + INFO("rxTm_s @%u", pkt.rxTm_s); + INFO("destTm_s @%u", destTm_s); + INFO("Offset: %lld", offset); + //INFO("Delay: %lld", delay); + INFO(" time: %u", time(NULL)); + //Set time accordingly + set_time( time(NULL) + offset ); + + m_sock.close(); +#endif // OS version + + #ifdef DEBUG + ctTime = time(NULL); + INFO(" ctime: %s", ctime(&ctTime)); + #endif + return NTP_OK; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/NTPClient/NTPClient.h Tue Nov 21 17:04:12 2017 +0000 @@ -0,0 +1,212 @@ +/* NTPClient.h */ +/* Copyright (C) 2012 mbed.org, MIT License + * + * 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. + * + */ + +/** \file +NTP Client header file +*/ + +#ifndef NTPCLIENT_H_ +#define NTPCLIENT_H_ + +#include <cstdint> + +using std::uint8_t; +using std::uint16_t; +using std::uint32_t; + +#include "EthernetInterface.h" +#include "UDPSocket.h" + +#define NTP_DEFAULT_PORT 123 +#define NTP_DEFAULT_TIMEOUT 4000 + +///NTP client results +enum NTPResult { + NTP_OK = 0, ///<Success + NTP_DNS, ///<Could not resolve name + NTP_PRTCL, ///<Protocol error + NTP_TIMEOUT, ///<Connection timeout + NTP_CONN, ///<Connection error +}; + +/** NTP Client to update the mbed's RTC using a remote time server +* +*/ +class NTPClient +{ +public: + /** + Instantiate the NTP client + * @param[in] net is a pointer to the EthernetInterface + */ + NTPClient(EthernetInterface * net = NULL); + + /**Get current time (blocking) + Update the time using the server host + Blocks until completion + @param[in] host NTP server IPv4 address or hostname (will be resolved via DNS) + @param[in] port port to use; defaults to 123 + @param[in] timeout waiting timeout in ms (osWaitForever for blocking function, not recommended) + @return 0 on success, NTP error code (<0) on failure + */ + NTPResult setTime(const char* host, uint16_t port = NTP_DEFAULT_PORT, uint32_t timeout = NTP_DEFAULT_TIMEOUT); //Blocking + +private: + EthernetInterface * net; + + /// The NTP Packet, as defined in RFC 4330 for Simple NTP + /// + /// @caution We are in Little Endian locally, and Big Endian at the Network + /// so some transformations are required. + /// + struct NTPPacket { + /// Mode is a 3-bit number indicating the protocol mode + /// + /// values + /// - 0 = reserved, + /// - 1 = symmetric active + /// - 2 = symmetric passive + /// - 3 = client + /// - 4 = server + /// - 5 = broadcast + /// - 6 = reserved for NTP control message + /// - 7 = reserved for private use + /// + unsigned mode : 3; + + /// Version Number + /// + /// The current version number for NTP/SNTP is 4 + /// + unsigned vn : 3; + + /// Leap Indicator + /// + /// values + /// - 0 = no warning, + /// - 1 = last min had 61s, + /// - 2 = last min had 59s, + /// - 3 = alarm - clock not in sync + /// + unsigned li : 2; + + /// This is indicates the stratum + /// + /// values + /// - 0 = kiss-o'-death + /// - 1 = primary reference + /// - 2-15 = secondary reference + /// - 16-255 = reserved + /// + uint8_t stratum; + + /// Poll interval + /// + /// This is an exponent of two, where the resulting value is the max + /// interval between successive messages in seconds. + /// + uint8_t poll; + + /// Precision of the clock + /// + /// This is an eight-bit signed integer used as an exponent of + /// two, where the resulting value is the precision of the system clock + /// in seconds. This field is significant only in server messages, where + /// the values range from -6 for mains-frequency clocks to -20 for + /// microsecond clocks found in some workstations. + /// + int8_t precision; + //32 bits header + + /// Root Delay + /// + /// This is a 32-bit signed fixed-point number indicating the + /// total roundtrip delay to the primary reference source, in seconds + /// with the fraction point between bits 15 and 16. Note that this + /// variable can take on both positive and negative values, depending on + /// the relative time and frequency offsets. This field is significant + /// only in server messages, where the values range from negative values + /// of a few milliseconds to positive values of several hundred + /// milliseconds. + /// + int32_t rootDelay; + + /// Root Dispersion: This is a 32-bit unsigned fixed-point number + /// indicating the maximum error due to the clock frequency tolerance, in + /// seconds with the fraction point between bits 15 and 16. This field + /// is significant only in server messages, where the values range from + /// zero to several hundred milliseconds. + /// + uint32_t rootDispersion; + + /// This idenfies a particular clock source + /// + uint32_t refId; + + /// Reference Timestamp identified when the clock was last set. + /// + /// It is a 64-bit value, this is in whole seconds + /// + uint32_t refTm_s; + /// Reference Timestamp identified when the clock was last set. + /// + /// It is a 64-bit value, this is in fractions of a second + /// + uint32_t refTm_f; + + /// Originate Timestamp identified when the request departed the client. + /// + /// It is a 64-bit value, this is in whole seconds + /// + uint32_t origTm_s; + /// Originate Timestamp identified when the request departed the client. + /// + /// It is a 64-bit value, this is in fractions of a second + /// + uint32_t origTm_f; + + /// Receive Timestamp identified when the request arrived at the server. + /// + /// It is a 64-bit value, this is in whole seconds + /// + uint32_t rxTm_s; + /// Receive Timestamp identified when the request arrived at the server. + /// + /// It is a 64-bit value, this is in fractions of a second + /// + uint32_t rxTm_f; + + /// Transmit Timestamp identified when the request departed the client/server. + /// + /// It is a 64-bit value, this is in whole seconds + /// + uint32_t txTm_s; + /// Transmit Timestamp identified when the request departed the client/server. + /// + /// It is a 64-bit value, this is in fractions of a second + /// + uint32_t txTm_f; + } __attribute__ ((packed)); + + UDPSocket m_sock; +}; + + +#endif /* NTPCLIENT_H_ */
--- a/TimeInterface.cpp Mon Nov 20 17:09:48 2017 +0000 +++ b/TimeInterface.cpp Tue Nov 21 17:04:12 2017 +0000 @@ -44,18 +44,23 @@ NTPResult TimeInterface::setTime(const char* host, uint16_t port, uint32_t timeout) { - NTPClient ntp(m_net); NTPResult res; - // int16_t tzomin = get_tzo_min(); - INFO("setTime(%s, %d, %d)\r\n", host, port, timeout); - res = ntp.setTime(host, port, timeout); - INFO(" ret: %d\r\n", res); - if (res == NTP_OK) { - // if the time was fetched successfully, then - // let's save the time last set with the local tzo applied - // and this saves the last time set for later precision - // tuning. - set_time(std::time(NULL)); + + if (m_net) { + NTPClient ntp(m_net); + // int16_t tzomin = get_tzo_min(); + INFO("setTime(%s, %d, %d)\r\n", host, port, timeout); + res = ntp.setTime(host, port, timeout); + INFO(" ret: %d\r\n", res); + if (res == NTP_OK) { + // if the time was fetched successfully, then + // let's save the time last set with the local tzo applied + // and this saves the last time set for later precision + // tuning. + set_time(std::time(NULL)); + } + } else { + res = NTP_CONN; } return res; }
--- a/TimeInterface.h Mon Nov 20 17:09:48 2017 +0000 +++ b/TimeInterface.h Tue Nov 21 17:04:12 2017 +0000 @@ -4,7 +4,7 @@ #include "mbed.h" #include <ctime> -#include "NTPClient.h" // ver 9 +#include "NTPClient.h" // Special Registers and their usage: // GPREG0: 32 bits @@ -15,10 +15,10 @@ extern "C" { -#include "time.h" +#include "time.h" // uses some std::time-functions } -/// The tm_ex structure is patterened after the traditional tm struct, however +/// The tm_ex structure is patterned after the traditional tm struct, however /// it adds an element - the time zone offset in minutes. From this, it is then /// readily able to create a "local time" instead of simply a UTC time. /// @@ -55,7 +55,7 @@ /// but manages the time-zone offset as it does so. /// /// @code -/// // TimeInterface Architecture +/// // TimeInterface Architecture and APIs /// // /// // +--------+ /// // | clock |----> clock_t clock() @@ -106,7 +106,8 @@ /// // | +- strftime(char * ptr, ..., tm_ex *) --> | buffer | /// // +----strptime(char * buf, ..., tm_ex *) --- | Www Mmm dd hh:mm:ss yyyy | /// // +--------------------------+ -/// // double difftime(time_t end, time_t) +/// // double difftime(time_t end, time_t) +/// // /// @endcode /// class TimeInterface @@ -117,6 +118,18 @@ /// @param[in] net is optional and provides the EthernetInterface which is /// used if you want to sync to an NTP server /// + /// @code + /// EthernetInterface net; + /// TimeInterface ntp(&net); + /// ... + /// ntp.set_tzo_min(-6 * 60); + /// if (NTP_OK == ntp.setTime("time.nist.gov", 123, 10)) { + /// time_t tNow = ntp.timelocal(); + /// printf("time is %s\r\n", ntp.ctime(&tNow)); + /// } + /// ... + /// @endcode + /// TimeInterface(EthernetInterface *m_net = NULL); /// Destructor, normally not used, because it is typically kept for the life @@ -130,10 +143,12 @@ /// /// @code /// clock_t tstart, tend; - /// tstart = clock(); - /// // do something long - /// tend = clock(); - /// printf("Elapsed time is %5.3f\r\n", (float)(tend - tstart)/CLOCKS_PER_SEC); + /// ... + /// tstart = clock(); + /// // do something long + /// tend = clock(); + /// printf("Elapsed time is %5.3f\r\n", (float)(tend - tstart)/CLOCKS_PER_SEC); + /// ... /// @endcode /// /// @returns elapsed tics. @@ -147,11 +162,14 @@ /// /// @code /// time_t t_ref1, t_ref2, t_ref3; - /// t_ref1 = time(NULL); t_ref2 = t.time(); t.time(&t_ref3); + /// t_ref1 = time(NULL); + /// t_ref2 = t.time(); + /// (void)t.time(&t_ref3); /// @endcode /// - /// @param[in,out] timer is an optional pointer to a time_t value that will be written. - /// This pointer is ignored when NULL. + /// @param[out] timer is an optional pointer to a time_t value that will + /// be written with the current time. This pointer is ignored + /// when NULL. /// @returns the UTC time value. /// time_t time(time_t * timer = NULL); @@ -160,40 +178,45 @@ /// to a provided buffer. /// /// This reads the real time clock and returns the current time, adjusted - /// for the local timezone and daylight savings time. + /// for the local time zone and daylight savings time. /// /// @code /// time_t t_ref2, t_ref3; - /// t_ref2 = t.time(); t.timelocal(&t_ref3); + /// t_ref2 = t.time(); + /// t.timelocal(&t_ref3); /// @endcode /// - /// @param[in,out] timer is an optional pointer to a time_t value that will be written. - /// This pointer is ignored when NULL. - /// @returns the LOCAL time value (UTC adjusted for the LOCAL time zone). + /// @param[out] timer is an optional pointer to a time_t value that will + /// be written. This pointer is ignored when NULL. + /// @returns the LOCAL time value (UTC adjusted for the LOCAL time zone and dst). /// time_t timelocal(time_t * timer = NULL); - /// Convert a time value structure into an ASCII printable time Www Mmm dd hh:mm:ss yyyy + /// Convert a time value structure into an ASCII printable time "Www Mmm dd hh:mm:ss yyyy" + /// + /// @note Watch out for race conditions as this returns a pointer to a + /// shared buffer. + /// @note Unlike the standard ctime function, this version DOES NOT append + /// a newline character to the buffer. + /// + /// @code + /// time_t tNow = timelocal(); + /// printf("time is %s\r\n", ctime(tNow)); + /// @endcode + /// + /// @param[in] timer is a pointer to a time_t value containing the time to convert. + /// @returns a pointer to a buffer containing the string. + /// + char * ctime(const time_t * timer); + + /// Convert a tm_ex structure into an ASCII printable "time Www Mmm dd hh:mm:ss yyyy" + /// + /// @note Unlike the standard asctime, this takes a pointer to a tm_ex, which + /// has a time zone offset element. /// /// @note Watch out for race conditions as this returns a pointer to a /// shared buffer. - /// @note Unlike the standard ctime function, this version DOES NOT append - /// a newline character to the buffer. /// - /// @code - /// time_t tsample = timelocal(); - /// printf("time is %s\r\n", ctime(tsample)); - /// @endcode - /// - /// @param[in] timeptr is a pointer to a tm structure containing the time to convert. - /// @returns a pointer to a private buffer containing the string. - /// - char * ctime(const time_t * timer); - - /// Convert a tm structure into an ASCII printable time Www Mmm dd hh:mm:ss yyyy - /// - /// @note Watch out for race conditions as this returns a pointer to a - /// shared buffer. /// @note Unlike the standard ctime function, this version DOES NOT append /// a newline character to the buffer. /// @@ -203,26 +226,39 @@ /// printf("Time is %s\r\n", asctime(tEx)); /// @endcode /// - /// @param[in] timeptr is a pointer to a tm structure containing the time to convert. + /// @param[in] timeptr is a pointer to a tm_ex structure containing the time to convert. /// @returns a pointer to a private buffer containing the string. /// char * asctime(const struct tm_ex *timeptr); /// Compute the difference in seconds between two time values. /// + /// @code + /// time_t tstart, tend; + /// ... + /// tstart = time(); + /// // do some long process now + /// tend = time(); + /// printf("Elapsed time is %5.3f\r\n", tend - tstart); + /// ... + /// @endcode + /// /// @param[in] end is the end time to compare to the beginning time. /// @param[in] beginning time is compared to the end time. /// @return the difference in seconds, as a double. /// double difftime(time_t end, time_t beginning); - /// Convert the referenced time_t value to a tm structure in UTC/GMT format. + /// Convert the referenced time_t value to a tm_ex structure in UTC/GMT format. + /// + /// @note Unlike the standard asctime, this return a pointer to a tm_ex, which + /// has a time zone offset element. /// /// @note Watch out for race conditions as this returns a pointer to a /// shared buffer. /// /// @param[in] timer is a pointer to a time_t structure to convert. - /// @returns pointer to a tm structure. + /// @returns pointer to a tm_ex structure. /// struct tm_ex * gmtime(const time_t * timer); @@ -506,7 +542,9 @@ /// @param[in] host NTP server IPv4 address or hostname (will be resolved via DNS) /// @param[in] port port to use; defaults to 123 /// @param[in] timeout waiting timeout in ms (osWaitForever for blocking function, not recommended) - /// @return 0 on success, NTP error code (<0) on failure + /// @returns NTP_OK on success, + /// @returns NTP_CONN if no network interface + /// @returns other NTP error code (<0) on failure /// NTPResult setTime(const char* host, uint16_t port = NTP_DEFAULT_PORT, uint32_t timeout = NTP_DEFAULT_TIMEOUT);