Я сам заинтересовался этим вопросом недавно и решил пощупать датчик CO2 MH-Z19B своими руками. Меня вдохновило много статей, например Тёмная сторона MH-Z19.
Однако нигде (даже в инструкции) я не нашел как работать с пином Vo…
Я обратил внимание вот на что: в статье Обзор инфракрасного датчика CO2 MH-Z19 упоминается такой пункт:
- Vo — выходное напряжение 3.3В, не более 10мА
На некоторых интернет-магазинах, например, "Датчик углекислого газа CO2 MH-Z19B" указывается подобная информация:
- Vo — выходное напряжение 3.3В, не более 10мА
(как под копирку)
Но в инструкции (тут) указано чуть подробнее:
- Vo Analog output (0.4~2 V)or (0~2.5V)
А если копнуть еще, то в инструкции (тут) указано еще подробнее:
- Vo (Pin2) — Analog output (0.4~2 V) (0~3V range could be customized)
Я быстро погуглил и… ничего не нашел про этот выход! Странно, подумал Штирлиц, быстро подключил этот выход на аналоговый вход ESP8266 (для Arduino это тоже актуально) и начал исследовать.
Выводы:
- Напряжение на аналоговом выходе изменяется от 0,4xx до 2,002в.
- Аналоговый выход повторяет напряжением цифрового сигнала ppm.
- При изменении командами UART диапазона ppm 2k, 5k, 10k соотвественно изменяется диапазон преобразования Vo.
- Максимальное значение Vo=2,001в не изменяется в зависимости от диапазона, в качестве диапазона преобразования нужно устанавливать максимальное значение в ppm (2/5/10к).
- Минимальное значение Vo изменяется в зависимости от диапазона (?), а сам диапазон начинается от 400ppm.
- Если подобрать фактическое значение АЦП контроллера, то можно получить достойное соотвествие между напряжением и уровнем ppm. В диапазоне до 2k погрешность между цифровым сигналом UART и аналоговым преобразованием Vo не превышает нескольких единиц ppm.
- Можно или с помощью преобразователя USB-TTL или с помощью Arduino+ первично настроить датчик (отключить автокалибровку и перевести в «домашний» диапазон 400...2000ppm) и далее работать с ним как с аналоговым сигналом.
Как я преобразовывал:
[...]
const word cADC00v = 3; // ADC = 3 уе. при 0.0v
const word cADC04v2k = 235; // ADC = 235 уе. при 400ppm @2k
const word cADC04v5k = 174; // ADC = 174 уе. при 400ppm @5k
const word cADC04v1k = 153; // ADC = 153 уе. при 400ppm @10k
const word cADC20v = 646; // ADC = 646 6уе. при 2.0v
word ADC = analogRead(A0); // Чтение 10 бит ADC 0...1023
long Vin = map(ADC, cADC00v, cADC20v, 0, 2001); // Напряжение 0..3,3v по даташиту, фактически до 2,001v
long ppm_2k_04_2 = map(ADC, cADC04v2k,cADC20v, 400, 2000); // Преобразование Vo в ppm в диапазоне 2k
long ppm_5k_04_2 = map(ADC, cADC04v5k,cADC20v, 400, 5000); // Преобразование Vo в ppm в диапазоне 5k
long ppm_10k_04_2 = map(ADC, cADC04v1k,cADC20v, 400,10000); // Преобразование Vo в ppm в диапазоне 10k
[...]
«Магические» числа подобрал экспериментально. Так получилось и быстрее и точнее. При попытке посчитать очень точно, математически, фактически получилась большая погрешность.
Почему у меня так работает (0,4...2,0в а не 0...2,5в) я не знаю. Датчик куплен пару дней назад, производство 26 сентября 2019г. На корпусе выбит диапазон до 5к ppm.
Зачем это мне нужно? (ответ на «вот я бы сделал не так...»)
Ну… есть люди, которые любят держать паяльник в руках, а вот с контроллерами не дружат. Для них аналоговый выход это как минимум включить/выключить вентилятор, как максимум — плавное управление скоростью вытяжки/приточки/рекуператора.
А конкретно у меня: в соседней комнате уже 2 года работает система вентиляции, которую я сделал на свободно-программируемом промышленном контроллере. Т.к. контроллер промышленный, то и система программирования специфическая, не уверен, что на нем можно написать свой протокол обмена по UART. Да, там есть последовательный порт RS484, который прекрасно работает по ModBus, но этот порт занят системой диспетчеризации.
А вот что есть — свободный аналоговый вход, который можно настроить на 0-10в. И так имея всего около 30$ можно проапгрейдить систему вентиляции на интеллектуальное энергосбережение (вентилировать только тогда, когда нужно, и ровно на столько, на сколько нужно).
P.S.: код SoftSerial.readBytes(mhzBuffer, 9); на моем ESP8266 отказался работать. Убил на это около пяти часов жизни. Здесь это обсуждали, но прочитал я уже позже. Писал свою функцию в стиле
if (SoftSerial.available() { x=SoftSerial.read(); }
.
vsergoog
А разве порт RS485 не подразумевает использования шины с разделением на master/slave(s)? То есть к нему можно подключать более одного устройства одновременно.
veoramid Автор
Да. И это параллельное подключение подразумевает арбитраж. В промышленных системах, с которыми я работал, обычно используется главное устройство диспетчеризации или мониторинга, которое собирает данные со всех слейвов. Поэтому стандартный modbus подразумевает, что 99.99% контроллеров — слейвы и только отвечают на команды, а мониторинги/диспетчеры 99.99% — мастера только посылают команды.
А тут мне надо со слейва опросить еще одно устройство — вообще не представляю как это сделать на готовой системе (*).
(*) если система своя, и проектируется с нуля, можно предусмотреть алгоритм передачи «мастера» от контроллера к контроллеру по команде или таймауту. Или придумать вариант «транзитной» передачи данных. Например у контроллеров MPXpro Carel есть возможность собрать мини-сеть из 6 контроллеров. Из этих 6 контроллеров один главный и 5 второстепенных. Главный может общаться со второстепенными по своему протоколу и при этом быть «слейвом» для системы мониторинга. Когда мониторинг присылает запрос на второстепенный контроллер, главный контроллер работает шлюзом, пропуская все команды сквозняком.
Но в любом случае этот алгоритм зашит на заводе Carel и я ничего не могу изменить. Только пользоваться «как есть».