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

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



  • 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 — начало любой команды
• 0x01 — первый сенсор (он всего один)
• 0x86 — команда
• 0x00, 0x00, 0x00, 0x00, 0x00 — данные
• 0x79 — контрольная сумма.

В ответ придет что-то такое:
• 0xFF — начало любого ответа
• 0x86 — команда
• 0x01, 0xC1 — старшее и младшее значение (256 * 0x01 + 0xC1 = 449)
• 0x3C, 0x04, 0x3C, 0xC1 — в документации сказано, что должно приходить что-то типа 0x47, 0x00, 0x00, 0x00, но на деле приходит непонятно что.
• 0x7B — контрольная сумма.

Контрольная сумма считается следующим образом: берутся 7 байт ответа (все кроме первого и последнего), складываются, инвертируются, увеличиваются на 1: 0x86 + 0x01… + 0xC1 = 0x85, 0x85 xor 0xFF = 0x7A, 0x7A + 1 = 0x7B.

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

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

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

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



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

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 секунд. Значения перестали прыгать когда я отошел от сенсора.


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


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


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


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

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

У меня всего один датчик и я очень не люблю разбирать то, что однажды сделал, поэтому предлагаю выбрать вам.
Что сделать с датчиком?
11%
(32)
Подключить к компьютеру и написать десктопное приложение для мониторинга
16%
(42)
Добавить его в погодную станцию
7%
(18)
Сделать гаджет для мобильного устройства
63%
(166)
Добавить к нему ESP8266, превратив в минималистичный компонент для «умного дома»
3%
(7)
Свой вариант в комментарии

Проголосовало 265 человек. Воздержалось 40 человек.

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

