Ott 132012
 

Oggi approfondiamo il precedente articolo e aggiungiamo l’ethernet shield in modo che il nostro Aruino possa inviare i dati via ethernet. L’idea è quella di usare il router di casa sia per la connessione tcp/ip che per l’alimentazione visto che è provvisto di una porta UBS. Prima di cominciare dobbiamo però dare uno sguardo all’ ethernet shield.

Ethernet Shield – introduzione

L’ethernet shield è una scheda che si “incastra” sopra il nostro amato Arduino e ne espande le possibilità permettendo una connessione alla rete ethernet 10/100 con la possibilità di aprire fino a 4 connessioni contemporanee. Vi ho allegato una foto qui vicino per chi non la conoscesse. Si vedono chiaramente nella parte inferiori i pin che vanno ad accoppiarsi a quelli di Arduino, la replica degli stessi nella parte superiore della scheda, il connettore RJ45 per la connessione del cavo ethernet e, non meno importante, uno slot microSD nella parte posteriore che permette di usare una scheda per la memorizzazione di dati, ottima per tutti i ditpi di datalogger che possano venirci in mente. Come sempre essendo questo il primo articolo sull’argomento ci accontenteremo delle funzionalità basilari per non mettere troppa carne al fuoco, ma avremo sicuramente occasione di approfondire e creare progetti sempre più complessi. La shield permette di montare anche un modulo opzionale (si può acuistare anche già completa) per usufruire della “Power Over Ethernet”, ossia la possibilità di prelevare corrente per l’alimentazione (fino 15W) direttamente dal cavo ethernet, cosa che in alcuni frangenti è davvero utile dato che permette di alimentare il nostro Arduino senza ulteriori cavi aggiuntivi. Tutte queste meraviglie ovviamente vanno ad occupare alcuni dei pin di Arduino, più precisamente parliamo del 4,10,11,12 e 13. Fra di essi il 4 è condiviso dalla scheda ethernet e dalla microSD, ne deriva che le due non possono essere usate in contemporanea ma solo alternativamente.

Iniziamo

Le prime cose da fare sono definire il MAC Address della scheda e l’indirizzo IP. Quest’ultimo può essere anche ottenuto tramite DHCP ma per ora cerchiamo di mantenere le cose semplici. Io penso che chi legge questo tutorial abbia già chiaro di cosa stò parlando, nel caso non fosse così fatemi sapere, magari ci scrivo un articolo di approfondimento. Per quanto riguarda il MAC Address, nelle versioni più recenti dell’ethernet shield trovate un’etichetta adesiva  sul versante inferiore che lo riporta, in caso contrario dovrete inventarne uno.

byte mac[] = { 0x90, 0xA2, 0xDA, 0x00,0x46, 0xE6 };
byte ip[] = {192, 168, 1, 15}; //nell'ultima versione della libreria possiamo utilizzare IPAddress ip(192,168,1,25);

Fatto ciò dobbiamo inizializzare il chip della Ethernet Shield con quei parametri e per farlo usiamo:

Ethernet.begin(mac, ip);

A questo punto dobbiamo decidere come procedere. Potremmo usare la scheda come un client che ad ogni lettura invia i dati di temperatura ad un server, oppure, viceversa, potremmo usare Arduino come server ed inviare i dati di temperatura quando giunge una richiesta dall’esterno ad esempio da una pagina web o un software remoto. Nulla vieta di fare entrambe le cose, ma per ora manteniamo un profilo basso limitandoci a rispondere ad una richiesta da parte di una pagina web. Perusare Arduino come server usiamo l’istruzione:

EthernetServer ArduinoServer(80);

che crea un oggetto “EthernetServer” dal nome “ArduinoServer” che starà in ascolto sulla porta 80, ossia la classica porta http. Attenzione che in rete ho trovato alcuni esempi in cui è usato Server anzichè EthernetServer, probabilmente perchè nel tempo hanno modificato il software. Una volta definito l’oggetto utilizziamo

ArduinoServer.begin();

per attivarlo e metterlo in ascolto.

Ora mettiamo tutto insieme e prepariamo la piccola sketch qui sotto. Vi faccio notare anche le due librerie incluse, ossia la ovvia Ethernet e la meno ovvia SPI. Quest’ultima è indispensabile perhè la comunicazione fra Arduino ed Ethernet Shield / MicroSD avviene tramite bus SPI, argomento che ignoreremo totalmente in quanto sarà oggetto di articoli futuri. Mettiamo tutto insieme e carichiamo questa shield di prova:


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

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

