Gestione NTP e client

Buonasera Forum.
Sto cercando di realizzare un programma per gestire un’impianto di irrigazione con le seguenti caratteristiche:

  • comunicazione con applicazione telefono (MIT)
  • sincronizzazione ora tramite NTP

L’hardware che sto utilizzando è il seguente:

  • Arduino Mega
  • Ethernet shield W5100
  • IDE 1.8.10

Premetto che ho letto molti post riguardante la programmazione NTP e gestione client e che, presi singolarmente funzionano. Il mio problema sta nel fatto che quando provo ad usarli entrambi, viene meno la comunicazione con il client.

Di seguito posto il codice

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

//Server Setting
IPAddress ip(192, 168, 1, 100);

EthernetServer server2(80);
EthernetClient client;
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };

//NTP Setting
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;
const int timeZone = 1;     // Central European Time
byte packetBuffer[ NTP_PACKET_SIZE];
EthernetUDP Udp;

//Irrigation variables
int Line1 = 4;
int MLine1[5];
int Line2 = 5;
int MLine2[5];
int Line3 = 6;
int MLine3[5];
int Line4 = 7;
int MLine4[5];
int Pos_M = 0;

void setup()
{
  //Initialization Server
  Ethernet.begin(mac, ip);

  server2.begin();
  Serial.begin(9600);

  //Initialization NTP
  while (!Serial)
  {
    ; // wait for serial port to connect. Needed for Leonardo only
  }
  if (Ethernet.begin(mac) == 0)
  {
    Serial.println("Failed to configure Ethernet using DHCP");
    for (;;)
      ;
  }

  Udp.begin(localPort);

  //Initialization Output line
  pinMode(Line1, OUTPUT);

  //Initialization Manual control
  MLine1[0, 1, 2, 3, 4] = 0;
  MLine2[0, 1, 2, 3, 4] = 0;
  MLine3[0, 1, 2, 3, 4] = 0;
  MLine4[0, 1, 2, 3, 4] = 0;
}

void loop()
{
  DataTransfer_From_To_Server();
  NTPFunction();
}

//Function to receive/send data from/to WebServer
void DataTransfer_From_To_Server()
{
  //Receive data from WebServer
  int bufLength;
  String readString;
  client = server2.available();
  {
    if (client)
    {
      boolean currentLineIsBlank = true;
      while (client.connected())
      {
        if (client.available())
        {
          char c = client.read();
          if (readString.length() < 100)
          {
            readString += c;
          }
          if (c == 'n')
          {
            
           codice

            client.println("HTTP/1.1 200 OK");
            client.println("Content-Type: text/html");
            client.println();

            //Send data to WebServer
            DataTransferToServer();

            delay(1);
            client.stop();
          }
        }
      }
    }
  }
}

//Function to send data to WebServer
void DataTransferToServer()
{
  
           codice

}

//Function NTP
void NTPFunction()
{
  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) + timeZone * SECS_PER_HOUR;
    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(1);
}

// 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();
}

Se disabilito l’NTP e l’UDP allora funziona la void “DataTransfer_From_To_Server”; se invece abilito tutto funziona solo l’NTP.

Grazie a tutti in anticipo per il prezioso aiuto.
Ciao
Roberto

Buonasera,
essendo il tuo primo post, nel rispetto del regolamento della sezione Italiana del forum (… punto 13, primo capoverso), ti chiedo cortesemente di presentarti IN QUESTO THREAD (spiegando bene quali conoscenze hai di elettronica e di programmazione ... possibilmente evitando di scrivere solo una riga di saluto) e di leggere con molta attenzione tutto il su citato REGOLAMENTO ... Grazie. :slight_smile:

Guglielmo

P.S.: Ti ricordo che, purtroppo, fino a quando non sarà fatta la presentazione nell’apposito thread, nessuno ti potrà rispondere, quindi ti consiglio di farla al più presto. :wink:

robypaga:
Se disabilito l’NTP e l’UDP allora funziona la void “DataTransfer_From_To_Server”; se invece abilito tutto funziona solo l’NTP.

Cosa intendi esattamente con “se invece abilito tutto funziona solo l’NTP”? Non riesci più a collegarti al server web di Arduino? O riceve le connessioni ma non manda i dati? Intanto in questi casi perché non metti un po’ di Serial.print() di debug per vedere sul monitor seriale cosa succede esattamente?

In ogni caso ho l’impressione che possa entrarci in qualche modo il Mac address “fittizio” che usi (il DEAD BEEF FEED per intenderci), per caso hai qualche altro Arduino già attivo con quel MAC?
Prova a generare un nuovo MAC address con qualche tool come QUESTO, ad esempio metti questo (che ho generato con quel tool) e vedi se funziona:
byte mac = { 0x3E, 0xDB, 0x7E, 0x95, 0x9C, 0xE2 };

Ciao, innanzitutto grazie per la risposta celere.

