According to the MH-Z19B datasheet, you can configure the measurement range by putting the desired range in byte 3 and 4. However, unlike what the MH-Z19B datasheet says, you can set the range using the following command (in this case 0x07d0 = 2000 ppm in byte 6 and 7)
#include <SoftwareSerial.h>
#define pwmPin 10
SoftwareSerial swSerial(A0, A1); // RX, TX
void setup() {
Serial.begin(9600);
swSerial.begin(9600);
pinMode(pwmPin, INPUT);
/*
Источник - https://revspace.nl/MHZ19
2000 ppm range: 0xFF, 0x01, 0x99, 0x00, 0x00, 0x00, 0x07, 0xD0, 0x8F
5000 ppm range: 0xFF, 0x01, 0x99, 0x00, 0x00, 0x00, 0x13, 0x88, 0xCB
*/
// Этот вариант ("A") с записью команды в 6й и 7й байт - работает
// bytes: 3 4 6 7
byte setrangeA_cmd[9] = {0xFF, 0x01, 0x99, 0x00, 0x00, 0x00, 0x13, 0x88, 0xCB}; // задаёт диапазон 0 - 5000ppm
unsigned char setrangeA_response[9];
swSerial.write(setrangeA_cmd,9);
swSerial.readBytes(setrangeA_response, 9);
int setrangeA_i;
byte setrangeA_crc = 0;
for (setrangeA_i = 1; setrangeA_i < 8; setrangeA_i++) setrangeA_crc+=setrangeA_response[setrangeA_i];
setrangeA_crc = 255 - setrangeA_crc;
setrangeA_crc += 1;
if ( !(setrangeA_response[0] == 0xFF && setrangeA_response[1] == 0x99 && setrangeA_response[8] == setrangeA_crc) ) {
Serial.println("Range CRC error: " + String(setrangeA_crc) + " / "+ String(setrangeA_response[8]) + " (bytes 6 and 7)");
} else {
Serial.println("Range was set! (bytes 6 and 7)");
}
delay(1000);
/*
// Этот вариант ("B") с записью команды в 3й и 4й байт, согласно даташиту - НЕ работает и поэтому закомментирован
// bytes: 3 4 6 7
byte setrangeB_cmd[9] = {0xFF, 0x01, 0x99, 0x00, 0x00, 0x00, 0x13, 0x88, 0xCB};
unsigned char setrangeB_response[9];
swSerial.write(setrangeB_cmd,9);
swSerial.readBytes(setrangeB_response, 9);
int setrangeB_i;
byte setrangeB_crc = 0;
for (setrangeB_i = 1; setrangeB_i < 8; setrangeB_i++) setrangeB_crc+=setrangeB_response[setrangeB_i];
setrangeB_crc = 255 - setrangeB_crc;
setrangeB_crc += 1;
if ( !(setrangeB_response[0] == 0xFF && setrangeB_response[1] == 0x99 && setrangeB_response[8] == setrangeB_crc) ) {
Serial.println("Range CRC error: " + String(setrangeB_crc) + " / "+ String(setrangeB_response[8]) + " (bytes 3 and 4)");
} else {
Serial.println("Range was set! (bytes 3 and 4)");
}
delay(1000);
*/
}
void loop() {
byte measure_cmd[9] = {0xFF,0x01,0x86,0x00,0x00,0x00,0x00,0x00,0x79};
unsigned char measure_response[9];
unsigned long th, tl, ppm = 0, ppm2 = 0, ppm3 = 0;
// ***** узнаём концентрацию CO2 через UART: *****
swSerial.write(measure_cmd,9);
swSerial.readBytes(measure_response, 9);
int i;
byte crc = 0;
for (i = 1; i < 8; i++) crc+=measure_response[i];
crc = 255 - crc;
crc += 1;
if ( !(measure_response[0] == 0xFF && measure_response[1] == 0x86 && measure_response[8] == crc) ) {
Serial.println("CRC error: " + String(crc) + " / "+ String(measure_response[8]));
}
unsigned int responseHigh = (unsigned int) measure_response[2];
unsigned int responseLow = (unsigned int) measure_response[3];
unsigned int ppm = (256*responseHigh) + responseLow;
// ***** узнаём концентрацию CO2 через PWM: *****
do {
th = pulseIn(pwmPin, HIGH, 1004000) / 1000;
tl = 1004 - th;
ppm2 = 2000 * (th-2)/(th+tl-4); // расчёт для диапазона от 0 до 2000ppm
ppm3 = 5000 * (th-2)/(th+tl-4); // расчёт для диапазона от 0 до 5000ppm
} while (th == 0);
Serial.print(ppm);
Serial.print(" <- ppm (UART) ");
Serial.print((ppm/5)*2);
Serial.println(" <- two fifths of it"); // Потом пришло озарение
Serial.print(th);
Serial.println(" <- Milliseconds PWM is HIGH");
Serial.print(ppm2);
Serial.println(" <- ppm2 (PWM) with 2000ppm as limit");
Serial.print(ppm3);
Serial.println(" <- ppm3 (PWM) with 5000ppm as limit");
Serial.println("-----------");
delay(5000);
}
— 350 — 450 ppm: Нормальный уровень на открытом воздухе.
— < 600 ppm: Приемлемые уровни. Уровень. рекомендованный для спален, детских садов и школ.
— 600 — 1000 ppm: Жалобы на несвежий воздух, возможно снижение концентрации внимания.
— 1000 ppm: Максимальный уровень стандартов ASHRAE (American Society of Heating, Refrigerating and Air-Conditioning Engineers) и OSHA (Occupational Safety & Health Administration).
— 1000 — 2500 ppm: Общая вялость, снижение концентрации внимания, возможна головная боль.
— 2500 — 5000 ppm: Возможны нежелательные эффекты на здоровье.
Range was set! (bytes 6 and 7)
1227 <- ppm (UART) 490 <- two fifths of it
606 <- Milliseconds PWM is HIGH
1208 <- ppm2 (PWM) with 2000ppm as limit
3020 <- ppm3 (PWM) with 5000ppm as limit
Range was set! (bytes 6 and 7)
1213 <- ppm (UART) 484 <- two fifths of it
241 <- Milliseconds PWM is HIGH
478 <- ppm2 (PWM) with 2000ppm as limit
1195 <- ppm3 (PWM) with 5000ppm as limit
+23 |
1638
52
|
+150 |
3475
43
|
+45 |
2398
83
|
ну и было бы полезно указать принцип действия датчика. IR в даташите внушает оптимизм. Но после сказанного про дата в том шите…
Картинка из www.co2meter.com/blogs/news/6010192-how-does-an-ndir-co2-sensor-work — тут объясняется принцип работы. Вкратце — датчик считает поглощение света определённой длины волны и так определяет концентрацию CO2
Я лет 20-30 назад долго ругался матом на апликейшн от интела.
Два дня потреял, когда попытался для экономии времени стянуть кусок кода из апликейгена.
А можете посоветовать датчик pm 2.5? А то у нас в городе не всегда хороший воздух (из-за того, что есть много частных домов с печками, где топят чем попало).
Купил такой для самодельной станции где-то год назад.
Пришлось повозиться с переделкой электроники с софтом и калибровкой ЦАПа на ESP.
Я немного копнул этот вопрос и сделал вывод, что проще всё делать в мелкой ардуинке, а на ESP транслировать показания, мороки меньше.
www.letscontrolit.com/wiki/index.php/Official_plugin_list
(я для MH-Z19 и Sensair S8 использую именно эту прошивку для ESP, в случае необходимости и самому чего подправить можно)
www.letscontrolit.com/wiki/index.php/GP2Y10
На выходе датчика поставил резисторный делитель чтобы уложить вольтаж в границы АЦП ESP 1.024 вольт. Схема есть, если надо.
Для ESP я использую прошивку WiFi IoT и там уже задействую АЦП и один GPIO (15), скрипт написан на С подобном языке, необходимые тайминги выдерживаются.
Поправочные коэффициенты подобрал укутывая датчик в кучу слоев спец фильтровальной ткани и прокачивая воздух насосом. Верхний предел — перекрывая окно карандашом.
Но все равно показания плавают, надо добавить фильтр скользящего окна.
Код выполняется каждые 20 секунд в общем цикле, в конструкторе выглядит так:
Ну или ткнуть пальцем в ссылочку где это лежит?
Схема переделки самого датчика
Полный листинг кода (см по схеме — у меня дисплей 4х20)
opkg install coreutils-stty kmod-usb-serial-ftdi nano
nano /root/mh_z19.sh
chmod +x /root/mh_z19.sh
nano /etc/rc.local
/etc/init.d/cron start
/etc/init.d/cron enable
export EDITOR=nano
crontab -e
Вот так выглядит, когда ты ушёл из дома, и чистота воздуха плавно стремится к отметке 400ppm :-) Когда дома подышишь, и график пересечёт 700ppm, включается вытяжка, и значение примерно так же падает к 500ppm, вытяжка выключается
Не совсем из китая. И после того, как $ скаканул в 2 раза — разумнее стало отнести денег даджету.
А на али ищите по CO2 logger, но дешево всё-равно не будет.
cdn-shop.adafruit.com/product-files/3660/BME680.pdf — см. стр. 8 и цветную табличку на стр. 10
зато в нём барометр и датчик влажности встроен
BME280 5V Atmospheric Pressure Sensor Module for Arduino High Precision New
придётся подождать, что фактически получит chaloc (и, разумеется, это не даст следующему покупателю никаких гарантий)
Это в шестнадцатиричном виде если 16-битное слово — FFFF, то байты у него — FF и FF.
А в восьмеричном если слово 177777, то байты — 377 и 377.
Хде логика, блин?
Практически приходилось в уме частично переводить в двоичный вид…
sqrt(017)=03.., всё честно
и так же красиво с эквивалентным 12-битным словом 07777 :)
Именно потому, что основание натурального логарифмы выгодно в качестве основания системы счисления для ЭВМ, но затруднительно как то сделать цифровую машину с дробным основанием системы счисления (блин, сам уже запутался), была таки машинка с троичным основанием.
Сетунь называлась, на год младше меня… :)
Вот мой, без b, график за полсуток.
Разные показания по UART и PWM у датчика были «из коробки», что и сподвигло на расследование, собственно.
Качественное «запарывание» описано тут — geektimes.ru/post/285572/ — товарищ аж в недокументированный режим MODBUS прибор загнал и снёс калибровку, но для этого он просто грубой силой перебирал коды.
Еще нюансы: датчик выходит на режим через 3 минуты после включения, по UART часто нельзя запрашивать данные (не чаще раза в 10 секунд), UART и PWM могут иметь разные диапазоны, встречал что UART 5000ppm, а PWM 2000ppm, работа сенсора заявлена от 3.6V, 3.3V не подойдут.
Да, 3.3 вольта не подходит — сенсор начинает дико «тупить» и выдаёт значения по верху диапазона.
Оттого веселят другие обзоры на этот модуль, где авторы просто втыкают его и верят показаниям.
Играюсь как раз с этим датчиком достаточно давно (около года). Но недавно решил сравнить MH-Z19, MH-Z19B и Sensair S8 (0053)
1) Этап 1:
В конце прошлого года тестировал одновременно (стояли на расстоянии 2см друг от друга):
2 штуки MH-Z19
2 штуки MH-Z19B (один с отключенной ABC, второй с включенной)
Вывод: MH-Z19B «из коробки» работет вполне нормально (если помещение нормально проветривается). Если помещение проветривается плохо (к примеру раз в неделю) — то обязательно нужно отключать ABC (у MH-Z19B она реализована очень странно, период калибровки всего 1 день).
MH-Z19 можно выкинуть (свои убрал куда подальше). 400 показывает нормально, а вот все что выше — может врать на 200-400ppm легко.
2) Этап 2 (в процессе):
Все теже 2 штуки MH-Z19B (c включенной ABC калибровкой и с отключенной)
1 штука Sensair S8 (купленный за адекватные 36$ на тао (вместе с доставкой!))
Вывод (основанный пока только на 3 днях тестов, что само собой мало):
Нормально откалиброванный MH-Z19B (с отключенной ABC) практически полностью совпадает с S8.
В моем случае все 3 датчика стоят в хорошо прповетриваемой комнате (как минимум раз в сутки открываем окно) — в итоге все показания всех 3 датчиков совпадают (разброс максимум 100ppm, что укладывается в погрешность).
Для себя сделал вывод что вместо MH-Z19B все таки лучше предпочесть S8. Его 8 дней на калибровку выглядят куда адекватнее 1 дня у MH-Z19B.
Итого, за 40$ можно собрать миниатюрный анализатор CO2. Очень компактный, достаточно точный и дешевый.
Все что нужно — платка Wemos, 4 проводка и сам сенсор (картинка не моя) + готовая прошивка (EspEasy):
С термостабилизацией у него, кажется, всё грамотно — на сквозняке от форточки при минус пяти и дома при плюс 25 показания не разбегаются.
Про нагрев — да, есть такой момент. По этому себе буду собирать с ESPхой вниз (у Wemos Mini Pro одна стороная «голая», к ней и будет прижат датчик.
К слову о нагреве — MH-Z19B анализирует раза в 2 чаще (сужу визуально по морганию лампы) чем MH-Z19. И за счет этого греет существенно больше. При комнатной температуре 25, MH-Z19 нагревается до 30, а MH-Z19B аж до 36 (данные по их же внутренним датчикам температуры).
У меня еще есть mh-z14, правда его показания я ни с чем не сравнивал.
Накопал еще статейку geektimes.ru/post/285572/
Про реплику — нет, врядли. Общие у них только распиновка, внешние размеры и принцип действия.
MH-Z14 у меня нет. слишком громоздкий. Он кстати тоже бывает в версии B (думаю что изменения аналогичны MH-Z19 -> MH-Z19B)
На сколько я читал, S8 вполне себе промышленный датчик и используется более менее серьезных устройствах (тот же тионовский MagicAir)
У меня два «безб» лежат дома рядышком и показания совпадают в нижнем диапазоне до 50 единиц, а в верхнем до 200.
Днём при проветривании 'через щелочку' показания становятся 400, ночью поднимаются до 1500..2500. Без проветривания через неделю показывают какой-то бред.
Как итог — оба с B совпадают между собой, плюс совпадают с SenseAir S8.
А вот оба без B — могут показать 400 тогда, когда S8 и B показывают 600-700.
Для себя решил что без B однозначно в мусорку. Но это про мои датчики. Я не берусь утверждать что они все такие.
Когда выбирал, самый дешевый вариант был за 28$, приятно удивился. Но в описании увидел, что точность указана — 70ppm, хотя на официальном сайте указана точность — 40ppm. Похоже дело в версии платы — такая точность (70ppm) указана для версии Senseair S8 Article No. 004-0-0013, хотя продавец на тао указал что версия 053, что очевидно не так (там и фото говорит, что не 053). В итоге нашел современную версию платы (053), но самый дешевый вариант нашел за 44$, что тоже дешевле ebay/aliexpress, но дороже чем у вас.
Вы когда покупали, учитывали версию платы? Интерес вызван большим разбросом цен (28$, 36$, 44$) и честностью продавцов, может чего не учел.
Брал тут https://item.taobao.com/item.htm?id=536930055559
Но по-моему за 170-180 там все именно 0053
0013 дороже — около 210
Ищу всегда по «Senseair»
А как можно убедиться, что версия платы такая-то, когда прибор будет в наличии?
Мой выглядит в точности вот так
ae01.alicdn.com/kf/HTB10JgyPVXXXXc4XFXXq6xXFXXXv/SenseAir-Infrared-CO2-Carbon-Dioxide-Sensor-S8-0053.jpg
В том числе есть слегка смущающие меня 01.14. Надеюсь это не дата производства :)
Смотрю на статус покупки:
и не могу понять уже пора беспокоиться или все в порядке?
С одной стороны в таблице — статус «оплачено» (я рассчитывал на — «отправлено»). С другой стороны этот заказ находится в статусе «To be shipped». Как правильно это перевести? Будет оправленно? Уже отправлено?
Заранее спасибо!
(«to be continue» — «продолжение следует», но когда?)
«to be received» — «ожидает получения»
Внешний вид такой:
Никаких проблем с датчиком, пока питаешь его не менее 4в, как положено.
Прибор работает уже больше года, круглосуточно, всё штатно.
А то думаю K30 или CozIR LP взять, на taobao они примерно одинаково в районе 70 баксов стоят.
CozIR LP конечно нравится размерами и питанием от 3,3 В, как раз stm32 тоже от 3.3 работает. K30 насколько понимаю от 12 вольт можно запитать.
Датчик CO2 CCS811 ( CJMCU-811 )
homes-smart.ru/index.php/component/kunena/4-zhelezo/1333-datchik-co2-ccs811-cjmcu-811
он по форме не напоминает ход какого-нибудь реального показателя в/вблизи вашей локации?
版本号:1.3
实施日期:2017-12-08
例,2000ppm 量程命令:0xFF 0x01 0x99 0x00 0x00 0x00 0x07 0xD0 0x8F
Кстати, заметьте что в этом даташите нарисован датчик как у автора этого обзора — с разъемом под шлейф. А в даташите по ссылке данной автором обзора плата датчика без разъема. Вероятно версии (прошивки) датчиков изменяют со временем, а продавцы не указывают ее.
За ссылку большое спасибо!
«Третий Ангел вострубил, и упала с неба большая звезда, горящая подобно светильнику, и пала на третью часть рек и на источники вод»
гугл пока справляется:^
«Номер версии: 1.3
Дата реализации: 2017-12-08
Пример: команда диапазона 2000ppm: ...»
____
^ (с технической фигнёй:)
MH-Z19
https://www.banggood.com/MH-Z19-0-5000PPM-Infrared-CO2-Sensor-For-CO2-Indoor-Air-Quality-Monitor-UARTPWM-p-1094463.html
MH-Z19B
https://www.banggood.com/MH-Z19B-Infrared-CO2-Sensor-For-CO2-Monitor-NDIR-Gas-Sensor-CO2-Gas-Sensor-0-5000PPM-p-1248315.html
Но случайно наткнулся на espeasy — тоже готовая прошивка для ESP8266, но с поддержкой S8.
В итоге бОльшая часть девайсов у меня на wifi-iot, а там где используются CO2 датчики, там espeasy.
EspEasy кстати имеет более широкую поддержку в том числе и MH-Z19B — имеется возможность «читать» символ «U» по которому можно судить о том когда девайс самокалбируется, а так же есть возмоность эту самую автокалибровку отключить.
Кстати, имейте ввиду что MH-Z19B хорошо работет в помещениях которые хотя бы раз в сутки проветриваются до 400ppm. Если нет — автокалибровку обязательно нужно отключать.
На PWM, судя по всему, диапазон измерения фиксированный — до 2000.
И нигде в статьях не встречал упоминания, что можно снимать показания с V0 — аналогового выхода, где уровнь CO2 показывается напряжением на выходе.
Датчик MH-Z19B, использую UART. По итогу испытаний: датчик работает достоверно в обоих диапазонах (2000 и 5000) без каких-то корректирующих коэффициентов. Конечно, неплохо бы сверить с заведомо точным датчиком, но пока такой возможности нет.
Кое-кто пишет, что по UART нельзя часто опрашивать данные. А в чем проблема? Специально сравнивал графики с интервалом 5с и 60с — показания в целом совпадают (на 5c, конечно, заметнее выбросы типа «случайно выдохнул в сторону датчика»).
Автокалибровку отключил, при этом показания изменились не более 5 ppe (очевидно, датчик еще не успел перекалиброваться).
В целом датчик очень чувствительный и малоинерционный. Думаю, в готовом устройстве вентилятор для принудительного обдува не понадобится.
Свой MH-Z19B я в целях эксперимента опрашивал 10 раз в секунду, всё ок.
По результатам эксперимента сделал вывод что чаще чем 3 секунды нету смысла, ибо датчик меряет CO2 раз в 3 секунды, и до следующего измерения каждый раз выдаёт предыдущий результат измерений.
Посмотрев дорожки на просвет, разводка по цветам проводов:
Желтый — не распаян
Зеленый — RX (уровень сигнала 3.3В)
Синий — TX (уровень сигнала 3.3В)
Красный — Ground
Черный — V0 (выходное напряжение 3.3В, не более 10мА)
Белый — не распаян
Коричневый — ?
Последний распаян, но назначение непонятно. По логике это должен быть HD, но дорожка ведет не к нему, а куда-то внутрь.
http://style.winsensor.com/pro_pdf/MH-Z19B.pdf
Вооружившись переводчиком имеем:
Выходит, его и запитывать можно с этого разъема, а к контактам с другой стороны есть смысл лезть только если хочешь снимать результаты в ШИМ.
С датчиком играюсь уже около двух месяцев.
Автор всё супер написал, спасибо. Можно всё делать как он, и не париться, если бы не упоминание про мифический коэффициент.
Я в итоге показания своего датчика сравнивал с другими приборами. Совпадают 1 в 1.
Подскажите пожалуйста как решить проблему?
Использую датчик MH-Z19, данные собираю через PWM. На макетной плате все отлично работало.
Спаял проводами с ардуино, собрал во временный корпус, через час показания свалились к 400ppm.
Показывает только 395-404 ppm. После перезагрузки немного работал в нормальном режиме. Показывал похожие на правду значения, но снова стал показывать около 400.
Уже перепаивал все, возвращал на макетную плату, ничего не помогает.
Вот мой скетч, если интересно.
Я кучу скетчей перепробовал, и PWM и UART, ничего не показывает.
И наразных arduino и даже esp8266 на lua скрипт пробовал.
Вообще ничего не показывает.
Может мне китаец херню прислал?
Подключаю mh-z19b к arduino uno используя ваш скетч и столкнулся с двумя проблемами:
1) При попытке компиляции вашего кода возникает конфликт переменных:
Заменил переменную PPM на PPM1, однако не уверен, что это правильный путь.
2) Похоже, что отображаемые данные не вполне соответствуют действительности. Ниже показания датчика, находящегося у открытого окна:
Если я правильно расшифровываю данные, ppm = 784, что довольно много при открытом окне (датчик по сути на улице)
значение PWM выглядит гораздо убедительнее, хотя тоже есть сомнения, в сторону занижения значений.
Как думаете, почему может быть некорректная выдача? И как было бы правильно решить проблему с конфликтом переменных?
при покупке на Али я видел 2 релиза данного датчика на 2000 и на 5000, и заказал на 5000.
Не совсем понял, с какой целью реализовано подключение одновременно по UART и по PWM?
расшифруйте, пожалуйста.
Сабж может выдавать концентрацию одновременно по трём каналам. Непоняток по этому сильно интеллектуальному датчику хватает, и кто умеет все каналы свести к одинаковым показаниям — тот красавчик.
Мне не повезло, по UART полный молчок «CCR Error 0/0», если кто может подсказать — буду признателен.
#include <SoftwareSerial.h>;
//////////////////////////////////////////////////////////////////////////////////////////////
SoftwareSerial co2Serial(D1, D2); // RX- D2, TX — D1; //Wemos D1 mini
void setup() {
Serial.begin(9600);
co2Serial.begin(9600);
}
void loop() {
readCo2Sensor();
}
//////////////////////////////////////////////////////////////////////////////////
void readCo2Sensor() // Initialisiere CO2 Sensor MH-Z19
{
byte cmd[9] = {0xFF,0x01,0x86,0x00,0x00,0x00,0x00,0x00,0x79};
unsigned char response[9];
co2Serial.write(cmd, 9);
memset(response, 0, 9);
co2Serial.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]));
}
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);
}
/////////////////////////////////////////////////////////////////////////////////////////////
Добавил не задокументируемые функции, такие как сброс. Если чего, пишите на e-mail, в программе всё есть.
"…
unsigned long th, tl, ppm = 0, ppm2 = 0, ppm3 = 0;
unsigned int ppm = (256*responseHigh) + responseLow;
..."
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.