Arduino ed Internet

Da GolemWiki.

Ci sono applicazioni per cui è comodo avere Arduino collegato ad internet, ad esempio per un sistema di domotica per gestire le luci di casa online, oppure per monitorare dei sensori ambientali dal cellulare.

Per questi piccoli progetti è adatta la Ethernet Shield, una scheda già pronta per essere "incastrata" su un Arduino e che mette a disposizione una porta Ethernet coordinata da un chip dedicato, ed uno slot per microSD, utile per esempio in progetti di monitoraggio per salvare le informazioni catturate. Esiste anche una wireless shield, molto simile, che però usa la rete senza fili.

Ma attenzione, questa scheda dipende molto dalla potenza del microprocessore di Arduino, poiché è lui che deve occuparsi, come vedremo, di tutti i protocolli di rete. Scordatevi quindi di poter tirare su in questo modo un server web con tante pagine, database, eccetera. In questo caso bisognerà optare per schede come [Raspberry Pi http://www.glgprograms.it/?p=tricks/raspberrypi-1] o BeagleBone, anziché Arduino.


Concetti di base

TCP/IP e protocolli

Tutti i dispositivi collegati in rete devono rispettare un certo protocollo, ovvero seguire regole per poter identificarsi fra loro senza creare conflitti.

Ogni dispositivo è in particolare identificato da un nome sulla rete, detto indirizzo IP. Ad esempio, nella mia rete di casa il computer che uso ha come indirizzo 192.168.142, e il cellulare 192.168.1.119.

Tutti gli indirizzi IP sono composti da una quartina di numeri compresi fra 0 e 255, separati da un punto. Solitamente, nelle reti di casa, la prima coppia è fissa a 192 e 168, mentre il terzo è solitamente 1. Quello a cui siamo interessati è quindi l'ultimo numero, che varia da dispositivo a dispositivo.

Fa eccezione 192.168.1.255, che è un indirizzo speciale detto broadcast che equivale a parlare contemporaneamente con tutti i dispositivi connessi alla rete di casa.


Chi è che dà gli indirizzi nella rete casalinga? Teoricamente ogni dispositivo può autoassegnarsi un indirizzo, ma questo non viene fatto su oggetti "quotidiani" (cellulari, computer, etc..) perché verrebbero a crearsi altrimenti conflitti quando due o più si assegnano lo stesso indirizzo. È quindi il router che ha l'autorità di dare ad ognuno il suo indirizzo evitando i conflitti; si dice che il router "è il DHCP (Dynamic Host Configuration Protocol)".

Il router sbriga tutta questa burocrazia, e cerca anche di associare ad uno stesso dispositivo lo stesso IP. Riesce a far ciò perché c'è una seconda informazione necessaria per "presentarsi sulla rete", il MAC address. Questo è un secondo codice, univoco, che viene assegnato in fabbrica ad ogni dispositivo.

Può sorgere la domanda "perché allora non si usa il MAC address per presentarsi in rete?". Senza perdersi in cose complicate, rispondo dicendo che sono due indirizzi che hanno un significato completamente diverso: l'indirizzo IP può variare a seconda delle reti, e inserisce il dispositivo in un "gruppo" di altri dispositivi connessi insieme (ricordi la parte fissa 192.168? Dice che tutti quelli che hanno questo indirizzo appartengono alla stessa rete casalinga). Il MAC è invece statico, tipico del dispositivo.

Per fare un paragone che renda chiaro il tutto, l'indirizzo IP è come fosse il soprannome di una persona, mentre il MAC address è il codice fiscale del dispositivo, che permette di "beccarlo" in qualsiasi gruppo.


Bene, detto questo, sappiamo che anche il nostro Arduino on the internet dovrà avere una coppia di indirizzi IP e un MAC. Il MAC è solitamente scritto su un adesivo sotto l'ethernet shield.


Per quanto riguarda l'IP, bisogna aggiungere qualcos'altro: per i dispositivi che si collegano ad internet non ci interessa che abbiano un IP particolare, e quindi lo possono anche chiedere al router col meccanismo del DHCP: si dice che hanno un IP dinamico.

I dispositivi a cui invece ci dobbiamo collegare dovrebbero avere sempre lo stesso IP, noto e indipendente dalle volontà del router: si dice che hanno un IP statico.

Anche il nostro Arduino dovrà avere un IP statico, che gli assegneremo nel programma. Prima però dovremo assicurarci di due cose:

  • quale "serie" di IP è in uso nella propria rete casalinga;
  • se l'IP scelto è disponibile, ovvero se il router non l'ha già assegnato a qualcuno;

Per il primo punto basta controllare a quale IP risponde il router: solitamente infatti ha ip 192.168.1.1 oppure 192.168.0.1. Basta quindi aprire un browser e digitare il primo e poi il secondo indirizzo. A noi interessa sapere se il terzo numero è 1 oppure 0.

Noto questo, basta tirare un numero a caso da aggiungere in coda a 192.168.1. per avere un IP, e verificare tramite un terminale (o il prompt di DOS su windows) se è disponibile: digita ping 192.168.1.245 e se nessuno risponde il comando dovrebbe dare come output un errore come Destination Unreachable. Appuntati quindi questo IP.

Servizi e Servitori

Quando ti colleghi ad un sito internet, chi ti dà la pagina non è una magica entità immateriale, bensì uno (o più) computer. Il questo caso il computer con cui ti colleghi al sito è un cliente (client), mentre il computer che risponde inviando la pagina è un server, ovvero offre un servizio.

Di servizi offribili sulla rete ve ne sono molti: oltre alla classica pagina web (gentilmente offerta dai server web), si possono avere servizi di trasferimento files (ftp oppure sftp), servizi di e-mail (POP3, IMAP, SMTP), e tanti altri. Uno stesso computer può offrire più servizi contemporaneamente, e per poter discriminare a chi mandare cosa, il protocollo TCP/IP prevede una struttura a porte.

Quando con un browser si richiede, ad esempio, una pagina web ad un computer, si "bussa" alla porta numero 80 del suo indirizzo IP, e tramite quella porta fluisce la compunicazione. Ogni servizio ha una porta dedicata, in particolare il servizio di trasferimento pagine web (HTTP) ha il numero 80.

Lo stesso dovrà succedere con il nostro Arduino: lo metteremo in grado di poter "rispondere" alle richieste fatte sulla porta 80.

HTTP & HTML

Ed eccoci quasi arrivati al termine. Dopo che il browser ha inviato la richiesta di connessione al server, incomincia uno scambio di informazioni fra i due, seguendo un protocollo molto rigoroso: l'HTTP (HyperText Transfer Protocol).

Il browser invia la richiesta della pagina web, seguita da una linea bianca per indicare il termine, e il server gli deve rispondere con un codice di approvazione o di errore (a seconda se la pagina è disponibile o no), specificare il formato della pagina (HTML nel nostro caso) e accodare finalmente il contenuto.

Vedremo queste cose direttamente nel codice Arduino, in particolare lo scambio di dati: attivando la trasmissione USB si vede in diretta tutta la comunicazione HTTP fra lui e il browser.

Il codice Arduino

Il codice altro non è che l'esempio EthernetServer di Arduino, leggermente modificato e commentato in italiano. In linea di massima questo programma resta in attesa di una richiesta HTTP, dopodiché legge il valore analogico del pin Analog0 e lo trasmette al client connesso. Per avere letture sensate bisognerà collegare un sensore analogico, che in questo esempio è stato simulato da un semplice potenziometro.

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

// Pin del sensore da monitorare via Ethernet
const byte PIN_SENSORE = A0;

// Qui va il MAC address della tua shield
byte mac[] = {
  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED
};

// E qui l'indirizzo IP che dovrà essere associato
IPAddress ip(192, 168, 1, 177);

// Inizializza il "server" EthernetServer con la porta 80 (HTTP)
EthernetServer server(80);

void setup() {
  // Apre la comunicazione seriale: è utile per monitorare via USB
  // cosa sta succedendo
  Serial.begin(9600);
  
  
  // Avvia la connessione ethernet ed il server
  Ethernet.begin(mac, ip);
  server.begin();
  
  // Messaggi di controllo inviati via USB
  Serial.print("Il server Arduino risponde all'indirizzo ");
  Serial.println(Ethernet.localIP());
}


void loop() {
  // "ascolta" sulla porta 80 in attesa di un client
  EthernetClient client = server.available();
  
  // se un client si è connesso...
  if (client) {
    Serial.println("Un nuovo client!");
    
    // una richiesta HTTP termina con una linea bianca (un doppio a-capo)
    // serve quindi una variabile per ricordare se si è già ricevuto un a-capo
    boolean lineaCorrenteBianca = true;
    
    // Finché il client è connesso avviene lo scambio dei dati
    while (client.connected()) {
      if (client.available()) {
        // Leggo un byte trasmesso
        char c = client.read();
        
        Serial.write(c);
        
        // se avevo già ricevuto un a-capo (lineaCorrenteBianca == true)
        // e ricevo un nuovo a-capo ('\n') è finita la richiesta del client
        // e posso inviare la pagina web
        if (c == '\n' && lineaCorrenteBianca) {
          // invio la risposta HTTP standard
          client.println("HTTP/1.1 200 OK");
          client.println("Content-Type: text/html");
          // Chiudo la connessione al termine della tramsissione
          client.println("Connection: close");
          // Aggiorna la pagina ogni 5 secondi
          client.println("Refresh: 5");
          client.println();
          
          /* ****** Qui comincia la pagina HTML vera e propria ****** */
          client.println("<!DOCTYPE HTML>");
          client.println("<html>");
          
          int lettura_sensore = analogRead(PIN_SENSORE);
          client.print("Il valore della lettura del pin ");
          client.print(PIN_SENSORE);
          client.print(" è ");
          client.print(lettura_sensore);
          
          client.println("</html>");
          /* ******  Termine della pagina HTML ****** */

          // break esce dal ciclo while, perché lo scambio dati è terminato
          break;
        }
        // Se ricevo un a-capo devo ricordare che ho iniziato una nuova riga
        if (c == '\n') {
          lineaCorrenteBianca = true;
        }
        // Se invece ricevo tutti gli altri caratteri, fatta eccezione per
        // '\r', significa che la richiesta del client non è ancora terminata
        else if (c != '\r') {
          lineaCorrenteBianca = false;
        }
      }
    }
    // piccola pausa per la trasmissione, dopodiché si chiude la connessione
    delay(1);
    client.stop();
    Serial.println("client disconnesso!");
  }
}

Prossimamente vedremo come interagire via Ethernet con i componenti collegati alla scheda.

[Category::Howto]