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 !!!

  • Questo sito ed i suoi contenuti è fornito "così com'è" e Mauro Alfieri non rilascia alcuna dichiarazione o garanzia di alcun tipo, esplicita o implicita, riguardo alla completezza, accuratezza, affidabilità, idoneità o disponibilità del sito o delle informazioni, prodotti, servizi o grafiche correlate contenute sul sito per qualsiasi scopo.
  • Ti chiedo di leggere e rispettare il regolamento del sito prima di utilizzarlo
  • Ti chiedo di leggere i Termini e Condizioni d'uso del sito prima di utilizzarlo
  • In qualità di Affiliato Amazon io ricevo un guadagno dagli acquisti idonei qualora siano presenti link al suddetto sito.

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

14 commenti

Vai al modulo dei commenti

    • Peppe il 7 Maggio 2015 alle 18:29
    • Rispondi

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

    1. 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.

    • Manuel il 2 Gennaio 2018 alle 18:52
    • Rispondi

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

    1. Ciao Manuel,
      che errore ti restituisce?

    • andrea il 19 Febbraio 2018 alle 14:29
    • Rispondi

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

    1. 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.

    • Gaetano il 14 Agosto 2018 alle 20:25
    • Rispondi

    Ciao Mauro,
    Ho usato uno sketch simile al tuo, è riesco a leggere correttamente l’ora. Premetto che sto usando NodeMCU. Per la mia applicazione, è necessario assegnare un indirizzo IP statico alla mia board, ma appena definisco un IP, nonostante la board si connette correttamente a Internet non restituisce più l’ora.
    Grazie in anticipo.
    Gaetano

    1. Ciao Gaetano,
      le due operazioni non sono direttamente legate.
      Che tu abbia un IP statico ( fisso ) o un IP assegnato da un DHCP non cambia nulla per lo sketch o per Arduino.
      Potrebbe dipendere dalla configurazione di rete; ad esempio mancando l’indicazione del DNS server, lo sketch, potrebbe non riuscire a risolvere “time.nist.gov”
      Oppure mancando il Gw e non essendo nella medesima subnet potresti avere difficoltà a raggiungere qualsiasi altro device fuori dalla subnet assegnata ad arduino.

      Sono solo ipotesi, ma indagherei su questi aspetti.

    • Antonio il 5 Gennaio 2019 alle 11:21
    • Rispondi

    Salve Mauro, vorrei sapere come si fa ad avere il cambio automatico tra ora solare e legale includendo qualche riga di codice in questo sketch. Hai qualche suggerimento a riguardo ? ..ti ringrazio anticipatamente. Saluti Antonio

    1. Ciao Antonio,
      il cambio ora da solare a legale è da gestire nel codice scrivendo una funzione che la calcoli in base al periodo dell’anno se aggiungere o sottrarre un ora.
      Io ho scritto qualcosa di simile a ciò che chiedi per la centralina orto idroponico.

    • Ennio il 20 Novembre 2019 alle 19:24
    • Rispondi

    Ciao Mauro,
    bell’articolo. Ho provato ad usarlo anche io solo che a volte fa cilecca e non mi so spiegare il motivo. Tenendo tutto in funzione per parecchio tempo a volte dà l’orario ogni 11 secondi (come dovrebbe fare, visto che c’è un delay di mille e diecimila) ma a volte passano anche 2 o 3 minuti. Siccome vorrei fare un aggiornamento dell’orario a richiesta, mi è scomodo non avere mai una risposta certa. Anche a te succede questo?
    PS: il cerca sul sito (in alto a destra) scrive in bianco su sfondo bianco

    1. Ciao Ennio,
      è molto tempo che non uso più questo progetto perché, come leggi, i miei progetti sono tanti e spesso riutilizzo le shield in altri.
      Al tempo in cui l’ho scritto non ricordo di questo problema, potrebbe essere legato a qualche altra istruzione che utilizzi nel tuo progetto ?

      P.S.: grazie per la dritta, controllerò prima possibile.

    • leonardo il 30 Marzo 2022 alle 23:27
    • Rispondi

    ciao, mi chiedevo se fossi a conoscenza di un server ntp dove tiene conto dell’ora solare e legale, un saluto.

    1. Ciao Leonardo,
      per la natura stessa del sistema NTP dubito che esista un tale server, tuttavia online si trovano diversi script che servono a tener traccia proprio di questa impostazione e che sono in grado di eseguire il cambio dell’ora in modo automatico.
      Io stesso ne ho utilizzato uno in passato per una centralina di irrigazione con l’arduino Yun

Lascia un commento

Il tuo indirizzo email non sarà pubblicato.

Questo sito usa Akismet per ridurre lo spam. Scopri come i tuoi dati vengono elaborati.