Ethernet IDE 1.0 e DNS

Scrivo due righe, così che possano essere d'aiuto se qualcuno dovesse avere le mie stesse esigenze.
Con l'IDE 1.0 la gestione dell' Ethernet.begin()è cambiata.
Attualmente supporta questi formati (dal forum internazionale):

void begin(uint8_t *mac_address, IPAddress local_ip);
void begin(uint8_t *mac_address, IPAddress local_ip, IPAddress dns_server);
void begin(uint8_t *mac_address, IPAddress local_ip, IPAddress dns_server, IPAddress gateway);
void begin(uint8_t *mac_address, IPAddress local_ip, IPAddress dns_server, IPAddress gateway, IPAddress subnet);

Significa che se si specifica qualcosa oltre a mac address ed ip bisogna per forza mettere anche il dns.
Laddove non si abbia la possibilità basta replicare il gateway.
Per intenderci:

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

Oppure, nel caso si voglia indicare un dns (come OpenDNS ad esempio) l'inizializzazione completa diventa:

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

Questo è fondamentale, se invertiamo le posizioni il dns verrà risolto con il gateway o la subnet a seconda.

Chiosando, nel caso si voglia implementare OpenDNS su IDE 1.0 il codice diventa:

/************ ETHERNET STUFF ************/
byte mac[] = { 
  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress ip(192,168,0,9); // IP in lan
IPAddress gateway(192,168,0,1); // Internet access via router
IPAddress subnet(255,255,255,0); // Subnet mask
IPAddress dns(208,67,222,222); // OpenDNS IP
char myserver[] = "google.com"; 
EthernetClient client;  // initialize the library instance

void.setup () {
  Ethernet.begin(mac,ip,dns,gateway,subnet);
  server.begin();
}

Questo codice ha però un problema.
Specificando che il problema potrebbe dipendere dalla mia attuale configurazione (oltre al webclient con dns ho anche un web server sulla porta 84) la questione è la seguente.

Quando andate ad avviare il client:

    if (client.connect(myserver, 80)  ) {

Questo ritorna TRUE in ogni caso. Quando c'è la connessione internet, risolvendo correttamente il server name. Ma anche quando la connessione internet non c'è od addirittura il cavo ethernet è staccato.

Non ho ancora ben chiaro il motivo di ciò ma la soluzione dovrebbe essere la seguente.

    if (client.connect(myserver, 80) && client.connected() ) {

Questo codice risolve il problema di cui sopra, regolarizzando il comportamento in connessione.
Porta inoltre un altro vantaggio.
Utilizzando il webclient senza dns, ovvero con la classe IPAddress myserver, il client.connect impiega anche 30 secondi a verificare la condizione qual ora non via sia connessione.
Utilizzando client.connect && client.connected il tempo di verifica si riduce a quasi la metà.

thanks

come e' la situazione sul fronte dhcp ?

Testato:
come e' la situazione sul fronte dhcp ?

Boh... Non lo uso, nella rete di casa lo tengo disabilitato e preferisco assegnare ip statici.

Testato:
come e' la situazione sul fronte dhcp ?

Dagli esempi e da sperimentazione diretta oserei afferamare con certezza che se si fa solo begin (mac) il resto se lo cerca da solo
Almeno nella mia rete dove il router Cisco fa anche da server DHCP

Controllando il file Ethernet.cpp si scoprono delle cose curiose.

Utilizzando int EthernetClass::begin(uint8_t *mac_address)Arduino attiva il DHCP e di ottenere tutti i dati dalla rate (ovvero, di solito, il server DHCP del Router).
Se fallisce la scheda non viene settata e viene restituito uno 0.

Se invece utilizziamo le altre espressioni

--> void EthernetClass::begin(uint8_t *mac_address, IPAddress local_ip)
--> void EthernetClass::begin(uint8_t *mac_address, IPAddress local_ip, IPAddress dns_server)
--> void EthernetClass::begin(uint8_t *mac_address, IPAddress local_ip, IPAddress dns_server, IPAddress gateway)

Esse si chiamano a cascata, aggiungendo ogni volta il dato mancante.
Così il DNS di default è X.X.X.1 (e nel mio caso non ci azzecca visto che uso il 254), il gateway di default e X.X.X.1 (stesso discorso) e la subnetmask e 255.255.255.0 , se non diversamente specificata.
L'ultima funzione che viene richiama, sempre in coda, é void EthernetClass::begin(uint8_t *mac, IPAddress local_ip, IPAddress dns_server, IPAddress gateway, IPAddress subnet) che inizializza l'ethernet shield assegnandogli i dati immessi o quelli di default.

Riguardo al Client.connect, il codice è questo, se si passa un nome host:

int EthernetClient::connect(const char* host, uint16_t port) {
  // Look up the host first
  int ret = 0;
  DNSClient dns;
  IPAddress remote_addr;
  dns.begin(Ethernet.dnsServerIP());
  ret = dns.getHostByName(host, remote_addr);
  if (ret == 1) {
    return connect(remote_addr, port);
  } else {
    return ret;
  }
}

quest'altro, se si passa un indirizzo IP.

int EthernetClient::connect(IPAddress ip, uint16_t port) {
  if (_sock != MAX_SOCK_NUM)
    return 0;

  for (int i = 0; i < MAX_SOCK_NUM; i++) {
    uint8_t s = W5100.readSnSR(i);
    if (s == SnSR::CLOSED || s == SnSR::FIN_WAIT) {
      _sock = i;
      break;
    }
  }

  if (_sock == MAX_SOCK_NUM)
    return 0;

  _srcport++;
  if (_srcport == 0) _srcport = 1024;
  socket(_sock, SnMR::TCP, _srcport, 0);

  if (!::connect(_sock, rawIPAddress(ip), port)) {
    _sock = MAX_SOCK_NUM;
    return 0;
  }

  while (status() != SnSR::ESTABLISHED) {
    delay(1);
    if (status() == SnSR::CLOSED) {
      _sock = MAX_SOCK_NUM;
      return 0;
    }
  }
  return 1;
}

Come si vede, nel primo caso, se non vi è risoluzione del nome DNS la funzione restituisce 0 cioè FALSE.
Come fa a restituirti 1 o un altro numero (cioè TRUE) se non è connesso ad internet o ha il cavo staccato?
Mistero! :astonished:

PaoloP:
Come si vede, nel primo caso, se non vi è risoluzione del nome DNS la funzione restituisce 0 cioè FALSE.
Come fa a restituirti 1 o un altro numero (cioè TRUE) se non è connesso ad internet o ha il cavo staccato?
Mistero! :astonished:

Misteri misteriosi.
Ti ho preparato uno sketch di prova, testalo!
A me restituisce sempre " Connecting to Pachube... Connection Done... Maybe. " sia che la connessione internet sia attiva o meno.

Le uniche note sono:
se è attiva la connessione l'if si risolve in qualche secondo, se è disattivata impiega anche 30 secondi.
Se quando avvio il micro con lo sketch la connessione è disabilitata in partenza, l'if si esegue correttamente. Tuttavia se attivo la connessione internet e poi la disattivo da li in poi l'if ritorna sempre TRUE.

// sketch test dns

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

/************ ETHERNET STUFF ************/
byte mac[] = { 
  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress ip(192,168,0,9); // IP in lan
IPAddress gateway(192,168,0,1); // Internet access via router
IPAddress subnet(255,255,255,0); // Subnet mask
IPAddress dns(208,67,222,222); // OpenDNS IP

/*--------- Pachube ---------*/
// IPAddress myserver(216,52,233,122); //  The address of the server you want to connect to (pachube.com)
char myserver[] = "pachube.com"; 
EthernetClient client;  // initialize the library instance

void setup(){
  Serial.begin(115200); 
  Serial.println(" Test DNS ");
  Ethernet.begin(mac,ip,dns,gateway,subnet);
}

void loop(){

  if (client.connect(myserver, 80)) {
    Serial.println("Connecting to Pachube...");
    Serial.println("Connection Done... Maybe.");
  } 
  else {
    // if you couldn't make a connection:
    Serial.println("Connection failed!");
  }
  client.stop();
  delay (1000);

}

Nel codice della funzione DNSClient::ProcessResponse(uint16_t aTimeout, IPAddress& aAddress), chiamata in causa da DNSClient::getHostByName(const char* aHostname, IPAddress& aResult) presente nella funzione EthernetClient::connect(const char* host, uint16_t port), c'è un timeout di 5 secondi ripetuti 3 volte. L'attesa massima per la risoluzione del nome DNS è quindi di 15 secondi.

Appena posso provo lo sketch.
Comunque tutti questi ragionamenti li ho fatti con le librerie dell'IDE 1.0.

Paolo.

PaoloP:
L'attesa massima per la risoluzione del nome DNS è quindi di 15 secondi.

Infatti, quando funziona, ho contato proprio 15 secondi...

Appena posso provo lo sketch.

Fammi sapere, che sono curiosa! :slight_smile:
Cmq si, anche io sto parlando di IDE 1.0

Mi restituisce sempre connection failed =(

Ho modificato il listato.
Adesso funziona!!! :grin: :grin: :grin:

// sketch test dns

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

/************ ETHERNET STUFF ************/
byte mac[] = { 
  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress ip(192,168,40,9); // IP in lan
IPAddress gateway(192,168,40,254); // Internet access via router
IPAddress subnet(255,255,255,0); // Subnet mask
IPAddress dns(208,67,222,222); // OpenDNS IP
// IPAddress dns(192,168,40,254); // Router DNS Agent

IPAddress remote_addr;
/*--------- Pachube ---------*/
// IPAddress myserver(216,52,233,122); //  The address of the server you want to connect to (pachube.com)
char myserver[] = "pachube.com"; 
EthernetClient client;  // initialize the library instance
DNSClient DNS;

void setup(){
  Serial.begin(9600); 
  Serial.println("Test DNS ");

  Ethernet.begin(mac,ip,dns,gateway,subnet);
  Serial.println("Ethernet started.");
  Serial.print("Local IP: ");
  Serial.println(Ethernet.localIP());
  Serial.print("SubnetMask: ");
  Serial.println(Ethernet.subnetMask());
  Serial.print("Gateway: ");
  Serial.println(Ethernet.gatewayIP()); 
  Serial.print("DNS Server: ");
  Serial.println(Ethernet.dnsServerIP());
}

void loop(){
  DNS.begin(Ethernet.dnsServerIP());
  int ret=0;
  while(ret==0){
    ret = DNS.getHostByName(myserver, remote_addr);
    Serial.print("DNS status: ");
    Serial.println(ret);
    //  if ( ret == 1)
    //  {
    Serial.print("Host: ");
    Serial.println(myserver);
    Serial.print("Remote IP: ");
    Serial.println(remote_addr);
    // }
    delay(2000);
  }


  if (client.connect(remote_addr, 80)) {
   Serial.println("Connecting to Pachube...");
   Serial.println("Connection Done... Maybe.");
   } 
   else {
   // if you couldn't make a connection:
   Serial.println("Connection failed!");
   }
   client.stop();
  delay (1000);
}

Non solo...
ho estratto le funzioni di chiamata del DNS dalla libreria così sarà possibile riutilizzare il codice.

Per poter utilizzare il DNS, oltre a tutto cio che serve per impostare la scheda ethernet, è necessario includere la libreria nel codice con

#include <Dns.h>

ho provato a non includerla perché richiamata dalla lib Ethernet ma il compilatore si lamenta.

Poi bisogna creare un'oggetto di classe DNS, con

DNSClient DNS;

ho usato il maiuscolo perche dns minuscolo è utilizzato per memorizzare l'indirizzo del server dns, ad esempio.

IPAddress dns(208,67,222,222); // OpenDNS IP

Se si infatti si usa lo stesso nome il compilatore ci segnala la doppia dichiarazione. (che permaloso questo compilatore)

Fatto ciò, si inizializza passando alla classe l'indirizzo del server DNS con

DNS.begin(Ethernet.dnsServerIP());

ad esempio in setup();

il codice per chiedere la risoluzione del nome DNS in indirizzo IP è

DNS.getHostByName(host, ip_addr);

che passando host restituisce su ip_addr l'indirizzo.

host e ip_addr devono essere dichiarate in questo modo

char host[] = "pachube.com"; 
IPAddress ip_addr;

La funzione DNS.getHostByName(host, ip_addr); restituisce diversi status

// Possible return codes from ProcessResponse
#define SUCCESS 1
#define TIMED_OUT -1
#define INVALID_SERVER -2
#define TRUNCATED -3
#define INVALID_RESPONSE -4

oppure 0 se fallisce il processo per un'altro motivo

un esempio di codice che restituisce lo status:

int ret=0;
ret = DNS.getHostByName(host, ip_addr);
Serial.print("DNS status: ");
Serial.println(ret);
if (ret==1){
 Serial.print("Host: ");
 Serial.println(host);
 Serial.print("Remote IP: ");
 Serial.println(ip_addr);}

Spero sia utile. :%

Paolo.

PaoloP:
Per poter utilizzare il DNS, oltre a tutto cio che serve per impostare la scheda ethernet, è necessario includere la libreria nel codice con

#include <Dns.h>

Spero sia utile. :%

Oddio utile è utile... ma è stranissimo!
Io sapevo che tutta questa procedura, inclusa l'inclusione (passami il giro di parole), valeva per gli IDE precedenti al 1.0.
Con il passaggio al nuovo ide il codice che hai scritto "non sarebbe dovuto servire" O_o
Ho uno sketch di prova per ide 22 che è fatto uguale!
Non è che scrivendo il codice che hai scritto, ti appoggi alla libreria esterna dns.h ed utilizzi quella al posto della ethernet.h per la risoluzione?

BTW io al momento sono a 30710 k di sketch, se lo modifico seguendo il tuo codice... sforo!
Quindi ho idea che rimarrò con il mio client.connect && client.connected che mi sembra una soluzione più economica in termini di codice e spazio. Visto che funziona da 3 giorni senza problemi mi accontento :slight_smile:
Cmq grazie dell'approfondimento, è di sicuro una cosa curiosa!

DanielaES:
Con il passaggio al nuovo ide il codice che hai scritto "non sarebbe dovuto servire" O_o

Il codice l'ho preso dalla libreria Ethernet, estraendolo dalla classe.

Non è che scrivendo il codice che hai scritto, ti appoggi alla libreria esterna dns.h ed utilizzi quella al posto della ethernet.h per la risoluzione?

No, la Ethernet.h ha al suo interno l'include del DNS.h.
L'ho dovuto esplicitare nuovamente perché altrimenti il compilatore mi dava errore. Non mi riconosceva la classe DNSClient.

BTW io al momento sono a 30710 k di sketch, se lo modifico seguendo il tuo codice... sforo!

Questo è strano. Dovrebbe aumentare ma non così tanto.
Infatti, il tuo codice richiama la funzione client.connect passando l'host e la porta.
Quella funzione richiama la classe DNSclient risolvendo il nome e ottenendo l'indirizzo ip, poi chiama la funzione client.connect passando l'indirizzo ip e la porta.
Nel codice riscritto da me, invece, cerco prima l'indirizzo ip relativo al host, tramite la classe DNSClient, e poi passo a client.connect direttamente indirizzo ip e porta.

Forse qualcuno esperto di c++ può spiegarci questi comportamenti?

PaoloP:
No, la Ethernet.h ha al suo interno l'include del DNS.h.
L'ho dovuto esplicitare nuovamente perché altrimenti il compilatore mi dava errore. Non mi riconosceva la classe DNSClient.

Questo lo ricordo anche io, quando ho cercato di portare il codice della 22 su ide 1.0 non mi riconosceva la classe dsnclient. Quindi ho dato per scontato che la classe non fosse più attiva ed ho lavorato senza!

Forse qualcuno esperto di c++ può spiegarci questi comportamenti?

Ripeto, misteri misteriosi. Ho chiesto anche delucidazioni sul forum internazionale ma le risposte languono... fortuna che ho trovato quella soluzione di straforo!