Lug 202014
 

Dai diciamoci la verità: 25€ per un’Arduino e altri 35 per l’ethernet shield fanno 60€ che sono decisamente troppi. Con quei soldi ci compriamo quasi due Raspberry che oltre alla porta ethernet ci fornisce usb, audio, grafica, e molto altro. Inoltre ci sono schede Arduino in cui non è possibile montare la classica shield, pensimao ad esempio alla nano, giusto per fare un esempio. La connettività ethernet è, secondo me, uno dei talloni d’achille di Arduino. Per fortuna ci sono dei moduli meno costosi che ci permettono di ridurre la spesa, specie se il nostro progetto necessita di più di un accesso ethernet. La prima cosa da sapere è che ci sono due famiglie principali di moduli / shield che si differenziano per il chip utilizzato che può essere il ENC28J60 oppure il w5100. Per entrambi abbiamo delle librerie per Arduino già pronte per cui non abbiamo il problema di scegliere in base alla disponibilità di una libreria già pronta e funzionante.

Tutte le implementazioni con il w5100  costano di più e c’è un motivo. Senza addentrarci in particolari troppo tecnici, diciamo che questo chip è già completo di tutto ciò che serve per la comunicazione con la rete ethernet, mentre con il ENC28J60 è necessario implementare via software alcune cose che l’altro chip già possiede, facendo aumentare la necessità di risorse impegnate da parte di Arduino, sia come potenza elaborativa, che memoria necessaria.

Il modulo in provaw5100

In casa ho un modulo ethernet comprato a circa 15€ un sacco di tempo fa insieme ad altri componenti e che non ho mai utilizzato. Il modulo in questione è basato sul chip w5100 ed è quello nella foto qui a lato. La prima cosa da fare è procurarsi il datasheet, infatti, anche se sul retro troviamo stampato il significato di ogni pin ciò non è sufficiente. Ad esempio VDD sappiamo essere il pin di alimentazione, ma non sappiamo se sia a 3.3 o 5V. Andando al negozio dove l’ho comprato, vedo subito che ci sono delle apparenti incongruenze. Prima è scritto “Operates 3.3V with 5V I/O signal tolerance” che io interpreterei che l’alimentazione è a 3.3V ma i pin dati funzionano sino a 5V, mentre più sotto è riportato “Power Supply : External 5V” che indica l’alimentazione a 5V. In questo caso, non essendoci certezze, ho deciso di usare la linea a 3.3V, mal che vada il modulo non si accende.

Guardando il retro della scheda vediamo che tre pin sono indicati come NC (Non collegati), poi abbiamo VDD (alimentazione, presunta 3.3V), GND (massa). Restano da decifrare NSS,RST,MO,SCK,MI. Se avevate letto l’articolo introduttivo sull’interfaccia SPI sicuramente avrete campito che MI, MO e SCK sono i pin miso, mosi e clock dell’interfaccia SPI. Allora, direte voi, manca il pin CS. Andando sullo schema della scheda vediamo che il chip possiede infatti un pin CSS che è connesso all’header esterno dove prende il nome di NSS. Resta il pin RST che immagino sia un pin di reset e che infatti si connette al pin 59 del w5100 e che guarda caso si chiama proprio reset.w5100header

Di fatto tutti quei pin non sono altro che l’alimentazione, un pin di reset e l’interfaccia SPI. Nello schemino qui vicino vi ripropongo i pin non come stampati sul retro ma come visti dall’alto, con l’orientamento della scheda come la foto precedente. Questa scelta nasce dal fatto che quando dovete fare le connessioni lo fate con la scheda rivolta verso l’alto, non è certo comodo girare la scheda per ogni pin che dovete connettere per capire cos’è. Mi resta una domanda: il pin di reset normalmente lo devo tenere a livello alto o basso? Ho cercato in rete alcuni tutorial già pronti per confrontarmi ma sinceramente non ne ho trovato nemmeno uno per cui non ci resta che guardare lo schema della scheda che vi ho linkato prima. Come possiamo vedere il pin di reset è normalmente connesso all’alimentazione tramite l’interposizione di un resistore da 10K per cui normalmente il pin è tenuto in uno stato HIGH e se vogliamo eseguire il reset dobbiamo portarlo ad uno stato LOW. Essendo normalmente collegato ai 3.3V attraverso un resistore, possiamo anche permetterci di lasciarlo non collegato in quanto non sarà un pin flottante.

I collegamenti…

Ora non resta che fare i collegamenti del caso ricordandoci che l’interfaccia SPI necessita di specifici pin che cambiano a seconda della scheda in nostro possesso. A tal proposito vi riporto la tabellina vw5100unoista nell’articolo prima citato in modo da aver sott’occhio tutte le informazioni necessarie:

