Sensori gas e Arduino

Ciao a tutti.

Ho comprato qualche tempo fa dei sensori di gas. In particolare si tratta di:

  • MQ 3 (alcool)
  • MQ 5 (benzene)
  • MQ 7 (monossido di carbonio)

Ho pensato, quindi, di costruire un piccolo circuito che mi permetta di visualizzare i valori letti da questi sensori su un display LCD che già possedevo. Per evitare di complicare troppo il circuito ho utilizzato un’interfaccia i2c per collegare Arduino al display LCD.

Nella foto seguente il progetto realizzato.

image


I sensori

Di seguito trovate la foto dei sensori che ho utilizzato.

Come si può vedere i sensori sono già montati su delle schedine (FC-22) che semplificano molto l’utilizzo con Arduino, soprattutto per quello che riguarda le resistenze di carico (non più necessarie) e le tensioni di lavoro (ampio range di utilizzo, comunque ai 5V lavorano bene).

image

Guardando la parte posteriore delle schedine, ci si rende conto che tutte hanno 4 pin di uscita, un potenziometro trimmer e 2 led di stato.

image

I pin di uscita sono i seguenti:

  • VCC a cui va collegata alla tensione di alimentazione +5V
  • GND a cui va collegata la massa comune
  • AOUT (o anche AO) su cui viene fornito un segnale analogico in tensione compreso tra 0 e 5 volt proporzionale alla lettura del sensore
  • DOUT (o anche DO) su cui viene fornito un segnale digitale che vale 0 o 5 volt a seconda che si superi o meno una soglia predefinita attraverso il trimmer

Il trimmer, come già detto, serve a definire una soglia per cui l’uscita digitale del sensore passi allo stato alto. Quindi, se il valore della concentrazione del gas rimane sotto la soglia definita dal trimmer, si avrà un segnale basso sul pin di uscita digitale (DOUT o DO); altrimenti, se la concentrazione del gas supera la soglia definita dalla regolazione del trimmer, si leggerà un segnale alto sul pin di uscita digitale (DOUT o DO).

Dei due led nella parte posteriore, uno ricalca perfettamente il comportamento del PIN di uscita digitale. L’altro led, invece, indica semplicemente che il circuito è alimentato.

Nel mio caso utilizzeremo soltanto l’uscita analogica in quanto quello che vogliamo non è conoscere se sia stata superata una soglia predefinita, ma leggere il valore di tensione fornito sull’uscita analogica.

In questa prima versione del circuito non ho voluto cimentarmi con valori assoluti delle concentrazioni. Questo è dovuto al fatto che risulta molto complesso tarare questo sensori in assenza di un vero e proprio laboratorio. A questo proposito si può leggere al link http://blog.arduino.cc/2010/09/23/arduino-breathalyzer-calibrating-the-mq-3-alcohol-sensor/ ed ancora meglio al link http://nootropicdesign.com/projectlab/2010/09/17/arduino-breathalyzer/.


Il display LCD e l’interfaccia i2c

Il display LCD è un display LCD standard 16×2 (16 colonne e 2 righe) che ho recuperato in una fiera dell’elettronica come risultato della cannibalizzazione di un tapis roulant rotto.

Il display non viene pilotato in parallelo, ma utilizzando una piccola interfaccia i2c, che essenzialmente si occupa di trasformare gli ingressi paralleli del display LCD in un segnale adatto all’interfaccia i2c.

Come si può ben capire questo tipo di interfaccia consente di risparmiare un bel po’ di pin su Arduino, il che va sempre bene. Inoltre si potrà fare a meno del potenziometro che regola il contrasto del display dato che lo stesso è presente sulla interfaccia sotto forma di trimmer.

image

