Creare un sensore con Arduino Yun

Questo tutorial vuole descrivere la creazione di un sensore con il microcontrollore Arduino YUN. È possibile, a meno che il sensore non sia stato spento, aprire il client web di demo.

E” anche possibile monitorare lo stream, in real time, sullo user portal.

Introduzione ad Arduino YUN

Arduino YÚN è il primo di una nuova linea innovativa di prodotti wifi che combinano la potenza di Linux con la facilità d’uso di Arduino. E’ la combinazione di un classico Arduino Leonardo (basato sul processore ATMEGA32U4) con un sistema-one-chip WiFi chiamato Linino (una CPU MIPS GNU che esegue una distribuzione Linux basata su OpenWRT). La componente ATMEGA è in grado di eseguire gli sketch (gli script di Arduino) classici e di accedere ai PIN a cui vengono connessi i sensori ma non ha accesso diretto alle componenti di rete (Ethernet e WiFI, alla porta USB di input e alla MicroSD Card). Viceversa, la componente Linino ha accesso alle nuove feature ma non può accedere direttamente alla parte sensori. Le due anime di YUN possono comunicare fra loro, per scambiarsi dati, tramite un bridge seriale. In tal modo è possibile, ad esempio, leggere i dati dei sensori tramite uno sketch eseguito sul processore ATMEGA e inviarsi via WIFI tramite Linino. La componente bridge deve essere utilizzata in modo corretto per non introdurre colli di bottiglia nell’architettura del sensore.

../../../_images/Sensore_Arduino1.png

Costruzione del sensore

Il sensore in costruzione vuole leggere e inviare a YUCCA i dati relativi a temperatura, umidità, luminosità e di pressione (on/off) di un pulsante. Per la creazione del sensore sono necessarie le seguenti componenti elettroniche:

  • una scheda Arduino YUN con cavo microUSB di alimentazione e reativo alimentatore
  • un sensore con fotoresistenza
  • uno switch aperto/chiuso
  • un sensore di umidità DHT11
  • una breadboard per la creazione di prototipi
  • alcuni cavetti colorati per prototipazione su breadboard

I sensori sono stati recuperati dal 37 kit sensor disponibile su Amazon o nei principali negozi di elettronica. Il progetto può essere costruito anche partendo da sensori di altri produttori ma, in tal caso, sarà necessario modificare lo schema di cablatura rispetto a quanto riportato in questa demo.

Utilizzando le componenti precedenti e, facendo molta attenzione alla polarità dei collegamenti, riprodurre il seguente circuito elettrico:

  • Switch digitale:
    • collegare il pin”-” al pin GND dell’arduino
    • collegare il pin”S” al pin Digital 10 dell’arduino
    • collegare il pin “+” al pin +5V dell’arduino
  • Sensore analogico di luminosità (fotoresistenza)

    • collegare il pin”-” al pin GND dell’arduino
    • collegare il pin”S” al pin A5 (input analogico) dell’arduino
    • collegare il pin “+” al pin +5V dell’arduino
  • Sensore digitale DHT11 (temperatura e luminosità)
    • collegare il pin”-” al pin GND dell’arduino
    • collegare il pin”S” al pin Digital 8 dell’arduino
    • collegare il pin “+” al pin +5V dell’arduino

Lo schema seguente mostra un dettaglio dei collegamenti da eseguire

../../../_images/Sensore_Arduino2.png

Programmazione dell’arduino YUN

NOTA: in questo tutorial si fa uso di script “standard” arduino. Non si è fatto uso di programmi in linguaggio python eseguiti lato linux.

Gli script di arduino si chiamano sketch; sono scritti in un dialetto derivato dal linguaggio C e possono essere estesi tramite librerie scritte nei linguaggi C e C++. Nella scrittura degli sketch bisogna fare molta attenzione alla quantità di memoria utilizzata: è limitata e si satura facilmente. Per tale motivo, se non è strettamente indispensabile, si suggerisce di limitare l’utilizzo di librerie esterne che, se usate solo in parte, concorrono a sprecare risorse (ad esempio, se si devono scrivere dei messaggi JSON molto semplici che non devono essere elaborati pesantemente, è molto più semplice e meno oneroso gestirli tramite i normali comandi di elaborazione stringhe al posto di utilizzare librerie object oriented, come aJSON, che hanno prestazioni inferiori e occupano più memoria. L’utilizzo di tali librerie è suggerito solo nel caso in cui sia necessario implementare e, soprattutto, elaborare pesantemente messaggi JSON molto complessi). Uno sketch può eseguire comandi su linino tramite l’interfacciamento con il BRIDGE.

