Micro sensor air, M5stack, PMSA003, challenge Air

De la pédagogie avec des micro capteurs sur la pollution de l’air pour que chacun puisse comprendre et s’approprier les enjeux de la qualité de l’air et favoriser ainsi les changements de comportements.

60 personnes travaillent a www.atmo-hdf.fr/ pour la région haut de France

Notre objectif est de quantifier la pollution de l’air dans le flux de circulation des voitures en velo et de choisir un itinéraire évitant la pollution mais qui ne rallonge pas trop le déplacement. Donc de réaliser un enregistreur de la pollution.

Mais que valent les micros capteurs sur l’analyse de l’air ? Que vaut les études et modèles existantes ?

Un challenge sur les micro-sensors les évalue sur ce lien……

http://www.airlab.solutions/fr/projets/microsensor-challenge

Mais aussi pour quantifier des valeurs et les partager dans un cloud……

grâce à de l’IOT

Mais rien sur le IOT et les capteurs choisis par « kanope » sur leur site

https://kanope.io/

Mais, il y a tout ici avec SDS011, HTU21DF

L’Europe pousserait les Mackers à utiliser de micro-capteurs pour quantifier la pollution ?

Mais quelle est la précision des micro-capteurs qui mesurent les gaz ?

Faut-il un étalonnage ? Avec quel protocole et ces difficiles à faire car il faut tester avec différents gaz ……

Des résultats à un challenge de micro capteurs

http://www.airlab.solutions/sites/default/files/brochure_2019_fr.pdf

http://www.airlab.solutions/fr/projets/microsensor-challenge

Des kits électroniques déjà toute faite de 2015,

Une thèse :

« Étude d’un système à base de micro-capteurs de gaz pour le suivi et la cartographie de la pollution atmosphérique »

https://hal.univ-lorraine.fr/tel-02940704/document

https://www.researchgate.net/publication/344218811_Etude_d’un_systeme_a_base_de_microcapteurs_de_gaz_pour_le_suivi_et_la_cartographie_de_la_pollution_atmospherique

https://www.researchgate.net/publication/318567495_Practical_Use_of_Metal_Oxide_Semiconductor_Gas_Sensors_for_Measuring_Nitrogen_Dioxide_and_Ozone_in_Urban_Environments

La thèse démontrent qu’il faut un étalonnage important pour certains capteurs NO2

Mais quelles sont les solutions pour mesurer la pollution avec une mobilité en velo ?

Quels sont les taux de nocivités des gazs et des particules ?

danger

si > à 25 μg/m3 en moyenne sur 24 heures pour les particules 2,5,

si > 50 μg/m3 en moyenne sur 24 heures pour les PM10.

Quelques exemples de capteurs et d IOT

Il y a un capteur PMSA003 de détection de particule chez M5stack

Mais ce capteur détermine un nombre de particules alors comment fait il pour estimer aussi une masse par m3 ? Sachant que la densité massique des particules doit etre connu pour cela !

En effet, les capteurs à filtre, mesure la masse de quantité de particule par volume d’air.

il y a un kit chez m5stack que l’on va utiliser

https://docs.m5stack.com/#/en/base/pm2.5

https://docs.m5stack.com/#/en/arduino/arduino_development

Mais le code sur la liaison serie n’est pas correcte car les valeurs sont sur un int 16 bits mais la trame est sur 8 bits.

Donc, voici un nouveau code ou la liaison serie fonctionne,

// PM2.5 module and SHT20 base to read, the current Particulate Matter concentration and temperature and humidity data.
// biblio M5stick, Exemple, M5Stack core ESP32 
#include <M5Stack.h>  //https://docs.m5stack.com/#/en/core/core2?id=schematic
#include "Free_Fonts.h" 
#include <Wire.h>
//#include <SD.h>      //https://github.com/arduino-libraries/SD/blob/master/examples/NonBlockingWrite/NonBlockingWrite.ino
#include "DFRobot_SHT20.h"
DFRobot_SHT20    sht20;
#include "SPIFFS.h"
#include "FS.h"        //https://github.com/dsiberia9s/DESKTOP_A-Explorer_File_Browser_via_Serial/blob/master/BRIDGE/BRIDGE.ino



#define TFT_GREY 0x7BEF

#define DATA_LEN 32

#define X_LOCAL 40
#define Y_LOCAL 30

#define X_OFFSET 160
#define Y_OFFSET 23


uint16_t CheckSum;
uint16_t CheckCode;

    float humd;
    float temp;

    uint8_t temp1;
    uint8_t humd1;
    
uint32_t x=0;
uint16_t x1=0;
uint8_t fileCSV[100000]={0};  //100 000octets*1seconde/4octets=>7heures  sinon utilisation de memoire flash
   
uint8_t Air_val[32]={0};
int16_t p_val[16]={0};
uint8_t i=0;


// Print the header for a display screen
void header(const char *string, uint16_t color)
{
  M5.Lcd.fillScreen(color);
  M5.Lcd.setTextSize(1);
  M5.Lcd.setTextColor(TFT_MAGENTA, TFT_BLUE);
  M5.Lcd.fillRect(0, 0, 320, 30, TFT_BLUE);
  M5.Lcd.setTextDatum(TC_DATUM);
  M5.Lcd.drawString(string, 160, 3, 4); 
}
void setup() {
  
  M5.begin();
  M5.Power.begin();
  Serial.begin(9600);
  Serial2.begin(9600, SERIAL_8N1, 16, 17);
  pinMode(10, OUTPUT);
  digitalWrite(10, 1);

  M5.Lcd.fillScreen(TFT_BLACK);     //2,0 "à 320 * 240 ILI9342C
//  M5.Lcd.setBrightness(200);               
  header("PM 2.5 IUT GEII SOISSONS", TFT_BLACK);

  sht20.initSHT20();                                  // Init SHT20 Sensor
  delay(100);
  sht20.checkSHT20();                       
/*
Serial.print("time");Serial.print(";");
Serial.print("0.3um:");Serial.print(";");Serial.print("0.5um:");Serial.print(";");Serial.print("1.0um:");Serial.print(";");
Serial.print("2.5umm:");Serial.print(";");Serial.print("5um:");Serial.print(";");Serial.print("10um:");Serial.print(";");
Serial.print("Temp");Serial.print(";");Serial.println("Humd");
*/
   //https://github.com/m5stack/m5-docs/blob/master/docs/en/api/M5Timer.md
}





