Per entrare a pieno titolo nell'Internet of Things, sfruttiamo il collegamento a Internet di Arduino (che abbiamo esaminato nelle lezioni precedenti) per realizzare una applicazione che ci permetta di trasmettere online il valore della temperatura rilevata nella nostra stanza.
Questo semplice sistema prevede una parte strettamente embedded che implementeremo subito e una parte di connessione al Cloud che affronteremo in seguito (nelle prossime lezioni).
Collegare il sensore di temperatura
Il primo passo è quello di procurarci un buon sensore di temperatura da poter collegare alla board. Esistono numerosi sensori di questo tipo, sia analogici che digitali, ma uno di quelli maggiormente utilizzati è il TMP102 della Texas Instruments. Questo componente fornisce un'interfaccia I2C (Inter Integrated Circuit) ossia una seriale sincrona caratterizzata dai soli due segnali:
Segnale | Descrizione |
---|---|
SDA (Serial DAta) | Per il trasferimento dati |
SCL (Serial CLock) | Per il clock |
Non è possibile approfondire in questa sede le caratteristiche del protocollo I2C ma è necessario sapere che sullo stesso bus possono essere collegati più device e che il master decide con chi stabilire la comunicazione semplicemente trasmettendo il relativo indirizzo sulla linea SDA.
Ovviamente, nella nostra configurazione, Arduino assume il ruolo di master (che quindi genera anche il segnale di clock) ed il TMP102 funge da slave, ricevendo le richieste e fornendo le relative risposte.
Dal punto di vista hardware, inoltre, sono necessarie due resistenze di pull-up in corrispondenza delle due linee, necessarie per mantenere il segnale alto (1 logico) nello stato di idle; viceversa, il master o lo slave hanno il compito di abbassare il segnale per trasferire il valore logico 0.
Per semplificarci la vita, possiamo acquistare un breakout board sulla quale è disponibile il sensore e tutti gli altri componenti necessari al suo funzionamento; possiamo trovarla sul sito della Sparkfun).
Breakout board con il sensore TMP102
La connessione alla board "Arduino Uno" è relativamente semplice: basta ricordare che il pin analogico 4 corrisponde all'SDA ed il pin analogico 5 all'SCL. Inoltre, l'alimentazione V+
della breakout board va collegata al pin che fornisce la tensione di 3.3 V
e la massa al corrispondente pin di GND
.
Infine, il sensore ha l'ulteriore pin ADD0
che serve a stabilire l'indirizzo del sensore stesso su un bus I2C (qualora volessimo collegarne più di uno). Colleghiamo il pin ADD0
alla massa (GND) dell'Arduino in modo che l'indirizzo diventi 0x48
(valore esadecimale). Tale informazione è disponibile sul datasheet del sensore stesso.
Utilizzando il tool Fritzing, già visto nelle lezioni precedenti, possiamo "disegnare" il circuito in modo da averlo a portata di mano nel momento della realizzazione:
Realizzare il "driver"
Dopo aver superato lo "scoglio" hardware, passiamo alla realizzazione di un semplice driver per utilizzare il sensore. Per driver intendiamo una libreria Arduino che contenga al suo interno tutta la logica necessaria per accedere al componente, definendo un livello di astrazione con l'applicazione che ne fa uso.
Il nostro obiettivo è quello di mettere a disposizione una semplicissima funzione getTemperature()
che ritorni il valore di temperatura acquisito dal sensore, nascondendo tutte le azioni necessarie. In questo modo, nel realizzare le applicazioni, possiamo ignorare la modalità di accesso al sensore (che in questo caso usa il bus I2C) e riusare la libreria senza riscrivere codice.
Ci serviamo della programmazione a oggetti (OOP) e definiamo una classe Tmp102 che rappresenti il sensore ed esponga l'unico metodo getTemperature()
. Creiamo quindi due file:
File | Descrizione |
---|---|
Tmp102.h | Il cosiddetto file "header", in cui dichiariamo la classe e la sua interfaccia verso l'esterno |
Tmp102.cpp | File che contiene la logica implementativa della classe |
Nel nostro caso la classe è abbastanza semplice: contiene solo il costruttore e la dichiarazione del metodo per la lettura della temperatura. Inoltre, l'header file contiene la definizione dell'indirizzo per l'accesso al sensore (TMP_102_ADDRESS
) e include l'header file "Arduino.h
" con tutte le dichiarazioni standard dei tipi e delle costanti supportate dalla piattaforma Arduino. Infine, l'utilizzo della "guardia" TMP_102
serve per evitare inclusioni e dichiarazioni ricorsive all'interno di più file sorgenti.
/** File: Tmp102.h */
#ifndef TMP_102
#define TMP_102
#include <Arduino.h>
#define TMP_102_ADDRESS 0x48 // indirizzo sensore (pin ADD0 a GND)
class Tmp102
{
public:
Tmp102();
float getTemperature();
};
#endif
L'implementazione della classe includerà sia l'header file corrispondente (Tmp102.h
), sia l'header file "Wire.h
" che ci permette di utilizzare tutte le funzioni della libreria Wire per l'accesso ai device I2C/TWI. Tale libreria espone una serie di funzioni che astraggono completamente l'utilizzo del bus I2C e che si preoccupano di eseguire tutte le operazioni di basso livello per poter leggere e scrivere da/verso un device.
La classe Tmp102
ha un costruttore praticamente vuoto in quando non sono necessarie particolari inizializzazioni per utilizzare il sensore ed ha il metodo getTemperature() che esegue nell'ordine le seguenti operazioni:
- utilizza
Wire.requestFrom(TMP_102_ADDRESS, 2)
per fare una richiesta di ricezione dati sul bus I2C dal device il cui indirizzo è il primo parametro (nel nostro casoTMP_102_ADDRESS
); il secondo parametro indica il numero di byte da leggere; - esegue due volte il metodo
Wire.read()
per leggere i byte richiesti dal bus; - dai due byte suddetti (MSB, Most Significative Byte e LSB, Last Significative Byte) ricava il valore intero a 12 bit che rappresenta il valore della temperatura secondo le specifiche del sensore TMP102 e che sono disponibili nel corrispondente datasheet;
- converte il valore intero suddetto nella corrispondente temperatura in gradi centigradi (Celsius) e lo restituisce al chiamante;
// Metodo che ritorna la temperatura dal sensore
float Tmp102::getTemperature()
{
// richiesta di lettura di 2 byte
// dal device I2C con indirizzo TMP_102_ADDRESS
Wire.requestFrom(TMP_102_ADDRESS, 2);
// lettura dei due byte (più e meno significativo)
byte MSB = Wire.read();
byte LSB = Wire.read();
// valore intero 12 bit ricavato dal sensore
int tempValue = ((MSB << 8) | LSB) >> 4;
// conversione in gradi centigradi
float celsius = tempValue * 0.0625;
return celsius;
}
Una volta completata la libreria non ci resta che utilizzarla in uno sketch di esempio!
Creiamo una sottocartella "Tmp102" nella cartella "libraries" dell'IDE di Arduino e copiamo al suo interno i due file che abbiamo creato. Rilanciando l'IDE, vedremo apparire la nostra libreria tra quelle di esempio in Sketch > Import Library ...
" e, cliccando su di essa, l'ambiente aggiungerà per noi la direttiva di include del file "Tmp102.h
" in testa al nostro sketch per poter utilizzare la libreria.
Libreria Tmp102 disponibile tra quelle di Arduino
Il nostro sketch è relativamente semplice, in quanto non fa altro che utilizzare un'istanza della classe Tmp102
per poter leggere continuamente il valore di temperature ed inviarlo al PC attraverso il collegamento seriale che abbiamo già utilizzato nei capitoli precedenti.
Nella funzione setup()
abbiamo l'inizializzazione della porta seriale ma soprattutto l'inizializzazione del bus I2C attraverso il metodo Wire.begin(): grazie a quest'ultimo la libreria Wire viene inizializzata in modo tale che l'Arduino assuma il ruolo di master sul bus.
È da sottolineare che questa operazione viene eseguita nello sketch e non nella libreria, in quanto tale azione va eseguita una ed una sola volta. Se utilizzassimo più librerie per device che fanno uso del bus I2C e ciascuna di queste librerie chiamasse al suo interno la Wire.begin()
, avremmo un'invocazione multipla di quest'ultima.
#include <Wire.h>
#include "Tmp102.h"
// dichiarazione oggetto Tmp102
Tmp102 tmp102;
void setup(){
Serial.begin(9600);
Wire.begin();
}
void loop(){
// lettura e trasmissione della temperatura
float celsius = tmp102.getTemperature();
Serial.print("Celsius : ");
Serial.println(celsius);
delay(1000);
}
Completato il nostro sketch possiamo verificarne la sintassi, compilarlo ed eseguirlo sulla nostra board Arduino. Lanciando il "Serial Monitor", possiamo visualizzare in tempo reale i valori ricevuti a seguito delle letture continue (ogni secondo) della temperatura nella nostra stanza.
Abbiamo posto le basi per la prossima parte in cui vediamo come inviare il valore della temperatura della nostra stanza nel Cloud, facendo rientrare di fatto il nostro sistema Arduino nel vasto mondo dell'Internet of Things!