Anatomia di uno sketch

Uno sketch richiede la presenza obbligatoria di due funzioni:

  • Setup: è la funzione di inizializzazione dello script. Viene eseguita una sola volta all’avvio dello sketch. Al suo interno vengono inizializzati gli oggetti da utilizzare all’interno del loop principale, le variabili, le modalità di accesso ai PIN di connessione ai sensori e vengono eseguite tutte le attività, una tantum, necessarie all’avvio dell’arduino.
  • Loop: è il ciclo principale di gestione dell’arduino. Le istruzioni presenti al suo interno sono eseguite in sequenza (dalla prima all’ultima) in un loop infinito. Al termine dell’esecuzione dell’ultima istruzione del loop, l’arduino ricomincia con la prima. All’interno di questo loop vengono gestiti i sensori.
../../../_images/Sensore_Arduino3.png

Per semplificare la scrittura della funzione di setup e del loop principale si suggerisce di creare delle function di supporto esterne ad esse.

Impostazione dei PIN

L’arduino YUN dispone di 6 PIN analogici (A0,… A5) in sola lettura e di 14 pin analogico/digitali impostabili sia in lettura che in scrittura (0..13). Essendo i pin analogici (A0… A5) solo in input, non è necessario impostarli.

I pin analogico/digitali (0..13) possono essere impostati in lettura o in scrittura tramite il comando pinmode:

  • Impostazione di un pin in lettura: pinmode(pin, INPUT)
  • Impostazione di un pin in scrittura: pinmode(pin, OUTPUT)

nella nostra demo è necessario impostare, il pin 10, a cui è associato lo switch, in modalità INPUT, con il comando pinmode(10, INPUT), in modo da poterne leggere il valore. Tale impostazione viene fatta nella funzione di setup. Non è necessario impostare i pin del sensore DHT11 in quanto gestito da una sua libreria e il pin del sensore di luminosità in quanto essendo connesso ad un pin analogico è in sola lettura.

  • I pin analogico/digitali (0..13) possono essere letti e scritti in modalità digitale con i comandi:
    • digitalWrite(pin, HIGH) – imposta il valore HIGHT o ‘1’ logico
    • digitalWrite(pin, LOW) – imposta il valore LOW o ‘0’ logico
    • digitalRead(pin) – legge il valore. Restituisce HIGH o LOW
  • I pin analogico/digitali (0..13) possono essere letti e scritti in modo “analogico” tramite i comandi:

    • analogWrite(pin, valore) – imposta il valore del pin in un range da 0 a 1023.
    • analogRead(pin) – legge il valore del pin e riceve un valore da 0 a 1023
  • I pin analogici (a0..a5) possono solo essere letti con il comando analogRead.

Programmiamo il sensore

La programmazione del sensore richiede i seguenti passaggi:

../../../_images/Sensore_Arduino4.png

Lettura dei dati dei sensori:

la lettura dello switch richiede di verificare il valore del pin digitale 10. Lo switch è di tipo “sempre chiuso” quindi restituisce valore LOW quando è premuto e valore HIGHT quanto è rilasciato. Si definisce quindi una function getSwitch che, tramite il comando digitalRead, restituisce il valore dell’interruttore in modo corretto (HIGHT quando è premuto e LOW quando è rilasciato).

int swPin = 10;

// This function get switch Value: 1 = pressed, 0 = released int getSwitch() {

int readVal = 1 - digitalRead(swPin); return readVal;

}

la lettura del sensore di luminosità richiede di leggere il pin analogico a5. Il sensore è una fotoresistenza per cui, più alta è la luminosità, minore sarà la tensione restituita. Il range di valori restituito è compreso fra 0 (luminosità massima) e 1023 (nessuna luminosità). Per semplicità si è deciso di inviare alla piattaforma il valore letto senza prima convertirlo in LUX. Tramite l’utilizo del CEP, successivamente, la piattaforma convertirà tale valore in una percentuale. Si definisce la funzione getLuminosity che legge il sensore e ne restituisce il valore:

int lumPin = A5; // This function get luminosity value (0 - 1023) from photoresistor int getLuminosity() {

int readVal = analogRead(lumPin); return readVal;

}

La lettura dei valori di temperatura e umidità, richiede la decodifica, bit per bit, dei segnali inviati dal sensore. Per fortuna, il produttore del sensore, ha rilasciato una libreria free in grado di gestire il DHT11. Tale libreria è scaricabile presso il sito del produttore ma è stata inclusa nei sorgenti dei questa demo. La lettura dei valori richiede l’istanziazione della libreria, la dichiarazione dell’oggetto DHT11 e la letura dei suoi valori. La lettura del valore viene eseguita nel loop principale.