Arduino Board MOSI MISO SCK SS (slave) SS (master)
Uno or Duemilanove 11 or ICSP-4 12 or ICSP-1 13 or ICSP-3 10
Mega1280 or Mega2560 51 or ICSP-4 50 or ICSP-1 52 or ICSP-3 53
Leonardo ICSP-4 ICSP-1 ICSP-3
Due ICSP-4 ICSP-1 ICSP-3 4, 10, 52

Se escludiamo il pin di reset che nella foto non ho collegato, il risultato finale dovrebbe essere simile a quello qui a lato. Come primo sketch consiglio di usare quello già usato usato in passato per i test iniziali e che era:

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

byte mac[] = { 0x90, 0xA2, 0xDA, 0x00,0x46, 0xE6 }; // MAC Address
IPAddress ip(192,168,1,25); // Indirizzo IP

EthernetServer ArduinoServer(80); // creazione oggetto EthernetServer

void setup()
{
  Serial.begin(9600);// inizializza la porta seriale a 9600

 Ethernet.begin(mac, ip); // Inizializzo il chip della scheda ethernet
 ArduinoServer.begin();  //Inizializzo il server in ascolto sulla porta 80
}

void loop()
{
delay(2000); // non faccio nulla di particolare
Serial.println(Ethernet.localIP());
}

Quando non tutto funziona la primo tentativo….

In questo modo da terminale potete fare un ping 192.168.1.25 (se non cambiate indirizzo) e controllare che la scheda risponda. Nel mio caso al primo tentativo non ha funzionato nonostante i led indicassero che il modulo era acceso e i collegamenti fossero corretti. Ho sostituito tutti i cavi e tutto ha funzionato egregiamente, perciò vi era probabilmente un cavo difettoso o mal inserito, cosa che purtroppo capita relativamente W5100_ip_errorspesso con i prototipi e i cavi “volanti”. Una volta ricevuta la risposta al ping siamo certi che tutto funziona…..o forse no?

Purtoppo a questo punto mi sono trovato di fronte ad un problema complesso: facendo il ping da terminale non c’era alcun problema, come detto, ma sulla porta seriale Arduino “crede” che l’indirizzo IP della scheda Ethernet sia 0.0.0.0, cosa sicuramente non corretta. Questo problema non è cosa da poco, infatti sappiamo per certo che i collegamenti sono corretti in quanto siamo riusciti ad impostare l’indirizzo IP della scheda la quale risponde al ping (per cui anche il “server” funziona), ma ci sono evidenti grossi problemi con la parte software.

Non vi tedio con questi “incidenti di percorso” giusto per scrivere qualche riga in più, ma trovo corretto descrivere i problemi che ho avuto e come li ho risolti, faccio fatica credere a tutti quelli che scrivono tutorial su tutorial e tutto funziona sempre al primo colpo. Analizziamo il problema: riusciamo a settare l’indirizzo IP e lo sappiamo perchè il ping funziona. Ciò significa che Arduino riesce ad inviare i comandi al modulo ethernet per cui la linea MOSI (Master Out Slave In) funziona correttamente. Ma nel momento in cui la board deve fornire un dato ad Arduino (l’indirizzo IP) questa fallisce miseramente, per cui è fortemente probabile che ci sia un problema sulla linea MISO (Master In Slave Out), quella connessa al pin 12 della UNO. Ho sostituito (di nuovo!) il cavo ma non è cambiato nulla. Togliendolo del tutto il comportamento rimane identico per cui la “diagnosi” sembrava essere corretta.

A questo punto ho provato un’altra Arduino UNO e, peggio che mai, non ha funzionato nulla, nemmeno il ping. A questo punto mi son detto: visto che l’uscita 3.3V della UNO è di soli 50mA, non è che sono insufficienti? Allora ho usato una Arduino DUE come “alimentatore” in quanto eroga sino a 800mA sulla linea 3.3V, ma sorpresa delle sorprese il modulo non si è nemmeno acceso. Allora ho provato ad usare una Mega (che comunque arriva a 50mA), con l’accensione del modulo ma senza risoluzione del problema.

Preso dalla “disperazione” ho iniziato a guardare lo schema del modulo dato che la mancata accesione con la DUE mi “puzzava” di una problematica di alimentazione, ed ho notato una cosa interessante: ricordate che avevamo deciso di usare l’alimentazione a 3.3V…beh abbiamo sbagliato, dovevamo usare i 5V. Quel che è riportato nei dati tecnici indicava che il chip w5100 funziona a 3.3v ma la board viene alimentata a 5V tramite un regolatore di tensione. Ho perciò spostato il cavo di alimentazione dalla 3.3V alla 5V e tutto è funzionato come avrebbe dovuto sin dall’inizio. 

Finalmente…