EthernetServer ArduinoServer(80); // creazione oggetto EthernetServer

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

void loop()
{
delay(10); // non faccio nulla di particolare
}

La sketch qui sopra non fà nulla di particolare se si esclude l’attivazione della scheda ethernet e l’avvio del server che ascolta sulla porta 80. E’ comunque una base molto utile in quanto ci permette di fare un ping da PC e vedere se ci sono problemi di connessione. Nel mio caso il ping mi restituisce quanto visibile nella foto sopra, quindi tutto funziona a dovere. A dirla tutta nel preparare questo articolo ho vuto un grosso problema che mi ha fatto perdere due giornate intere. In realtà non c’era modo di riuscire a fare il ping sulla scheda nonostante fossi certo che era tutto fatto correttamente. Ho cercto in rete ma non ho trovato nessuna soluzione. Alla fine, casualmente, ho collegato un pc aggiuntivo via cavo al router, un pc che dovrei riparare e, da quando l’ho connesso, la ethernet shield è diventata improvvisamente visibile. Non ho capito se il problema è del mio router o meno. Tra le altre vi aggiungo che se invece di usare l’ip statico utilizzavo l’assegnamento tramite DHCP, la scheda funzionava senza problemi motivo per cui il problema si verifica solamente con l’uso dell’IP statico e nemmeno specificando subnet, dns e gateway le cose cambiavano (cosa che risulta ovvia andando a guardare il codice della libreria ethernet). Spero di non avervi annoiato con questo angolo di tecnicismi ma se vi ritrovate con lo stesso problema forse avete una soluzione: collegate un PC aggiuntivo al router (come se una persona normale avesse PC extra da attaccare per far funzionare Arduino 🙂 ).

Rispondere alle richieste del client

Ora dobbiamo proseguire e creare la porzione di software che risponde al client, che nel nostro cso sarà un web-browser. In poche parole quando sul web broswer andiamo a scrivere l’indirizzo IP della scheda ethernet, arriverà una richiesta ad Arduino alla quale dobbiamo rispondere in qualche modo.

Per far questo usiamo la funzione available() che permette di conoscere se sul nostro server ci sono richieste client pendenti. In caso affermativo viene ritornato true per cui possiamo intercettare la richiesta esterna ed andare ad analizzarla:

Client pc_client = ArduinoServer.available();
if (pc_client == true) { ....}

A questo punto possiamo usare un un loop per continuare a leggere i dati in arrivo dal client finchè questo rimane connesso:

while(pc_client.connected()){ ... }

Verificato che il client è connesso andiamo a vedere se ci sono dati da leggere, in caso affermativo, per questo step, scriviamo i dati su porta seriale così possiamo verificare se i dati giungono correttamente al nostro sistema. Ora mettiamo tutto insieme e compiliamo la sketch così modificata:

//Librerie impiegate per il progetto
#include <SPI.h>
#include <Ethernet.h>

byte mac[] = { 0x90, 0xA2, 0xDA, 0x00, 0x46, 0xE6 };
IPAddress ip(192,168,1,25); // ho  usato la nuova forma
char RX;

EthernetServer ArduinoServer(80);

void setup()
{

Serial.begin(9600);
Ethernet.begin(mac,ip);
Serial.println(Ethernet.localIP());
ArduinoServer.begin();
}

void loop()
{
EthernetClient pc_client = ArduinoServer.available();
if (pc_client != false)
{
while(pc_client.connected())
{
if (pc_client.available())
{
RX = pc_client.read();
Serial.write(RX);
}
}
}
delay(200);
}

A questo punto se nella barra degli indirizzi del nostro browser inseriamo l’indirizzo della scheda che nell’esempio è 192.168.1.15, nel monitor seriale di Arduino dovrebbe comparire una schermata simile a quella qui sotto:

Queste scritte non sono altro che la richiesta da parte del browser di aprire una comunicazione http. Ora il nostro compito è andare ad interpretare queste strighe e rispondere al browser in modo che possa visualizzare ciò che noi vogliamo. In realtà quanto vi dico non è del tutto vero nel senso che a noi non interessa interpretare correttamente la richiesta del browser perchè il nostro output sarà sempre lo stesso. A noi interessa solamente che ci venga fatta una richiesta a cui inviare la risposta. Sarebbe invece molto diverso se creassimo una sorta di vero e proprio web server con pagine multiple e quindi risposte diverse a seconda dei casi. Quindi non facciamo altro che ignorare quanto ci giunge sino ad intercettare il carattere di fine richiesta (\n) a seguito del quale inviamo la nostra risposta. La risposta prevederà una prima parte che è l’header http e successivamente il codice html che vogliamo inviare per formare la pagina di risposta. A questo punto chiudiamo il client precedentemente aperto ed il gioco è fatto.

