«

»

Mar 25

Stampa Articolo

Tutorial ntp arduino

Questo tutorial ntp arduino nasce da qualche giorno di test sul protocollo ntp per sincronizzare l’ora del mio prossimo progetto arduino.

Ethernet Shield Arduino

Spesso nei tupo progetti con arduino è necessario sincronizzare l’ora e come sai arduino uno è sprovvisto di RTC ( Real Time Clock )

Esistono diverse shield per dotare arduino di un RTC ed io stesso ne ho testate alcune come:

Tuttavia spesso questo tipo di shield si sono rivelate poco precise e con il trascorrere del tempo la loro affidabilità è divenuta nulla.

Sopratutto se il tuo progetto prevede già la possibilità di connettersi ad Internet è semplice usare il protocollo ntp per sincronizzare l’ora del progetto con quella di Greenwitch ( ricorda che l’Italia è a +1 ora da quella del meridiano di Greenwitch ).

Cosé l’ntp

Il protocollo ntp, network time protocol, è utilizzato per allineare l’ora di applicazioni e prodotti elettronici in genere che non sono dotati di RTC proprio o il cui RTC interno non sia ritenuto sufficientemente affidabile.

Da wikipedia:

L’NTP è uno dei più vecchi protocolli tuttora in uso, ed è giunto alla sua quarta versione. Fu sviluppato presso l’Università del Delaware da Dave Mills, che ne segue tuttora lo sviluppo. Il funzionamento si basa sul rilevamento dei tempi di latenza nel transito dei pacchetti sulla rete. Utilizza il tempo coordinato universale ed è quindi indipendente dai fusi orari. Attualmente è in grado di sincronizzare gli orologi dei computer suinternet entro un margine di 10 millisecondi e con una accuratezza di almeno 200 microsecondi all’interno di una LAN in condizioni ottimali.

nel tuo progetto, connesso ad internet, potresti utilizzarlo per aggiornare l’ora del progetto in un datalogger o altro progetto che abbia necessità di rilevare l’ora corrente o di far scattare un evento ad un orario preimpostato come una centralina di irrigazione o sveglia.

Lo sketch dell’ntp arduino client

L’esempio che ho usato è già presente nell’IDE:

sketch udp ntp arduino

e che ti riporto di seguito:

/*

 Udp NTP Client

 Get the time from a Network Time Protocol (NTP) time server
 Demonstrates use of UDP sendPacket and ReceivePacket
 For more on NTP time servers and the messages needed 
 to communicate with them,
 see http://en.wikipedia.org/wiki/Network_Time_Protocol

 created 4 Sep 2010
 by Michael Margolis
 modified 9 Apr 2012
 by Tom Igoe

 This code is in the public domain.

 */

#include <SPI.h>
#include <Ethernet.h>
#include <EthernetUdp.h>

// Enter a MAC address for your controller below.
// Newer Ethernet shields have a MAC address printed on a sticker 
// on the shield
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
unsigned int localPort = 8888;       // local port to listen for UDP packets

char timeServer[] = "time.nist.gov"; // time.nist.gov NTP server

const int NTP_PACKET_SIZE = 48; // NTP time stamp is in the first 
                                // 48 bytes of the message

byte packetBuffer[ NTP_PACKET_SIZE]; //buffer to hold incoming 
                                     //and outgoing packets
// A UDP instance to let us send and receive packets over UDP
EthernetUDP Udp;

void setup()
{
  // Open serial communications and wait for port to open:
  Serial.begin(9600);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for Leonardo only
  }


  // start Ethernet and UDP
  if (Ethernet.begin(mac) == 0) {
    Serial.println("Failed to configure Ethernet using DHCP");
    // no point in carrying on, so do nothing forevermore:
    for (;;)
      ;
  }
  Udp.begin(localPort);
}