Dopo tante ore perse eccoci finalmente  a poter provare un esempio concreto:  creiamo un piccolo sketch che preleva l’ora via internet e la scrive a terminale, cosa che avevamo già visto in precedenti articoli.  Vi ricordo di scaricare l’ultima versione della libreria DateTime per la corretta gestione dell’ora. Il codice non è nulla di nuovo:

 

//una sola definizione....
#define NTP_PACKET_SIZE 48 
//faccio le necessarie inclusioni
#include <SPI.h>
#include <Ethernet.h>
#include <Time.h>

//prototipi delle funzioni (non sono indispensabili)

int getTimeAndDate();
unsigned long sendNTPpacket(IPAddress*);
void printDigits(int);

// definizione delle variabili usate


IPAddress ip(192,168,1,25); // indirizzo ip usato sulla LAN locale
byte remote[]={192,254,224,62}; // indirizzo del server remoto a cui inviare i dati di temperatura
byte mac[] = { 0x90, 0xA2, 0xDA, 0x00, 0x46, 0xE6 }; // indirizzo MAC
IPAddress timeServer(193,204,114,232); // Server NTP - 37,247,48,64 (vecchio)
const long timeZoneOffset = 7200L;  
EthernetUDP Udp; 
byte packetBuffer[NTP_PACKET_SIZE];  

unsigned long tempo=0;


EthernetClient ArduinoClient;


void setup(void)
{
 
  Serial.begin(9600);// inizializza la porta seriale a 9600
     
  Ethernet.begin(mac,ip); // inizializzo la ethernet
  delay(150);
  Serial.println(Ethernet.localIP());
  // pinMode(10,OUTPUT);
  //digitalWrite(10,LOW);
  //ArduinoServer.begin(); 

  
  /*int trys; // per prima cosa faccio 10 tentativi di sincronizzazione oraria
  for(trys=0;trys<10;trys++) 
  {
   Serial.println("Try to sync... ");
   if(getTimeAndDate()) break; 
   Serial.println("Sync error... ");
   delay(1000);
 }*/
 
}

int getTimeAndDate() // ottengo l'ora dal server NTP
 {
   int neterror=1;
   Udp.begin(8888); // localport
   sendNTPpacket(timeServer);
   delay(1000);
   if (Udp.parsePacket())
   {
     Udp.read(packetBuffer,NTP_PACKET_SIZE);  // read the packet into the buffer
     unsigned long highWord, lowWord, epoch;
     highWord = word(packetBuffer[40], packetBuffer[41]);
     lowWord = word(packetBuffer[42], packetBuffer[43]);  
     epoch = highWord << 16 | lowWord;
     epoch = epoch - 2208988800UL ;//+ timeZoneOffset;
     setTime(epoch);
     tempo=now();
     neterror=0;
     
     Serial.println("Time sync OK");
  
     
     
   }
   else  Serial.println("Time sync ERROR");
   return !(neterror); // occhio al not!!
}
 
// Do not alter this function, it is used by the system
unsigned long sendNTPpacket(IPAddress& address)
{
  memset(packetBuffer, 0, NTP_PACKET_SIZE);
  packetBuffer[0] = 0b11100011;
  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);
  Udp.write(packetBuffer,NTP_PACKET_SIZE);
  Udp.endPacket();
}


void printDigits(int digits) // stampa numeri a due cifre con due numeri
{
  if(digits < 10) Serial.print('0');
  Serial.print(digits);
}



void loop(void) // loop principale....
{
  if(now()>tempo+45 || tempo==0)  // sincronizza orario in caso di errore o ogni ora
  {
    if(!getTimeAndDate()) {Serial.println("Error sync");}
    else
    {
     // se la sincronizzazione va a buon fine, scrivo l'ora su seriale....
     
     time_t t = now()+timeZoneOffset;
     Serial.print("Time: ");
     Serial.print(hour(t));
     Serial.print(":");
     printDigits(minute(t));
     Serial.print(":");
     printDigits(second(t));
     String str;
     str=" - "+String(day(t))+"/";
     str=str+String(month(t))+"/";
     str=str+String(year(t));
     //str=str+" "+String(hour(t))+":"+String(minute(t))+" ";
     Serial.println(str);
     tempo=now(); // aggiorno la variabile "tempo" per verificare il passaggio della prossima ora....

   } //else
  
  }
 
  delay(100); // piccolo ritardo
}

Il risultato è quello della figura qui a lato. pingtimeE con questa immagine anche per oggi abbiamo terminato. In futuro vedremo anche quanto sia utile il pin di reset che permette di bypassare alcuni noti problemi che si hanno con l’ethernetshield classica, inoltre vedremo come utilizzare in contemporanea il modulo ethernet e quello nrf24 che funzionano entrambi sull’interfaccia SPI.