+33 (0) 9 80 65 78 95

Bonjour ! Nous sommes basés à Paris (Boutique au 16, rue Alexandre Dumas), proposons une expédition rapide partout en France et en Europe.

Contrôler et comprendre la qualité de l'air intérieur avec Arduino

Qualité de l'air avec Arduino et Grove

Il y a des fois où l'on veut pouvoir influer sur son environnement, et comprendre ce qui rend la qualité de l'air chez soi bonne ou mauvaise, quand faut-il aérer, etc. est une bonne méthode pour le faire.

Prenons un exemple concret (ce que j'ai fait), j'ai été voir sur les internets quels sont les facteurs pouvant gêner la respiration et tout simplement être nocifs à un bébé (eh oui, ce que l'on ne fait pas pour soi, on le fait pour sa progéniture :-)).

Mes recherches m'ont montré qu'il y a plusieurs facteurs environnementaux (ce que l'on pourra mesurer de façon objective avec des capteurs) à prendre en compte : la température et la circulation de l'air (facilement mesurable au taux de CO2 mesuré près du couchage).

D'autre part, je souhaitais également mesurer la présence d'autres polluants comme les particules fines dans l'atmosphère (diesel, pics de pollution, etc.) et la présence de formaldéhydes (également nocifs pour les plus petits) dégagés par nos chers meubles suédois.

Plan de bataille :

  • Idéalement, il me fallait deux centrales de mesure :
    • une dans la chambre, au plus près du lit mesurant les polluants de l'air intérieur (CO2, formaldéhydes) et la température
    • une dans le salon, mesurant la pollution en particules (PM25) et la température générale de l'appartement.

Bien que j'ai opté pour ce choix, pour cet exemple nous mettrons tout sur un seul Arduino, à vous de séparer ensuite et de peaufiner les détails selon vos goûts :)

Quelle plateforme ?

Seeeduino_Lotus_photo

J'ai opté pour une plateforme Arduino et plus particulièrement pour la Seeeduino Lotus (une Seeeduino - carte compatible Arduino - avec le Grove Base Shield intégré), mais toute autre carte Arduino (ou autre micro-contrôleur à ce compte là) conviendra et les mêmes capteurs dans leurs versions indépendantes conviendront également.

Quels capteurs ?

_MG_5661

Pour la température, un Grove Temp & Humi Sensor (DHT 11) ou Grove Temp & Humi Sensor Pro (DHT 22) nous donnent la température en degrés Celsius et le pourcentage d'humidité de façon fiable (plus fiable et précis dans le cas de la version pro, la version normale ne passe pas en dessous de 0°C).

_MG_5662

Pour la qualité de l'air, un petit capteur très pratique nous donne un indicateur global de qualité de l'air tenant compte de nombre de facteurs dont le niveau de CO2, alcool, monoxyde de carbone, formaldéhydes dans l'air : Grove Air Quality Sensor (TP-401A ou MQ 135). C'est ce senseur que j'utilise pour savoir quand aérer (trop d'air respiré, trop de polluants, etc)

_MG_5660

Pour le niveau de pollution aux particules, le Grove Dust Sensor  (ou DSM501A en version non grove) est très précis et utile pour donner une indication précise du niveau de particules dans l'air.

Comment afficher ces données ?

Dans le cas d'une installation complète, on fera remonter les informations à un serveur pour pouvoir les visualiser, les comparer, etc. Pour cet exemple, nous ferons un dispositif séparé, affichant ses valeurs sur son écran : c'est largement suffisant pour contrôler la qualité de son air et avoir une information rapide sur la marche à suivre.

Ma priorité est d'avoir un écran petit (discret) et ne diffusant pas trop de lumière pour ne pas être gênant la nuit (rétro-éclairage proscrits). Il faut néanmoins pouvoir l'activer et l'utiliser pour envoyer de la lumière, voire flasher, dans le cas où une action est nécessaire (haut niveau de pollution). Pour cela, une technologie est toute indiquée : l'afficheur OLED.

Nous choisirons plus précisément l'écran Grove Oled 96x96 pour sa connectique grove facile à connecter (presque équivalent non grove l'écran oled 128x64)

Étape 1 : Assemblage

Ce qui est pratique avec Grove, c'est qu'il suffit d'assembler les pièces :

Ou :

Puis :

_MG_5668

Étape 2 : Installation des librairies

Nous allons installer les librairies Arduino nécessaires aux capteurs utilisés :

Nous avons également préparé une archive contenant toutes ces librairies.

Pour cela décompacter les archives dans le répertoires libraries de votre "sketchbook" arduino.

Étape 3 : Écrivons notre programme

Il faut prendre les divers bouts d'exemples des librairies utilisées (pages du wiki seeed studio) :

Puis assembler tout cela dans un programme qui gère et affiche les données lorsqu'elles sont disponibles (grosso modo : copier le code de "setup", et remplacer les Serial.print par des écritures sur l'api de l'afficheur).

Commençons avec le Air Quality Sensor et le Temp & Humi Sensor :

#include <Wire.h>
#include <SeeedGrayOLED.h>
#include "AirQuality.h"
#include "Arduino.h"
#include "DHT.h"
#define DHTPIN 2     // what pin the DHT is connected to

// Uncomment whatever type you're using!
//#define DHTTYPE DHT11   // DHT 11 
#define DHTTYPE DHT22   // DHT 22  (AM2302)
DHT dht(DHTPIN, DHTTYPE);

AirQuality airqualitysensor;
int current_quality =-1;
#define BUZZER 10
void setup()
{
  Wire.begin();    // initialize i2c
  SeeedGrayOled.init();  //initialze SEEED OLED display
  SeeedGrayOled.clearDisplay();          //clear the screen and set start position to top left corner
  SeeedGrayOled.setNormalDisplay();      //Set display to normal mode (i.e non-inverse mode)
  SeeedGrayOled.setVerticalMode();    
  //SeeedGrayOled.setPageMode();           //Set addressing mode to Page Mode
  SeeedGrayOled.setTextXY(0,0);          //Set the cursor to Xth Page, Yth Column  
  SeeedGrayOled.putString("Initializing"); //Print the String

  pinMode(BUZZER, OUTPUT);
  digitalWrite(BUZZER, LOW);
  airqualitysensor.init(14);
  dht.begin();
}

void loop()
{
  // Reading temperature or humidity takes about 250 milliseconds!
  // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
  float h = dht.readHumidity();
  float t = dht.readTemperature();
  SeeedGrayOled.setTextXY(8,0);
  // check if returns are valid, if they are NaN (not a number) then something went wrong!
  if (isnan(t) || isnan(h)) 
  {
      SeeedGrayOled.putString("DHT ERROR");
  } 
  else 
  {
      SeeedGrayOled.putString("Humid: "); 
      SeeedGrayOled.setTextXY(9,0);
      SeeedGrayOled.putString("   "); 
      SeeedGrayOled.putFloat(h);
      SeeedGrayOled.putString("%"); 
      SeeedGrayOled.setTextXY(10,0);
      SeeedGrayOled.putString("Temp.: "); 
      SeeedGrayOled.setTextXY(11,0);
      SeeedGrayOled.putString("   "); 
      SeeedGrayOled.putFloat(t);
      SeeedGrayOled.putString("C");
  }

  current_quality=airqualitysensor.slope();
  if (current_quality >= 0) {
    if (current_quality <= 1)
      // Air quality is bad. Let's revert display to make some light !
      SeeedGrayOled.setInverseDisplay();
    else
      SeeedGrayOled.setNormalDisplay();

    SeeedGrayOled.setTextXY(1,0);
    SeeedGrayOled.putString("Air Quality:"); //Print the String
    SeeedGrayOled.setTextXY(2,0);
    SeeedGrayOled.putString("   "); // Empty the number line
    SeeedGrayOled.setTextXY(2,0);
    SeeedGrayOled.putNumber(airqualitysensor.first_vol); //Print the String

    SeeedGrayOled.setTextXY(0,0);
    if (current_quality==0)
    SeeedGrayOled.putString("Emergency   ");
    else if (current_quality==1)
    SeeedGrayOled.putString("Hi Pollution");
    else if (current_quality==2)
    SeeedGrayOled.putString("Lo Pollution");
    else if (current_quality ==3)
    SeeedGrayOled.putString("Fresh air   ");

    if (current_quality<1)
      digitalWrite(BUZZER, HIGH);
    else
      digitalWrite(BUZZER, LOW);
  }
}

ISR(TIMER2_OVF_vect)
{
if(airqualitysensor.counter==122)//set 2 seconds as a detected duty
{

airqualitysensor.last_vol=airqualitysensor.first_vol;
airqualitysensor.first_vol=analogRead(A0);
airqualitysensor.counter=0;
airqualitysensor.timer_index=1;
//PORTB=PORTB^0x20;
}
else
{
airqualitysensor.counter++;
}
}

Dans ce code on a repris les éléments des divers exemples et on affiche sur l'écran les valeurs. Si la pollution est élevée, on inverse l'écran (noir sur fond blanc), de façon à envoyer une lumière dans une pièce sombre. Si le niveau est critique, on active le port 10 de l'arduino (sur lequel on peut brancher un buzzer, une led puissante, etc).

La Partie "ISR(TIMER2_OVF_vect)" sert à mettre à jour les valeurs du air quality sensor, je n'ai pas touché à grand chose sur le code fourni dans l'exemple.

Basiquement, des valeur air quality de moins de 100 indiquent un air assez pur, plus de 100 c'est moyen, mais tant que cela n'est pas sur une longue période pas de problème. Dans une chambre non aérée toute une nuit, cela peut monter à 300-400, ce qui est très mauvais : il faut aérer.

Ajoutons à présent le dust sensor à tout cela (j'ai mis en gras les lignes nouvelles) :

#include <Wire.h>
#include <SeeedGrayOLED.h>
#include "AirQuality.h"
#include "Arduino.h"
#include "DHT.h"
#define DHTPIN 2     // what pin the DHT is connected to

// Uncomment whatever type you're using!
//#define DHTTYPE DHT11   // DHT 11 
#define DHTTYPE DHT22   // DHT 22  (AM2302)
DHT dht(DHTPIN, DHTTYPE);

AirQuality airqualitysensor;
int current_quality =-1;
#define BUZZER 10

int dust_pin = 4;
unsigned long duration;
unsigned long starttime;
unsigned long sampletime_ms = 30000;//sampe 30s ;
unsigned long lowpulseoccupancy = 0;
float ratio = 0;
float concentration = 0;

void setup()
{
  Wire.begin();    // initialize i2c
  SeeedGrayOled.init();  //initialze SEEED OLED display
  SeeedGrayOled.clearDisplay();          //clear the screen and set start position to top left corner
  SeeedGrayOled.setNormalDisplay();      //Set display to normal mode (i.e non-inverse mode)
  SeeedGrayOled.setVerticalMode();    
  //SeeedGrayOled.setPageMode();           //Set addressing mode to Page Mode
  SeeedGrayOled.setTextXY(0,0);          //Set the cursor to Xth Page, Yth Column  
  SeeedGrayOled.putString("Initializing"); //Print the String
  
  pinMode(BUZZER, OUTPUT);
  digitalWrite(BUZZER, LOW);
  airqualitysensor.init(14);
  dht.begin();
  
  pinMode(dust_pin,INPUT);
  starttime = millis();//get the current time;
}

void loop()
{
  // Reading temperature or humidity takes about 250 milliseconds!
  // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
  float h = dht.readHumidity();
  float t = dht.readTemperature();
  SeeedGrayOled.setTextXY(8,0);
  // check if returns are valid, if they are NaN (not a number) then something went wrong!
  if (isnan(t) || isnan(h)) 
  {
      SeeedGrayOled.putString("DHT ERROR");
  } 
  else 
  {
      SeeedGrayOled.putString("Humid: "); 
      SeeedGrayOled.setTextXY(9,0);
      SeeedGrayOled.putString("   "); 
      SeeedGrayOled.putFloat(h);
      SeeedGrayOled.putString("%"); 
      SeeedGrayOled.setTextXY(10,0);
      SeeedGrayOled.putString("Temp.: "); 
      SeeedGrayOled.setTextXY(11,0);
      SeeedGrayOled.putString("   "); 
      SeeedGrayOled.putFloat(t);
      SeeedGrayOled.putString("C");
  }
  
  // Checking Air Quality 
  current_quality=airqualitysensor.slope();
  if (current_quality >= 0) {
    if (current_quality <= 1)
      // Air quality is bad. Let's revert display to make some light !
      SeeedGrayOled.setInverseDisplay();
    else
      SeeedGrayOled.setNormalDisplay();
      
    SeeedGrayOled.setTextXY(1,0);
    SeeedGrayOled.putString("Air Quality:"); //Print the String
    SeeedGrayOled.setTextXY(2,0);
    SeeedGrayOled.putString("   "); // Empty the number line
    SeeedGrayOled.setTextXY(2,0);
    SeeedGrayOled.putNumber(airqualitysensor.first_vol); //Print the String
    
    SeeedGrayOled.setTextXY(0,0);
    if (current_quality==0)
    SeeedGrayOled.putString("Emergency   ");
    else if (current_quality==1)
    SeeedGrayOled.putString("Hi Pollution");
    else if (current_quality==2)
    SeeedGrayOled.putString("Lo Pollution");
    else if (current_quality ==3)
    SeeedGrayOled.putString("Fresh air   ");
    
    if (current_quality<1)
      digitalWrite(BUZZER, HIGH);
    else
      digitalWrite(BUZZER, LOW);
  }
  
  // Checking Dust Sensor
  duration = pulseIn(dust_pin, LOW);
  lowpulseoccupancy = lowpulseoccupancy+duration;

  if ((millis()-starttime) > sampletime_ms) //if the sample time == 30s
  {
    ratio = lowpulseoccupancy/(sampletime_ms*10.0);  // Integer percentage 0=>100
    concentration = 1.1*pow(ratio,3)-3.8*pow(ratio,2)+520*ratio+0.62; // using spec sheet curve
    /*Serial.print(lowpulseoccupancy);
    Serial.print(",");
    Serial.print(ratio);
    Serial.print(",");
    Serial.println(concentration);*/
    SeeedGrayOled.setTextXY(4,0);
    SeeedGrayOled.putString("Particles:");
    SeeedGrayOled.setTextXY(5,0);
    SeeedGrayOled.putString("    ");
    SeeedGrayOled.setTextXY(5,0);
    SeeedGrayOled.putNumber(concentration);
    lowpulseoccupancy = 0;
    starttime = millis();
  }
}

ISR(TIMER2_OVF_vect)
{
if(airqualitysensor.counter==122)//set 2 seconds as a detected duty
{

airqualitysensor.last_vol=airqualitysensor.first_vol;
airqualitysensor.first_vol=analogRead(A0);
airqualitysensor.counter=0;
airqualitysensor.timer_index=1;
//PORTB=PORTB^0x20;
}
else
{
airqualitysensor.counter++;
}
}

On a juste ajouté les lignes présentes dans l'exemple fourni par le distributeur et affiché une valeur importante.

La concentration en particules varie beaucoup entre les lectures. Un temps de plus de 30 secondes peut permettre de meilleures valeurs. Un lissage aussi. Mais cela donne déjà un bon aperçu de la qualité de l'air en terme de pollution. (Exemple : pendant les pics de pollution à Paris, cette valeur peut monter à plus de 6000/7000. Un air pur à la campagne - hors printemps et pollens - dépasse rarement les 500. A l'heure exacte où j'écris cet article, le capteur affiche 1500).

Avec le temps vous apprendrez à comprendre ce qui fait monter les valeurs. Par exemple, buvez une bière forte et respirez à coté de l'Air Quality Sensor, ou faites votre menage avec des produits chimiques et voyez la qualité de l'air baisser considérablement, aérez et comparez, etc.

_MG_5666

Quelles évolutions ?

On peut imaginer envoyer les données pour en faire des statistiques, ou même contrôler une ventilation chez soi. On peut également mettre un plus grand écran et afficher des images ou avoir une interface tactile. En bref : tout est possible. N'hésitez pas à partager vos variantes et idées dans les commentaires.

Qui sait ? Si ce projet intéresse suffisamment de monde, on pourrait, chez HackSpark, être tentés d'en faire un produit fini prêt à être utilisé par tout un chacun, et ce en open hardware :)

Laisser un commentaire