Vediamo la sketch completa:

//Librerie impiegate per il progetto
#include <SPI.h>
#include <Ethernet.h>

byte mac[] = { 0x90, 0xA2, 0xDA, 0x00, 0x46, 0xE6 };
IPAddress ip(192,168,1,25); // ho  usato la nuova forma
char RX;

EthernetServer ArduinoServer(80);

void setup()
{

Serial.begin(9600);
Ethernet.begin(mac,ip);
Serial.println(Ethernet.localIP());
ArduinoServer.begin();
}

void loop()
{
EthernetClient pc_client = ArduinoServer.available();
if (pc_client != false)
{
while(pc_client.connected())
{
if (pc_client.available())
{
RX = pc_client.read();
Serial.write(RX);
if (RX == '\n') //Richiesta terminata - Invio la risposta al client
{
pc_client.println("HTTP/1.1 200 OK"); //invio lo status code
pc_client.println("Content-Type: text/html"); //imposto il data type
pc_client.println();
pc_client.print("<html><body><h1>"); //inizia codice html
pc_client.print("Ciao, io sono Arduino</h1></body></html>");
delay(5);  // piccolo ritardo
break;   // e chiudiamo il ciclo
}
}

}

pc_client.stop(); // termino la comunicazione
}
delay(200);
}

Bene, a queso punto se riprovate a scrivere nella barra dl vostro browser l’indirizzo 192.168.1.25 otterrete il risultano che potete vedere nella prossima schermata.

A questo punto la parte più importante del lavoro è fatta. Non ci resta che unire il lavoro di oggi con quello dello scorso articolo per fare in modo che al posto della scritta “Ciao, io sono Arduino” compaiano i valori di temperatura rilevati. Per far ciò ho pensato di fare anche una modifica al precedente progetto. Infatti ci vuole mediamente un secondo per ogni misura su ogni sensore per cui, nel caso di una decina di secondi, ci vorrebbe tutto quel tempo prima di avere la pagina web pronta con le temperature. Per questo motivo mi pare più saggio usre un array per memorizzare le temperature che poi vengono inviate in tempo reale qualora vengano richieste via web.

Il progetto completo è qui sotto.

#define N_SENSORI 10

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

byte G_addr[N_SENSORI][8]; //qui memorizzo fino a 10 indirizzi
byte G_Devices; // variabile usata per tenere traccia del numero di sensori riconosciuti nel bus
float temps[N_SENSORI]; // memorizzo i valori di temperatura
OneWire ow(8); // inizializza il bus onewire sulla porta n°8 (se avete collegato i sensori su un'altro pin dovete modificare qui)
byte mac[] = { 0x90, 0xA2, 0xDA, 0x00, 0x46, 0xE6 };
IPAddress ip(192,168,1,25); // ho  usato la nuova forma
char RX;
unsigned long tempo=0;

float GetTemp(OneWire *,byte *);
void PrintAddress(byte *);
void lookUpSensors();
int CheckSensor(byte *);

EthernetServer ArduinoServer(80);

void setup(void)
{
Serial.begin(9600);// inizializza la porta seriale a 9600
Ethernet.begin(mac,ip);
Serial.println(Ethernet.localIP());
ArduinoServer.begin();
G_Devices=0;      // imposta a 0 il numero di sensori attualmente riconosciuti
lookUpSensors(); // avvia la ricerca delle sonde di temperatura
}

void lookUpSensors()
{
byte address[8]; // questo array conterrà l'indirizzo locale dei sensori

Serial.print("--Ricerca avviata--"); // avvia la ricerca e lo scrive sulla porta seriale

while (ow.search(address)) // loop finchè trova nuovi dispositivi
{
// Se il primo byte dell'indirizzo è 0x10, si tratta di una sonda DS18S20
// il primo byte dell'indirizzo identifica il tipo di sensore
// se  0x10 è un DS18S20, se è 0x28 è un DS18B20 (notare la S e B)
if (address[0] == 0x10 || address[0] == 0x28)
{
if(CheckSensor(address)==1) //crc ok
{
Serial.println("");
if (address[0] == 0x10) Serial.print("Found DS18S20 : "); // notare che la S invece che la B
else if (address[0] == 0x28) Serial.print("Found DS18B20 : ");
PrintAddress(address);
for(int aa=0;aa<8;aa++) G_addr[G_Devices][aa]=address[aa]; // copia l'indirizzo
G_Devices++; // incrementa il numero di devices memorizzati
}
}

}//end while
Serial.println("");
Serial.println("--Ricerca terminata--");
}

