I need a code explanation

Hello everyone,

This is a code for getting the time from a NTP server using Arduino and Ethernet Shield.
I have already explained some parts of the code, but I'd like to understand every part of it. PRecisely the blond type, parts with comments in capital letters. Can anybody help me out?

#include <SPI.h>
#include <Ethernet.h> //Librería para usar la shield Ethernet
#include <EthernetUdp.h>
#include <Time.h>
 
/* Configurar la dirección MAC de la tarjeta Ethernet
Aseguraos de que no existe otro dispositivo con esta misma dirección*/
byte mac[] = { 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF };
 
/*Configuración del servidor NTP
 us.pool.ntp.org NTP servidor
 IP que Arduino tomará como servidor a la cual se accede para ver los datos 
   (Configurar la IP del servidor elegido) */
IPAddress timeServer(81,19,96,148); 
 
/* Configuración de la hora. España->GTM+2 en segundos 
horario de verano */
const long timeZoneOffset = 7200L;  
 
/* Sincroniza con el servidor NTP cada 15 s. para realizar el testeo. 
Aumentar el tiempo posteriormente.*/
unsigned int ntpSyncTime = 15;        
 
 
/* ALTER THESE VARIABLES AT YOUR OWN RISK */
//Puerto para escuchar los paquetes UD.
unsigned int localPort = 8888; // WHY 8888? WHICH PORT IS IT? WHY UNSIGNED INT?
//La hora viene en los primeros 48 bytes del paquete UDP.
const int NTP_PACKET_SIZE= 48;      
//Buffer para almacenar los paquetes entrantes y salientes.
byte packetBuffer[NTP_PACKET_SIZE];  
// Sentencia que permite enviar y recibir paquetes UDP.
EthernetUDP Udp; //THIS SENTENCE ALLOWS TO SEND AND RECIEVE DATA, BUT WHY NOT DECLARED IT AT FIRST PLACE?                
/* Comprobar el tiempo que ha pasado desde que se actualizo el sevidor
NTP por última vez.*/
unsigned long ntpLastUpdate = 0;    
//Comprobar la última vez que el reloj mostró "Not in Production".
time_t prevDisplay = 0;            
 
void setup() {
   Serial.begin(9600);// Velocidad de la transmisión y apertura del puerto.
   
   int i = 0;
   int DHCP = 0;
   DHCP = Ethernet.begin(mac); //Asigna direción MAC a la placa Ethernet.
   //Se intenta 30 veces y si no es posible, se desiste.
   while( DHCP == 0 && i < 30){
     delay(1000);
     DHCP = Ethernet.begin(mac);
     i++;
   }
   //Si DHCP es falso (DHCP=0):
   if(!DHCP){
     Serial.println("DHCP FAILED");
     for(;;); //Bucle infinito puesto que DHCP ha fallado.
   }
   Serial.println("DHCP Success"); //Ha sido exitosa la comunicación.
   
   //Intentar obtener fecha y hora 10 veces.
   int trys=0;
   while(!getTimeAndDate() && trys<10) {
     trys++;
   }
  // Inicializar el pin digital 5 como salida.
  pinMode(5, OUTPUT);
}
 
// Obtener fecha y hora.
int getTimeAndDate() {
   int flag=0;
  Udp.begin(localPort); //ANOTHER PORT TO CONFIGURE? WHAT'S THIS?
   sendNTPpacket(timeServer); //Envía un paquete NTP al servidor de tiempo.
   delay(1000);
   if (Udp.parsePacket()){//I JUST DON'T UNDERSTAND THIS WHOLE PART OF THE CODE BELOW. THE HIGHWORD/LOWWORD...WHY BUFFER [41]... ETC....
     Udp.read(packetBuffer,NTP_PACKET_SIZE);  // lee el paquete.
     /* Variable long sin firmar almacena números, 32 bits (4 bytes). 
     Por el contrario que las variables long estándar, las unsigned long 
     no almacenan números negativos.*/
     */ Palabra alta: mitad más significativa (16 bits)
     Palabra baja: mitad menos significativa (16 bits)
     epoch: tiempo Unix */
     unsigned long highWord, lowWord, epoch;
     highWord = word(packetBuffer[40], packetBuffer[41]); 
     lowWord = word(packetBuffer[42], packetBuffer[43]);  
     epoch = highWord << 16 | lowWord;
     // 2208988800 corresponde a 00:00 1 Enero 1970 GMT
     epoch = epoch - 2208988800 + timeZoneOffset;  //TILL HERE i don't understand it
     flag=1;
     setTime(epoch);//Config. hora 
     ntpLastUpdate = now();//Última actualización
   }
   return flag;
}
 
//Enviar un paquete al servidor de hora. 
unsigned long sendNTPpacket(IPAddress& address) //SEND A PACKAGE TO THE NTP SERVER?
{
  //Todos los bytes del buffer a 0.
  memset(packetBuffer, 0, NTP_PACKET_SIZE);
  packetBuffer[0] = 0b11100011; //WHAT THE HELL¿? WHY LL THESE NUMBERS IN HEX.?
  packetBuffer[1] = 0;
  packetBuffer[2] = 6;
  packetBuffer[3] = 0xEC;
  packetBuffer[12]  = 49;
  packetBuffer[13]  = 0x4E;
  packetBuffer[14]  = 49;
  packetBuffer[15]  = 52;                  
  Udp.beginPacket(address, 123);  //THIS TOO
  Udp.write(packetBuffer,NTP_PACKET_SIZE);
  Udp.endPacket();
}
 
// Muestra la hora y la fecha.
void clockDisplay(){
  Serial.print(hour());
  printDigits(minute());
  printDigits(second());
  Serial.print(" ");
  Serial.print(day());
  Serial.print(" ");
  Serial.print(month());
  Serial.print(" ");
  Serial.print(year());
  Serial.println();
}
 
