Differenze tra le versioni di "Arduino ed Internet"

Da GolemWiki.
Jump to navigation Jump to search
m
(Risistemata, riscritta la parte sulle reti in maniera semplificata, eliminati imprecisioni e errori, tuttavia rimane un "corso molto molto accelerato sulle reti")
 
Riga 1: Riga 1:
Ci sono applicazioni per cui è comodo avere Arduino collegato ad
+
{{Guide
internet, ad esempio per un sistema di '''domotica''' per gestire le luci
+
|immagine=Pulsante-arduino.gif
di casa online, oppure per '''monitorare''' dei sensori ambientali dal cellulare.
+
|guida=Arduino
 +
|precedente=Arduino_e_comunicazione_seriale
 +
|indice=Elettronica_Opensource
 +
|successiva=Elettronica_Opensource}}
  
Per questi piccoli progetti è adatta la Ethernet Shield, una scheda
+
Ci sono applicazioni per cui è comodo avere Arduino collegato in rete, ad esempio per un sistema di '''domotica''', cioè di controllo intelligente della casa: per controllare le luci da remoto, per accendere il riscaldamento o controllare la temperatura dall'ufficio o dal cellulare.
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
+
Per questi piccoli progetti è adatta la ''Ethernet Shield'', una scheda già pronta per essere montata su un Arduino e che mette a disposizione:
dipende molto dalla potenza del microprocessore di Arduino, poiché è lui
+
* una porta Ethernet controllata tramite un chip dedicato, per la connettività in rete;
che deve occuparsi, come vedremo, di tutti i ''protocolli di rete''.
+
* uno slot per microSD, utile per salvare le informazioni catturate (monitor);
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.
 
  
 +
Esistono anche delle ''wireless shield'', molto simili, che però usano le reti senza fili, tra cui WiFi.
  
=Concetti di base=
+
Ma '''attenzione''': questa scheda dipende molto dalla potenza del microprocessore di Arduino, poiché è lui che deve occuparsi, come vedremo, di diversi ''protocolli di rete''.
  