#define FRONT 2

void LCD_Display_Val(void){              

     for(int i=0,j=0;i<32;i++){
        if(i%2==0){
          p_val[j] = Air_val[i];
          p_val[j] = p_val[j] <<8;
        }else{
          p_val[j] |= Air_val[i];
          j++;
        }
      
     }

// fichier CSV de données sur moniteur serie, mettre l'horodatage pour connaitre le temps d'echantillonnage
/*
Serial.print(millis());Serial.print(";");
Serial.print(p_val[8]);Serial.print(";");Serial.print(p_val[9]);Serial.print(";");Serial.print(p_val[10]);Serial.print(";");
Serial.print(p_val[11]);Serial.print(";");Serial.print(p_val[12]);Serial.print(";");Serial.print(p_val[13]);Serial.print(";");
Serial.print(temp,2);Serial.print(";");Serial.println(humd,2);
*/



//     M5.Lcd.setTextSize(FRONT);
     M5.Lcd.setTextColor(TFT_RED,TFT_BLACK);
     M5.Lcd.setCursor(X_LOCAL, Y_LOCAL , FRONT);
     M5.Lcd.print("Standard"); 

     M5.Lcd.setTextColor(TFT_WHITE,TFT_BLACK);
     
     M5.Lcd.setCursor(X_LOCAL, Y_LOCAL + Y_OFFSET, FRONT);
     M5.Lcd.print("                     ");
     M5.Lcd.setCursor(X_LOCAL, Y_LOCAL + Y_OFFSET, FRONT);
     M5.Lcd.print("PM1.0 : "); 
     M5.Lcd.print( p_val[2]); 

     M5.Lcd.setCursor(X_LOCAL, Y_LOCAL + Y_OFFSET*2 , FRONT);
     M5.Lcd.print("                     ");
     M5.Lcd.setCursor(X_LOCAL, Y_LOCAL + Y_OFFSET*2 , FRONT);
     M5.Lcd.print("PM2.5 : "); 
     M5.Lcd.print(p_val[3]); 

     M5.Lcd.setCursor(X_LOCAL, Y_LOCAL + Y_OFFSET*3, FRONT);
     M5.Lcd.print("                     ");
     M5.Lcd.setCursor(X_LOCAL, Y_LOCAL + Y_OFFSET*3, FRONT);
     M5.Lcd.print("PM10  : "); 
     M5.Lcd.print(p_val[4]); 


M5.Lcd.setCursor(200, 40, FRONT); 
M5.Lcd.print("data=");M5.Lcd.print(x);M5.Lcd.print("   ");

  

     M5.Lcd.setTextColor(TFT_RED,TFT_BLACK);
     M5.Lcd.setCursor(X_LOCAL+ X_OFFSET/4, Y_LOCAL + Y_OFFSET*4 , FRONT);
     M5.Lcd.print("Number of particles"); 

     M5.Lcd.setCursor(X_LOCAL, Y_LOCAL + Y_OFFSET*5 , FRONT);
     M5.Lcd.print("                     ");
     M5.Lcd.setTextColor(TFT_WHITE,TFT_BLACK);
     M5.Lcd.setCursor(X_LOCAL, Y_LOCAL + Y_OFFSET*5 , FRONT);
     M5.Lcd.print("0.3um : "); 
     M5.Lcd.print(p_val[8]); 

     M5.Lcd.setCursor(X_LOCAL, Y_LOCAL + Y_OFFSET*6 , FRONT);
     M5.Lcd.print("                     ");
     M5.Lcd.setCursor(X_LOCAL, Y_LOCAL + Y_OFFSET*6 , FRONT);
     M5.Lcd.print("0.5um : ");
     M5.Lcd.print(p_val[9]); 

     M5.Lcd.setCursor(X_LOCAL, Y_LOCAL + Y_OFFSET*7 , FRONT);
     M5.Lcd.print("1.0um : "); 
     M5.Lcd.print(p_val[10]); 

     M5.Lcd.setCursor(X_LOCAL + X_OFFSET, Y_LOCAL + Y_OFFSET*5 , FRONT);
     M5.Lcd.print("                     ");
     M5.Lcd.setCursor(X_LOCAL + X_OFFSET, Y_LOCAL + Y_OFFSET*5 , FRONT);
     M5.Lcd.print("2.5um : "); 
     M5.Lcd.print(p_val[11]); 

     M5.Lcd.setCursor(X_LOCAL+ X_OFFSET, Y_LOCAL + Y_OFFSET*6 , FRONT);
     M5.Lcd.print("                     ");
     M5.Lcd.setCursor(X_LOCAL+ X_OFFSET, Y_LOCAL + Y_OFFSET*6 , FRONT);
     M5.Lcd.print("5.0um : "); 
     M5.Lcd.print(p_val[12]); 

     M5.Lcd.setCursor(X_LOCAL+ X_OFFSET, Y_LOCAL + Y_OFFSET*7 , FRONT);
     M5.Lcd.print("                     ");
     M5.Lcd.setCursor(X_LOCAL+ X_OFFSET, Y_LOCAL + Y_OFFSET*7 , FRONT);
     M5.Lcd.print("10um  : "); 
     M5.Lcd.print(p_val[13]);    


     
//enregistrement en memoire fichier CSV
if (p_val[2]>255) {p_val[2]=255;}  //inutile car toujours inferieur à 255
if (p_val[3]>255) {p_val[3]=255;}
if (p_val[4]>255) {p_val[4]=255;}
if (p_val[8]>255) {p_val[8]=255;}
if (p_val[9]>255) {p_val[9]=255;}
if (p_val[10]>255) {p_val[10]=255;}
if (x<=50000)  {x=x+11;   
fileCSV[x]=p_val[2];
fileCSV[x+1]=p_val[3];      
fileCSV[x+2]=p_val[4];   
fileCSV[x+3]=temp1;
fileCSV[x+4]=humd1;
fileCSV[x+5]=p_val[8];
fileCSV[x+6]=p_val[9];
fileCSV[x+7]=p_val[10]; 
fileCSV[x+8]=p_val[11];
fileCSV[x+9]=p_val[12];
fileCSV[x+10]=p_val[13];
   }

if (M5.BtnA.pressedFor(1000)) {          //https://github.com/m5stack/m5-docs/blob/master/docs/en/api/button.md
M5.Lcd.setCursor(200, 40, FRONT);       //envoie des données sur le moniteur serie
M5.Lcd.print("Transfert");      
    
    for(int i=0,j=0; i<=x; i++)     {j++;Serial.print(fileCSV[i]);Serial.print(";");   
      if (j>=11) {Serial.println();j=0;}      
                            }
                                }//fin if bouton A


    if (M5.BtnB.isPressed()) {     //Reset memory if bouton A
    M5.Lcd.setCursor(200, 40 , FRONT);      
    M5.Lcd.printf("         ");
    x=0;}

}