void loop(void)
{
if(millis()>tempo+60000 || tempo==0)
{

float temperatura; // uso questa variabile per tenere la lettura della temperatura
for(int num=0;num<G_Devices;num++) // vado a leggere tutti i sensori registrati
{
temperatura=GetTemp(&G_addr[num][0]); // lego la temperatura
PrintAddress(G_addr[num]);
Serial.print(" -> ");
Serial.println(temperatura);
temps[num]=temperatura;

}
tempo=millis();
}

EthernetClient pc_client = ArduinoServer.available();
if (pc_client != false)
{
while(pc_client.connected())
{
if (pc_client.available())
{
RX = pc_client.read();
Serial.write(RX);
if (RX == '\n') //Richiesta terminata - Invio la risposta al client
{
pc_client.println("HTTP/1.1 200 OK"); //invio lo status code
pc_client.println("Content-Type: text/html"); //imposto il data type
pc_client.println();
pc_client.print("<html><body>"); //inizia codice html

pc_client.print("<h1>Arduino - Misura temperature</h1>");
for(int num=0;num<G_Devices;num++) // vado a leggere tutti i sensori registrati
{
pc_client.println("<br>Sensore ");
pc_client.print(num);
pc_client.print(" : ");
pc_client.print(temps[num]);

}
pc_client.print("</body></html>");

delay(5);  // piccolo ritardo

break; // e chiudiamo il ciclo
}
}

}
pc_client.stop(); // termino la comunicazione
}

delay(100); // aspetto 5 secondi prima di fare una nuova misurazione
}

float GetTemp(byte * addr)
{
byte present = 0;
byte data[12];
int i;
byte address[8];
for(i=0;i<8;i++) address[i]=*(addr+i); //copia l'indirizzo nella stringa locale
ow.reset();
ow.select(addr);
ow.write(0x44,1);         // start conversion, with parasite power on at the end
delay(1000);     // maybe 750ms is enough, maybe not
// we might do a ds.depower() here, but the reset will take care of it.

present = ow.reset();

ow.select(addr);
ow.write(0xBE);         // Read Scratchpad

for ( i = 0; i < 9; i++) data[i] = ow.read();// we need 9 bytes //Serial.print(data[i], HEX);

int HighByte, LowByte, TReading, SignBit, Tc_100, Whole, Fract;

double result;

LowByte = data[0];
HighByte = data[1];

TReading = (HighByte << 8) + LowByte;
SignBit = TReading & 0x8000;  // test most sig bit

if (SignBit) TReading = (TReading ^ 0xffff) + 1; // 2's comp // negative

Tc_100 = (6 * TReading) + TReading / 4;    // multiply by (100 * 0.0625) or 6.25

Whole = Tc_100 / 100;  // separate off the whole and fractional portions
Fract = Tc_100 % 100;

result = Whole;
result += ((double)Fract/100);

if(SignBit) result *= -1;

return result;

}

void PrintAddress(byte * address)
{
int i;
for (i=0;i<8;i++)
{
if (address[i] < 9) Serial.print("0");
Serial.print(address[i],HEX);
if (i<7) Serial.print("-");
}
}

int CheckSensor(byte * address)
{
if (OneWire::crc8(address, 7) != *(address+7)) return(-1);// faccio il controllo del CRC8, se fallito ritorno -1
else return(1); // cr8 OK, ritorno 1
}

Per concludere vorrei sottolineare che abbiamo in parte sorvolato su alcuni dettagli come la possibilità di usare il DHCP, specificare il gateway, dns, subnet-mask cosa che andremo ad approfondire in prossimi articoli anche perchè nelle ultime versioni della liberia ethernet ci sono state sostanziali modifiche rispetto al passato che di fatto rendono incompatibili gran parte delle sketch che trovate attualmente in rete. Nel prossimo articolo vediamo come inviare i dati ad un server esterno e fare in modo che uno script php vada a preparare le pagine web per noi anzichè farlo con Arduino.

  No Responses to “Arduino, sensore 1wire DS18B20 e shield Ethernet.”