Capteur de dioxyde de carbone MH-Z16
Ce tutoriel explique comment mettre en oeuvre un module UART capteur de gaz carbonique MH-Z16 Grove avec MicroPython basé sur une technologie d’illumination infrarouge non-dispersive. La fiche technique du MH-Z16 est disponible ici
Ce capteur renvoie des valeurs en parties par million (symbole ppm). Le ppm permet de savoir combien de molécules du gaz mesuré (en l’occurence le CO2) on trouve sur un million de molécules d’air. Il peut mesurer des concentrations entre 0 et 2000 ppm avec une résolution de 1 ppm et une précision (reproductibilité) de 200 ppm (d’où la nécessité, une fois encore, de le calibrer).
La concentration moyenne de CO2 dans l’air extérieur est de l’ordre de 300 à 400 ppm. Les niveaux intérieurs sont en général plus élevés, en raison du CO2 exhalé par les occupants du bâtiment. Il est intéressant de surveiller la concentration en CO2 à l’intérieur d’un logement car celle-ci donne une assez bonne estimation de la qualité de son aération. Les valeurs limites supérieures réglementaires actuelles varient entre 1000 et 1500 ppm (source: ANSES).
Matériel requis
- Une carte d’extension de base Grove
- La carte NUCLEO-WB55
- Un module capteur de gaz carbonique MH-Z16 Grove
Le module Grove capteur de gaz carbonique MH-Z16 :