void TempHumRead(void)
{   M5.update();
  
    humd = sht20.readHumidity();                  // Read Humidity
    temp = sht20.readTemperature();               // Read Temperature
    humd1=(humd);    //_byte sans virugle
    temp1=temp;
     M5.Lcd.setTextColor(TFT_GREEN,TFT_BLACK);
     M5.Lcd.setCursor(X_LOCAL, Y_LOCAL + Y_OFFSET*8, FRONT);
     M5.Lcd.print("                     ");
     M5.Lcd.setCursor(X_LOCAL, Y_LOCAL + Y_OFFSET*8 , FRONT);
     M5.Lcd.print("T M P : "); 
     M5.Lcd.print(temp);

     M5.Lcd.setCursor(X_LOCAL + X_OFFSET, Y_LOCAL + Y_OFFSET*8, FRONT);
     M5.Lcd.print("                     ");
     M5.Lcd.setCursor(X_LOCAL + X_OFFSET, Y_LOCAL + Y_OFFSET*8, FRONT);
     M5.Lcd.print("H U M : "); 
     M5.Lcd.print(humd); 
}

void loop() {


  
   if(Serial2.available()) {
      Air_val[i] = Serial2.read();   //lecture de 
 //     Serial.write( Air_val[i]);   
      i++;
   }else{
      i=0;
   }

  if(i==DATA_LEN){
    LCD_Display_Val();
    TempHumRead();
  }
}

Les données sont enregistrées dans la mémoire de l’ESP 32

Mais il serait possible de l’enregistrer dans les 16 Mégas de la mémoire flash, car dans la PSRAM, les data seront perdus si l’on a plus d’alimentation.

mais aussi avec l’enregistrement et transfert de donnée, traitée dans Excel

Nous avons monté le kit sur un velo

Il serait possible de synchroniser les données avec un GPS, ou envoyer les données par Bluetooth sur un smartphone et de synchroniser les données avec la position du velo

Si ce premier code correspond à notre objectif, il y a de nombreuses options possibles…sur ce sujet

mais il faut avoir des connaissances sur l’air et ces particules et sur les micro capteurs. :hot_face:

Chez Plantower plusieurs capteurs de particules ???

Sont-ils plus ou moins précis ? Voilà leur lien mais sans réponse à la question

http://www.plantower.com/list/?6_1.html

Un autre projet open source Airbeam qui utilise une PPD60PV-T2 analogique QUI COUTE 300€ mais qui a une bonne évaluation pour mesurer les particules

Voici le data sheet

voici les differents lien

Le Traffic routiers des moteurs thermiques provoque du NO2, donc il serait intéressant de le mesurer qui est entre 30 et 300µg/m3

Dioxyde d'azote — Wikipédia.

sur ce lien; différents types de capteurs air avec leur principe bien vulgarisé

https://www.figaro.co.jp/en/technicalinfo/principle/mos-type.html

Mais quels sont leurs prix et leurs sensibilités ?

En fonction du debit d’air sur le capteur, est ce qu’il y a une difference de mesure?

Les capteurs MOS sont en générale pas cher mais

les capteur MOS tel les MICS-4514 ou le -MiCS-6814, mesure 0.05 entre 10ppm NO2

or, 0.05 ppm/m3 de NO2 correspond à 0.05* 41 *10^-6 46 ~ 9310^-6 g/m3 = ~93 µg/m3

donc pas assez sensible pour notre objectif

un convertisseur en ligne ppm à µg/m3

https://www.herramientasingenieria.com/onlinecalc/ppm-mg_m3.php

kenet Pyroelectric Detectors le capteur est à 100euros, mais le capteur n’est pas assez sensible.

https://www.infratec-infrared.com/sensor-division/gas-analysis/

Le figaro electrochemical FECS42 est à 200 euros de (12µA pour 20 ppm) donc 1.2V dans une résistance de 100kΩ donc 0.06V/ppm en 12 bits 50decimal/ppm.

La sensibilité de ce capteur a une sensibilité très linaire mais à 1ppm en résolution

Bref, pas facile de faire un choix de capteur
De plus, pour un IOT autonome, le temps de fonctionnement et la gestion de charge doit être connue ? Sachant que la consommation du processeur est de 0.12A avec le OLED

or, le M5stack core1 basic a une batterie 0.11A.h, donc une autonomie 1 heure.

le shematic est ici