==TCP/IP e protocolli==
+
Dimenticatevi quindi di poter tirare su in questo modo un sito web con tante pagine, database, eccetera: per questo serve un computer ad uso generale, per quanto piccolo, ad esempio un [https://www.raspberrypi.org/ Raspberry Pi], una [https://beagleboard.org/bone BeagleBone] o un [https://www.olimex.com/Products/OLinuXino/open-source-hardware Olinuxino], anziché Arduino.
Tutti i dispositivi collegati in rete devono rispettare un certo
+
 
protocollo, ovvero seguire regole per poter identificarsi fra loro
+
= Concetti di base sulle reti =
senza creare conflitti.
+
In questa sezione sono introdotti alcuni concetti '''fondamentali''' sulle reti, che è bene aver chiaro '''prima''' di cimentarsi nella realizzazione di dispositivi di rete.
   
+
 
Ogni dispositivo è in particolare identificato da un nome sulla
+
Alcuni concetti sono stati volutamente trascurati o omessi per semplicità di trattazione (e si vedrà che già così la strada è lunga).
rete, detto indirizzo IP. Ad esempio, nella mia rete di casa
+
 
il computer che uso ha come indirizzo <code>192.168.142</code>, e il cellulare
+
Si consiglia comunque di leggere la parte di teoria anche a coloro che già la sanno, a mo' di ripasso, dopodiché passare alla pratica.
<code>192.168.1.119</code>.
+
 
   
+
{{Note
Tutti gli indirizzi IP sono composti da una quartina di numeri
+
|type=warning
compresi fra 0 e 255, separati da un punto. Solitamente, nelle
+
|text=I concetti espressi nella seguente trattazione non sono sufficienti per la creazione di dispositivi sicuri, perciò è bene utilizzarli solo come punto di partenza per la realizzazione di prototipi, e successivamente approfondire. Potrebbe essere molto fastidioso - o addirittura pericoloso - che qualcuno possa, dalla rete Internet, accendere la nostra caldaia mentre non siamo a casa o spiare i nostri familiari.
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
+
== Teoria ==
da dispositivo a dispositivo.
+
=== Livelli di astrazione ===
   
+
[[File:osi-network-stack.png|thumb|Livelli di astrazione nelle reti]]
Fa eccezione <code>192.168.1.255</code>, che è un indirizzo speciale detto
+
Tutti i dispositivi collegati in rete devono rispettare certe regole per potersi identificare, e per poter parlare fra loro senza creare conflitti, così come ogni persona ha un proprio nome, e segue delle regole durante una conversazione (es. non parla mentre altri stanno già parlando).
broadcast che equivale a parlare contemporaneamente con tutti i
+
 
dispositivi connessi alla rete di casa.
+
Per avere connettività, è stato pensato uno schema che permette la connettività a 7 diversi livelli.
 +
 
 +
Ne descriveremo 5, fondamentali per capire il funzionamento, ma ci preoccuperemo solo dei "dettagli" dei 3 a più alto livello.
 +
 
 +
==== Livello fisico ====
 +
Non ci interesseremo del livello fisico, basta sapere che - com'è ovvio immaginare - una rete cablata su Ethernet è ben diversa da una rete senza fili WiFi già a partire dal mezzo fisico in cui si propagano le informazioni.
 +
 
 +
==== Livello di collegamento ====
 +
Permette ai dispositivi in una (piccola) rete di parlare tra di loro.
 +
 
 +
A questo livello appartiene il '''MAC Address''', un indirizzo univoco per ogni dispositivo esistente.
 +
Esso viene assegnato in fabbrica ad ogni dispositivo e non cambia mai.
 +
 
 +
==== Livello di rete ====
 +
Permette ai dispositivi di parlare non solo con i dispositivi della stessa rete, ma anche con quelli di reti diverse.
 +
 
 +
A questo livello appartiene l''''indirizzo IP''', un indirizzo univoco nella rete.
 +
 
 +
===== IP vs MAC =====
 +
Perché c'è bisogno di due indirizzi, un MAC e un IP, per presentarsi in rete? Non sarebbe sufficiente il MAC che è già univoco di suo?
 +
 
 +
I due indirizzi hanno uno scopo e una struttura completamente diversa:
 +
* l'indirizzo MAC (Media Access Control) serve per permettere la comunicazione in presenza di più dispositivi, serve cioè per identificarli. Ogni produttore assegna gli indirizzi MAC ai propri dispositivi facendo uso di un registro in comune con gli altri produttori, in modo da evitare che due dispositivi diversi possano avere lo stesso indirizzo. Come i ''codici fiscali'', anche gli indirizzi MAC quindi sono molto variegati tra loro, e non danno alcuna informazione su come raggiungere il dispositivo, ma si limitano ad identificarlo.
 +
* l'indirizzo IP (Internet protocol) serve invece per permettere la comunicazione tra dispositivi di reti diverse, per trovare la loro collocazione, e per creare reti logiche di dispositivi, raggruppandone un sottoinsieme. Come gli ''indirizzi geografici'', anche gli indirizzi IP di dispositivi "vicini" tra loro sono simili (cioè di dispositivi che si trovano nella stessa rete), e forniscono una chiara identificazione di dove si trovano.
 +
 
 +
Un dispositivio con indirizzo MAC <code>AA:BB:CC:DD:EE:FF</code> lo conserverà per sempre, ma potrà cambiare il suo indirizzo IP da <code>192.0.2.100</code> a <code>192.168.0.100</code> a seconda della rete in cui si trova.
 +
 
 +
Si rifletta sull'analogia (MAC = codice fiscale) e (IP = indirizzo geografico).
 +
 
 +
==== Livello di trasporto ====
 +
Il livello di rete permette di trasportare i dati da un dispositivo ad un altro, ma, quando questi dati sono giunti al dispositivo di destinazione, a chi vanno consegnati? Se un dispositivo esegue più applicazioni di rete, a quale applicazione devo dare questi dati?
 +
Si pensi al computer, che permette di navigare sul web e contemporaneamente vedere un film in streaming: quando i dati giungono al computer, come fa a sapere se sono un pezzo di film o una pagina web?
 +
 
 +
Questo problema viene risolto dal livello di trasporto.
 +
 
 +
Il '''protocollo TCP''' che utilizziamo si occupa anche del controllo di trasmissione, cioè di rendere affidabile la comunicazione dati in rete tra mittente e destinatario: farà di tutto per impedire che i dati vengano persi o giungano corrotti a destinazione, eventualmente trasmettendoli più volte. TCP si occupa anche di controllare il flusso della trasmissione e evitare congestione all'interno della rete.
 +
 
 +
Una caratteristica importante di TCP è il concetto di '''porta''', un numero che permette di distinguere i diversi processi a cui sono destinati i dati trasmessi. Un Arduino, pur eseguendo un solo compito, dovrà comunque offrire il suo servizio ''rimanendo in ascolto dietro una certa porta''.
 +
 
 +
==== Livello applicazione ====
 +
Una volta giunti al dispositivo giusto e al servizio giusto, i dati vengono finalmente letti dal nostro programma, secondo un altro protocollo detto ''di livello applicativo''. Dovremo preoccuparci di scrivere le azioni che deve eseguire il protocollo nel nostro sketch di Arduino.
 +
 
 +
Il protocollo che useremo è '''HTTP''' (Hyper Text Transfer Protocol), che sta alla base del web.
 +
 
 +
{{Note
 +
|type=info
 +
|text=Ogni livello realizza un''''astrazione''', ossia può essere utilizzato senza doversi preoccupare di come funzionino i livello sottostanti.
 +
Per esempio, se abbiamo in casa uno smartphone collegato tramite WiFi e un computer fisso tramite Ethernet, se ci preoccupiamo di farli dialogare a livello di rete, ci basta conoscere i loro indirizzi IP senza doversi preoccupare dei livelli sottostanti (collegamento e fisico) che sono realizzati con due tecnologie differenti.
 +
}}
 +
 
 +
== Pratica ==
 +
=== MAC Address ===
 +
La libreria per lo shield necessita dell'indirizzo MAC del dispositivo, che è solitamente scritto su un adesivo sotto l'Ethernet Shield. Ha una forma del tipo <code>d8:ab:cd:ef:03:69</code>
 +
 
 +
=== IP Address ===
 +
La libreria necessita anche dell'indirizzo IP del dispositivo. Questo viene solitamente scritto nella forma <code>1.2.3.4</code>, dove ogni numero rappresenta un byte (e quindi può assumere valori che vanno da <code>0</code> a <code>255</code>). Ricordiamo che l'IP cambia a seconda della rete in cui questo è inserito: pertanto, può darsi che debba essere modificato nello sketch quando ci si sposta tra casa, ufficio, officina.
 +
 
 +
==== Come scegliere l'indirizzo IP? ====
 +
Chi è che dà gli indirizzi in una rete?
 +
 
 +
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. C'è quindi un computer dedicato che esegue il protocollo '''DHCP''' ''Dynamic Host Configuration Protocol'' che ha l'autorità di dare ad ognuno il suo indirizzo evitando i conflitti; nelle reti domestiche questo compito viene svolto dal modem del gestore telefonico.
 +
 
 +
Il server DHCP dispone di un insieme di indirizzi da cui ''pescarne a caso'' uno libero da assegnare a chi ne faccia richiesta. Nelle reti domestiche, questo insieme è in genere nell'intervallo <code>192.168.0.2</code>-<code>192.168.0.254</code> oppure <code>192.168.1.2</code>-<code>192.168.1.254</code>.
 +
 
 +
Per i dispositivi che ''si collegano'' a servizi offerti da ''altri'' su Internet non ci interessa che abbiano un IP particolare, e quindi lo possono anche chiedere al modem col meccanismo del DHCP: ogni volta che si collegano, può essergli assegnato un indirizzo diverso: si dice che hanno un ''IP dinamico''.
 +
 
 +
I dispositivi a cui invece ''ci dobbiamo collegare'', che sono essi stessi fornitori di servizi, dovrebbero avere sempre lo stesso IP, noto e indipendente dalle volontà del server DHCP, così da poter essere facilmente raggiungibili: 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 rete di IP è in uso nella propria rete domestica;
 +
* se l'IP scelto è disponibile, ovvero se il server DHCP non l'ha già assegnato a qualcuno;
 +
 
 +
Da un computer connesso, vediamo la rete:
 +
 
 +
Su Linux:
 +
$ '''ip addr'''
 +
1: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
 +
    link/ether aa:bb:cc:dd:ee:ff brd ff:ff:ff:ff:ff:ff
 +
    inet 192.168.0.6/24 brd 192.168.0.255 scope global noprefixroute eth0
 +
      valid_lft forever preferred_lft forever
 +
    inet6 fe80::aabb:ccff:fedd:eeff/64 scope link
 +
      valid_lft forever preferred_lft forever
 +
 
 +
Su Windows:
 +
> '''ipconfig'''
 +
Connection-specific DNS Suffix  . : golem.linux.it.
 +
IP Address. . . . . . . . . . . . : 192.168.0.6
 +
Subnet Mask . . . . . . . . . . . : 255.255.255.0
 +
Default Gateway . . . . . . . . . : 192.168.0.1
  
 +
In questi casi la rete è <code>192.168.0.0</code>.
  
Chi è che dà gli indirizzi nella rete casalinga?
+
Sempre dal computer connesso, proviamo a contattare un indirizzo a caso per vedere se è già stato assegnato. Per scegliere un indirizzo, basterà scegliere casualmente un numero da inserire al posto dell'ultimo byte. (Questo è vero solo se la maschera di rete è 255.255.255.0 o /24, come nella totalità delle reti domestiche, come nell'esempio).
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
+
Supponiamo di aver scelto <code>192.168.0.80</code>.
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.
 
  
 +
ping 192.168.0.80
  
Bene, detto questo, sappiamo che anche il nostro Arduino on the internet
+
Se il comando risponde ''Destination Host Unreachable'', allora l'indirizzo è libero e possiamo usarlo; altrimenti è già occupato e occorre sceglierne un altro.
dovrà avere una coppia di indirizzi IP e un MAC.
 
Il MAC è solitamente scritto su un adesivo sotto l'ethernet shield.
 
  
   
+
{{Note
Per quanto riguarda l'IP, bisogna aggiungere qualcos'altro:
+
|type=warning
per i dispositivi che <b>si collegano</b> ad internet non ci interessa
+
|text=Il fatto che l'indirizzo sia libero in questo momento non dà nessuna garanzia sul fatto che lo sarà anche in futuro: il server DHCP, completamente ignaro dei vostri esperimenti, potrebbe decidere di assegnare quell'indirizzo a qualche nuovo dispositivo che si collega, per esempio allo smartphone di un vostro amico che è venuto a farvi visita. E quando lo inviterete a collegarsi al vostro sensore perfettamente funzionante con Arduino, non funzionerà niente e farete una brutta figura. Perciò, è bene modificare le impostazioni del server DHCP sul vostro modem e ridurre l'intervallo degli indirizzi liberi, per esempio <code>192.168.0.2</code>-<code>192.168.0.127</code>, e assegnare al vostro Arduino un indirizzo al di fuori, per esempio <code>192.168.0.128</code>.
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 <b>ci dobbiamo collegare</b> dovrebbero
+
=== Servizi e Servitori ===
avere sempre lo stesso IP, noto e indipendente dalle volontà
+
Quando ci colleghiamo ad un server, questo può offrire diversi servizi: trasferimento files (FTP), posta elettronica (POP3, IMAP, SMTP), e tanti altri.
del router: si dice che hanno un IP statico.
+
Uno stesso computer può offrire più servizi contemporaneamente e, per poter discriminare a chi mandare cosa, il protocollo TCP prevede una struttura a porte. Ad ogni servizio è associato un numero di porta.
  
Anche il nostro Arduino dovrà avere un IP statico, che gli assegneremo
+
Per esempio, quando con un browser si richiede una pagina web, si ''bussa'' alla sua porta TCP numero 80, e tramite quella porta fluisce la comunicazione.
nel programma. Prima però dovremo assicurarci di due cose:
 
  
<ul>
+
Metteremo il nostro Arduino in grado di poter rispondere alle richieste fatte sulla porta 80, così basterà collegarsi ad esso con un comune browser per poterci interagire.
    <li>quale "serie" di IP è in uso nella propria rete casalinga;</li>
 
    <li> se l'IP scelto è disponibile, ovvero se il router non l'ha già
 
    assegnato a qualcuno;</li>
 
</ul>
 
  
Per il primo punto basta controllare a quale IP risponde il router:
+
=== HTTP ===
solitamente infatti ha ip 192.168.1.1 oppure 192.168.0.1.
+
Dopo che il browser ha inviato la richiesta di connessione al server, incomincia uno scambio di informazioni fra i due che gli permette di accordarsi sul tipo di dati che si stanno scambiando.
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
+
In particolare, il browser invia la richiesta della pagina web, seguita da una linea bianca per indicare il termine:
a <code>192.168.1.</code> per avere un IP, e verificare tramite un terminale
 
(o il prompt di DOS su windows) se è disponibile:
 
digita ping <code>192.168.1.245</code> e se nessuno risponde
 
il comando dovrebbe dare come output un errore come
 
<code>Destination Unreachable</code>.
 
Appuntati quindi questo IP.
 
  
===Servizi e Servitori===
+
GET /sensore.html HTTP/1.1
Quando ti colleghi ad un sito internet, chi ti dà la pagina non
+
Host: 192.168.0.80
è 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
+
e il server, cioè il nostro Arduino, gli deve rispondere con un codice di approvazione o di errore (a seconda se la pagina è disponibile o no), specificando il formato e la lunghezza della pagina (testo semplice di 3 bytes nel nostro caso) e accodare finalmente il contenuto:
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
+
HTTP/1.1 200 OK
"bussa" alla porta numero 80 del suo indirizzo IP, e tramite quella
+
Content-Length: 3
porta fluisce la compunicazione.
+
Content-Type: text/plain; charset=utf-8
Ogni servizio ha una porta dedicata, in particolare il servizio
+
di trasferimento pagine web (HTTP) ha il numero 80.<br/>
+
497
  
Lo stesso dovrà succedere con il nostro Arduino: lo metteremo in grado
+
Interrogando il nostro Arduino con un browser all'indirizzo <code>http://192.168.0.80/</code> vedremo quindi ''497''.
di poter "rispondere" alle richieste fatte sulla porta 80.
 
</p>
 
  
===HTTP &amp; HTML===
+
Questo può essere visto nel dettaglio direttamente nel seguente codice Arduino, in particolare lo scambio di dati: attivando la trasmissione
Ed eccoci quasi arrivati al termine.
+
USB si vede in diretta tutta la comunicazione HTTP fra lui e il browser.
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,
+
Si osservi che in realtà questo codice non è strettamente conforme al protocollo HTTP, in quanto esegue nessun controllo sulla pagina che viene richiesta (risponde sempre con la stessa pagina), né tantomeno specifica la lunghezza dei dati (chiude brutalmente la connessione dopo che ha trasmesso tutti i dati).
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 Arduino==
Riga 266: Riga 283:
 
}
 
}
 
</pre>
 
</pre>
 
Prossimamente vedremo come interagire via Ethernet con i componenti collegati
 
alla scheda.
 
  
 
[[Category:Howto]]
 
[[Category:Howto]]

Versione attuale delle 20:17, 1 giu 2018

Pulsante-arduino.gif Stai leggendo la guida "Arduino"


Ci sono applicazioni per cui è comodo avere Arduino collegato in rete, ad esempio per un sistema di domotica, cioè di controllo intelligente della casa: per controllare le luci da remoto, per accendere il riscaldamento o controllare la temperatura dall'ufficio o dal cellulare.

Per questi piccoli progetti è adatta la Ethernet Shield, una scheda già pronta per essere montata su un Arduino e che mette a disposizione:

  • una porta Ethernet controllata tramite un chip dedicato, per la connettività in rete;
  • uno slot per microSD, utile per salvare le informazioni catturate (monitor);

Esistono anche delle wireless shield, molto simili, che però usano le reti senza fili, tra cui WiFi.

Ma attenzione: questa scheda dipende molto dalla potenza del microprocessore di Arduino, poiché è lui che deve occuparsi, come vedremo, di diversi protocolli di rete.

Dimenticatevi quindi di poter tirare su in questo modo un sito web con tante pagine, database, eccetera: per questo serve un computer ad uso generale, per quanto piccolo, ad esempio un Raspberry Pi, una BeagleBone o un Olinuxino, anziché Arduino.

Concetti di base sulle reti

In questa sezione sono introdotti alcuni concetti fondamentali sulle reti, che è bene aver chiaro prima di cimentarsi nella realizzazione di dispositivi di rete.

Alcuni concetti sono stati volutamente trascurati o omessi per semplicità di trattazione (e si vedrà che già così la strada è lunga).

Si consiglia comunque di leggere la parte di teoria anche a coloro che già la sanno, a mo' di ripasso, dopodiché passare alla pratica.

Golem-template-note-warning.png I concetti espressi nella seguente trattazione non sono sufficienti per la creazione di dispositivi sicuri, perciò è bene utilizzarli solo come punto di partenza per la realizzazione di prototipi, e successivamente approfondire. Potrebbe essere molto fastidioso - o addirittura pericoloso - che qualcuno possa, dalla rete Internet, accendere la nostra caldaia mentre non siamo a casa o spiare i nostri familiari.


Teoria

Livelli di astrazione

Livelli di astrazione nelle reti

Tutti i dispositivi collegati in rete devono rispettare certe regole per potersi identificare, e per poter parlare fra loro senza creare conflitti, così come ogni persona ha un proprio nome, e segue delle regole durante una conversazione (es. non parla mentre altri stanno già parlando).

Per avere connettività, è stato pensato uno schema che permette la connettività a 7 diversi livelli.

Ne descriveremo 5, fondamentali per capire il funzionamento, ma ci preoccuperemo solo dei "dettagli" dei 3 a più alto livello.

Livello fisico

Non ci interesseremo del livello fisico, basta sapere che - com'è ovvio immaginare - una rete cablata su Ethernet è ben diversa da una rete senza fili WiFi già a partire dal mezzo fisico in cui si propagano le informazioni.

Livello di collegamento

Permette ai dispositivi in una (piccola) rete di parlare tra di loro.

A questo livello appartiene il MAC Address, un indirizzo univoco per ogni dispositivo esistente. Esso viene assegnato in fabbrica ad ogni dispositivo e non cambia mai.

Livello di rete

Permette ai dispositivi di parlare non solo con i dispositivi della stessa rete, ma anche con quelli di reti diverse.

A questo livello appartiene l'indirizzo IP, un indirizzo univoco nella rete.

IP vs MAC

Perché c'è bisogno di due indirizzi, un MAC e un IP, per presentarsi in rete? Non sarebbe sufficiente il MAC che è già univoco di suo?

I due indirizzi hanno uno scopo e una struttura completamente diversa:

  • l'indirizzo MAC (Media Access Control) serve per permettere la comunicazione in presenza di più dispositivi, serve cioè per identificarli. Ogni produttore assegna gli indirizzi MAC ai propri dispositivi facendo uso di un registro in comune con gli altri produttori, in modo da evitare che due dispositivi diversi possano avere lo stesso indirizzo. Come i codici fiscali, anche gli indirizzi MAC quindi sono molto variegati tra loro, e non danno alcuna informazione su come raggiungere il dispositivo, ma si limitano ad identificarlo.
  • l'indirizzo IP (Internet protocol) serve invece per permettere la comunicazione tra dispositivi di reti diverse, per trovare la loro collocazione, e per creare reti logiche di dispositivi, raggruppandone un sottoinsieme. Come gli indirizzi geografici, anche gli indirizzi IP di dispositivi "vicini" tra loro sono simili (cioè di dispositivi che si trovano nella stessa rete), e forniscono una chiara identificazione di dove si trovano.

Un dispositivio con indirizzo MAC AA:BB:CC:DD:EE:FF lo conserverà per sempre, ma potrà cambiare il suo indirizzo IP da 192.0.2.100 a 192.168.0.100 a seconda della rete in cui si trova.

Si rifletta sull'analogia (MAC = codice fiscale) e (IP = indirizzo geografico).

Livello di trasporto

Il livello di rete permette di trasportare i dati da un dispositivo ad un altro, ma, quando questi dati sono giunti al dispositivo di destinazione, a chi vanno consegnati? Se un dispositivo esegue più applicazioni di rete, a quale applicazione devo dare questi dati? Si pensi al computer, che permette di navigare sul web e contemporaneamente vedere un film in streaming: quando i dati giungono al computer, come fa a sapere se sono un pezzo di film o una pagina web?

Questo problema viene risolto dal livello di trasporto.

Il protocollo TCP che utilizziamo si occupa anche del controllo di trasmissione, cioè di rendere affidabile la comunicazione dati in rete tra mittente e destinatario: farà di tutto per impedire che i dati vengano persi o giungano corrotti a destinazione, eventualmente trasmettendoli più volte. TCP si occupa anche di controllare il flusso della trasmissione e evitare congestione all'interno della rete.

Una caratteristica importante di TCP è il concetto di porta, un numero che permette di distinguere i diversi processi a cui sono destinati i dati trasmessi. Un Arduino, pur eseguendo un solo compito, dovrà comunque offrire il suo servizio rimanendo in ascolto dietro una certa porta.

Livello applicazione

Una volta giunti al dispositivo giusto e al servizio giusto, i dati vengono finalmente letti dal nostro programma, secondo un altro protocollo detto di livello applicativo. Dovremo preoccuparci di scrivere le azioni che deve eseguire il protocollo nel nostro sketch di Arduino.

Il protocollo che useremo è HTTP (Hyper Text Transfer Protocol), che sta alla base del web.

Golem-template-note-info.png Ogni livello realizza un'astrazione, ossia può essere utilizzato senza doversi preoccupare di come funzionino i livello sottostanti. Per esempio, se abbiamo in casa uno smartphone collegato tramite WiFi e un computer fisso tramite Ethernet, se ci preoccupiamo di farli dialogare a livello di rete, ci basta conoscere i loro indirizzi IP senza doversi preoccupare dei livelli sottostanti (collegamento e fisico) che sono realizzati con due tecnologie differenti.


Pratica

MAC Address

La libreria per lo shield necessita dell'indirizzo MAC del dispositivo, che è solitamente scritto su un adesivo sotto l'Ethernet Shield. Ha una forma del tipo d8:ab:cd:ef:03:69

IP Address

La libreria necessita anche dell'indirizzo IP del dispositivo. Questo viene solitamente scritto nella forma 1.2.3.4, dove ogni numero rappresenta un byte (e quindi può assumere valori che vanno da 0 a 255). Ricordiamo che l'IP cambia a seconda della rete in cui questo è inserito: pertanto, può darsi che debba essere modificato nello sketch quando ci si sposta tra casa, ufficio, officina.

Come scegliere l'indirizzo IP?

Chi è che dà gli indirizzi in una rete?

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. C'è quindi un computer dedicato che esegue il protocollo DHCP Dynamic Host Configuration Protocol che ha l'autorità di dare ad ognuno il suo indirizzo evitando i conflitti; nelle reti domestiche questo compito viene svolto dal modem del gestore telefonico.

Il server DHCP dispone di un insieme di indirizzi da cui pescarne a caso uno libero da assegnare a chi ne faccia richiesta. Nelle reti domestiche, questo insieme è in genere nell'intervallo 192.168.0.2-192.168.0.254 oppure 192.168.1.2-192.168.1.254.

Per i dispositivi che si collegano a servizi offerti da altri su Internet non ci interessa che abbiano un IP particolare, e quindi lo possono anche chiedere al modem col meccanismo del DHCP: ogni volta che si collegano, può essergli assegnato un indirizzo diverso: si dice che hanno un IP dinamico.

I dispositivi a cui invece ci dobbiamo collegare, che sono essi stessi fornitori di servizi, dovrebbero avere sempre lo stesso IP, noto e indipendente dalle volontà del server DHCP, così da poter essere facilmente raggiungibili: 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 rete di IP è in uso nella propria rete domestica;
  • se l'IP scelto è disponibile, ovvero se il server DHCP non l'ha già assegnato a qualcuno;

Da un computer connesso, vediamo la rete:

Su Linux:

$ ip addr
1: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
   link/ether aa:bb:cc:dd:ee:ff brd ff:ff:ff:ff:ff:ff
   inet 192.168.0.6/24 brd 192.168.0.255 scope global noprefixroute eth0
      valid_lft forever preferred_lft forever
   inet6 fe80::aabb:ccff:fedd:eeff/64 scope link 
      valid_lft forever preferred_lft forever

Su Windows:

> ipconfig
Connection-specific DNS Suffix  . : golem.linux.it.
IP Address. . . . . . . . . . . . : 192.168.0.6
Subnet Mask . . . . . . . . . . . : 255.255.255.0
Default Gateway . . . . . . . . . : 192.168.0.1

In questi casi la rete è 192.168.0.0.

Sempre dal computer connesso, proviamo a contattare un indirizzo a caso per vedere se è già stato assegnato. Per scegliere un indirizzo, basterà scegliere casualmente un numero da inserire al posto dell'ultimo byte. (Questo è vero solo se la maschera di rete è 255.255.255.0 o /24, come nella totalità delle reti domestiche, come nell'esempio).

Supponiamo di aver scelto 192.168.0.80.

ping 192.168.0.80

Se il comando risponde Destination Host Unreachable, allora l'indirizzo è libero e possiamo usarlo; altrimenti è già occupato e occorre sceglierne un altro.

Golem-template-note-warning.png Il fatto che l'indirizzo sia libero in questo momento non dà nessuna garanzia sul fatto che lo sarà anche in futuro: il server DHCP, completamente ignaro dei vostri esperimenti, potrebbe decidere di assegnare quell'indirizzo a qualche nuovo dispositivo che si collega, per esempio allo smartphone di un vostro amico che è venuto a farvi visita. E quando lo inviterete a collegarsi al vostro sensore perfettamente funzionante con Arduino, non funzionerà niente e farete una brutta figura. Perciò, è bene modificare le impostazioni del server DHCP sul vostro modem e ridurre l'intervallo degli indirizzi liberi, per esempio 192.168.0.2-192.168.0.127, e assegnare al vostro Arduino un indirizzo al di fuori, per esempio 192.168.0.128.


Servizi e Servitori

Quando ci colleghiamo ad un server, questo può offrire diversi servizi: trasferimento files (FTP), posta elettronica (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 prevede una struttura a porte. Ad ogni servizio è associato un numero di porta.

Per esempio, quando con un browser si richiede una pagina web, si bussa alla sua porta TCP numero 80, e tramite quella porta fluisce la comunicazione.

Metteremo il nostro Arduino in grado di poter rispondere alle richieste fatte sulla porta 80, così basterà collegarsi ad esso con un comune browser per poterci interagire.

HTTP

Dopo che il browser ha inviato la richiesta di connessione al server, incomincia uno scambio di informazioni fra i due che gli permette di accordarsi sul tipo di dati che si stanno scambiando.

In particolare, il browser invia la richiesta della pagina web, seguita da una linea bianca per indicare il termine:

GET /sensore.html HTTP/1.1
Host: 192.168.0.80

e il server, cioè il nostro Arduino, gli deve rispondere con un codice di approvazione o di errore (a seconda se la pagina è disponibile o no), specificando il formato e la lunghezza della pagina (testo semplice di 3 bytes nel nostro caso) e accodare finalmente il contenuto:

HTTP/1.1 200 OK
Content-Length: 3
Content-Type: text/plain; charset=utf-8

497

Interrogando il nostro Arduino con un browser all'indirizzo http://192.168.0.80/ vedremo quindi 497.

Questo può essere visto nel dettaglio direttamente nel seguente 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.

Si osservi che in realtà questo codice non è strettamente conforme al protocollo HTTP, in quanto esegue nessun controllo sulla pagina che viene richiesta (risponde sempre con la stessa pagina), né tantomeno specifica la lunghezza dei dati (chiude brutalmente la connessione dopo che ha trasmesso tutti i dati).

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!");
  }
}