Crédit image : Seeed Studio
Connectez le capteur sur une broche I2C.
Le code MicroPython sans blibliothèque
Les scripts présentés ci-après sont disponibles dans la zone de téléchargement.
Créez un fichier main.py dans PYBFLASH et copiez-collez dans celui-ci le code qui suit :
# Objet du script : mise en oeuvre d'un capteur de CO2 MU-Z16 de Grove
# Ce script est adapté du forum https://forum.pycom.io/topic/4821/solved-uart-and-mh-z16-co2-sensor/13
# Fiche technique du capteur : https://www.winsen-sensor.com/d/files/MH-Z16.pdf
from pyb import UART # Classe pour gérer l'UART
from time import sleep_ms
uart = UART(2) # Instance de l'UART numéro 2
# Initialisation à 9600 bauds, trames de 8 bits, sans contrôle de parité avec un stop bit à 1
uart.init(9600, bits=8, parity=None, stop = 1)
# Doit-on recalibrer le capteur ?
calibrate = False
# Doit-on préchauffer le capteur ?
preheat = False
# Correction décalage du thermomètre (dépend du module/capteur)
TEMP_OFFSET = const(5)
# Correction décalage du capteur de CO2 (déterminé avec un autre capteur étalonné de CO2)
CO2_OFFSET = const(0)
# Séquence d'octets pour lancer une mesure
CMD_MEASURE = b'\xFF\x01\x86\x00\x00\x00\x00\x00\x79'
# Séquence d'octets pour calibrer
CMD_CALIBRATE = b'\xFF\x87\x87\x00\x00\x00\x00\x00\xF2'
# Nombre d'itérations de chauffe du capteur
HEATING_ROUNDS = const(300)
if preheat:
print("Préchauffe (%d minutes)\n" %(HEATING_ROUNDS // 60))
p = 0
for i in range(HEATING_ROUNDS):
# Lance une mesure
uart.write(CMD_MEASURE)
# Pause d'une seconde
sleep_ms(1000)
p += 1
if p == 60:
p = 0
print(str((i+1) // 60) + " minute(s) écoulée(s)")
# Calibration éventuelle (dans une pièce aérée ou à l'extérieur)
if calibrate:
print("Démarrage de la calibration\n")
uart.write(CMD_CALIBRATE)
print("Fin de la calibration\n")
# Précision du capteur (ppm)
#ACCURACY = const(200)
# Seuil de détection haut du capteur (ppm)
THRESHOLD = const(2000)
while True:
# Lance une mesure
uart.write(CMD_MEASURE)
sleep_ms(10)
# Attends d'avoir reçu la réponse (9 caractères)
while uart.any() < 9:
sleep_ms(1)
# Lecture de la réponse
resp = bytearray(uart.read(9))
# Extrait la température et la concentration de CO2 de la réponse
co2_ppm = int(resp[2]) * 256 + int(resp[3]) + CO2_OFFSET
temp_celsius = (int(resp[4]) - 40) + TEMP_OFFSET
# Affiche la température et la concentration de CO2
print("Temperature : %d °C" %temp_celsius)
if co2_ppm < THRESHOLD:
print("Concentration en CO2 : %d ppm\n" %co2_ppm)
else:
print("Concentration en CO2 > %d ppm\n" %THRESHOLD)
# Temporisation de cinq secondes
sleep_ms(5000)
Affichage sur le terminal série
Une fois le script lancé avec CTRL+D, une série de valeurs s’affiche dans le terminal série de l’USB user toutes les cinq secondes.

Le code MicroPython, avec une bibliothèque
Les scripts présentés ci-après sont disponibles dans la zone de téléchargement.
Cette deuxième version déplace les insructions pour gérer le MH-Z16 dans une bibliothèque de classes. Commencez par copier le fichier mhz16.py dans le dossier PYBFLASH. Créez ensuite un fichier main.py dans PYBFLASH et copiez-collez dans celui-ci le code qui suit :
# Objet du script : mise en oeuvre d'un capteur de CO2 MU-Z16 de Grove
# On utilise cette fois-ci une bibliothèque de classes (mhz16.py).
import mhz16
from pyb import UART # Classe pour gérer l'UART
from time import sleep_ms
uart = UART(2) # Instance de l'UART numéro 2
# Initialisation de l'UART
uart.init(9600, bits=8, parity=None, stop = 1)
# Correction décalage du thermomètre (dépend du module/capteur)
TEMP_OFFSET = const(5)
# Instance du capteur
sensor = mhz16.MHZ16(uart, temp_offset = TEMP_OFFSET)
# Doit-on recalibrer le capteur ?
calibrate = False
# Doit-on préchauffer le capteur ?
preheat = False
# Démarrage de la calibration (dans une pièce aérée ou à l'extérieur)
if preheat:
print("Préchauffe (5 minutes)")
sensor.preheat()
if calibrate:
print("Calibration\n")
sensor.calibrate()
# Précision du capteur (ppm)
#ACCURACY = const(200)
# Seuil de détection haut du capteur (ppm)
THRESHOLD = const(2000)
while True:
# Lance une mesure
(temp_celsius, co2_ppm) = sensor.measure()
# Si les valeurs remontées sont positives
if temp_celsius != -1 and co2_ppm != -1:
# Affiche la température et la concentration de CO2
print("Temperature : %d °C" %temp_celsius)
if co2_ppm < THRESHOLD:
print("Concentration en CO2 : %d ppm\n" %co2_ppm)
else:
print("Concentration en CO2 > %d ppm\n" %THRESHOLD)
else:
print("Problème de mesure")
# Temporisation de cinq secondes
sleep_ms(5000)
Et voici le contenu du fichier mhz16.py :
# Pilote du module Grove capteur de CO2 MH-Z16
# Ce script est adapté du forum :
# https://forum.pycom.io/topic/4821/solved-uart-and-mh-z16-co2-sensor/13
# Fiche technique du capteur :
# https://www.winsen-sensor.com/d/files/MH-Z16.pdf
from time import sleep_ms
# Taille de la réponse du capteur, en octets
NB_BYTES = const(9)
# Séquence d'octets pour lancer une mesure
CMD_MEASURE = b'\xFF\x01\x86\x00\x00\x00\x00\x00\x79'
# Séquence d'octets pour calibrer
CMD_CALIBRATE = b'\xFF\x87\x87\x00\x00\x00\x00\x00\xF2'
class MHZ16:
def __init__(self, uart, temp_offset = 0, co2_offset = 0):
self._uart = uart
self._temp_offset = temp_offset
self._co2_offset = co2_offset
def preheat(self):
p = 0
for i in range(300):
# Lance une mesure
self._uart.write(CMD_MEASURE)
# Pause d'une seconde
sleep_ms(1000)
p += 1
if p == 60:
p = 0
def calibrate(self):
self._uart.write(CMD_CALIBRATE)
def measure(self):
try:
# Lance une mesure
self._uart.write(CMD_MEASURE)
sleep_ms(10)
# Attends d'avoir reçu la réponse (9 caractères)
while self._uart.any() < NB_BYTES:
sleep_ms(1)
# Lis la réponse
resp = bytearray(self._uart.read(NB_BYTES))
# Extrait la température et la concentration de CO2 de la réponse
co2_ppm = (resp[2] * 256 + resp[3]) + self._co2_offset
temp_celsius = (resp[4] - 40) + self._temp_offset
return(temp_celsius, co2_ppm)
except:
return(-1, -1)