Nous avons rajouté un élément de 750mA.h au coré 1, pour avoir 8 heures de fonctionnement

Le M5stack core 2 a un axp192 ou l’on peut connaitre la tension de la batterie très précisément. le courant de charge et de décharge via une communication I2C

Ce n’est pas le cas pour M5stack core 1 qui a le régulateur IP5306 qui a une communication I2C, mais qui permet de connaitre le level charge à 25%.
on aurait pu mettre un pont diviseur pour connaitre la tension precisement

Dont voici le data sheet

http://www.injoinic.com/wwwroot/uploads/files/20200221/0405f23c247a34d3990ae100c8b20a27.pdf

La librairie qui gère IP5306 et la batterie est ici

https://docs.m5stack.com/en/api/power?id=getbatterylevel

voici la nouvelle gestion de l’afficheur

voici le nouveau code

// PM2.5 module and SHT20 base to read, the current Particulate Matter concentration and temperature and humidity data.
// biblio M5stick, Exemple, M5Stack basic core ESP32 
#include <M5Stack.h>  //https://docs.m5stack.com/#/en/core/core2?id=schematic
#include "Free_Fonts.h" 
#include <Wire.h>
//#include <SD.h>      //https://github.com/arduino-libraries/SD/blob/master/examples/NonBlockingWrite/NonBlockingWrite.ino
#include "DFRobot_SHT20.h"
DFRobot_SHT20    sht20;
#include "SPIFFS.h"
#include "FS.h"        //https://github.com/dsiberia9s/DESKTOP_A-Explorer_File_Browser_via_Serial/blob/master/BRIDGE/BRIDGE.ino



#define TFT_GREY 0x7BEF
#define DATA_LEN 32




uint16_t CheckSum;
uint16_t CheckCode;

    float humd;
    float temp;

    uint8_t temp1;
    uint8_t humd1;
    
uint32_t x=0;
uint16_t x1=0;
uint8_t fileCSV[100000]={0};  //100 000octets*1seconde/4octets=>7heures  sinon utilisation de memoire flash
   
uint8_t Air_val[32]={0};
int16_t p_val[16]={0};
uint8_t i=0;

 


// Print the header for a display screen
void header(const char *string, uint16_t color)
{
  M5.Lcd.fillScreen(color);
  M5.Lcd.setTextSize(1);
  M5.Lcd.setTextColor(TFT_MAGENTA, TFT_BLUE);
  M5.Lcd.fillRect(0, 0, 320, 30, TFT_BLUE);
  M5.Lcd.setTextDatum(TC_DATUM);
  M5.Lcd.drawString(string, 160, 3, 4); 
}
void setup() {
  
  M5.begin();
  M5.Power.begin();
  Serial.begin(9600);
  Serial2.begin(9600, SERIAL_8N1, 16, 17);
  pinMode(10, OUTPUT);
  digitalWrite(10, 1);

  M5.Lcd.fillScreen(TFT_BLACK);     //2,0 "à 320 * 240 ILI9342C
  M5.Lcd.setBrightness(255);               
  header("PM 2.5 IUT GEII SOISSONS", TFT_BLACK);

  sht20.initSHT20();                                  // Init SHT20 Sensor
  delay(100);
  sht20.checkSHT20();                       
/*
Serial.print("time");Serial.print(";");
Serial.print("0.3um:");Serial.print(";");Serial.print("0.5um:");Serial.print(";");Serial.print("1.0um:");Serial.print(";");
Serial.print("2.5umm:");Serial.print(";");Serial.print("5um:");Serial.print(";");Serial.print("10um:");Serial.print(";");
Serial.print("Temp");Serial.print(";");Serial.println("Humd");
*/
   //https://github.com/m5stack/m5-docs/blob/master/docs/en/api/M5Timer.md
}





#define FRONT 2