Per quanto riguarda questo tipo di interfaccia ho utilizzato una libreria che consente di utilizzarla senza particolari problemi. La libreria utilizzata viene descritta al link (http://www.dfrobot.com/wiki/index.php?title=I2C/TWI_LCD1602_Module_(SKU:_DFR0063)).

Comandi i2c

Di questa libreria useremo pochi comandi:

  • il costruttore
  • init
  • backlight
  • createChar
  • home
  • print
  • setCursor

Il costruttore dell’oggetto lcd richiede di definire l’indirizzo della scheda sul bus (che è definito sulla scheda i2c, nel nostro caso 0x27), il numero di colonne (nel nostro caso 16) ed il numero di righe (nel nostro caso 2). In maniera opzionale si può definire un quarto parametro che definisce il tipo di caratteri da usare, per mezzo delle costanti (abbastanza esplicative) LCD_5x10DOTS or LCD_5x8DOTS (di default).

LiquidCrystal_I2C lcd(0x27,16,2);

Il comando init si occupa di inizializzare il display e non ha bisogno di parametri.

 lcd.init();

Il comando backlight si occupa di accendere la retroilluminazione, per spegnerla si usa il comando noBacklight. Non ha bisogno di parametri.

 lcd.backlight();

Il comando createChar  ci permette di riempire le prime 8 locazioni CGRAM con caratteri customizzati. Necessita di un parametro intero che si riferisce alla locazione di memoria e di un array di interi che contiene il disegno (binario) del carattere customizzato.

 lcd.createChar(3, heart);

Il comando home si occupa di posizionare il cursore alla posizione zero (in alto a sinistra del display).

lcd.home();

Il comando print si occupa di scrivere sul display la stringa argomento.

lcd.print("Gas sensors");

Il comando setCursor si occupa di posizionare il cursore alla colonna e riga (in questo ordine) definite dai parametri.

 lcd.setCursor(0, 1);

Il circuito

Il circuito è molto semplice ma non sono ancora riuscito a disegnarlo su Fritzing poichè devo disegnare alcune parti non standard (tutti e tre i sensori con le loro breakout boards) e l’interfaccia i2c LCD.

Essenzialmente i piedini VCC e GND dei tre sensori e dell’interfaccia sono collegati rispettivamente a +5V e a massa.

I piedini AOUT dei sensori sono collegati rispettivamente ai piedini di ingresso analogico A0, A1, A2.

I piedini SDA ed SCL dell’interfaccia I2C sono collegati ai piedini A4 ed A5 della scheda.

Questo è tutto.


Il codice

Analisi del codice

Il codice è molto semplice.

Si esegue una prima fase di inizializzazione (praticamente del solo display) nella funzione setup.

In seguito ad ogni ciclo di loop si va a leggere il valore di tensione presente sui pin A0, A1, A2 (che l’ADC ci restituisce sotto forma di intero con valore tra 0 e 1024). Questo valore viene anche trasformato in un numero decimale che esprime i Volt letti su quei pin attraverso la formula seguente:

 vol[i]=(float)sensorValue[i]/1024*5.0;

che semplicemente moltiplica per 5 e divide per 1024 il valore che viene restituito dall’ADC di Arduino, forzandolo ad un tipo float, di modo da non perdere le cifre decimali nell’operazione. Al termine di questa operazione avremo un valore decimale compreso tra 0 e 5. Questo valore in realtà non viene usato proprio per i problemi descritti riguardo la taratura dei sensori. Trovo molto più significativo utilizzare il valore puro che proviene dall’ADC. Inoltre tale valore è più breve comodo da scrivere, di un valore decimale, data la dimensione ridotta del display.

Infine si stampa sul display il valore letto, dopo aver ripulito le posizioni in cui era contenuta la vecchia lettura.

Si aspetta quindi un secondo e si riprende il loop.

Codice completo

// LCD I2C Library available at:
// https://github.com/fdebrabander/Arduino-LiquidCrystal-I2C-library

// Libraries call
#include <Wire.h>
#include <LiquidCrystal_I2C.h>

// constant definitions
#define MQ3_pin A1
#define MQ5_pin A2
#define MQ7_pin A0

// Conditional definition of function printByte
// based on Arduino board type
#if defined(ARDUINO) && ARDUINO >= 100
#define printByte(args) write(args);
#else
#define printByte(args) print(args,BYTE);
#endif

// Definition of special characters
uint8_t bell[8] = {0x4, 0xe ,0xe, 0xe, 0x1f,0x0, 0x4};
uint8_t note[8] = {0x2, 0x3 ,0x2, 0xe, 0x1e,0xc, 0x0};
uint8_t clock[8] = {0x0, 0xe ,0x15,0x17,0x11,0xe, 0x0};
uint8_t heart[8] = {0x0, 0xa ,0x1f,0x1f,0xe, 0x4, 0x0};
uint8_t duck[8] = {0x0, 0xc ,0x1d,0xf, 0xf, 0x6, 0x0};
uint8_t check[8] = {0x0, 0x1 ,0x3, 0x16,0x1c,0x8, 0x0};
uint8_t cross[8] = {0x0, 0x1b,0xe, 0x4, 0xe, 0x1b,0x0};
uint8_t retarrow[8] = {0x1, 0x1 ,0x5, 0x9, 0x1f,0x8, 0x4};

// Creation of the global object used to 
// control LCD display through I2C interface
LiquidCrystal_I2C lcd(0x27,16,2); // set the LCD address to 0x27 
                                  // for a 16 chars and 2 line display

void setup()
{ 
 // LCD initialisation
 lcd.init();
 lcd.backlight();
 
 // creation of special characters to be used on LCD display
 lcd.createChar(0, bell);
 lcd.createChar(1, note);
 lcd.createChar(2, clock);
 lcd.createChar(3, heart);
 lcd.createChar(4, duck);
 lcd.createChar(5, check);
 lcd.createChar(6, cross);
 lcd.createChar(7, retarrow);
 
 // Position cursor at home (0, 0)
 lcd.home();
 
 // Write startup message
 lcd.print("Gas sensors");
 lcd.setCursor(0, 1);
 lcd.print(" I ");
 lcd.printByte(3); // write special character "heart"
 lcd.print(" Arduino!");
 
 // Add a delay and clean the display
 delay(5000);
 lcd.clear();
 
 // Write information on the LCD 
 lcd.home();
 lcd.print("Alc:");
 lcd.setCursor(8, 0);
 lcd.print("Ben:");
 lcd.setCursor(0, 1);
 lcd.print("CO :");
}

void loop()
{
 // Array variable containing tension measured 
 // at the analog pin on the sensor
 float vol[3];
 // Array variable containing value read 
 // at the analog pin on the sensor on a scale [0...1024]
 int sensorValue[3];
 
 // Value assignment
 sensorValue[0] = analogRead(A1);
 sensorValue[1] = analogRead(A2);
 sensorValue[2] = analogRead(A0);
 
 // Calculate tension value
 for (int i=0; i<3; i++){
 vol[i]=(float)sensorValue[i]/1024*5.0;
 }
 
 // Data presentation on the display
 lcd.setCursor(4,0);
 lcd.print(" "); //clean area
 lcd.setCursor(4,0);
 lcd.print(sensorValue[0], DEC); //write value
 lcd.setCursor(12,0);
 lcd.print(" "); //clean area
 lcd.setCursor(12,0);
 lcd.print(sensorValue[1], DEC); //write value
 lcd.setCursor(4,1);
 lcd.print(" "); //clean area
 lcd.setCursor(4,1);
 lcd.print(sensorValue[2], DEC); //write value
 
 // delay before next reading
 delay(1000);
}

18 comments

  1. Salve,
    questo progetto mi piace ed è sinceramente molto utile in casa.
    Ma come avete scelto i sensori? in base a quale criterio?
    Poi sarebbe interessante implementare il progetto con un piccola sirena che ne dite?

    "Mi piace"

    1. Salve Stefano,
      Grazie per l’apprezzamento.
      Le possibili espansioni del progetto sono molteplici, come anche i sensori di questo tipo a disposizione.
      Sinceramente ho deciso di utilizzare tre sensori della serie MQ per sperimentare con essi e cercare di capire quali fossero le possibilità e potenzialità.
      La cosa più difficile di questi sensori è riportare i valori di tensione a valori reali di concentrazione dei gas, ovvero tarare i sensori.
      Per quanto riguarda le possibilità di espansione questi sensori sono ottimi per essere integrati in una applicazione domotica. Potrebbe essere aggiunta una sirena, certo, ma anche un qualche sistema di avviso wireless (SMS o chiamata telefonica o una notifica su una app, ecc…), o anche sistemi di apertura automatica delle finestre. Tutto sta alla fantasia di chi lo realizza.
      Se decide di occuparsi dell’espansione del progetto, la prego di farmi sapere.
      A presto
      Pierluigi

      "Mi piace"

  2. Ciao Pierlu complimenti per il lavoro, mi sto cimentando da poco anch’io con i sensori di gas, ho intenzione di costruirmi una circuito per il rilevamento di GPL e CO per il mio camper. Sto aspettando che mi arrivino a giorni un MQ-2 per il GPL e un MQ7 per il monossido di carbonio, quindi ho letto con interesse il tuo articolo sperando di trovare risposte ad alcune domande fondamentali che mi sono sorte leggendo il difficile (come interpretazione) datasheet dell’MQ-07, ma così non è stato quindi provo a portele direttamente:
    nel datasheet si riporta che il funzionamento del sensore dovrebbe avvenire dopo un preriscaldamento di 48 ore, ma ho trovato indicazioni e codici che si accontentano di un ciclo di ritardo di 5 minuti, penso che dovrò adattarmi a quest’ultimi dato che l’uso in camper comporta che le centraline vengano attivate non con così largo anticipo rispetto alla partenza, cosa ne pensi?
    Sempre il datasheet indica che il sensore dovrebbe funzionare in due cicli, uno da 90 secondi dove c’è un riscaldamento della parte sensibile ed uno di 60 secondi dove avviene il rilevamento vero e proprio del gas, i sensori che riporti tu sono dotati di un circuito di interfaccia, credi che queste 2 fasi vengano gestite direttamente dall’interfaccia?
    Ho visto poi che il valore della resistenza del sensore e quindi dell’uscita analogica varia in maniera importante al variare della temperatura e dell’umidità dell’ambiente, ho quindi ordinato anche un DHT11 sensore di umidità e temperatura che intendo implementare nel circuito per correggere il responso dell’MQ-7 in funzione del cambiamento di queste, cosa ne dici?

    "Mi piace"

    1. Ciao Giorgio e scusa per il ritardo con cui ti rispondo.
      Grazie per i complimenti.
      Ormai avrai già finito il tuo circuito, ma provo a risponderti lo stesso.

      Per quanto riguarda l’uso in camper credo che una attivazione che identifichi o meno la presenza di gas nocivi possa essere considerata una applicazione “digitale” nel senso dell’output. Per dirla in parole povere: se c’è troppo gas manda un allarme, altrimenti non far nulla. In questo caso la precisione della lettura dipende solo dalla scelta di una soglia opportuna. Tieni conto che le letture si stabilizzano già dopo pochi minuti e per una applicazione ON/OFF come la tua, in realtà potresti già cominciare ad avere risultati plausibili dopo pochi secondi.

      Sinceramente non avevo fatto caso alla gestione delle fasi riscaldamento/lettura, ed effettivamente i sensori si comportano coerentemente, per cui non ho trovato alcuna possibilità di identificare le fasi dalle breakout board in mio possesso. Inoltre le letture risultano coerenti, per cui credo che la gestione avvenga come dici tu, tramite la breakout board, semplificando moltissimo la gestione del tutto.

      Hai ragione a puntualizzare la questione umidità/temperatura, ed in effetti questa sarebbe stata la prossima evoluzione del mio progetto. Anche qui però credo che sia importante guardare all’applicazione che vuoi realizzare. Nel tuo caso si tratta di un ON/OFF, per cui probabilmente ti basterebbe abbassare la soglia di rilevamento gas sotto il valore minimo della curva per ottenere una risposta affidabile. Questo potrebbe provocare falsi positivi, certo, ma visto il rischio associato ad un mancato rilevamento, credo che questa sia la strada migliore.
      Se invece vuoi andare avanti con il tuo progetto, non ho sentito parlare benissimo del DHT11, molti preferiscono il modello superiore (DHT21 o DHT22, non ricordo in questo momento).

      Ancora in bocca al lupo con il tuo progetto e fammi sapere come va.

      Ciao
      Pierluigi

      "Mi piace"

  3. ciao Pierluigi, ti ho letto con qualche gg di ritardo 🙂 grazie per la tua risposta, in effetti la mia applicazione andrebbe bene come ON/OFF e non avevo pensato ad utilizzare la curva peggiore, ottimo consiglio. Ieri mi sono arrivati i componenti per ciò inizierò a giocarci appena ho un po’ di fiato. E’ vero, non parlano molto bene del DHT11 e nei forum suggeriscono un DHT22 o meglio un Dallas, il DS18S20, ma ormai l’ho preso e ne verificherò la precisione.

    Piace a 1 persona

  4. Non capisco il perchè i miei 3 sensori inviano i 3 valori, che dovrebbero essere intorno allo zero standard, e raggiungono i 200

    "Mi piace"

  5. Ciao Pierluigi e tutti voi,
    io ho realizzato un circuito con un Arudino Uno+Ethernet Shield+1 Powerline (per il collegamento(e rilevazione) del tutto via Web) dei sensori digitali Dallas ds18b20 (collegati in serie per rilevare le temperature di vari ambienti), un sensore MQ4 per rilevazione concentrazioni di metano e dei sensori di luminosità per rilevare la stessa in vari ambienti…
    Non ho utilizzato alcun display (visto che la cassetta di derivazione su cui ho collegato il tutto è i un posto abbastanza difficile da raggiungere per cui sarebbe inutile), ma leggo tutti i dati istantanei rilevati tramite il sito EmonCms.
    Devo dire che sono molto soddisfatto visto che è da fine febbraio che rilevo costantemente i valori monitorando vari ambienti domestici.

    Ho appena acquistato due TGS2602 della Figaro per rilevare i Voc e non appena avrò collegato anche questi vi darà info sull’evoluzione 🙂
    Comunque complimenti per il progetto…

    "Mi piace"

    1. Ciao Pietro,
      mi aggancio al tuo post in quanto dovrei fare anch’io un misuratore per VOC. Mi potresti aggiornare sull’evoluzione del tuo progetto con il TGS2602?
      Immagino che tu abbia dovuto lavorare, dal punto di vista hardware, per compensare il sensore in temperatura e dal punto di vista software, per mediare i valori letti su un periodo medio lungo.

      Poi avrei un’altra domanda: come hai fatto a calibrare i singoli sensori della tua scheda? Hai dovuto sottoporli a miscele di gas note?
      Ti ringrazio!

      "Mi piace"

      1. Infatti, questa è la domanda…. infatti (pr quello che riguarda il benzene) le sogli di legge fanno riferimento a parti per miliardo (5 ug/m3 =la soglia di media annua che equivale a 0.0015661 ppm (in volume)). Volevo avere la vs conferma che questi sensori NOn possono essere utilizzati per studi ‘epidemiologici’ fai da te low cost ma solo come allarmi…. vi torna ?

        "Mi piace"

  6. Scusate, ho visto solo ora il thread.
    Pensate che questo sistema possa avere una qualche validità dal punto di vista della realizzazione di uno studio epidemiologico a basso costo ? Mi spiego meglio. La misurazione del MQ-5 (sono interessato al benzene ed ai cov) è affidabile per ambienti esterni (balconi dei palazzi)?Quanto potrebbe essere il costo complessivo dell’unità di misura?

    Grazie
    Gcarlo

    "Mi piace"

  7. Salve, avrei bisogno di un aiuto per dei codici che mi danno problemi.
    Premetto che il mio sensore gas è un MQ2 e un LCD I2C 16×2 con arduino, funziona tutto e rileva il gas il problema sta nell’LCD, appare “rilevamento gas” nella prima riga, ma nella seconda riga appare zero fisso e non da valori rilevati. Metto i miei codici, cortesemente mi correggete l’errore, grazie.

    //Moreno Radi
    #include
    #include

    LiquidCrystal_I2C lcd(0x3f,16, 2);
    const int sensorPin= A0;
    const int buzzerPin= 13;
    int smoke_level;

    void setup()
    {
    lcd.init();
    lcd.backlight();
    lcd.setCursor(0, 0);
    lcd.print(“Rilevamenti GAS”);
    lcd.setCursor(0, 1);
    lcd.print(smoke_level);
    Serial.begin(9600); //sets the baud rate for data transfer in bits/second
    pinMode(sensorPin, INPUT);//the smoke sensor will be an input to the arduino
    pinMode(buzzerPin, OUTPUT);//the buzzer serves an output in the circuit
    }

    void loop() {
    Serial.println(smoke_level);//prints just for debugging purposes, to see what values the sensor is picking up
    smoke_level= analogRead(A0); //arduino reads the value from the smoke sensor
    if(smoke_level > 200){ //if smoke level is greater than 200, the buzzer will go off
    digitalWrite(buzzerPin, HIGH);
    }
    else{
    digitalWrite(buzzerPin, LOW);
    }
    }

    "Mi piace"

    1. Ciao, in realtà è molto semplice, anche nella funzione loop devi scrivere le istruzioni di stampa su LCD, altrimenti lo fa solo durante il setup.
      lcd.setCursor(0,1);
      lcd.print(smoke_level);

      "Mi piace"

    1. Ciao Fil… Mi hai beccato. Sono un refuso, le avevo utilizzate in fase di debug per controllare il valore di tensione, ma nel programma finale avrei dovuto eliminarle.
      Grazie per il commento

      "Mi piace"

Lascia un commento

Questo sito utilizza Akismet per ridurre lo spam. Scopri come vengono elaborati i dati derivati dai commenti.