Комментарии (51)


  1. nochkin
    04.03.2016 05:40
    +2

    Как раз получил такой же и планирую подключить с ESP8266.
    Насколько этот датчик врёт? Есть какой-то другой прибор для измерения что бы сравнить?


    1. Hellsy22
      04.03.2016 06:45
      +1

      У меня не с чем сравнивать.
      В комментариях на Али пишут: I compared the results with EXTECH-CO250 and MH-Z19 seems to be very accurate.
      Статья, где его сравнивают с другим похожим датчиком: http://foogadgets.blogspot.com/2016/02/new-co2-sensor-support-for-wms-mk3.html


      1. nochkin
        04.03.2016 07:06
        +1

        Спасибо за линк. Предполагаю, что точность ещё может от экземпляра зависеть.
        Я заказал более "взрослое" устройство для измерения CO2, но пока не получил. Попробую с ним сравнить тогда.


        1. Hellsy22
          04.03.2016 07:10

          О, будет замечательно, если вы выложите результаты сравнения.


        1. ShtAnigga
          04.03.2016 15:02

          а можно модель более "взрослого" устройства узнать?


          1. nochkin
            04.03.2016 20:12

            Обычно оно идёт как модель HT2000.

            Заголовок спойлера
            image


            1. ShtAnigga
              05.03.2016 03:09

              Благодарю, я почему-то подумал о другом, принципиальном решении.
              Предполагал голую железку с датчиком и контроллером.
              Для иных целей требуется


    1. SergeyRyb
      04.03.2016 10:11

      У нас в офисе уже почти год висит девайс на MH-Z14 (предыдущая версия датчика от того же производителя, протокол такой же) и ESP8266.

      Как выглядит

      Благодаря ему нашли проблемы с вентиляцией.
      Сейчас уже используем MH-Z19, датчик работает стабильно, но на мой взгляд слегка занижает показания CO2 (при открытом окне через полчаса после завершения рабочего дня выдаёт 400 ppm, при том что уровень CO2 в атмосфере уже слегка превышает эту отметку), либо у нас в городе (Ярославская область) очень чистый воздух.


      1. Duti_Fruti
        04.03.2016 10:24

        Жду MH-Z14. Решил заказать его. Если это младшая модель, почему она немного дороже? У нее есть преимущества?


        1. SergeyRyb
          04.03.2016 10:59
          +1

          Кажется, мне нельзя вставлять картинки (read&comment аккаунт), поэтому вот ссылка, как выглядит девайс:
          https://habrastorage.org/files/dd6/f94/c3d/dd6f94c3d4ca44888d2d287074a660b7.jpg
          Вроде как из видимых отличий — только наличие DAC-выхода у MH-Z14 и более высокая скорость реакции (не очень актуально для измерения CO2, его концентрация нарастает очень плавно).
          Скорее всего оптимизировали производство, сократили себестоимость и таким образом продвигают новый продукт.
          Для нас MH-Z19 оказался наилучшим вариантом, т.к. имеет гораздо меньшие габариты, два окна воздухозабора и потребляет почти в 3 раза меньше энергии.


          1. Duti_Fruti
            04.03.2016 12:32

            Все ясно. Значит зря переплачивал, если он хуже или такой-же за большие деньги.


          1. Rumlin
            04.03.2016 15:00

            Напишите публикацию про это. Какой средний потребляемый ток? Как часто просыпается для измерений и передачи данных?


          1. ShtAnigga
            05.03.2016 03:11

            А брали на али или ебее? Дорогой модуль?
            Сейчас по 7 тысяч они.


          1. ShtAnigga
            05.03.2016 03:20

            все нашел.Вопрос исчерпан


      1. Norno
        05.03.2016 15:29

        Чтобы датчик показывал истинные значения его требуется периодически калибровать на специальных смесях газов, в противном случае неизбежен дрейф показаний. Не знаю как в этом датчике, но K-22, K-30 (или во всяком случае приборы на их основе) имеют, так называемую ABC калибровку, они принимают за 400 ppm самый свежий воздух которые видели, кажется, за неделю, при этом сдвиг не может превышать 50 ppm. Соответственно такой прибор показыват не концентрацию CO2, а «отношения» к концентрации за окном. Ну а если хочется именно настоящие показания, то без калибровки по смесям не обойтись, причем делать это придется раз полгода-год.


  1. tormozedison
    04.03.2016 06:38

    Если к PWM подключить интегрирующую цепочку, получится замечательный аналоговый выход.


    1. Zolg
      04.03.2016 09:11
      +1

      Суровая получится rc-цепочка. Частота ШИМа-то 1гц.


      1. tormozedison
        06.03.2016 14:11

        Электролит на 5000 мкФ ничуть не суров.


        1. Zolg
          06.03.2016 16:16
          +2

          Электролит в измерительной цепи? Хорошая шутка.


          1. tormozedison
            07.03.2016 16:18

            Это смотря какая точность нужна.


  1. alexpp
    04.03.2016 10:08

    Спасибо за обзор. Как раз собираю домашнюю метеостанцию на esp — пригодится.


  1. KorDen32
    04.03.2016 10:12

    Предложу два варианта, второй предпочтительнее IMHO:
    1) подключить к GPIO малины и написать bash/perl/… скрипт
    2) подключить к PL2303 и написать bash/perl/… скрипт для того чтобы можно было подключать к роутерам (MR3020, etc), компьютерам и прочему без привязки к платформе


    1. Hellsy22
      04.03.2016 19:55

      А чем плох вариант с FT232RL?


      1. KorDen32
        04.03.2016 23:15
        -1

        Ну или FT232RL, речь о реализации готового скрипте, который будет выводить в ответ человекочитаемые значения (как в примере через ардуину), проверяя CRC-суммы и проч (скажем, предусмотреть возможность указания номера датчика, если есть модели с несколькими датчиками и т.д.).
        Т.е. чтобы было что-то типа
        ```~# ./getco2.sh
        785
        Возможно, сразу предусмотреть обнаруженные вами проблемы, скажем с ключом --check выводить предупреждения, если значение является заведомо ошибочным (скажем ниже 300) или скачет (например, запрашивая дважды с интервалом в 10-15 сек, если значения разнятся (учитывая ваши примеры ошибок)).


        1. Hellsy22
          05.03.2016 03:04
          +1

          Не думаю, что стоит оформлять это отдельной статьей, хотя если кому-то нужно, то могу раскрыть тему подробнее.

          В конфиге ядра должен быть ключ: CONFIG_USB_SERIAL_FTDI_SIO=m
          При подключении девайса в dmesg появится что-то типа:

          ftdi_sio 2-1.7:1.0: FTDI USB Serial Device converter detected
          usb 2-1.7: FTDI USB Serial Device converter now attached to ttyUSB0
          Получить название порта можно командой: ls /dev/ttyUSB*
          Чтобы номер не скакал, его можно намертво прибить с помощью udev.
          Для работы perl-скрипту нужна библиотека Device::SerialPort, легко ставится через cpan или вручную.

          Скрипт будет выглядеть так
          #!/usr/bin/perl
          
          use strict;
          use Device::SerialPort qw( :PARAM :STAT 0.07 );
          
          # ls -al /dev/ttyUSB* or set manualy
          my $port_id = `ls /dev/ttyUSB*`;
          chomp $port_id;
          $port_id =~s/\s+.+//;
          print "Using port: $port_id\n";
          
          my $ob = new Device::SerialPort($port_id, 1) || die "Can't open $port_id: $!\n";
          
          $ob->baudrate(9600);
          $ob->parity('none');
          $ob->databits(8);
          $ob->stopbits(1);
          $ob->handshake('none');
          $ob->write_settings();
          
          $ob->write("\xFF\x01\x86\x00\x00\x00\x00\x00\x79");
          sleep 1;
          
          my $c = $ob->input || die "Read fail";
          my @data = map {ord($_)} split('', $c);
          
          my $crc = 0;
          $crc+=$data[$_] for (1..7);
          $crc = (($crc & 255) ^ 255) + 1;
          die "CRC error: $crc / $data[8]" if ($crc ne $data[8]);
          
          my $co2 = $data[2] * 256 + $data[3];
          print "OK: $co2\n";
          
          $ob->close();


  1. eta4ever
    04.03.2016 11:16
    +1

    Раз уж проголосовал за свой вариант — закажу и сделаю пока что простейший показометр с маленьким дисплеем (четыре семисегментника) на AVR. А потом уже подумаю, куда его внедрить.


  1. jehy
    04.03.2016 11:17

    Четвёртый вариант из голосования (погодная станция с ESP8266) я как раз делал. По SoftwareSerial тоже. Можете в ссылки добавить — по комментариям видно, что много кого интересует, а время на повторное написание имеющегося кода тратить не очень осмысленно. Кстати, опрашиваю раз в 5 секунд — проблем не замечено. У меня, кстати, инициализируется и выдаёт корректные данные после второго обращения, ждать 3 минуты не нужно — а как у вас? И реагирует на изменение обстановки мгновенно. Да, вы проверяли контрольную сумму? И у меня и у другого программиста из моего топика она упорно не сходилась, так что в результате остановились на проверке первых трёх байт.


    1. Hellsy22
      04.03.2016 19:52

      Конечно добавлю, хотя результатов голосования это не отменит.

      Опрашивать датчик можно хоть сразу, но с холодного старта он будет выдавать ошибочные значения. Однако есть тонкость — если вы датчик просто перезапустили, то ждать не надо.

      Насчет контрольной суммы — она у меня сходится, даже в статье пример взят с реальных данных. Если у вас не сошлась, то предположу, что проблема или в unsigned, или в том, что вы не ограничили ее одним байтом. Попробуйте вручную посчитать — там всего-то 8 операций.


  1. KonstantinSoloviov
    04.03.2016 11:33

    Что сделать с датчиком?
    Добавить индикатор, таким образом получить полнофункциональное устройство аналогичное:

    image
    но ВДВОЕ дешевле. Датчики температуры и влажности по вкусу.


    1. jehy
      04.03.2016 11:43

      Справедливости ради скажу, то не вдвое. MH-Z19 стоит уже сам по себе заметных денег, а ещё нужен корпус, дисплей, ардуина и желательно датчик температуры и влажности. У меня вот получилось за 44$, что на данный момент равно 3224р. Даджетовский сейчас, судя по сайту, стоит 4950 — дороже в полтора раза. "Всего" или "в целых" полтора раза — это уже от ваших потребностей, способностей и достатка зависит.


      1. KonstantinSoloviov
        04.03.2016 12:55
        +2

        Даджетовский сейчас, судя по сайту, стоит 4950 — дороже в полтора раза.
        Вполне адекватная цена, у меня как раз такой — отличная вещь.
        Но далеко не все считают эту цену приемлемой.
        В 2500 вписаться легко можно: датчик, attiny2313 (удобный UART, ножек в достатке, кварц не нужен на таких скоростях), индикатор на выбор ЖК или цифровой-LED и по-мелочи — пара конденсаторов.
        Пожалуй и на температурный датчик останется )

        Если уж считаем копейки, то ардуино здесь лишнее.


        1. jehy
          04.03.2016 13:01

          Ардуино нынче меньше 2$ стоит — не дороже датчика температуры, это уже совсем копейки.
          Сэкономить значимо можно разве что на корпусе — если руки не кривые, можно из чего угодно собрать.


      1. Hellsy22
        04.03.2016 20:00
        +1

        Да, как-то так.

        MH-Z19 — 26$
        Arduini Mini Pro — $1
        ESP8266 — 1.5$
        LCD 16x2 — 1.5$
        DHT22 — 2.5$
        BMP180 — 1.5$
        Итог: 34$

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


        1. Nerten
          07.03.2016 01:13

          А зачем здесь Arduino?


          1. Hellsy22
            07.03.2016 03:19

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


            1. jehy
              07.03.2016 10:45

              Берите arduino compatible плату на ESP — будет одна плата с кучей выходов и интернетом. У меня вот используется wemos d1.


  1. Bobnecat
    04.03.2016 12:04
    +2

    Я свою метеостанцию как раз на этом датчике собрал, только с ШИМ. По своим впечатлениям скажу что датчику действительно нужно 3 минуты разогрева чтобы начать работать более менее адекватно, и около 1-3 дня работы для повышения чувствительности. Когда его включил в систему первый раз, то был немного разочарован так как в большой комнате по началу он не замечал присутствия 1 человека, но поработав пару дней научился этому. Сейчас даже чувствует присутствие человека находящегося в другой комнате в квартире размером в 100м2. «Чистое» значение воздуха находится в пределах 420 ppm.


  1. iliasam
    04.03.2016 12:52
    +2

    Тоже экспериментировал с этим датчиком. Повторюсь здесь, а то в обзоре нет информации по потреблению питания.

    Напряжение питания должно быть больше 3.6В, если оно будет ниже, то датчик начинает сильно завышать показания.
    Потребление тока у датчика импульсное, при напряжении 5В большую часть времени ток 4 мА, но каждые 6 сек потребление возрастает до 100 мА, длительность импульса меньше секунды.
    То есть датчик производит измерения раз в 6 секунд, и нет смысла опрашивать его чаще.
    Ранее написал маленькую программу — построитель графика CO2 для этого датчика, может, кому нибудь пригодится.
    Сложилось ощущение, что датчик начинает работать точней, проработав хотя бы полдня.
    Сейчас делаю автономный прибор для измерения CO2, с возможностью строить графики и передавать данные на компьютер при помощи NRF24L01:


    1. CAJAX
      04.03.2016 13:14
      +2

      В темноте, кстати, через фильтры видно пульсацию диода во время замеров.


  1. Big_Lebowski
    04.03.2016 15:09

    Отвлеченный вопрос — А как ESP8266 подключить что угодно с PWM? По-видимому, нужны какие-то дополнительные внешние компоненты, но какие?


    1. Rumlin
      04.03.2016 15:44

      В случае "что угодно" может быть понадобится согласовать уровни 3.3/5.


      1. Big_Lebowski
        04.03.2016 16:23

        Не, это я понимаю. У ESP есть только GPIO ноги, видимо нужен внешний АЦП, вот я и хочу узнать какой можно использовать.


        1. Rumlin
          04.03.2016 17:10

          PWM — это не аналоговый сигнал.

          image
          К слову АЦП там есть http://esp8266.ru/forum/threads/tochnost-adc.598/


          1. Big_Lebowski
            04.03.2016 17:17

            Спасибо. У меня в голове каша :) Это чтобы выдавать на GPIO ноге определенное напряжение используют PWM, а не наоборот


            1. Rumlin
              04.03.2016 18:50

              PWM — ШИМ. Широтно Импульсная Модуляция — Импульсы разной длительности, что отображает картинка выше.
              А "определенное напряжение" — этим занимаются ЦАП(DAC) — Цифро-аналоговые преобразователи.


    1. Big_Lebowski
      04.03.2016 16:28

      А, я все понял. Это у меня неудачная плата esp8266 на которой не распаян ADC. Вопрос снимается.


    1. Hellsy22
      04.03.2016 20:07

      Ничего не нужно. Ловите переходы GPIO HIGH/LOW и засекаете время каждого состояния. Время, пока GPIO был HIGH — это и есть Thigh в формуле, Tlow — соответственно.

      Для Arduino код выглядит так
      void loop() {
        long tt = millis();
        int myVal = digitalRead(pwmPin);
      
        //Если обнаружили изменение
        if (myVal == HIGH) {
          digitalWrite(LedPin, HIGH);
          if (myVal != prevVal) {
            h = tt;
            tl = h - l;
            prevVal = myVal;
          }
        }  else {
          digitalWrite(LedPin, LOW);
          if (myVal != prevVal) {
            l = tt;
            th = l - h;
            prevVal = myVal;
            ppm = 5000 * (th - 2) / (th + tl - 4);
            if (ppm < 5000 && (!last_ppm || ppm > last_ppm + 10 || ppm < last_ppm - 10)) {
              last_ppm = ppm;
              Serial.println("PPM = " + String(ppm));
            }
          }
        }
      }

      Это не лучший вариант, правильно было бы отслеживать изменения через прерывание, но вполне рабочий.


  1. boogiebomzh
    04.03.2016 16:32

    Объясните, пожалуйста, как работает этот датчик. Если это он инфракрасный, то, чтобы получить точные значения содержания углекислого газа в воздухе, надо знать еще и влажность, либо как-то осушать воздух, поскольку у водяного пара и углекислого газа спектры поглощения в ИК-диапазоне частично совпадают. В одном из проектов по изучению фотосинтеза, в котором я принимал участие, воздух сперва проходил через холодильник, где конденсировалась лишняя влага, а затем через химический осушитель, и только после этого подавался на инфракрасный газоанализатор для определения содержания углекислого газа.


    1. Hellsy22
      04.03.2016 20:17
      +1

      Судя по обрывочным сведениям о его младшем брате MH-Z14 и других NDIR-датчиках, он умеет измерять температуру и влажность, корректируя свои показания. MH-Z14 даже возвращал температуру пользователю.

      Ваши приготовления, вероятно, были связаны с высокой точностью измерений, тогда как у Z19 точность ± (50ppm + 5% reading value).


  1. 3cky
    07.03.2016 04:30

    Все-таки непонятно, по документации вход RX 5v tolerant или нет? В статье написано, «5В не рекомендуется», но модуль при этом воткнут в ардуину без каких-либо логических трансляторов.


    1. Hellsy22
      07.03.2016 04:40

      Это разница между макетом для проверки и готовой схемой. На макете кратковременное подключение к 5В проблем не выявило, но я не знаю, что будет если подавать пять вольт в течение полугода. Поэтому в конечной схеме я бы использовал резисторный делитель.

      Аналогично и с напряжением питания. Сенсор работает даже на 3.3В. Но производитель указывает в качестве напряжения питания 3.6 — 5.5В, причем отдельной строкой подчеркивает важность соблюдения этих рамок.