void LCD_Display_Val(void){              

     for(int i=0,j=0;i<32;i++){
        if(i%2==0){
          p_val[j] = Air_val[i];
          p_val[j] = p_val[j] <<8;
        }else{
          p_val[j] |= Air_val[i];
          j++;
        }
      
     }

// fichier CSV de données sur moniteur serie, mettre l'horodatage pour connaitre le temps d'echantillonnage
/*
Serial.print(millis());Serial.print(";");
Serial.print(p_val[8]);Serial.print(";");Serial.print(p_val[9]);Serial.print(";");Serial.print(p_val[10]);Serial.print(";");
Serial.print(p_val[11]);Serial.print(";");Serial.print(p_val[12]);Serial.print(";");Serial.print(p_val[13]);Serial.print(";");
Serial.print(temp,2);Serial.print(";");Serial.println(humd,2);
*/


     M5.Lcd.setCursor(0,35,2);
     M5.Lcd.setTextColor(TFT_WHITE,TFT_BLACK);
     M5.Lcd.print("PM1: "); 
     M5.Lcd.printf("%d",p_val[2]); 
     M5.Lcd.print("   PM2.5: "); 
     M5.Lcd.printf("%d",p_val[3]); 
     M5.Lcd.print("   PM10: "); 
     M5.Lcd.printf("%d",p_val[4]);
    M5.Lcd.println("    ");  




if (M5.BtnA.pressedFor(1000)) {          //https://github.com/m5stack/m5-docs/blob/master/docs/en/api/button.md
        M5.Lcd.print("Transfert");        ////envoie des données sur le moniteur serie   
    
        for(int i=0,j=0; i<=x; i++)     {j++;Serial.print(fileCSV[i]);Serial.print(";");   
        if (j>=11) {Serial.println();j=0;}      
                            }
                                } else  {M5.Lcd.print("data=");M5.Lcd.printf("%lu",x); }


    if (M5.BtnB.isPressed()) {x=0;}     //Reset memory if bouton B

M5.Lcd.println("    ");
uint8_t bat = M5.Power.getBatteryLevel();
M5.Lcd.printf("Battery Level %d", bat);M5.Lcd.print("   "); 
M5.Lcd.fillRect(120,68,116,10,RED);
M5.Lcd.progressBar(123, 70, 110, 5, bat);    //progressBar(int x, int y, int w, int h, uint8_t val);

 
M5.Lcd.setCursor(0, 100,2);   //M5.Lcd.setTextSize(7);
     M5.Lcd.setTextColor(TFT_RED,TFT_BLACK);
     M5.Lcd.println("Number of particles"); 
 
     M5.Lcd.setTextColor(TFT_WHITE,TFT_BLACK);
     M5.Lcd.setCursor(0, 120,4);   
     M5.Lcd.print("0.3um:"); M5.Lcd.printf("%d",p_val[8]);
M5.Lcd.setCursor(140, 120,4);          
     M5.Lcd.print("0.5um:");M5.Lcd.print(p_val[9]);M5.Lcd.print("   ");  
M5.Lcd.setCursor(0, 150,4);  
     M5.Lcd.print("1 um: ");  M5.Lcd.print(p_val[10]);M5.Lcd.print("  ");
M5.Lcd.setCursor(140, 150,4);   
     M5.Lcd.print("2.5um:");M5.Lcd.print(p_val[11]);M5.Lcd.print("   "); 

M5.Lcd.setCursor(0, 180,4);  
     M5.Lcd.print("5 um: "); M5.Lcd.print(p_val[12]);M5.Lcd.print("   ");
M5.Lcd.setCursor(140, 180,4); 
     M5.Lcd.print("10um: "); M5.Lcd.print(p_val[13]);M5.Lcd.print("   ");    


     
//enregistrement en memoire fichier CSV
if (p_val[2]>255) {p_val[2]=255;}  //inutile car toujours inferieur à 255
if (p_val[3]>255) {p_val[3]=255;}
if (p_val[4]>255) {p_val[4]=255;}
if (p_val[8]>255) {p_val[8]=255;}
if (p_val[9]>255) {p_val[9]=255;}
if (p_val[10]>255) {p_val[10]=255;}
if (x<=50000)  {x=x+11;   
fileCSV[x]=p_val[2];
fileCSV[x+1]=p_val[3];      
fileCSV[x+2]=p_val[4];   
fileCSV[x+3]=temp1;
fileCSV[x+4]=humd1;
fileCSV[x+5]=p_val[8];
fileCSV[x+6]=p_val[9];
fileCSV[x+7]=p_val[10]; 
fileCSV[x+8]=p_val[11];
fileCSV[x+9]=p_val[12];
fileCSV[x+10]=p_val[13];
   }




}


void TempHumRead(void)
{   M5.update();
  
    humd = sht20.readHumidity();                  // Read Humidity
    temp = sht20.readTemperature();               // Read Temperature
    humd1=(humd);    //_byte sans virugle
    temp1=temp;
     M5.Lcd.setTextColor(TFT_GREEN,TFT_BLACK);
     M5.Lcd.setCursor(0,210,4);
     M5.Lcd.print("T M P : "); 
     M5.Lcd.print(temp,1);

     M5.Lcd.setCursor(150,210);
     M5.Lcd.print("H U M : "); 
     M5.Lcd.print(humd,0); 
}

void loop() {


  
   if(Serial2.available()) {
      Air_val[i] = Serial2.read();   //lecture de   
      i++;
   }else{
      i=0;
   }

  if(i==DATA_LEN){    //DATA_LEN=32         
    LCD_Display_Val();
    TempHumRead();
  }
}
1 Like

La pollution domestique est relativement importante, d’où l’aération et la ventilation motorisé

Quelle est la pollution de la cigarette ?

Quelle est la pollution des fumées de l’encens, des bougies…. ?

Quelle est la pollution de la cuisine ?

Selon, l’organisation mondiale de la santé (OMS), 40% des maladies liées au cancer sont causées par la fumée de cigarette, 30% par la nourriture et les boissons et 10% par la qualité de l’air à proximité de chez nous

Il y a un article qui a testé différents capteurs de chez MQ sur le sujet de la fumée de cigarette mais qui donne la tension et pas l’évolution de la résistance et ne donne pas le volume d’une fumée de cigarette.

Mais quel sont les concentrations de gaz de la cigarette

https://www.researchgate.net/publication/237444303_Why_ventilation_is_not_a_viable_alternative_to_a_complete_smoking_ban

La famille des capteurs MQ est relativement grande pour détecter un certain nombre de gaz.

Certains capteurs

La data sheet du MQ2,

Pas grand choses chez le fabricant

https://www.hwsensor.com/

La valeur de R0 est du calibrage est problématique ???

Des codes intéressant

Il y a 0.77V aux bornes de la résistance de 5.1Ω donc un courant de 0.15A

Il faut 200 secondes pour stabiliser la résistance du MQ2 dans l’air classique en interieur

On a soufflé 2 fois dessus sur le capteur pour voir l’évolution de la resistance MQ2

L’équation de la résistance vs smoke correspond à la courbe suivante

La résistance et la concentration de fumée du MQ2 est donnée par l’équation suivante sachant que la résistance de charge est de 1 KOhm, on considèrera que R0=1Kohm

Pour vérifier l’évolution du capteur, on a mis une cigarette dans une bouteille de 1litre comme on peut l’observer sur la figure suivante.

le capteur de particule fine, c’est affolée pourtant, il était relativement loin de la fumée

Voici la courbe d’évolution de la resistance avec un echantillon tous les 1s

il y a une saturation à 2000ohms

Pourquoi avoir choisi une cigarette comme valeur étalon ?
Car c’est une chose que tout le monde peut reproduire facilement l’expérience

voici le code

#include <LiquidCrystal.h>

#define bouton  12
#define MQ2  A0

float valeur_brut;
float Vsmoke;      //variable voltage GAZ
float R0=1000;
float Res;
float PPM;
float PPM1;