void loop()
{
  sendNTPpacket(timeServer); // send an NTP packet to a time server

  // wait to see if a reply is available
  delay(1000);
  if ( Udp.parsePacket() ) {
    // We've received a packet, read the data from it
    Udp.read(packetBuffer, NTP_PACKET_SIZE); // read the packet 
                                             // into the buffer
    //the timestamp starts at byte 40 of the received packet 
    // and is four bytes, or two words, long. First, 
    // esxtract the two words:

    unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
    unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);
    // combine the four bytes (two words) into a long integer
    // this is NTP time (seconds since Jan 1 1900):
    unsigned long secsSince1900 = highWord << 16 | lowWord;
    Serial.print("Seconds since Jan 1 1900 = " );
    Serial.println(secsSince1900);

    // now convert NTP time into everyday time:
    Serial.print("Unix time = ");
    // Unix time starts on Jan 1 1970. In seconds, that's 2208988800:
    const unsigned long seventyYears = 2208988800UL;
    // subtract seventy years:
    unsigned long epoch = secsSince1900 - seventyYears;
    // print Unix time:
    Serial.println(epoch);


    // print the hour, minute and second:
    Serial.print("The UTC time is ");       // UTC is the time at Greenwich 
                                            // Meridian (GMT)
    Serial.print((epoch  % 86400L) / 3600); // print the hour 
                                            // (86400 equals secs per day)
    Serial.print(':');
    if ( ((epoch % 3600) / 60) < 10 ) {
      // In the first 10 minutes of each hour, we'll want a leading '0'
      Serial.print('0');
    }
    Serial.print((epoch  % 3600) / 60); // print the minute 
                                        // (3600 equals secs per minute)
    Serial.print(':');
    if ( (epoch % 60) < 10 ) {
      // In the first 10 seconds of each minute, we'll want a leading '0'
      Serial.print('0');
    }
    Serial.println(epoch % 60); // print the second
  }
  // wait ten seconds before asking for the time again
  delay(10000);
}

// send an NTP request to the time server at the given address
unsigned long sendNTPpacket(char* address)
{
  // set all bytes in the buffer to 0
  memset(packetBuffer, 0, NTP_PACKET_SIZE);
  // Initialize values needed to form NTP request
  // (see URL above for details on the packets)
  packetBuffer[0] = 0b11100011;   // LI, Version, Mode
  packetBuffer[1] = 0;     // Stratum, or type of clock
  packetBuffer[2] = 6;     // Polling Interval
  packetBuffer[3] = 0xEC;  // Peer Clock Precision
  // 8 bytes of zero for Root Delay & Root Dispersion
  packetBuffer[12]  = 49;
  packetBuffer[13]  = 0x4E;
  packetBuffer[14]  = 49;
  packetBuffer[15]  = 52;

  // all NTP fields have been given values, now
  // you can send a packet requesting a timestamp:
  Udp.beginPacket(address, 123); //NTP requests are to port 123
  Udp.write(packetBuffer, NTP_PACKET_SIZE);
  Udp.endPacket();
}

inizia subito dalle prime linee 020-022: in cui includi le librerie necessarie alla connessione Ethernet ed Udp;

linea 027: imposta il Mac con cui la scheda ethernet si presenterà al router;

linea 028: definisci la porta locale su cui attenderai le risposte udp;

linea 030: imposta il nome del server che contatterai per ricevere l’Ntp

linea 032: definisci una costante NTP_PACKET_SIZE del valore di 48 pari al numero di bytes in cui è contenuto l’NTP Timestamp;

linea 035: definisci una variabile di tipo byte packetBuffer della dimensione impostata alla linea 032;

linea 038: inizializza l’istanza Udp della classe EthernetUdp;

linee 040-057: la funzione setup() non esegue nessuna operazione sconosciuta a parte la linea 056 in cui inizializzi la Udp chiamando il metodo begin();

linea 061: richiama la funzione sendNTPpacket con la quale invierai un ntp packet al server Ntp definito alla linea 30, descriverò alla linea 115 la funzione sendNTPpacket;

