synchronise AVR clock using the Network Time Protocol (NTP)

For my 7-segment display AVR clock i was searching for a way to keep the clock accurate. One option would be using a DCF77 standard time receiver. The module I had seemed to be broken so I decided to use an ethernet interface and NTP.

For this I purchased a small ENC28J60 + AVR board from Simon Küppers. This small board is only about 38x30mm in size and includes an ATMega168, ENC28J60, Ethernet-Jack + Magnetics and a voltage regulator.
The 14 available I/O Pins are connected to a standard 0.1″ header, so there are possibilities to extend the board.

The Network Time Protocol (NTP) is described in RFC958. 

The protocol defines 48 Bytes of data and splits up into some control bytes and four 64bit timestamp. Each timestamp is the number of seconds to UTC since Jan. 1st 1900. Basically the NTP server receives the timestamp of the local requester, exchanges the adress fields, fills in it’s timestamp and sends the packet back. With the 4 timestamp fields routing delay and therefor absolute time errors can be reduced to a minima.

Simon also provides a port of the UIP TCP/IP Stack from Adam Dunkes including a sample web-server application. Also a simple framework for implementing additional applications was added by Simon. This NTP Clock implementation uses that framework. To get this port running correct for UDP applications some changes have to be made to the original code.

Find the following in file main.c and add the below:

int i= UIP_CONNS;
while (i)
{
    i--;
    uip_periodic(i);
    if (uip_len > 0)
    {
        uip_arp_out();
        Enc28j60Transmit(uip_buf, uip_len);
    }
}

/* ++ begin added by Tobias Floery */
i = UIP_UDP_CONNS;
while (i)
{
    i--;
    uip_udp_periodic(i);
    if (uip_len > 0)
    {
        uip_arp_out();
        Enc28j60Transmit(uip_buf, uip_len);
    }
}
/* ++ end */

Navigate to folder Net -> uip and add the following line to uip_UdpAppHub.h:

#include "../UdpApps/ntpD.h"

and then change uip_UdpAppHub.c to:

void uip_UdpAppHubCall(void)
{
    switch (uip_udp_conn->lport)
    {
        case (HTONS(PORT_NTPD)):
            NtpDCall((ntpMsg *)uip_appdata, uip_datalen());
            break;
    }

}

void uip_UdpAppHubInit(void)
{
    NtpDInit();
}

With this the TCP/IP Stack and the framework now knows how to pass and handle our data. Under the direcory net create a new one called UdpApps (like the TcpApps folder).

Add the following two files to this directory:
ntpD.h:

/*
 * Implements RFC958 - Network Time Protocol (NTP)
 * 
 * Author: Tobias Flöry
 * Email: tobias.floerycable.vol.at
 * Homepage: http://tobiscorner.floery.net
 * 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program.  If not, see .

 Copyright 2008 Tobias Floery
 * */

#ifndef NTPD_H_
#define NTPD_H_

#include 
#include 

#define PORT_NTPD    123
// hour offset from utc
#define UTC_OFFSET +1
// update interval in seconds
#define NTP_UPDATE 3600

typedef struct t_ntpMsg {
    uint8_t status;
    uint8_t type;
    uint16_t precision;

    uint32_t est_error;
    uint32_t est_driftrate;
    uint32_t ref_clk_ident;

    uint32_t ref_timestamp_i;
    uint32_t ref_timestamp_f;
    uint32_t orig_timestamp_i;
    uint32_t orig_timestamp_f;
    uint32_t rx_timestamp_i;
    uint32_t rx_timestamp_f;
    uint8_t tx_timestamp_i[4]; 
    uint32_t tx_timestamp_f; 

} ntpMsg;

void incTime();
void NtpDInit();
void NtpDCall(ntpMsg *msg, uint16_t len);
unsigned char *getTime();
void sendNTPRequest();

#endif /*NTP_H_*/

ntpD.c

#include 
#include 
#include "ntpD.h"
#include "../uip/uip.h"
#include "../../Hardware/GPIO.h"

static struct uip_udp_conn *ntp_conn = NULL;
static unsigned char time_req = 0;

volatile uint32_t time = 0;
uint8_t timeStr[10]={0};

void sendNTPRequest() {
    ntpMsg *m = (ntpMsg *)uip_appdata;

    m->status=0xE3;
    m->type = 0x00;
    m->precision = 0xFA04;
    m->est_error = 0x00000100;
    m->est_driftrate = 0;
    m->ref_clk_ident = 0;

    m->ref_timestamp_i = 0;
    m->ref_timestamp_f = 0;
    m->orig_timestamp_i = 0;
    m->orig_timestamp_f = 0;
    m->rx_timestamp_i = 0;
    m->rx_timestamp_f = 0;
    //m->tx_timestamp_i = 0;
    m->tx_timestamp_i[0]=0;
    m->tx_timestamp_i[1]=0;
    m->tx_timestamp_i[2]=0;
    m->tx_timestamp_i[3]=0;
    m->tx_timestamp_f = 0;
    uip_udp_send(48);
}

void NtpDInit()
{
    uip_ipaddr_t ntp_server;    
    uip_ipaddr(ntp_server, 192, 168, 1, 2);

    if(ntp_conn != NULL) {
        uip_udp_remove(ntp_conn);
    }    

    ntp_conn = uip_udp_new(&ntp_server, HTONS(PORT_NTPD));

    if (ntp_conn == NULL) {
        time = 0xAA;
        return;
    } else {
        uip_udp_bind(ntp_conn, HTONS(PORT_NTPD));
    }
    time_req = 0;
    time = 0;
}

void NtpDCall(ntpMsg *msg, uint16_t len)
{
    if (uip_poll()) {
        if (time_req == 0 || time < = 100) {
            sendNTPRequest();
            time_req++;
        }
    }

    if(uip_newdata()) {
        time = msg->tx_timestamp_i[0];
        time = time < < 8;
        time += msg->tx_timestamp_i[1];
        time = time < < 8;
        time += msg->tx_timestamp_i[2];
        time = time < < 8;
        time += msg->tx_timestamp_i[3];
    }
}

void incTime() {
    if (time%NTP_UPDATE==0) time_req = 0;
    time++;
}

unsigned char *getTime() {
    uint32_t tmp;
    unsigned char hour, min, sec;

    tmp = time;

    sec = tmp%60;
    tmp = tmp/60;
    min = tmp%60;
    tmp = tmp/60;
    hour = (tmp + (UTC_OFFSET))%24;

    timeStr[0] = '0'+hour/10;
    timeStr[1] = '0'+hour%10;
    timeStr[2] = ':';
    timeStr[3] = '0'+min/10;
    timeStr[4] = '0'+min%10;
    timeStr[5] = ':';
    timeStr[6] = '0'+sec/10;
    timeStr[7] = '0'+sec%10;
    timeStr[8] = 0;

    return timeStr;
}

Find the changed files and the NTP code in the ZIP archive below.

Downloads:

uWebSrv-changed

Leave a Reply

Your email address will not be published. Required fields are marked *


7 × = sixty three

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>