enc28J60 and Arduino (13)

by luca
27 comments

Today’s tutorial is about getting an accurate time via Internet, using the NTP (Network Time Protocol) service.

NTP

NTP is a client-server protocol at application layer; it uses UDP port 123 as transport protocol.

If you send a request to a NTP server, you receive in its response a 64bit value (timestamp) made by:

  • the first 32bits represent the number of seconds since 01/01/1900
  • the last 32bits are the fraction of the actual second

Many NTP servers are available on Internet: in the United States for example the NIST (National Institute of Standards and Technology) runs an entire network of severs. I live in Italy, so I chose as time server for this example the one run by the INRiM (Istituto Nazionale di Ricerca Metereologica):

static byte ntpServer[] = {193,204,114,232};

Arduino

The EtherCard library has two methods for NTP queries:

  • ntpRequest(ntpServer, srcPort), sends a request to the specificed server;
  • ntpProcessAnswer(&timeStamp, srcPort), gets the response and extracts the timestamp (only the first 32bits).

The srcPort parameter is used to find, among the many packets your Ethernet shield receives, the one that contains NTP server’s response: you can choose its value but it has to be the same both in the Request and in the ProcessAnswer.

Got the timestamp value, you have to convert it in date-time format. The method is the following:

  • count the number of complete years contained in the timestamp;
  • then find out how many complete months are in the rest of the timestamp;
  • do the same for days, hours, minutes;
  • the final rest is the number of seconds.

Just a few complications:

Years could be leap years: for those you have to consider 366*86500 seconds instead of 365*86500. To check if actual year is a leap one, you can use the following method:

boolean isLeapYear(unsigned int year) {
 
  return (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0));
}

The number of days in a month is variable: the value if saved in an array:

static int days_in_month[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

If we’re in a leap year, February has 29 days:

if(isLeapYear(year) && month == 1) seconds = SECONDS_IN_DAY * 29;

Finally, the timestamp value is referred to GMT, if you live in a different time zone, you have to adjust its value:

#define TIME_ZONE               +1
[...]
printDate(timeStamp + 3600 * TIME_ZONE);

The complete sketch is available on Github… here’s a screenshot about its working:

Related Posts

27 comments

MARCO Sunday January 6th, 2013 - 11:47 AM

Grazie,cercavo proprio un’esempio del genere!

Reply
Sol Saturday February 9th, 2013 - 09:10 PM

I love your tutorials! You are the only person who understands AND shares his wisdom about the ENC28j60 – thank you so much!

Do you know how to put the value of a variable on the webserver so the user who is on the website can read it?

Reply
luca Tuesday February 12th, 2013 - 09:25 AM

Hi Sol

could you please better explain what do you need?

Reply
claudio Tuesday February 12th, 2013 - 03:20 PM

grazie per questo esempio, ho tuttavia un problema per un mio progettino su un antifurto casalingo.
Come posso ottenere il tempo da server senza inviare un comando da seriale?
Ho stupidamente ponticellato rx e tx ed inviato con il comando Serial.print la lettera ‘n’ il sisytema si ferma al messaggio ‘ request send ‘
grazie Claudio

Reply
hernan Saturday March 23rd, 2013 - 12:38 PM

Ciao Luca scusami per il disturbo, ma mi sapresti spiegare come inviare e ricevere dati fra arduino ed una pagina web senza che quest’ultima si trovi nella memoria di arduino ? io vorrei inviare qualche byte a una pagina web, questa la elabori tramite qualche script e mostri i risultati, ad esempio arduino acquisisce la temperatura di un posto, elabora e spedisce i dati a questa pagina web che visualizzerà la temperatura, come lo faresti?? grazie ancora per i tuoi tutorial.

Reply
luca Monday March 25th, 2013 - 09:53 AM

Ciao Hernan,

la cosa è molto semplice: puoi realizzare una pagina web dinamica (in php, asp…) e inviare ad essa i dati con Arduino, tramite i metodi GET o POST.

Reply
hernan Tuesday March 26th, 2013 - 12:00 PM

potresti farci un breve tutorial su questo argomento? in internet non si trova proprio esempi del genere tranne due o tre esempi fatti anche male, comunque complimenti, girando in rete i tuoi tutorial sono molto apprezzati dalla comunità arduino, ho trovato tanti riferimenti al tuo sito in siti portoghesi, inglesi, spagnoli, ecc..

Reply
luca Wednesday March 27th, 2013 - 08:53 AM

ok! ci lavoro…

Reply
igor Sunday April 14th, 2013 - 02:03 PM

Grande tutorial.
Ho però un problema. Usando il suo esempio (senza seriale) il server INRIM mi risponde sempre con timestamp=0
Ho dhcp disabilitato su router e perciò ho modificato lo sketch dando
ether.staticSetup(myip,mygt,mydns); con i miei valori. Per provare ho anche modificato nella libreria ethercard di default la mask a 255.255.255.0.

Ho anche provato ad aprire sul router la porta udp 123 verso l’ip dell’arduino, ma ricevo sempre 0.

Reply
luca Tuesday April 16th, 2013 - 08:28 PM

Igor,

sicuramente non serve il NAT perché la comunicazione è da Arduino verso Internet. Arduino ti stampa via seriale anche “NTP answer received”?

Reply
igor Thursday April 18th, 2013 - 10:25 AM

Grazie per la risposta. Si, la risposta arriva ma sempre con tutti i byte a zero (ho stampato il buffer di risposta e dal byte 30° mi sembra in poi è tutto a 0). Con il server INRIM non mi funziona.
Ho cambiato server a cui fare la richiesta e ora funziona (non ricordo però quale ho messo, rispondo da ufficio e non da casa).

Reply
luca Friday April 26th, 2013 - 02:08 PM

ottimo Igor, strano che non ti vada INRIM: ho appena provato lo sketch e mi risponde…

Reply
igor Friday May 3rd, 2013 - 03:09 PM

Infatti trovo strano anche io. Non ho più riprovato. Magari bisogna essere registrati?

tom Wednesday May 1st, 2013 - 05:36 PM

Hi, your tutorials are the only decent tutorial about ENC28j60 I can find. Can you give me an example how to apply the data/time result on webserver?

Reply
luca Wednesday May 1st, 2013 - 07:21 PM

Hi Tom and thanks! Could you please explain better what you need? Display the time got from NTP on a webpage?

Reply
tom Sunday May 12th, 2013 - 07:10 PM

Hi, me again. Please ignore my last question. Here is the new one – no matter I change whatever other NTP IP, they still show me the same timestamp, do you know why?

Reply
luca Monday May 13th, 2013 - 09:42 AM

Hi Tom, could you please explain better? Do you get always the same timestamp? Doesn’t it change?

Reply
francesco Friday June 21st, 2013 - 02:12 PM

Ho scoperto perche a me non va …da bravo cieco che sono io monto un chip Wiznet W5100 e per questomotivo (credo on vada)..comeposso adattare questo (stupendo e ricercatissimo )codice?

Reply
luca Friday June 21st, 2013 - 06:41 PM

ciao, è impossibile adattarlo: il W5100 è completamente diverso. Per tale chip esistono le librerie ufficiali Arduino.
bye

Reply
Marco Wednesday July 10th, 2013 - 09:31 AM

fatal error: avr/pgmspace.h: No such file or directory
compilation terminated.

Eppure il file c’è e come… Come mai?

Reply
luca Wednesday July 10th, 2013 - 03:38 PM

Ciao Marco

stai provando a compilare l’esempio di questo post?

Reply
Ivan Thursday August 15th, 2013 - 11:19 AM

Hi, Luca,

I find your tutorials extremely well-written and useful. One question regarding this particular example – ntpProcessAnswer indeed processes only the first timestamp and drops the fraction altogether.
Could you provide any tips on how we could process the fraction timestamp manually?
I was considering setting the software RTC via NTP with a bit more accuracy, and accounting for the milliseconds would increase the accuracy to +/- network latency.

Reply
luca Tuesday September 10th, 2013 - 08:25 AM

Hi Ivan

take a look at the file “tcpip.cpp” in the EtherCard library folder: the ntpProcessAnswer() method gets the first 4 bytes of the NTP packet: if you need also to get the milliseconds, retrieve also the next 4 bytes: http://www.meinbergglobal.com/english/info/ntp-packet.htm

Reply
Piero Sunday January 5th, 2014 - 02:26 AM

Ciao Luca,
anzitutto grazie davvero per tutti i tuoi post, in particolare proprio questi sulla ENC28J60, sei utilissimo e preziosissimo 🙂

Ti disturbo per un consiglio. Vorrei implementare il controllo dell’orario NTP in uno sketch che usa la libreria RTClib (https://github.com/adafruit/RTClib) , che di certo conosci. Per ora vorrei usare soltanto il softrtc, e poi magari ” infierire” configurando un DS1307 che interroga un server NTP per settare data e ora esatte. Visto che l’rtc va configurato nel setup(), avevo pensato di fare una cosa del genere, per ottenere il TIMESTAMP dal server NTP e poi, dopo averlo “lavorato”, configurarci appunto l’rtc

#include “EtherCard.h”

static byte mymac[] = {0x00,0x1A,0x4B,0x38,0x0C,0x5C};
byte Ethernet::buffer[700];

static byte ntpServer[] = {65,55,56,206}; // è time.windows.com, ma non funzionano anche altri
static byte srcPort = 0;

uint32_t timeStamp;
boolean requestSent;

void setup(){

Serial.begin(9600);

… istruzioni per la rete…

ether.packetLoop(ether.packetReceive());
ether.ntpRequest(ntpServer, srcPort);
Serial.println(“NTP request sent”);
ether.ntpProcessAnswer(&timeStamp, srcPort);
Serial.println(“NTP answer received”);
Serial.print(“Timestamp: “);
Serial.println(timeStamp);

… istruzioni per trasformare il timestamp e settare l’rtc …

} // fine setup

void loop(){

} // fine loop

Ma restituisce soltanto 0.
Uhm… cosa mi sfugge? non è che ntpRequest & C. possono funzionare soltanto in un loop?
Riesco a farlo in un loop, ma non vorrei inviare una richiesta al server NTP ogni secondo (ehm… per visualizzare data e ora su un LCD :-))
A me servirebbe il timestamp soltanto una volta alla partenza dello sketch o in seguito a un eventuale reset… Cosa mi consigli?

Grazie in anticipo per la risposta, un saluto e… continua così!
😉

Reply
luca Tuesday January 14th, 2014 - 06:57 PM

Ciao Piero

grazie per i complimenti 😉 è come giustamente hai intuito: per come funziona la libreria, è necessario il loop perché con le istruzioni ether.packetLoop(ether.packetReceive()) la libreria processa un pacchetto alla volta, quindi hai bisogno che tali istruzioni vengano eseguite più volte per stabilire la connessione, inviare la richiesta, gestire la risposta…
Puoi comunque usare una variabile di servizio booleana (es. “timeSet”) da configurare a false dopo che hai ricevuto almeno una risposta dal timeserver e quindi nel loop controllarne il valore: se è a false non mandi più nuove richieste.

Reply
Piero Sunday January 19th, 2014 - 02:59 PM

Ciao Luca,
grazier per il consiglio… mi sembra di esserci finalmente riuscito 🙂

Non ti posto qui il codice per non incasinarti oltre il blog. Se ti va di darci un occhio, lo trovi qui:

http://pastebin.com/Rz2RGTFq

ora bisogna “soltanto” trovare il modo di passare il TIMESTAMP all’istruzione rtc.begin(), che però è nel setup() e se lo metto nel loop() sembra non funzionare… mumble mumble….
grazie ancora,
ciao

Reply
luca Monday January 27th, 2014 - 10:08 PM

Ottimo lavoro Piero! Invece che rtc.begin() puoi usare il metodo adjust() a cui passi un oggetto di tipo DateTime…

Reply

Leave a Comment

six + 2 =