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.
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.
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:
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.”