LiquidCrystal lcd(3,8,4,5,6,7); // // LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
                    


void setup() {
 Serial.begin(9600);
 pinMode(bouton,INPUT); //bouton définit en entrée
 pinMode(MQ2, INPUT); 
 
lcd.begin(16,2); //initialisation de l'écran LCD
// lcd.begin(20,4);
//  lcd.clear();
 lcd.setCursor(0,0);
 lcd.print("IUT GEII Soissons");
//calibrage et mesure de R0
lcd.setCursor(0,1);
lcd.print("time");
 
for (int i=200; i>0; i--)     { //temps du calibrage 200*1000ms=200s 
valeur_brut = analogRead(A0); //lecture de la broche LM35 afin de déterminer la température
Vsmoke=(valeur_brut*5)/1023;         //nano
Res=(1000*(5-Vsmoke))/Vsmoke;  
delay(1000);
lcd.setCursor(5,1);
lcd.print(i);lcd.print("  ");           //affiche le compte à rebours du calibrage

//Serial.print(i);Serial.print(";");
Serial.println(Res,0);  }              //envoie des données tous les 1s

}



void loop() {
 
valeur_brut=analogRead(MQ2); //lecture de la broche LM35 afin de déterminer la température
Vsmoke=(valeur_brut*5)/1023;          //nano
Res=(1000*(5 -Vsmoke))/Vsmoke;       //resistance sensor
PPM1=(((log(Res/R0)/log(10))-(log(40)/log(10)))/(-0.45));    //log=logneperien et pas base 10
PPM=pow(10,PPM1);                                          // 
lcd.setCursor(0,1);
lcd.print("R:");
lcd.print(Res,0);
lcd.print(" ");
lcd.print("PPM");
lcd.print(PPM,0);
lcd.print("       ");
Serial.println(Res,0);
delay(1000);
}

voici la simulation qui a permis de vérifier le code

il y a saturation des particules fines de 0.3um à 5cm à uint16

1 Like

Au niveau de l’échappement d’une voiture, comment réagit les capteurs précédents

comment lire les informations des gaz d’échappement ?
http://ww2.ac-poitiers.fr/vehicules-materiels/sites/vehicules-materiels/IMG/pdf/le_catalyseur.pdf
l’ordre de grandeur est de 130 g/km correspond donc à une consommation moyenne de 4,8 l/100 km

Les valeurs de l’échappement, pour un moteur 3 cylindres essence de 2020 au ralenti avec le MQ2 et le PM sont les suivantes placés à 20 cm :
Les valeurs de la photo lorsque le moteur n’est pas encore activé.

Etant donné que la valeur de R0 du MK2 peut varier de 250Ω à 2000 Ω
D’ailleurs, on peut observer que la valeur dans un air sain est de 3650 Ω à la place de 6000 Ω sur le test precedent. donc les PPM doivent etre calibrer

Le nombre de particule à 20cm et à 5cm. on peut s’apercevoir que le nombre de particule fine est relativement important mais plus faible qu’une cigarette.

En réelle, les gaz d’échappements sont dilués à 1 m de hauteur et par le vent et que la nocivité des différents gaz d’échappement sont différents.
Mais, les PM0.3um sont entre 300 et 800 et lorsque il n’y a pas de voiture et dans le traffic des voitures à l’arret PM0.3um, les valeurs passe entre 1500 et 3000.
Bref, dans tous les cas, il y a de l’inhalation passif en velo et à l’intérieur des voitures.

1 Like

LE MICS-4514 qui mesure le No2 et le CO2 vont etre etudier

le NO2 a une resistance de charge de pull down de 22K ohms que l’on peut observer sur le shield

Quelques explications ici

http://openaccess.uoc.edu/webapps/o2/bitstream/10609/40042/6/mcolombTFG0115memoria.pdf