docdoc:
Cosa intendi esattamente con "se invece abilito tutto funziona solo l'NTP"? Non riesci più a collegarti al server web di Arduino? O riceve le connessioni ma non manda i dati? Intanto in questi casi perché non metti un po' di Serial.print() di debug per vedere sul monitor seriale cosa succede esattamente?

In ogni caso ho l'impressione che possa entrarci in qualche modo il Mac address "fittizio" che usi (il DEAD BEEF FEED per intenderci), per caso hai qualche altro Arduino già attivo con quel MAC?
Prova a generare un nuovo MAC address con qualche tool come QUESTO, ad esempio metti questo (che ho generato con quel tool) e vedi se funziona:
byte mac = { 0x3E, 0xDB, 0x7E, 0x95, 0x9C, 0xE2 };

Quando mi collego con l'NTP non riesco più a collegarmi al server web nel senso che non riesco ne a mandare i date, ne a riceverli.
Per il discorso del MAC provo e ti faccio sapere.
Grazie per il momento. :slight_smile:
Ciao

Eccomi ancora qua, ho provato ad applicare il suggerimento di docdoc di modificare il MAC ma senza successo.
Aggiungendo i Serial.print() vedo che il codice si blocca all' if(client)

void DataTransfer_From_To_Server()
{
  //Receive data from WebServer
  int bufLength;
  String readString;
  client = server2.available();
  {
    if (client)
    {
        Serial.print("ciao");

nel senso che non mi visualizza il "ciao".

robypaga:
Aggiungendo i Serial.print() vedo che il codice si blocca all' if(client)
nel senso che non mi visualizza il "ciao".

Ma se è solo quel print() che hai messo (non potevi postare comunque l'intero programma?), non sai se non entra affatto o se entra e salta la if()... Devi mettere qualche altro Serial.print() per capire meglio.

Però vedo anche un paio di cose che non mi convincono affatto!
Primo, una riga "if (c == 'n')" che è apparentemente sbagliata sei sicuro che non intendevi attendere un line feed, ossia "if (c == '\n')"?
Ma soprattutto vedo che fai DUE volte Ethernet.begin(), una con mac e ip e la seconda solo col mac (che di fatto significa DHCP)!!! Togli quella relativa al solo mac, temo che quando "becchi" un IP tramite DHCP non potrai più connetterti al 192.168.1.100 ma a chissà quale altro indirizzo si è configurato!

Altre cose "accessorie".
La parte del "while (!Serial)" non ti serve, puoi toglierla (come è nel commento, serve solo per la Leonardo).
Se puoi leva quella String che non va usata mai su Arduino, o quantomeno creala quando ti serve, e non ad ogni ciclo.
E la variabile "int bufLength;" la definisci ma non sembri usarla mai, serve?

docdoc:
Primo, una riga "if (c == 'n')" che è apparentemente sbagliata sei sicuro che non intendevi attendere un line feed, ossia "if (c == '\n')"?

Con if (c == 'n') la comunicazione con il web server funziona correttamente.

docdoc:
Ma soprattutto vedo che fai DUE volte Ethernet.begin(), una con mac e ip e la seconda solo col mac (che di fatto significa DHCP)!!! Togli quella relativa al solo mac, temo che quando "becchi" un IP tramite DHCP non potrai più connetterti al 192.168.1.100 ma a chissà quale altro indirizzo si è configurato!

Se tolgo Ethernet.begin(mac) non mi funziona l'NTP ma solo il web server

docdoc:
La parte del "while (!Serial)" non ti serve, puoi toglierla (come è nel commento, serve solo per la Leonardo).

Idem come sopra, se la tolgo non mi funziona l'NTP
:o
Dilemma dei dilemmi :confused:

robypaga:
Con if (c == 'n') la comunicazione con il web server funziona correttamente.

Scusa ma non ha senso, con quella if() tu semplicemente consideri completa la request http appena ricevi il primo carattere 'n', quando la richiesta invece, come standard, finisce al carattere '\n' (ossia line feed)...

Se tolgo Ethernet.begin(mac) non mi funziona l'NTP ma solo il web server

Ripeto, non ha alcun senso fare due volte la begin(), perché la porta Ethernet è una e non puoi avere contemporaneamente sia un IP statico (192.168.1.100) sia DHCP (quindi IP dinamico assegnato dal tuo router). Se vuoi che Arduino abbia un IP statico tu devi lasciare solo la begin(mac,ip).

Fatto questo, se non funziona l'NTP con un IP statico ma solo con quello dinamico il problema è sicuro che è proprio nel fatto che tu hai indicato "time.nist.gov" come server e mentre con l'IP dinamico ottieni dal router anche l'impostazione del DNS, con quello statico tu non avendo messo alcun DNS non puoi risolvere quel nome, per cui la chiamata all'NTP non funzionerà mai.

Quindi se vuoi avere un indirizzo IP statico, tu devi impostare anche l'IP (quindi togliere la begin senza l'indirizzo IP) ma se vuoi usare l'NTP "time.nist.gov" (ma ti consiglio di usare il pool italiano quindi "it.pool.ntp.org") tu devi specificare anche il DNS (che sarà il tuo router, quindi forse 192.168.1.1).

Insomma, dovresti prima cercare di studiare un pochino i protocolli ed il funzionamento di base delle reti TCP/IP, stai cercando di fare cose che non comprendi ancora un minimo e soprattutto fai prove che apparentemente ti danno risultati incoerenti se non impossibili (come l'NTP che non funziona se non attendi con la "while (!Serial)")!

docdoc:
Scusa ma non ha senso, con quella if() tu semplicemente consideri completa la request http appena ricevi il primo carattere 'n', quando la richiesta invece, come standard, finisce al carattere '\n' (ossia line feed)...

Perfettamente ragione, così come "while (!Serial)", effettivamente è inutile.

Ho provato ad assegnare il dns ma non cambia nulla.

Attualmente il setup è il seguente

void setup()
{
  //Initialization Server
  Serial.begin(9600);
  Ethernet.begin(mac, ip, dns);

  //Initialization NTP
  Udp.begin(localPort);

  server.begin();
  //Initialization Output line
  pinMode(Line1, OUTPUT);

  //Initialization Manual control
  MLine1[0, 1, 2, 3, 4] = 0;
  MLine2[0, 1, 2, 3, 4] = 0;
  MLine3[0, 1, 2, 3, 4] = 0;
  MLine4[0, 1, 2, 3, 4] = 0;

  if (Ethernet.begin(mac) == 0)
  {
    // no point in carrying on, so do nothing forevermore:
  }
}

void loop()

se tolgo "if (Ethernet.begin(mac) == 0)" e lascio l'inizializzazione con IP statico, l'NTP non funziona.

Grazie ancora per il paziente supporto. :slight_smile:

robypaga:
Ho provato ad assegnare il dns ma non cambia nulla.
Attualmente il setup è il seguente

Ethernet.begin(mac, ip, dns);

E dov'è la definizione della variabile "dns"? Dovresti puntare all'indirizzo del tuoi router (in genere per le reti 192.168.1 è 192.168.1.1), tu cosa hai messo?
Comunque sia ti consiglio di mettere tutti i parametri espliciti in caso di IP statico, esempio:

IP: 192.168.1.100
Subnet: 255.255.255.0
Gateway: 192.168.1.1
DNS: 192.168.1.1

Cioè:

...
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };  
IPAddress dnServer(192, 168, 1, 1);
IPAddress gateway(192, 168, 1, 1);
IPAddress subnet(255, 255, 255, 0);
IPAddress ip(192, 168, 1, 100);

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

  Ethernet.begin(mac, ip, dnServer, gateway, subnet);
...

Perfetto docdoc.
Tutto risolto, ora funziona sia l’NTP che il webserver.

Di seguito il codice

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

//Server Setting
IPAddress ip(192, 168, 1, 100);
IPAddress dns(192, 168, 1, 254);
IPAddress gateway(192, 168, 1, 254);
IPAddress subnet(255, 255, 255, 0);

EthernetServer server(80);
EthernetClient client;

byte mac[] = {  0x3E, 0xDB, 0x7E, 0x95, 0x9C, 0xE2 };

//NTP Setting
IPAddress timeServer(217, 147, 223, 78); //europe.pool.ntp.org

unsigned int localPort = 8888;       // local port to listen for UDP packets
const int NTP_PACKET_SIZE = 48;
const int timeZone = 1;     // Central European Time
byte packetBuffer[ NTP_PACKET_SIZE];
EthernetUDP Udp;

void setup()
{
  //Initialization Server
  Serial.begin(9600);
  Ethernet.begin(mac, ip, dns, gateway, subnet);
  server.begin();

  //Initialization NTP
  Udp.begin(localPort);

}

void loop()
{
  DataTransfer_From_To_Server();
  NTPFunction();
}

//Function to receive/send data from/to WebServer
void DataTransfer_From_To_Server()
{
  codice web server
}

//Function to send data to WebServer
void DataTransferToServer()
{
   codice web server
}

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

  // wait to see if a reply is available
  delay(1000);
  if ( Udp.parsePacket() )
  {
    //Serial.print(Udp.parsePacket());//da eliminare
    Serial.print("ciaontp");//da eliminare
    // 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) + timeZone * SECS_PER_HOUR;
    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(10);
}

// send an NTP request to the time server at the given address
unsigned long sendNTPpacket(IPAddress& 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();
}

Ho dovuto usare l’IP anzichè l’indirizzo per il server NTP, altrimenti la richiesta UDP si bloccava appena accedevo al webserver.

Ti ho karmato :slight_smile:
Grazie mille e alla prossima

Ottimo! Ciao!