#include <dht11.h>
dht11 DHT; int chk; chk = DHT.read(DHpin); // READ DATA hum = DHT.humidity; temp = DHT.temperature;

Costruzione del messaggio JSON

Per poter utilizzare il sensore, è necessario censirlo sulla platform. Creare un sensore avente uno stream con i seguenti valori:

  • temperatura di tipo double;
  • luminosita di tipo double;
  • umidita di tipo double;
  • switch di tipo int.

se non sai come censire uno smart object e uno stram, vedi questo tutorial. Se vuoi utilizzare il sensore utilizzare il sensore usato nella costruzione della demo, vai al seguente link. Il messaggio JSON utilizzato dal sensore è il seguente:

{

«stream»: «environment», «sensor»: «922c0438-9dfd-4ce2-fd3c-b17960b189cb», «values»: [ {

«time»: «2015-03-17T13:21:16Z», «components»: {

«temperatura»: «22.2», «umidita»: «31.2», «switch»: «0», «luminosita»: «271»

} } ]

}

essendo questo messaggio molto semplice, per generarlo, si è fatto utilizzo delle normali funzioni di generazione delle stringhe. E’ stata quindi creata una funzione buildMessage che riceve in input i valori rilevati dai sensori e restituisce in output il messaggio formattato. La funzione è la seguente:

String buildMessage(int temp, int humidity, int luminosity, int sw) {
String msg = «»;
// create JSON message msg += «{«stream»: «environment», «sensor»: «922c0438-9dfd-4ce2-fd3c-b17960b189cb»,»values»: [{«time»: «»; msg += getTimeStamp(); msg += «», «components»: {«temperatura»:»; msg += String(temp, DEC); msg += «, «umidita»:»; msg += String(humidity, DEC); msg += «, «switch»:»; msg += String(sw, DEC); msg += «, «luminosita»:»; msg += String(luminosity, DEC); msg += «},»validity»: «valid»}]}»; return msg;

}

Invio del messaggio a YUCCA

Tramite arduino YUN è possibile inviare, a YUCCA, i messaggi in tre modalità distinte:

  • Utilizzo del protocollo HTTP;
  • Utilizzo di MQTT tramite la libreria PubSub;
  • Utilizzo di MQTT tramite Mosquitto Client.

Ognuna delle modalità ha i suoi vantaggi e svantaggi. Se non si hanno esigenza particolari si suggerisce di utilizzare l’invio tramite la libreria PubSub

Invio tramite protocollo HTTP

L’invio tramite protocollo HTTP richiede l’utilizzo del bridge e l’utilizzo di Linino in quanto la componente ATMeta non è in grado di utilizzare questo protocollo. La comunicazione HTTP avviene tramite il comando CURL di linux che viene eseguito tramite la funzione runShellCommand() di Arduino. La creazione della command line per CURL viene implementata tramite le normali funzioni di elaborazione delle stringhe. L’utilizzo del Bridge e della command line può creare problemi prestazionali per cui si suggerisce di non utilizzare questa modalità quando si devono inviare molti messaggi in meno di un secondo.

Si è quindi creata la funzione stpSendHTPP che dopo aver ricevuto in input il messaggio JSON e le credenziali provvede ad eseguire l’invio dello stesso a YUCCA:

void sdpSendHTTP(String msg, String user, String pwd){
Process p; String curlCMD; String credenziali; credenziali = user + «:» + pwd; // create CURL command curlCMD = «curl -H «Content-Type: application/json»«; curlCMD += » -u » + credenziali; curlCMD += » -X POST -d “»; curlCMD += msg; curlCMD += «” http://stream.smartdatanet.it/api/input/smartlab»; p.runShellCommand(curlCMD);

Invio tramite MQTT e libreria PubSub

PubSub è una libreria open source in grado di eseguire una chiamata MQTT e di supportare tutti i principali QOS del protocollo. L’arduino YUN non è in grado di funzionare con la libreria ufficiale in quanto richiede una patch per la gestione di messaggi di dimensione maggiore. Sulla rete è disponibile una versione di PubSub specifica per l’arduino YUN. Tale libreria è scaricabile da GIT Hub ed è pure allegata ai sorgenti della demo. PubSub utilizza la libreria YUNClient per creare una connessione TCP/IP sulla quale inviare i messaggi MQTT. Non utilizzando funzioni specifiche di Linino le prestazioni sono molto buone e, nei nostri test, siamo riusciti a inviare senza cali prestazionali alcune decine di messaggi al secondo.

L’invio dei dati tramite MQTT richiede i seguenti passaggi:

  • importare le librerie necessarie:

    #include <PubSubClient.h> #include <YunClient.h>

  • definire le code e istanziare il client MQTT

    #define MQTT_HOST «stream.smartdatanet.it» char TOPIC[] = «input/smartlab»; YunClient yun; PubSubClient client(MQTT_HOST, 1883, callback, yun);

in questa demo la funzione di callback è stata lasciata vuota in quanto al di fuori dello scopo dimostrativo:

void callback(char* topic, byte* payload, unsigned int length) { }
  • eseguire la connessione (nella funzione startup) al server MQTT

client.connect(«arduinoClient», «user», «password»);

e inviare i dati a YUCCA tramite la funzione sendMQTT da noi creata:

sendMQTT(buildMessage(temp, hum, lum, s));

La funzione sendMQTT, che riceve in input il messaggio JSON e lo invia a YUCCA è la seguente:

*void sendMQTT(String msg) {

int l = msg.length() + 1; char m[l]; msg.toCharArray(m, l); boolean r = client.publish(TOPIC, m);

}*

Invio dei dati tramite MQTT e Mosquitto

La componente Linino è consente di inviare messaggi MQTT tramite l’utilizzo del framework Mosquitto per il quale esiste una versione specifica per lo YUN. Per poterlo utilizzare è necessario installarlo sulla componente Linino. L’installazione del client Mosquitto richiede le seguenti operazioni:

1 – con un client SSH eseguire una connessione ad arduino (nel caso in cui il nome di default sia stato cambiato, modificare il comando di conseguenza): > ssh root@arduino.local

2 – installare il client di mosquitto con i seguenti comandi:

> opkg update > opkg install mosquitto mosquitto-client libmosquitto

A questo punto è possibile inviare i messaggi MQTT tramite command line utilizzando il comando mosquitto_pub.

A tal fine è stata creata una funzione sdpSendMosquitto che, dati in input il tenant a cui inviare i dati, il messaggio e le credenziali di accesso, invia i dati a YUCCA:

void sdpSendMosquitto(String tenant, String msg, String user, String pwd){
Process p; String command; // crea il comando CURL che invia i dati tramite un’HTTP POST command = «mosquitto_pub -h «stream.smartdatanet.it» -t «input/» + tenant +»» -u «» + user + «» -P «» + pwd + «» -m “» +msg + «”»; p.runShellCommand(command);

}

L’utilizzo del Bridge e della command line può creare problemi prestazionali per cui si suggerisce di non utilizzare questa modalità quando si devono inviare molti messaggi in meno di un secondo.

La funzione di startup e il loop principale

Per rendere funzionante la demo è necessario inserire nello sketch la funzione di startup e il loop principale. A titolo di esempio vengono riportate quelle relative alla chiamata MQTT con PubSub ma nei sorgenti della demo, sono disponibili anche gli esempi con Moquitto e HTTP.

#include <Bridge.h> #include <Console.h> #include <PubSubClient.h> #include <YunClient.h> #define MQTT_HOST «stream.smartdatanet.it» #include <math.h> #include <dht11.h> dht11 DHT;

char TOPIC[] = «input/smartlab»; YunClient yun; PubSubClient client(MQTT_HOST, 1883, callback, yun); int DHpin = 8; // Pin for humidity and temperature digital sensor byte dat [5]; // array for store humidity and temperature value int lumPin = A5; // pin for luminosity sensor int swPin = 10; // pin for switch

// setup iniziale di Arduino void setup() {

Bridge.begin(); client.connect(«arduinoClient», «user», «password»); // create MQTT client pinMode (swPin, INPUT); // set the switch pin in input mode.

}

// Loop di lettura dei dati void loop() {

int temp; int hum; int s; int lum; int chk; chk = DHT.read(DHpin); // READ DATA hum = DHT.humidity; temp = DHT.temperature; lum = getLuminosity(); // get luminosity s = getSwitch(); // get switch position
// send Data to YUCCA
sendMQTT(buildMessage(temp, hum, lum, s));

delay(5000); }

Dove scaricare i sorgenti

I sorgenti completi della demo sono disponibili su GIT Hub dove è possibile trovare:

  • le librerie utilizzate (PubSub e HDT11)
  • la demo con MQTT PubSub
  • la demo con MQTT Mosquitto
  • la demo con HTTP
  • un esempio di client di fruizione.