Общие сведения
Датчик углекислого газа MH-Z19B — датчик, который детектирует уровень СО2 по принципу недисперсионного инфракрасного излучения (NDIR). Данный датчик имеет внутреннюю температурную компенсацию, может подключаться по 3-м интерфейсам (аналоговый, ШИМ, UART), имеет малые размеры и большой срок службы.
Приблизительные нормы содержания уровня CO2 в помещениях:
| Уровень CO2, ppm | Качество воздуха |
| 300-400 | Эталонное качество воздуха |
| 400-600 | Нормальное качество воздуха |
| 600-800 | Среднее качество воздуха |
| 800-1000 | Предельно допустимое качество воздуха |
| Свыше 1000 | Низкое качество воздуха |
| Свыше 2000 | Критически низкое качество воздуха |
Характеристики
- Рабочее напряжение: от 4.5В до 5В
- Потребление тока: <60мА (150мА в пиковой нагрузке)
- Диапазон измерений: 0~5000ppm
- Интерфейс: UART (3.3В) / ШИМ (3.3В, толерантен к 5В)
- Время прогрева датчика: 3мин
- Рабочая температура: от 0 до 50℃
- Влажность: от 0 до 95%
- Вес: 5г
Подключение
Датчик подключается по шине UART.
- Vcc — +5В.
- GND — Земля.
- RX — подключается к выводу TX порта UART указанного в скетче.
- TX — подключается к выводу RX порта UART указанного в скетче.
На плате Arduino UNO напряжение на выходах TX и RX равно 5В. Напряжение на входах TX и RX датчика не должно превышать 3,3В. В связи с этим подключение необходимо производить через делитель напряжения.
Для делителя напряжения Вам понадобятся резисторы номиналом 470 Ом и 1 кОм. Соберите Вашу схему так, как показано на схеме:

Назначение выводов
- +Vin (Vcc) - Напряжение питания +4,5 ... +5,5В
- GND - Общий вывод питания.
- Vo - Выходной аналоговый сигнал: +0,4 ... +2,0В (можно настроить на 0 ... 3,0В)
- PWM - Сигнал ШИМ (3,3В)
- HD - Калибровка нулевой точки. Выполняется подачей 0 более 7 секунд.
- TX - Выход данных UART (3,3В).
- RX - Вход данных UART (3,3В).