[file:///C:/Users/geii/Downloads/Smart_CEI_Moncloa_An_IoT-based_Platform_for_People.pdf](file:///C:\Users\geii\Downloads\Smart_CEI_Moncloa_An_IoT-based_Platform_for_People.pdf)

Le schéma du shield suivant est correcte

A partir du datasheets of Sensor, la valeur de la tension et la valeur de la résistance du capteur en fonction du PPM peut être retrouvé par les équations suivantes
mais, la valeur de R0 pour le NO2 est tres variable entre 820ohm et 20kohm donc il faut calibrer dans un air sans NO2, la valeur de R0

La courbe pour le NO2 donnée par le fabricant du capteur est la suivante ??????

Sur Excel, l’estimation du NO2 par les data du constructeur donne la courbe de tendance suivante pour avoir les PPM

Par conséquent, voici les équations pour retrouver les PPM en fonction des mesures

mais les valeurs de R0 peuvent aller de 0.82kΩ à 20kΩ ??????


Est-ce que le fabricant a donné un code ? rien trouvé sur le net !

Un programme sur ce lien donne une courbe de tendance du second degrès du NO2 ??????

Sur ce programme, pas de calibrage, pas de préchauffage ??????

sur cet autre programme, il y a le préchauffage et un calibrage du capteur……

Ce serait bien que le fabricant donne le code ! et leur procéder de test….

quel est le temps de chauffe du capteur pour avoir un calibrage stabilisé ?

Il y a des articles de recherches mais ce n’est pas clair non plus

https://www.researchgate.net/figure/MiCS-4514-calibration-curves-a-CO-concentration-b-NO-2-concentration-30_fig7_321690263

En plus, de nombreux articles disent qu’il faut recalibrer le capteur relativement souvent !…..

mais refaire un calibrage toutes les heures, tous les 24 heures, tous les mois !

avec le préchauffage des capteurs, la temperature du MQ2 atteint 60°C et celle du MICS-4514 40°C comme on peut l’observer sur la figure suivante

Etant donné que la résistance CO2, NO2, MQ2 n’est pas du tout la meme, il faut mettre des coefficients comme sur le traitement Excel suivant ou les 2 capteurs ont été soumis au test de la cigarette dans la bouteille

on peut remarquer que la résistance C02 et MQ2 diminue bien en fonction de la fumée de cigarette comme la datasheet l’indique. mais la resistance N02 diminue aussi alors qu’elle aurait du augmentée ????

Voici le code utilisé ave R0=1Kohm et sans calibrage

#include <LiquidCrystal.h>
//#include <SoftwareSerial.h>   

#define bouton  12
#define MQ2  A0
#define CO2  A1
#define NO2  A2

float valeur_brut;
float Vsmoke;      //variable voltage GAZ
float R0=1000;
float ResSm;
float ResNO;
float ResCO;

uint16_t Res16;
float PPM;
float PPM1;

uint16_t x=0;
uint16_t fileCSV[600]={0};   //SRAM 2kb   73% d'utilisation  enregistrement 10 minutes

LiquidCrystal lcd(3,8,4,5,6,7); // // LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
                    


void setup() {
 Serial.begin(9600);

//SoftwareSerial BTo(0, 1);   // RX, TX mais il n'y a pas trop le choix sur la nano
//BTo.begin(9600);

 pinMode(bouton,INPUT); //bouton définit en entrée
 pinMode(MQ2, INPUT);
 pinMode(CO2, INPUT);
 pinMode(NO2, INPUT);  
 
 
lcd.begin(16,2); //initialisation de l'écran LCD
// lcd.begin(20,4);
//  lcd.clear();
// lcd.print("IUT GEII Soissons");
//calibrage et mesure de R0

lcd.setCursor(0,1);
lcd.print("time");

/* 
for (int i=200; i>0; i--)     { //temps du calibrage 200*1000ms=200s 
valeur_brut = analogRead(A0); //lecture de la broche LM35 afin de déterminer la température
Vsmoke=(valeur_brut*5)/1023;         //nano
ResSm=(1000*(5-Vsmoke))/Vsmoke;  
delay(1000);
lcd.setCursor(5,1);
lcd.print(i);lcd.print("  ");           //affiche le compte à rebours du calibrage

//Serial.print(i);Serial.print(";");
Serial.println(Res,0);  }              //envoie des données tous les 1s
*/
}



void loop() {

if(digitalRead(bouton)==0) //Si bouton est appuyé  transfer des données
  { Serial.println("transfert");      
  for(int i=0; i<=x; i++)   {Serial.println(fileCSV[i]); }                         
    Serial.println("fin");    
    x=0;}


 
valeur_brut=analogRead(MQ2); //lecture de la broche LM35 afin de déterminer la température
Vsmoke=(valeur_brut*5)/1023;          //nano
ResSm=(1000*(5 -Vsmoke))/Vsmoke;       //resistance sensor
//Res16=ResSm;
//fileCSV[x]=Res16;   //enregistrement des données
x++;
if (x>=600) {x=600;  }

PPM1=(((log(ResSm/R0)/log(10))-(log(40)/log(10)))/(-0.45));    //log=logneperien et pas base 10
PPM=pow(10,PPM1);                                          
lcd.setCursor(0,1);
lcd.print("R:");
lcd.print(ResSm,0);
lcd.print(" ");
lcd.print("PPM");
lcd.print(PPM,0);
lcd.print("       ");


valeur_brut=analogRead(NO2); //lecture de la broche LM35 afin de déterminer la température
Vsmoke=(valeur_brut*5)/1023;          //nano
ResNO=(1000*(5 -Vsmoke))/Vsmoke;
PPM=((ResNO/R0)-0.4)/6.6;  
lcd.setCursor(0,0);
lcd.print("NO");
lcd.print(ResNO,0);

valeur_brut=analogRead(CO2); //lecture de la broche LM35 afin de déterminer la température
Vsmoke=(valeur_brut*5)/1023;          //nano
ResCO=(1000*(5 -Vsmoke))/Vsmoke;
PPM1=(((log(ResCO/R0)/log(10))-(0.54)))/(-0.841);    //log=logneperien et pas base 10
PPM=pow(10,PPM1); 
lcd.setCursor(8,0);
lcd.print("CO");
lcd.print(ResCO,0);


//traceur serie avec mise à l'echelle
Serial.print(ResSm/10,0);Serial.print(",");
Serial.print(ResNO*3,0);Serial.print(",");
Serial.print(ResCO/50,0);Serial.println(",");
//envoie des données en bluetooth 
/*if (BTo.available()){
BTo.print("*S"+String(ResSm)+"*");    
BTo.print("*N"+String(ResNO)+"*");
BTo.print("*C"+String(ResCO)+"*");
BTo.println();
}
*/
delay(1000);
}


1 Like

Avec le préchauffage de 30s, le MICS-4514 a le résultat suivant lorsqu’il est soumis à une fumée de bâton d’encens, le NO2 ne réagit pas du tout à la fumée à cette fumée ???

Capture1

#include <Wire.h>
#include <Adafruit_Sensor.h>
#include "Adafruit_BME680.h"  
#include <LiquidCrystal.h>


#define SEALEVELPRESSURE_HPA (1013.25)
#define MPU_ADDRESS 0x77   //adresse 0x77


Adafruit_BME680 bme; // I2C
LiquidCrystal lcd(3,8,4,5,6,7); // // LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

#define bouton  12
#define preHeaterNO 11
#define MQ2  A0
#define CO2  A1
#define NO2  A2


float valeur_brut;
float Vsmoke;      //variable voltage GAZ
float R0=1000;
float ResSm;
float ResNO;
float ResCO;
uint16_t temps=0;

uint16_t Res16;
float PPM;
float PPM1;

uint16_t x=0;
uint16_t fileCSV[200]={0};   //SRAM 2kb   73% d'utilisation  enregistrement 10 minutes





//*************************************
void setup() {
 pinMode(bouton,INPUT); //bouton définit en entrée
 pinMode(MQ2, INPUT);
 pinMode(CO2, INPUT);
 pinMode(NO2, INPUT); 
 pinMode(preHeaterNO, OUTPUT); 
  
  lcd.begin(16,2); //initialisation de l'écran LCD
  Serial.begin(9600);
// lcd.print("IUT GEII Soissons");  
  while (!Serial);
  Serial.println(F("BME680 test"));


  if (!bme.begin()) {                   //tres important avant le calibrage
    Serial.println("not valid BME680 sensor");
    while (1);
  }

  // Set up oversampling and filter initialization
  bme.setTemperatureOversampling(BME680_OS_8X);  //suréchantillonnage
  bme.setHumidityOversampling(BME680_OS_2X);
  bme.setPressureOversampling(BME680_OS_4X);
  bme.setIIRFilterSize(BME680_FILTER_SIZE_3);   //filtre IIR interne
  bme.setGasHeater(320, 150); //prechauffage 320°C for 150 ms
}

void loop() {
  if (! bme.performReading()) {
    Serial.println("Failed to perform reading :(");
    return;
  }

valeur_brut=analogRead(MQ2); //lecture de la broche LM35 afin de déterminer la température
Vsmoke=(valeur_brut*5)/1023;          //nano
ResSm=(1000*(5 -Vsmoke))/Vsmoke;       //resistance sensor

PPM1=(((log(ResSm/R0)/log(10))-(log(40)/log(10)))/(-0.45));    //log=logneperien et pas base 10
PPM=pow(10,PPM1);                                          
lcd.setCursor(0,1);
lcd.print("R:");
lcd.print(ResSm,0);
lcd.print(" ");
lcd.print("PPM");
lcd.print(PPM,0);
lcd.print("       ");


if (temps<30)  {   //prechauffage de 30s
digitalWrite(preHeaterNO,1);                   
valeur_brut=analogRead(NO2); //lecture de la broche LM35 afin de déterminer la température
Vsmoke=(valeur_brut*5)/1023;          //nano
ResNO=(1000*(5 -Vsmoke))/Vsmoke;
//PPM=((ResNO/R0)-0.4)/6.6;  
lcd.setCursor(0,0);
lcd.print("heatNO");
lcd.print(temps);}

if (temps>30)  {
digitalWrite(preHeaterNO,0);    
lcd.setCursor(0,0);
lcd.print("NO");
lcd.print(ResNO,0);
lcd.print("   ");
}

valeur_brut=analogRead(CO2); //lecture de la broche LM35 afin de déterminer la température
Vsmoke=(valeur_brut*5)/1023;          //nano
ResCO=(1000*(5 -Vsmoke))/Vsmoke;
//PPM1=(((log(ResCO/R0)/log(10))-(0.54)))/(-0.841);    //log=logneperien et pas base 10
//PPM=pow(10,PPM1); 
lcd.setCursor(8,0);
lcd.print("CO");
lcd.print(ResCO,0);
lcd.print("    ");

  
lcd.setCursor(8,1);
lcd.print("gas");
lcd.print(bme.gas_resistance/1000); 
lcd.print("K");
lcd.print((char)244);//Caractère ohm

//traceur serie avec mise à l'echelle ou fichier CSV
Serial.print(ResSm/10,0);Serial.print(",");
Serial.print(ResNO*3,0);Serial.print(",");
Serial.print(ResCO/50,0);Serial.print(",");
Serial.print(bme.gas_resistance/1000);   //renvoie la résistance au gaz
Serial.print(",");
Serial.print(bme.temperature);
Serial.print(",");
Serial.print(bme.pressure / 100.0);
Serial.print(",");
Serial.print(bme.humidity);
Serial.print(",");
Serial.println(bme.readAltitude(SEALEVELPRESSURE_HPA));


/*  
  Serial.print("Temperature= ");
  Serial.print(bme.temperature);
  Serial.println(" °C");

  Serial.print("Pressure= ");
  Serial.print(bme.pressure / 100.0);
  Serial.println(" hPa");

  Serial.print("Humidity= ");
  Serial.print(bme.humidity);
  Serial.println(" %");

  Serial.print("Gas= ");
  Serial.print(bme.gas_resistance/1000);   //renvoie la résistance au gaz
  Serial.println(" KOhms");

  Serial.print("Altitude= ");
  Serial.print(bme.readAltitude(SEALEVELPRESSURE_HPA));
  Serial.println(" m");
*/



  
  delay(1000);
  temps++;
}

Sensor BME680

le datasheet

le code

le code et le cablage

le code avec la bibliothèque adafruit

https://adafruit.github.io/Adafruit_BME680/html/class_adafruit___b_m_e680.html

l’interprétation du gaz ou de la qualité de l’air sur ce lien

https://learn.pimoroni.com/tutorial/sandyj/getting-started-with-bme680-breakout

avec la librairie BSEC valable avec un ESP32, il y a directement (AQI Indoor Air Quality).

je n’arrive pas à trouver le code qui donne cette interpretation du IQA

la qualité de l’aire est donné serait donné par l’equation suivante ??

comp_gas = log(R_gas[ohm]) + 0.04 log(Ohm)/%rh * hum[%rh]

bref, cela manque de transparence pour une library open source ????

1 Like

Avec une bougie parfumée qui libèrent environ 10 grammes de CO2 par heure, les capteurs ont donné une très faible variation comme on peut l’observer sur la figure suivante
Dans l’atmosphère 376 ppm de CO2 0,04%.
Pour convertir du CO2 des 1 [ppm] vaut 1.96 [mg/m3]
Pour l’air 1 ppm vaut 1.3 mg/m3

Mais, la résistance du BME680 a augmenté puis diminué, bizarre ??????
De même pour le NO2 mais c’est dû au préchauffage
Capture1

Des explications sur la qualité de l’air, interressante....

1 Like

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.