Tiny SNTP(NTP) Client

Dependencies:   EthernetNetIf mbed

TinySNTP.cpp

Committer:
okini3939
Date:
2011-07-28
Revision:
1:d3c1871be1e9
Parent:
0:41e7cfdbd23a

File content as of revision 1:d3c1871be1e9:

/*
 * mbed Tiny SNTP(NTP) Client
 * Copyright (c) 2011 Hiroshi Suga
 * Released under the MIT License: http://mbed.org/license/mit
 */

/** @file
 * @brief Tiny DNS Resolver
 */

#include "mbed.h"
#include "EthernetNetIf.h"
#include "UDPSocket.h"
#include "DNSRequest.h"
#include "TinySNTP.h"

// host to network short
#define htons( x ) ( (( (x) << 8 ) & 0xFF00) | (( (x) >> 8 ) & 0x00FF) )
#define ntohs( x ) htons(x)
// host to network long
#define htonl( x ) ( (( (x) << 24 ) & 0xFF000000)  \
                   | (( (x) <<  8 ) & 0x00FF0000)  \
                   | (( (x) >>  8 ) & 0x0000FF00)  \
                   | (( (x) >> 24 ) & 0x000000FF)  )
#define ntohl( x ) htonl(x)

static UDPSocket *sntp;
static volatile unsigned long sntptime;
static volatile int dns_status;
extern EthernetNetIf eth;

int createSntpRequest (char *buf) {
    struct SNTPPacket *sntp;

    sntp = (struct SNTPPacket *)buf;
    memset(sntp, 0, sizeof(struct SNTPPacket));
    sntp->info = 0x1b; // Ver.3, client
    sntp->txTm_s = htonl(NTP_TIMESTAMP_DELTA + time(NULL));

    return sizeof(struct SNTPPacket);
}

int getSntpResponse (const char *buf, uint32_t *tim) {
    struct SNTPPacket *sntp;
    uint32_t now;
//    long int delay, offset;

    sntp = (struct SNTPPacket *)buf;
    if ((sntp->info & 0x3f) == 0x1c || (sntp->info & 0x3f) == 0x24) {
        // Ver.3or4, Server

        now = htonl(NTP_TIMESTAMP_DELTA + time(NULL));
/*
        delay = (now - sntp->origTm_s) - (sntp->rxTm_s - sntp->txTm_s);
        offset = ((sntp->rxTm_s - sntp->origTm_s) + (sntp->txTm_s - now));

        *tim = ntohl(sntp->txTm_s) - NTP_TIMESTAMP_DELTA + (delay / 2);
*/
        *tim = ntohl(sntp->txTm_s) - NTP_TIMESTAMP_DELTA;
#ifdef DEBUG
        printf("now %08x\r\n", ntohl(now));
        printf("ref %08x\r\n", ntohl(sntp->refTm_s));
        printf("orig %08x\r\n", ntohl(sntp->origTm_s));
        printf("rx %08x\r\n", ntohl(sntp->rxTm_s));
        printf("tx %08x\r\n", ntohl(sntp->txTm_s));
//        printf("delay %d / offset %d\r\n", delay, offset);
#endif
        return 0;
    }

    return -1;
}

void isr_dns (DNSReply r) {

#ifdef DEBUG
    printf("dns(%d)\r\n", r);
#endif
    if (DNS_FOUND) {
        dns_status = 1;
    } else {
        dns_status = -1;
    }
}

void isr_sntp (UDPSocketEvent e) {
    char buf[100];
    Host dsthost;
    int len;

    if (e == UDPSOCKET_READABLE) {
        // recv responce;
        len = sntp->recvfrom(buf, sizeof(buf), &dsthost);
#ifdef DEBUG
        for (int i = 0; i < len; i ++) {
            printf(" %02x", (unsigned char)buf[i]);
        }
        puts("\r");
#endif
        if (len >= sizeof(struct SNTPPacket)) {
            getSntpResponse(buf, (uint32_t*)&sntptime);
        }
    }
}

int ntpdate (const char* name, uint32_t *tim) {
    UDPSocketErr err;
    Host sntphost;
    Timer timeout;
    char buf[100];
    int i, len;

    sntptime = 0;
    sntp = new UDPSocket;
    sntp->setOnEvent(isr_sntp);

    sntphost.setName(name);
    {
        // resolv
        DNSRequest dns;
        dns_status = 0;
        dns.setOnReply(isr_dns);
        if (dns.resolve(&sntphost) != DNS_OK) goto exit;
        timeout.reset();
        timeout.start();
        while (timeout.read_ms() < NTP_TIMEOUT) {
            if (dns_status) break;
            Net::poll();
        }
        timeout.stop();
        if (dns_status <= 0) goto exit;
#ifdef DEBUG
        printf("%s [%d.%d.%d.%d]\r\n", sntphost.getName(), (unsigned char)sntphost.getIp()[0], (unsigned char)sntphost.getIp()[1], (unsigned char)sntphost.getIp()[2], (unsigned char)sntphost.getIp()[3]);
#endif
    }
    sntphost.setPort(NTP_PORT);

    // send request
    len = createSntpRequest(buf);
#ifdef DEBUG
    for (int i = 0; i < len; i ++) {
        printf(" %02x", (unsigned char)buf[i]);
    }
    puts("\r");
#endif
    sntp->sendto(buf, len, &sntphost);

    // wait responce
    i = 0;
    timeout.reset();
    timeout.start();
    while (timeout.read_ms() < NTP_TIMEOUT) {
        if (sntptime) {
            *tim = sntptime;
            break;
        }
        if (timeout.read_ms() / 5000 < i) {
            sntp->sendto(buf, len, &sntphost);
            i ++;
        }
        Net::poll();
    }
    timeout.stop();

exit:
    sntp->resetOnEvent();
    delete sntp;

    return sntptime ? 0 : -1;
}