Питание
Входное напряжение питания от 4.5В до 5В постоянного тока, подаётся на выводы Vcc и GND модуля.
Подробнее о датчике
Управляющие команды
Датчик имеет несколько режимов работы, которые зависят от управляющего байта:
- 0х86 — Прочитать данные с датчика — считывает значения с датчика, где в HIGH (3) и LOW (4) байтах указаны значения CO2;
Пример:
Отправка
| Байт 0 | Байт 1 | Байт 2 | Байт 3 | Байт 4 | Байт 5 | Байт 6 | Байт 7 | Байт 8 |
| 0xFF | 0x01 | 0x86 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x79 |
Приём
| Байт 0 | Байт 1 | Байт 2 | Байт 3 | Байт 4 | Байт 5 | Байт 6 | Байт 7 | Байт 8 |
| 0xFF | 0x86 | HIGH | LOW | -- | -- | -- | -- | 0х79 |
- 0х87 — Калибровка нуля — используйте этот режим для калибровки в бытовых условиях (400ppm)
Пример:
Отправка
| Байт 0 | Байт 1 | Байт 2 | Байт 3 | Байт 4 | Байт 5 | Байт 6 | Байт 7 | Байт 8 |
| 0xFF | 0x01 | 0x87 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | CRC |
- 0х88 — Калибровка в заданном диапазоне измерений — используйте этот режим для калибровки диапазона, указав значение диапазона в HIGH (3) и LOW (4) байтах;
Пример:
Отправка
| Байт 0 | Байт 1 | Байт 2 | Байт 3 | Байт 4 | Байт 5 | Байт 6 | Байт 7 | Байт 8 |
| 0xFF | 0x01 | 0x88 | HIGH | LOW | 0x00 | 0x00 | 0x00 | CRC |
- где HIGH и LOW для 5000ppm считаются по формуле: HIGH = 5000 / 256; LOW = 5000 % 256;
- 0х79 — Вкл/Выкл Автоматическую калибровку датчика — при отсутствии необходимости, режим автокалибровки можно отключить, указав в (3) байте 0хА0/0х00 (Вкл/Выкл);
Пример:
Отправка
| Байт 0 | Байт 1 | Байт 2 | Байт 3 | Байт 4 | Байт 5 | Байт 6 | Байт 7 | Байт 8 |
| 0xFF | 0x01 | 0x88 | HIGH | LOW | 0x00 | 0x00 | 0x00 | CRC |
- 0x99 — Установить диапазон измерений (2000ppm; 5000ppm) — выбор диапазона измерений. Указывается в (6) и (7) байтах в 16-ной кодировке. (2000 = 07D0; 5000 = 1388)
Пример:
Диапазон задаётся в (6) и (7) байтах.
Для диапазона 2000ppm (2000 в 10-ной кодировке = 07D0 в 16-ной кодировке):
Отправка
| Байт 0 | Байт 1 | Байт 2 | Байт 3 | Байт 4 | Байт 5 | Байт 6 | Байт 7 | Байт 8 |
| 0xFF | 0x01 | 0x99 | 0х00 | 0х00 | 0х00 | 0х07 | 0хD0 | CRC |
Для диапазона 5000ppm (5000 в 10-ной кодировке = 1388 в 16-ной кодировке):
Отправка
| Байт 0 | Байт 1 | Байт 2 | Байт 3 | Байт 4 | Байт 5 | Байт 6 | Байт 7 | Байт 8 |
| 0xFF | 0x01 | 0x99 | 0х00 | 0x00 | 0x00 | 0x13 | 0x88 | CRC |
CRC
Во всех командах в последнем (8) байте указана контрольная сумма - CRC. Для её подсчёта используется указанная производителем функция:
char getCheckSum(char *packet) {
char i, checksum;
for( i = 1; i < 8; i++){
checksum += packet[i];
}
checksum = 0xff – checksum;
checksum += 1;
return checksum;
}
Более подробную информацию вы найдёте в DataSheet'ах (на английском v1.0 от 21/01/16 и на китайском v1.3 от 08/12/2017).
Примеры
Вывод значений датчика в Монитор последовательного порта и на LED-индикатор
#include <SoftwareSerial.h> // подключаем библиотеку SoftwareSerial
#include <iarduino_4LED.h> // подключаем библиотеку iarduino_4LED
SoftwareSerial co2Serial(3, 2); // объявляем объект для работы с функциями библиотеки SoftwareSerial, с указанием выводов ( RX, TX )
iarduino_4LED dispLED(4, 5); // объявляем объект для работы с функциями библиотеки iarduino_4LED, с указанием выводов дисплея ( CLK , DIO )
byte getCheckSum(char *packet) { // функция проверки контрольной суммы
byte i; // создаём переменную для счёта
unsigned char checksum = 0; // создаём переменную для значения контрольной суммы
for (i = 1; i < 8; i++) { // считаем контрольную сумму по принятому в массив response значению
checksum += packet[i]; // суммируем все байты массива
}
checksum = 0xff - checksum; // вычитаем сумму байтов из 255 (0xff)
checksum += 1; // прибаляем к сумме 1
return checksum; // возвращаем значение контрольной суммы принятого значения с датчика
}
void setup() {
Serial.begin(9600); // инициируем подключение монитора последовательного порта на скорости 9600 бит/сек
co2Serial.begin(9600); // инициируем подключение по последовательному порту на скорости 9600 бит/сек
dispLED.begin(); // инициируем LED дисплей
}
void loop() {
byte cmd[9] = {0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79}; // массив под запрос значений датчика
byte response[9]; // массив под полученный ответ от датчика
Serial.println("Sending CO2 request..."); // выводим текст в монитор порта "Запрос значений..."
co2Serial.write(cmd, 9); // отправляем команду датчику сформировать 9-байтовое значение
memset(response, 0, 9); // обнуляем массив
int i = 0; // создаём переменную для счёта секунд
while (co2Serial.available() == 0) { // пока данные не приняты
// Serial.print("Waiting for response "); // выводим в монитор порта текст "Ожидание получения ответа"
// Serial.print(i); // выводим количество секунд
// Serial.println(" s"); // выводим текст "секунд"
delay(1000); // ждём 1 секунду
i++; // увеличиваем значение на 1
}
if (co2Serial.available() > 0) { // если данные приняты, то
co2Serial.readBytes(response, 9); // считваем их в массив
}
for (int i = 0; i < 9; i++) { // считываем 9 байт, принятых от датчика
Serial.print(String(response[i], HEX)); // выводим их в монитор порта
Serial.print(" "); // через пробел
}
Serial.println(""); // переходим на новую строку
byte check = getCheckSum(response); // выполняем проверку контрольной суммы
if (response[8] != check) { // если значение отправленное и полученное контрольной суммы не совпадает, то
Serial.println("Checksum not OK!"); // выводим сообщение в монитор порта об этом
Serial.print("Received: "); // выводим текст в монитор порта "Принято:"
Serial.println(response[8]); // выводим байт контрольной суммы, который был принят
Serial.print("Should be: "); // выводим текст "Должно быть" (было отправлено)
Serial.println(check); // выводим байт контрольной суммы, который был отправлен
}
int ppm_uart = 256 * (int)response[2] + response[3]; // вычисляем значение CO2
Serial.print("CO2 Level = "); // выводим текст в монитор последовательного порта "Уровень СО2:"
Serial.println(ppm_uart); // выводим значение СО2 в монитор порта
dispLED.print(ppm_uart); // выводим значение СО2 на дисплей
byte crc[9] = {}; // обнуляем байт контрольной суммы
delay(5000); // ждём 5 секунд
}
Короткий пример вывода данных в монитор
#include <SoftwareSerial.h> // Подключаем библиотеку для работы с программной шиной UART.
SoftwareSerial SerialS(3, 2); // Создаём объект для работы с программной шиной UART, указав выводы ( RX, TX ).
//
void setup(){ //
Serial.begin(9600); // Инициируем подключение монитора последовательного порта на скорости 9600 бит/сек
SerialS.begin(9600); // Инициируем подключение по последовательному порту на скорости 9600 бит/сек
} //
//
void loop(){ //
// Передаём команду запроса данных датчика MH-Z19B: //
byte comand[9] = {0xFF,0x01,0x86,0x00,0x00,0x00,0x00,0x00,0x79}; // Массив байт команды запроса данных.
SerialS.write(comand, 9); // Отправляем команду запроса, 9 байт.
//
// Получаем данные от датчика MH-Z19B: //
byte answer[9] = {0,0,0,0,0,0,0,0,0}; // Массив для получения ответа.
while( SerialS.available()==0 ){ delay(10); } // Ждём ответ по шине UART.
SerialS.readBytes(answer, 9); // Читаем 9 байт ответа в массив answer.
while( SerialS.available() ){ SerialS.read(); delay(10); } // Чистим буфер UART от мусора.
int ppm = 256 * (int)answer[2] + answer[3]; // Вычисляем значение CO2.
//
// Вычисляем контрольную сумму: //
uint8_t sum = 0; for(uint8_t i=1; i<8; i++){ sum += answer[i]; } // Сумма байтов ответа, без учета старшего и младшего байта.
sum = 0xff - sum + 1; // Вычитаем сумму из 255 и прибавляем 1.
//
// Выводим ответ в монитор: //
Serial.print("CO2 = "); //
if( answer[8] == sum ){ Serial.print(ppm); } // Если совпала контрольная сумма, то выводим результат.
else { Serial.print('?'); } // Иначе, выводим знак '?'.
Serial.println(" ppm"); //
} //
В этом примере не выводится ничего кроме результата CO2 в ppm
Комплектация
- 1х Датчик углекислого газа MH-Z19B;
Ссылки
- DataSheet (английский v1.0 21/01/16 / китайский v1.3 08/12/2017);















