Обзор инфракрасного датчика CO2 MH-Z19

В последнее время популярны гаджеты, показывающие уровень CO2, равно как и статьи, рассказывающие как монитор CO2 можно превратить в подключенный к компьютеру датчик. Я хочу показать решение задачи с другой стороны.

В отличие от старых датчиков CO2, MH-Z19 не требует специфического напряжения или высокой мощности и умеет передавать данные через UART и PWM.

6140afe1056643be99c19152e6654788.png

  • Hd — калибровка нуля начнется, если на Hd более 7 секунд подается LOW. Калибровку проводить не нужно.
  • SR — не используется
  • Tx — уровень сигнала — 3.3В
  • Rx — тоже 3.3В (работает и с 5В, но я бы не рекомендовал)
  • Vo — выходное напряжение 3.3В, не более 10 мА


  • PWM, данные снимаются так: длина цикла 1004 мс, первые 2 мс всегда HIGH, последние — всегда LOW, а «середина» пропорциональна концентрации CO2 в пределах 0 — 5000ppm (а не 2000ppm как в документации).
    Cppm = 5000 * (Thigh — 2ms)/(Thigh + Tlow — 4ms)
    Отмечу, что PWM — штука очень капризная, требующая аккуратной пайки и 3.3В.
  • AOT — не используется
  • Gnd — земля
  • Vin — напряжение питания 3.6 — 5.5В (сенсор работает и выдает те же значения при питании 3.3В, но производитель настоятельно рекомендует придерживаться рамок)

Не то, чтобы я не доверял PWM, но лучше получать данные в цифре и с контрольной суммой. UART позволяет запрашивать уровень концентрации CO2 и заниматься двумя видами калибровки. Оставим калибровку Гаррусу и рассмотрим запрос данных. Для этого на скорости 9600 (8 bit, stop — 1, parity — none) нужно отправить следующие девять байт:
• 0xFF — начало любой команды
• 0×01 — первый сенсор (он всего один)
• 0×86 — команда
• 0×00, 0×00, 0×00, 0×00, 0×00 — данные
• 0×79 — контрольная сумма.

В ответ придет что-то такое:
• 0xFF — начало любого ответа
• 0×86 — команда
• 0×01, 0xC1 — старшее и младшее значение (256×0x01 + 0xC1 = 449)
• 0×3C, 0×04, 0×3C, 0xC1 — в документации сказано, что должно приходить что-то типа 0×47, 0×00, 0×00, 0×00, но на деле приходит непонятно что.
• 0×7B — контрольная сумма.

Контрольная сумма считается следующим образом: берутся 7 байт ответа (все кроме первого и последнего), складываются, инвертируются, увеличиваются на 1: 0×86 + 0×01… + 0xC1 = 0×85, 0×85 xor 0xFF = 0×7A, 0×7A + 1 = 0×7B.

Согласно документации сенсору требуется около трех минут, чтобы выйти на рабочий режим. Первое время после включения он будет выдавать или 5000ppm, или 400ppm. После особо усердной пайки может приходить в себя несколько часов.

Сенсор реагирует на изменение концентрации CO2 с задержкой около минуты. При превышении концентрации в 5000ppm (например, вы минуту интенсивно на него дышали), он некоторое время будет выдавать ложные данные, занижая уровень CO2 — я так получал даже 80ppm.

В документации это не отражено, но не стоит запрашивать данные по UART чаще раза в 10 секунд, иначе сенсор начинает выдавать что-то странное.

Пришло время картинок. Подключим сенсор к Arduino Uno через Software Serial, RX/TX в A0/A1, питание в 5В, землю — в Gnd:

d76a7d7a65c446eda4f8cd04ea5b4433.jpg

Позаимствованный скетч, в который добавлена проверка контрольной суммы
#include ;

SoftwareSerial mySerial(A0, A1); // A0 - к TX сенсора, A1 - к RX

byte cmd[9] = {0xFF,0x01,0x86,0x00,0x00,0x00,0x00,0x00,0x79}; 
unsigned char response[9];

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

void loop() 
{
  mySerial.write(cmd, 9);
  memset(response, 0, 9);
  mySerial.readBytes(response, 9);
  int i;
  byte crc = 0;
  for (i = 1; i < 8; i++) crc+=response[i];
  crc = 255 - crc;
  crc++;

  if ( !(response[0] == 0xFF && response[1] == 0x86 && response[8] == crc) ) {
    Serial.println("CRC error: " + String(crc) + " / "+ String(response[8]));
  } else {
    unsigned int responseHigh = (unsigned int) response[2];
    unsigned int responseLow = (unsigned int) response[3];
    unsigned int ppm = (256*responseHigh) + responseLow;
    Serial.println(ppm);
  }
  delay(10000);
}



Каждое измерение идет с интервалом 10 секунд. Значения перестали прыгать когда я отошел от сенсора.
6ad9d4dc1c18422498ae365a1fb04cd2.png

Теперь сделаем датчик мобильным. Для этого потребуется устройство с OTG и приложение типа DroidTerm.
Тут есть тонкость: чтобы связь установилась — нужно перезагрузить Arduino.
c8b149796c824abb89c0f129d36a2999.jpg

Убедившись, что все работает, уберем Arduino, заменив его на FTDI FT232RL.
8b16f8e2d4884545aa66cb173181a90f.png

Питание на датчик стоит подавать уже после подключения чтобы не было проблем с соединением.
Для отправки бинарных данных через COM-порт я использую RealTerm:
e618412d627f4ed5b40ec29b474246b1.png

Возможно, стоит добавить управление питанием через DTR, чтобы можно было перезапускать датчик.

Полезные ссылки:
Мануал по датчику MH-Z19
Схема подключения и код для PWM
Сравнение с другим датчиком

У меня всего один датчик и я очень не люблю разбирать то, что однажды сделал, поэтому предлагаю выбрать вам.

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

© Geektimes