Contents
Intro
Dans un article précédent, j’abordais le détournement du boitier de mesure de qualité de l’air Vindriktning commercialisé par IKEA. Celui ci est équipé d’un capteur Pm2.5 pm1006 qui fonctionne correctement mais la gamme de mesure se limite au PM2.5 et la précision semble t’il n’est pas au rendez vous.
En parcourant les sensors « Air Quality » de EspHome, j’ai découvert le PMS5003 qui propose lui une étendue de mesures bien supérieure pour un prix raisonnable. J’en ai profité pour y associer un MHZ-19B qui lui mesure le taux de CO2 plus un afficheur local et un bandeau de 8 leds.
L’ensemble est raccordé à un ESP32 Mini qui assure une puissance de traitement supérieure à un ESP8266.
Enfin j’ai intégré le tout sur un joli circuit imprimé dont j’ai sous traité la fabrication à https://jlcpcb.com/ pour un rapport qualité prix très attrayant.
Descriptif matériel
Capteur de particules PMS 5003
Mesure fiable et en temps réel des concentrations de poussières PM2,5 ! Ce capteur utilise la diffusion laser pour rayonner les particules en suspension dans l’air, puis recueille la lumière diffusée pour obtenir la courbe de la variation de la lumière diffusée dans le temps. Le microprocesseur calcule le diamètre équivalent des particules et le nombre de particules de diamètre différent par unité de volume.
Concentrations de PM1,0, PM2,5 et PM10,0 dans les unités standard et environnementales
Particules par 0,1 L d’air, classées en bacs de 0,3 um, 0,5 um, 1,0 um, 2,5 um, 5,0 um et 10 um.
Plage de mesure : 0,3~1,0;1.0~2,5;2.5~10 μ m
Portée effective : 0~500 μ g/m³
Portée maximale : * ≥1000 μ g/m³
Résolution : 1 μ g/m³
Volume standard : 0.1L
Alimentation en courant continu : Typ:5.0 / Min:4.5 / Max : 5.5V
Courant actif : ≤100 mA
Courant de veille : ≤200 μ μ A
Plage de température de fonctionnement : -10~+60 ℃
Plage d’humidité de travail : 0~99%
Plage de température de stockage : -40~+80 ℃
Dimensions du module capteur : 50 × 38 × 21mm
Poids (câble et adaptateur inclus) : 42.2g
Fiche technique
- Sur le PMS-5003:
- PMS-5003 <–> ESP32 Mini
- Validation <–> SET (IO27)
- TX <–> RX (IO25)
- GND <–> GND
- VCC (+5v) <–> VCC
Capteur de Co2
Le capteur Co2 est un MH-Z19B qui mesure la teneur de CO2 sur une échelle de 400 à 10000 ppm et communique en UART.
- Sur le MH-Z19:
- MH-Z19 <–> ESP32 Mini
- TX <–> RX (IO16)
- RX <–> TX (IO17)
- GND <–> GND
- VCC (+5v) <–> VCC
ESP32 Mini
J’ai choisi un ESP32 Mini, car l’ESP8266 est trop juste en puissance de calcul si on utilise l’intégration ESPHOME plus un afficheur plus un bandeau Led, j’ai galéré avec, je ne le recommande pas.
Afficheur Oled
Vous en trouverez sans problème sur Amazon ou en chine
L’afficheur utilise une police de caractère « Arial », vous pouvez télécharger le fichier arial.ttf ici puis le transférer dans un dossier « fonts » de votre « /config/esphome ». Bien entendu vous pouvez utiliser une autre police de caractéres, « size » détermine la taille des caractères.
Bandeau led
Il n’est pas indispensable mais il permet de visualiser rapidement d’un simple coup d’œil le niveau de pollution.
Stick NeoPixel RGB 8 leds
Mise en oeuvre
j’utilise le logiciel Eagle d’Autodesk pour la création des circuits imprimé, puis je sous traite la fabrication chez https://jlcpcb.com/ pour un rapport qualité prix très attrayant et un rendu « professionnel »
Schéma Electronique
Circuit imprimé
Mise en boitier
Cela reste à faire, je cherche le boitier idéal!
Logiciel
J’ai raccordé un bouton poussoir me permettant ainsi de faire défiler différentes pages de mesure sur l’ecran Oled.
Code ESP Home
substitutions:
device_name: esp138-qualite-air
adress_ip: "192.168.0.138"
friendly_name: esp138
time_timezone: "Europe/Paris"
esphome:
name: ${device_name}
platform: ESP32
board: mhetesp32minikit
#board: wemos_d1_mini32
platformio_options:
lib_deps: NeoPixelBus@2.6.0
on_boot:
then:
- light.control:
id: rgb_led
brightness: 0.25
state: on
wifi:
networks:
- ssid: !secret wifi_mi4
password: !secret mdpwifi_mi4
priority: 2
- ssid: !secret wifi_esp
password: !secret mdpwifi_esp
priority: 1
- ssid: !secret wifi
password: !secret mdpwifi
priority: 0
reboot_timeout: 5min
manual_ip:
static_ip: ${adress_ip}
gateway: 192.168.0.1
subnet: 255.255.255.0
# Enable logging
logger:
baud_rate: 0
# Enable Home Assistant API
api:
ota:
web_server:
port: 80
font:
- file: "fonts/arial.ttf"
id: arial
size: 15
# Led WS2812 RGB
light:
- platform: neopixelbus
num_leds: 8
pin: GPIO4 #gpio0
name: "RGB strip"
variant: ws2812
id: rgb_led
default_transition_length: 0s
i2c:
sda: GPIO21 #
scl: GPIO22 #
scan: True
id: bus_a
uart:
- id: uart_a
rx_pin: GPIO25 #
baud_rate: 9600
- id: uart_b
rx_pin: GPIO16 # GPIO16
tx_pin: GPIO17 # GPIO17
baud_rate: 9600
# debug:
# direction: BOTH
globals:
- id: bp_mode
type: int
initial_value: '1'
sensor:
- platform: pmsx003
uart_id: uart_a
type: PMSX003
pm_1_0:
name: "Concentration de particules <1.0µm"
id: pm1_0
accuracy_decimals: 0
filters:
- sliding_window_moving_average:
window_size: 15
send_every: 10
send_first_at: 1
pm_2_5:
name: "Concentration de particules <2.5µm"
id: pm2_5
accuracy_decimals: 0
filters:
- sliding_window_moving_average:
window_size: 15
send_every: 10
send_first_at: 1
on_value:
# de 0 à 7 / 8 leds
# Led 0
- if:
condition:
sensor.in_range:
id: pm2_5
below: 25
then:
- light.addressable_set:
id: rgb_led
range_from: 0
range_to: 0
red: 0%
green: 100%
blue: 0%
color_brightness: 100%
- light.addressable_set:
id: rgb_led
range_from: 1
range_to: 7
red: 0%
green: 0%
blue: 0%
color_brightness: 0%
# led 1
- if:
condition:
sensor.in_range:
id: pm2_5
above: 25
below: 35
then:
- light.addressable_set:
id: rgb_led
range_from: 0
range_to: 1
red: 0%
green: 100%
blue: 0%
color_brightness: 100%
- light.addressable_set:
id: rgb_led
range_from: 2
range_to: 7
red: 0%
green: 0%
blue: 0%
color_brightness: 0%
# Led 2
- if:
condition:
sensor.in_range:
id: pm2_5
above: 35
below: 45
then:
- light.addressable_set:
id: rgb_led
range_from: 0
range_to: 2
red: 0%
green: 100%
blue: 0%
color_brightness: 100%
- light.addressable_set:
id: rgb_led
range_from: 3
range_to: 7
red: 0%
green: 0%
blue: 0%
color_brightness: 0%
# led 3
- if:
condition:
sensor.in_range:
id: pm2_5
above: 45
below: 55
then:
- light.addressable_set:
id: rgb_led
range_from: 0
range_to: 3
red: 75%
green: 75%
blue: 0%
color_brightness: 100%
- light.addressable_set:
id: rgb_led
range_from: 4
range_to: 7
red: 0%
green: 0%
blue: 0%
color_brightness: 0%
# led 4
- if:
condition:
sensor.in_range:
id: pm2_5
above: 55
below: 65
then:
- light.addressable_set:
id: rgb_led
range_from: 0
range_to: 4
red: 75%
green: 75%
blue: 0%
color_brightness: 100%
- light.addressable_set:
id: rgb_led
range_from: 5
range_to: 7
red: 0%
green: 0%
blue: 0%
color_brightness: 0%
# led 5
- if:
condition:
sensor.in_range:
id: pm2_5
above: 65
below: 75
then:
- light.addressable_set:
id: rgb_led
range_from: 0
range_to: 5
red: 100%
green: 0%
blue: 0%
color_brightness: 100%
- light.addressable_set:
id: rgb_led
range_from: 6
range_to: 7
red: 0%
green: 0%
blue: 0%
color_brightness: 0%
# led 6
- if:
condition:
sensor.in_range:
id: pm2_5
above: 75
below: 85
then:
- light.addressable_set:
id: rgb_led
range_from: 0
range_to: 6
red: 100%
green: 0%
blue: 0%
color_brightness: 100%
- light.addressable_set:
id: rgb_led
range_from: 7
range_to: 7
red: 0%
green: 0%
blue: 0%
color_brightness: 0%
# Led 7
- if:
condition:
sensor.in_range:
id: pm2_5
above: 85
then:
- light.addressable_set:
id: rgb_led
range_from: 0
range_to: 7
red: 100%
green: 0%
blue: 0%
color_brightness: 100%
pm_10_0:
name: "Concentration de particules <10.0µm"
id: pm10_0
accuracy_decimals: 0
filters:
- sliding_window_moving_average:
window_size: 15
send_every: 10
send_first_at: 1
pm_0_3um:
name: "Nb de particules >0.3"
id: pm_03_um
unit_of_measurement: "ppd"
accuracy_decimals: 0
pm_0_5um:
name: "Nb de particules >0.5"
id: pm_05_um
unit_of_measurement: "ppd"
accuracy_decimals: 0
pm_1_0um:
name: "Nb de particules >1.0"
id: pm_10_um
unit_of_measurement: "ppd"
accuracy_decimals: 0
pm_2_5um:
name: "Nb de particules >2.5"
id: pm_25_um
unit_of_measurement: "ppd"
accuracy_decimals: 0
pm_5_0um:
name: "Nb de particules >5.0"
id: pm_50_um
unit_of_measurement: "ppd"
accuracy_decimals: 0
pm_10_0um:
name: "Nb de particules >10.0"
id: pm_100_um
unit_of_measurement: "ppd"
accuracy_decimals: 0
- platform: wifi_signal
name: "${friendly_name} WiFi Signal Sensor"
update_interval: 60s
- platform: uptime
name: "${friendly_name} Uptime Sensor"
update_interval: 60s
- platform: mhz19
uart_id: uart_b
id: mh_z19
co2:
name: "MH-Z19 CO2 Value"
id: co2
accuracy_decimals: 0
filters:
- sliding_window_moving_average:
window_size: 12 # moyenne sur 12 lecture
send_every: 6 # envoi toutes les 6 lectures
send_first_at: 1 # envoi à la premiere lecture
temperature:
name: "MH-Z19 Temperature"
id: temp
filters:
- sliding_window_moving_average:
window_size: 24
send_every: 12
send_first_at: 1
update_interval: 10s
automatic_baseline_calibration: false
# Il s'agit d'un modèle de capteur qui effectue une moyenne mobile de 30 minutes de PM2,5
# il supprime les valeurs aberrantes et de rendre le contrôle plus fluide
- platform: template
name: "Concentration de particules <2.5µm median"
id: pm2_5_median
icon: mdi:chemical-weapon
unit_of_measurement: µg/m³
lambda: |-
return id(pm2_5).state;
update_interval: 60s
filters:
- median:
window_size: 30
send_every: 30
send_first_at: 15
binary_sensor:
- platform: gpio
name: bp_mod
id: button
pin:
number: GPIO14 # GPIO 15TMS
inverted: true
mode:
input: true
pullup: true
on_click:
- min_length: 25ms
max_length: 500ms
then:
- lambda: |-
if (id(bp_mode) < 3) { id(bp_mode) += 1; }
else { id(bp_mode) = 1; }
- platform: gpio
name: bp_mod2
id: button2
pin:
number: GPIO13 # TCK
inverted: true
mode:
input: true
pullup: true
#Etat de la connection
- platform: status
name: "${friendly_name} Status"
interval:
- interval: 60s
then:
- switch.turn_on: val_mes
- delay: 10s
# - switch.turn_on: val_mes
- switch.turn_off: val_mes
- delay: 30s
switch:
- platform: gpio
pin:
number: GPIO27 #
id: val_mes
name: "${friendly_name} Validation mesure"
- platform: template
name: "Calibration zero Mh-Z19 CO2"
turn_on_action:
- mhz19.calibrate_zero: mh_z19
- switch.turn_off: co2_cal
id: co2_cal
- platform: restart
name: "${friendly_name} Restart"
# gestion afficheur
display:
- platform: ssd1306_i2c
model: "SSD1306 128x64"
address: 0x3C
brightness: 100%
lambda: |-
if (id(bp_mode) == 1 )
{
it.printf(0,0,id(arial),"PM1.0=%.2f",id(pm1_0).state);
it.printf(0,15,id(arial),"PM2.5=%.2f",id(pm2_5).state);
it.printf(0,30,id(arial),"PM10.0=%.2f",id(pm10_0).state);
it.printf(0,45,id(arial),"CO2=%.0f",id(co2).state);
}
else if (id(bp_mode) == 2 )
{
it.printf(0,0,id(arial),"0.3UM=%.1f",id(pm_03_um).state);
it.printf(0,15,id(arial),"0.5UM=%.1f",id(pm_05_um).state);
it.printf(0,30,id(arial),"1.0UM=%.1f",id(pm_10_um).state);
it.printf(0,45,id(arial),"2.5UM=%.1f",id(pm_25_um).state);
}
else if (id(bp_mode) == 3 )
{
it.printf(0,0,id(arial),"5.0UM=%.1f",id(pm_50_um).state);
it.printf(0,15,id(arial),"10.0UM=%.1f",id(pm_100_um).state);
}
Affichage Local
Un exemple minimaliste.
type: entities
entities:
- entity: sensor.concentration_de_particules_1_0um
- entity: sensor.concentration_de_particules_10_0um
- entity: sensor.concentration_de_particules_2_5um
- entity: sensor.concentration_de_particules_2_5um_median
- entity: sensor.mh_z19_co2_value
- entity: sensor.mh_z19_temperature
- entity: sensor.nb_de_particules_0_3
- entity: sensor.nb_de_particules_0_5
- entity: sensor.nb_de_particules_1_0
- entity: sensor.nb_de_particules_10_0
- entity: sensor.nb_de_particules_2_5
- entity: sensor.nb_de_particules_5_0
- entity: binary_sensor.bp_mod
- entity: binary_sensor.bp_mod2
- entity: automation.vmc_niv0_automatismes
title: esp138-qualite-air
Conclusion
Après quelques semaines d’expérience, les retours sont positifs, reste à valider la véracité des mesures, mais sans mesure étalon, on ne peut que faire confiance aux constructeurs.
J’ai remarqué que de temps en temps, la mesure de CO2 ne varie plus, j’active le switch « calibration zero mh-z19 ».
je l’utilise pour piloter ma VMC en cas de valeur PM2.5 ou de CO2 trop élévée.
Bonjour,
J’utilise le PMS5003 depuis pas mal d’années et au final je suis assez sceptique sur la qualité des données. En effet je possède actuellement 4 capteurs du même modèle acheté chez différents fournisseurs et aucun ne délivre les mêmes valeurs dans des conditions d’utilisations identiques.
J’ai également récupéré les données d’Airparif pour avoir une référence mais la corrélation entre Airparif et PMS5003 est très mauvaise (ceci dit, on ne peut rien en conclure, car la concentration en particules fines varie beaucoup selon le lieu et je ne sais pas où se trouve la station d’Airparif).
Donc, au final, en l’absence de données d’un capteur étalon, je suis dans l’incapacité de conclure sur la qualité du PMS5003.
Bonjour, merci pour ce retour, effectivement pas facile à étalonner sans instrument de précision.