linea 065: quando ricevi un pacchetto Udp valido, ossia parsabile dal metodo Udp.parsePacket() esegui le righe successive;

linea 067: leggi il pacchetto Udp e memorizzalo nella variabile packetBuffer;

linee 073-074: leggi le due parole ( 4byte in tutto ) contenute a partire dalla posizione 40 del buffer, questi 4 byte contengono il secondi trascorsi dal 1 gennaio 1900;

linea 077: combina le due parole eseguendo lo shift della prima di 16 bit ed aggiungendovi la seconda con la concatenazione “|”;

linee 078-079: scrivi sul monitor seriale il valore del timestamp appena recuperato;

linea 084: poiché nel sistema Unix il timestamp inizia il 1 gennaio 1970 e non il 1 gennaio 1900 c’è una differenza in secondi, da colmare tra le due date. Tale differenza è di 70 anni = 2208988800UL

linea 068: calcola la differenza tra le due date ed ottieni il numero di secondi trascorsi dal 1 gennaio 1970 che potrai convertire in data completa.

linea 094: calcola e scrivi sul monitor seriale l’ora di Greenwitch, il calcolo è ( secondi trascorsi dal 1/1/1970 % 84600 ) ossia il resto della divisione tra il numero di secondi trascorsi ed il numero di secondi da cui è composto un giorno. Il resto rappresenta il numero di ore rimaste per cui dividendolo ulteriormente per il numero di secondi presenti in un ora ( 3600 ) ottieni il numero di ore;

linee 097-101: calcola il numero di minuti trascorsi eseguendo la medesima operazione precedente ma usando come dividendo le ore e quindi dividendo ulteriormente le ore rimanenti per il numero di secondi presenti in un ora;

linee 104-109: esegui nuovamente le operazioni già viste per i soli secondi trovando il resto dei secondi rimasti;

linea 118: imposta a 0 tutti i valori contenuti nel packetBuffer;

linee 121-129: componi il pacchetto Ntp da inviare come richiesta all’ntp server;

linea 133: inizializza una comunicazione UDP verso il server definito alla linea 030 e passata alla funzione sendNTPpacket dalla linea 061 presente nel loop();

linea 134: invia il pacchetto Ntp preparato alle linee 121-129;

linea 135: termina l’invio del pacchetto NTP mediante Udp;

Se tutto funziona correttamente otterrai sul monitor seriale dei messaggi simili al seguente:

ntp arduino serial monitor

Buona sincronizzazione ntp arduino !!!

Permalink link a questo articolo: http://www.mauroalfieri.it/elettronica/tutorial-ntp-arduino.html

6 comments

Vai al modulo dei commenti

  1. Peppe

    Ciao Mauro,
    ma in questo modo si ottiene l’ora di due ore indietro. Come si può risolvere?

    1. Mauro Alfieri

      Ciao Peppe,
      ottieni l’ora di Greenwich a cui devi sommare il fusoorario in cui ti trovi, per noi Roma +1 e un’ora in più perché abbiamo l’ora legale.
      Trovi una applicazione di tutte queste funzioni nell’articolo dedicato all’orto.

  2. Manuel

    Qualcuno ha testato lo sketch di recente ? Quali sono i possibili motivo del non funzionamento ?

    1. Mauro Alfieri

      Ciao Manuel,
      che errore ti restituisce?

  3. andrea

    ciao Mi funziona tutto molto bene con arduino leonardo.
    Mi servirebbe una mano per calcolare data ( anno, mese, giorno)
    grazie

    1. Mauro Alfieri

      Ciao Andrea,
      trovi molti tutorial sulla conversione dello unixtimestamp in data completa di anno,mese,giorno hai provato a leggerli?
      Se applichi lo stesso metodo utilizzato per ore:minuti:secondi ottieni i dati che ti mancano.

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *

Puoi usare i seguenti tag ed attributi HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>