Ethernet clock

ok, first exhibition project here, go easy on me!

I've built a NTP based ethernet clock using used the ethernet shield from nuelectronics, based on the Microchip ENC28J60 chip with updated library that I put together, plus 4x20 character lcd display. Using the big font code from another post in this forum enables me to select whether I want a full date/time display, 2 row clock or 4 row clock. The big font displays include the blinking dots between the hours and minutes.

It is configurable via a simple web page enabling the IP, gateway and time server addresses to be set. This config information is then stored in the eeprom for when its powered off.

Details, code and pictures are at http://tinyurl.com/le57l2

There are still some improvements to be done such as cleaning up the code as its quite messy then implement the eeprom reset and possibly an alarm option.

Now this is at version 1.0 I can move onto something else for a while.

Thanks to those whose code or code fragments I've used in this project. Much appreciated guys :wink:

Cheers

Andy

Hi Andy

FWIW, you can simplify the code by using millis() instead of the timer1 code. There is also a dateTime library in the playground that makes this even easier and you can use this to do the conversion from NTP time.

If you are interested, here is some code that uses the ENC28j60 Ethernet shield to sync the dateTime library.

 // NTP Tiny
#include <enc28j60.h>
#include <etherTiny.h>
#include <ip_arp_udp_tcp.h>
#include <net.h>

#include <DateTime.h>  // mem's Date and Time library from the Arduino Playground

static uint8_t mymac[6] = { 0x54,0x55,0x58,0x10,0x00,0x24}; 
static uint8_t myip[4] = { 192,168,1,15};
static uint8_t gwip[4] = { 192,168,1,254};  // gateway
static char baseurl[]="http://192.168.1.15/";
static uint8_t ntpclientportL=0;

// --------------- NTP servers------
static uint8_t ntpip[4] = { 17,254,0,31}; // time.apple.com (any of 17.254.0.31, 17.254.0.26, 17.254.0.27, 17.254.0.28):

int8_t hours_offset_to_utc = 1;
time_t prevNtpReq = 0; // time last ntp request sent
boolean haveGWip = false;

#define BUFFER_SIZE 560
static uint8_t buf[BUFFER_SIZE+1];
#define STR_BUFFER_SIZE 22
static char strbuf[STR_BUFFER_SIZE+1];

EtherTiny es = EtherTiny();

void processNtpState(){
//  if ( es.linkUp() ){  // link up ?
  if( enc28j60linkup()){ 
    if (haveGWip == false){  // have gateway IP ?
      Serial.println("waiting for GW IP");
      es.client_arp_whohas(buf,gwip);
    }
    else if ( DateTime.available() == false) { // never been synced ?
      if(  (prevNtpReq == 0) || (prevNtpReq + 10 < DateTime.now()) ){ 
        // send out initial request
        ntpclientportL++;
        Serial.println("req init NTP sync");
        es.client_ntp_request(buf,ntpip,ntpclientportL);
        prevNtpReq += 10;
      }
    }    
    else if( prevNtpReq + 60 < DateTime.now() ){ // synced over 1 hr ago
      Serial.println("req NTP update");
      ntpclientportL++;
      es.client_ntp_request(buf,ntpip,ntpclientportL);
      Serial.println(prevNtpReq,DEC);
      prevNtpReq += 60; // schedule another request in 1 min (irrespective of whether a response received or not)    
      Serial.println(prevNtpReq,DEC);
      Serial.print(DateTime.now(),DEC);
    }
  }
  else{
    Serial.println("Eth-link down");
  }
}  

void setup(){
  Serial.begin(19200); 

  enc28j60Init(mymac);
  enc28j60clkout(2); // change clkout from 6.25MHz to 12.5MHz
  delay(10);  
  enc28j60PhyWrite(PHLCON,0x476);   // 0x476 is PHLCON LEDA=links status, LEDB=receive/transmit
  delay(100);
  init_ip_arp_udp_tcp(mymac,myip,80);   //init the ethernet/ip layer:
}

void timeDigitToString(uint8_t value, char seperator, char * destination){
  // utility to write the string representation of the given value to the destination, values should be 0-59
  if( value < 10)
    destination[0] = '0'; // pad with zero if single digit
  else
    destination[0] = '0' + value / 10;  // most significant digit of value
  destination[1] = '0' + value % 10;     // least siginificant digit of value 
  destination[2] = seperator; 
  destination[3] = 0; // null terminate
}

char * timeNowToString(){
  // convert the time now to a srting of the form "hh:mm:ss "  
  static char timestr[10];
  DateTime.available();
  timeDigitToString(DateTime.Hour, ':', &timestr[0] );
  timeDigitToString(DateTime.Minute, ':', &timestr[3] );
  timeDigitToString(DateTime.Second, ' ', &timestr[6] ); 
  return timestr;
}

void sendTCP(unsigned int plen){
  es.make_tcp_ack_from_any(buf); // send ack for http get
  es.make_tcp_ack_with_data(buf,plen); // send data    
}


void processPacket(uint16_t plen){
  uint16_t dat_p;
  byte on_off = 1;
  int8_t cmd;

  // arp is broadcast if unknown but a host may also verify the mac address by sending it to a unicast address.
  if(es.eth_type_is_arp_and_my_ip(buf,plen)){
    if (es.eth_type_is_arp_req(buf)){
      es.make_arp_answer_from_request(buf);
    }
    if (es.eth_type_is_arp_reply(buf)){
      if (es.client_store_gw_mac(buf,gwip)){
        haveGWip=1;
      }
    }  
  }
  if(buf[IP_PROTO_P]==IP_PROTO_ICMP_V && buf[ICMP_TYPE_P]==ICMP_TYPE_ECHOREQUEST_V){
    es.make_echo_reply_from_request(buf,plen);  // its a ping so do reply
    Serial.print("Got ping: ");   
    Serial.println(timeNowToString());
  }

  if (buf[IP_PROTO_P]==IP_PROTO_UDP_V&&buf[UDP_SRC_PORT_H_P]==0&&buf[UDP_SRC_PORT_L_P]==0x7b){
    time_t time = 0;
    if (es.client_ntp_process_answer(buf,&time,ntpclientportL)){
      // convert to unix time:
      if ((time & 0x80000000UL) == 0)  
        time += 2085978496;  // 7-Feb-2036 @ 06:28:16 UTC it will wrap:
      else                               
        time -= GETTIMEOFDAY_TO_NTP_OFFSET;         // from now until 2036:
      time += (hours_offset_to_utc * 3600L);    
      Serial.print("Got Time packet: ");   
      DateTime.sync(time);  // set the internal clock 
      Serial.println(timeNowToString());
      prevNtpReq = time;
    }
  }  
}

void loop(){
  uint16_t plen;

  plen = enc28j60PacketReceive(BUFFER_SIZE, buf);
  /* plen will be unequal to zero if there is a valid packet (without crc error) */
  if(plen!=0)
    processPacket(plen); 
  else
    processNtpState();           
}