//Visualización del reloj: grabados anterior colon y 0 a la izquierda.
void printDigits(int digits){
  Serial.print(":");
  if(digits < 10)
    Serial.print('0');
  Serial.print(digits);
}
 
// This is where all the magic happens...
void loop() {
    /* Actualizar la hora a través del servidor NTP con la regularidad con que se 
    estableció arriba. */
    [b]if(now()-ntpLastUpdate > ntpSyncTime) {  //I got lost[/b]
      int trys=0;
      while(!getTimeAndDate() && trys<10){
        trys++;
      }
      if(trys<10){
        Serial.println("ntp server update success");
      }
      else{
        Serial.println("ntp server update failed");
      }
    }
    // Display the time if it has changed by more than a second.
    if( now() != prevDisplay){
      prevDisplay = now();
      clockDisplay();  
    }

Thanks!!!

Udp.begin(localPort); //ANOTHER PORT TO CONFIGURE? WHAT'S THIS?

In socket programming clients use different port number for every new connection.
The server side has - for a given protocol like NTP - a fixed port number

wikipedia explains quite well how TCP/UDP connections work.

unsigned int localPort = 8888; // WHY 8888? WHICH PORT IS IT? WHY UNSIGNED INT?

Its upto you what value it is, it sends it in the NTP request so the NTP server can respond to port 8888, unsigned int is probably how the UDP library describes a port number. ( have a look in the core )

EthernetUDP Udp; //THIS SENTENCE ALLOWS TO SEND AND RECIEVE DATA, BUT WHY NOT DECLARED IT AT FIRST PLACE?

No need to declare it until used.

packetBuffer[0] = 0b11100011; //WHAT THE HELL¿? WHY LL THESE NUMBERS IN HEX.?

Its in binary, you can also use the Arduio equivalents: https://github.com/arduino/Arduino/blob/master/hardware/arduino/cores/arduino/binary.h

Might be better to use the defines, as binary literals are a GCC specific.

if (Udp.parsePacket()){//I JUST DON'T UNDERSTAND THIS WHOLE PART OF THE CODE BELOW. THE HIGHWORD/LOWWORD...WHY BUFFER [41]... ETC....

It is the NTP protocol that describes what is going on there. It is receiving data from an NTP server.

unsigned long sendNTPpacket(IPAddress& address) //SEND A PACKAGE TO THE NTP SERVER?

Yes, as you can see in getDateAndTime(), sendNTPpacket is called first to request the NTP data, then the part you questioned above ( if (Udp.parsePacket()){ ) starts receiving the result if there is data/ Udp.parsePacket() == true .

cross post merged ... (please don't post same question on multiple subsections of the forum)

Hi!

I have found this code that returns the IP of a NTP server.
I would like to understand a few things of the code, which are written in the comments. Please feel free to correct it if its wrong.
Thank you

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

byte mac[] = { 0x00, 0xAA, 0xBB, 0xCC, 0xDE, 0x02 };

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

  pinMode(4,OUTPUT);
  digitalWrite(4,HIGH);

  if (Ethernet.begin(mac) == 0) {
    Serial.println("Failed to configure Ethernet using DHCP");
    for(;;)
      ;
  }
  Serial.print("My IP address: ");
  Serial.println(Ethernet.localIP());
//Till here everything OK.

  IPAddress ntpIP; //what does this sentence?

  DNSClient dns; //what does this sentence ?
  dns.begin(Ethernet.dnsServerIP());//begins to ..?
  if(dns.getHostByName("pool.ntp.org",ntpIP)) {//if I get the IP of the NTP server, then show it.
    Serial.print("NTP IP from the pool: ");
    Serial.println(ntpIP);
  }
  else Serial.println("DNS lookup failed");
}

void loop() {
}

Moderator mode:
Please do not post your question multiple times on the forum that wastes every bodies time including yours.

First of all, thank you for your answers and for the patience of robtillaart

I have been expending some time looking for more information and definetly I dare to say I understand almost the whole code.
But... I still have a few doubts.

  • In function unsigned long sendNTPpacket(IPAddress& address):
    packetBuffer[0] = 0b11100011;
    packetBuffer[1] = 0;
    .....
    Is there any reason for being the first number in binary and the rest in hex.?

  • packetBuffer[0] = 0b11100011;* is refering to Leap Indicator (LI), version or mode?

  • Once we are in the "void loop" when do we get out? Is the sentence "unsigned int ntpSyncTime = 15; " that obliged us to do it? And then.. where do we go to?

the computer starts reading line by line until the loop where it keeps repeating the code inside it, isn't it? but we have to syncronize..

  • So the port 8888 is for clients using UDP or TCP and the port 123 is ....just the same?? I do not see when I'd have to use one and not the other.

So the port 8888 is for clients using UDP or TCP and the port 123...

Internet connections (UDP or TCP) have two port numbers involved. The "destination port" of a client usually defines the service being requested. TCP port 25 is SMTP Email, 123 is Network Time Protocol. The "source port" is supposed to be random and unique on the client, and should not conflict with any service that the client might be offering at the same time. In this case, it looks like the programmer has chosen 8888 as the initial "random" port.

westfw:

So the port 8888 is for clients using UDP or TCP and the port 123...

Internet connections (UDP or TCP) have two port numbers involved. The "destination port" of a client usually defines the service being requested. TCP port 25 is SMTP Email, 123 is Network Time Protocol. The "source port" is supposed to be random and unique on the client, and should not conflict with any service that the client might be offering at the same time. In this case, it looks like the programmer has chosen 8888 as the initial "random" port.

I understand it now. Thanks!