Кто пробовал пользоваться бюджетным датчиком вредных газов MQ-135 для анализа концентрации углекислого газа CO2 в воздухе, то сталкивались с неточностями в показаниях, с зависимостью от температуры окружающей среды, с трудностями калибровки и перевод в стандартные единицы PPM. Рассмотрим датчик уровня углекислого газа MH-Z19, который и точнее и эргономичнее и удобнее.
Сперва просто подключим датчик к Arduino Nano, а ниже соберем прибор с дисплеем и датчиком температуры.
Необходимые материалы
Подключения к ШИМ выходу
Скетч
Чтение показаний датчика с выхода PWM и вывод значений в монитор порта. Формулу для перевода синала ШИМ в PPM единицы можно найти в тех.описании датчика.
#define pwmPin 5 #define LedPin 13 int prevVal = LOW; long th, tl, h, l, ppm; void setup() { Serial.begin(9600); pinMode(pwmPin, INPUT); pinMode(LedPin, OUTPUT); } 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); Serial.println("PPM = " + String(ppm)); } } }
Теперь соберем мобильный прибор для измерения уровня CO2, температуры и влажности. Так можно будем измерить показания как в помещении так и на улице.
Схема подключения
Скетч
Для переносного датчика MH-Z19 с дисплеем Nokia 5110. Потребуются библиотеки Adafruit_GFX_5110 и DHT11.
#include <SPI.h> #include <Adafruit_GFX.h> #include <dht11.h> #include <Adafruit_PCD8544.h> Adafruit_PCD8544 display = Adafruit_PCD8544(7, 6, 5, 4, 3); dht11 DHT; #define DHT11_PIN 8 #define pwmPin 2 #define LedPin 13 int prevVal = LOW, times = 0; long th, tl, h, l, ppm; void setup() { Serial.begin(9600); pinMode(pwmPin, INPUT); pinMode(LedPin, OUTPUT); display.begin(); display.clearDisplay(); display.setContrast(60); display.setTextSize(2); display.setTextColor(BLACK); } void loop() { long tt = millis(); //чтение PWM от CO2 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); Serial.println("PPM = " + String(ppm)); display.clearDisplay(); display.setCursor(0, 0); if (ppm < 1000) display.println(String(ppm) + "ppm"); else display.print(String(ppm) + "ppm"); times++; if (times >= 3) { times = 0; int chk = DHT.read(DHT11_PIN); // Чтение данных switch (chk) { case DHTLIB_OK: break; case DHTLIB_ERROR_CHECKSUM: display.println("Checksum error, \t"); break; case DHTLIB_ERROR_TIMEOUT: display.println("Time out error, \t"); break; default: display.println("Unknown error, \t"); break; } // Выводим показания влажности и температуры display.print("Hum:"); display.println(DHT.humidity, 1); display.print("Temp:"); display.println(DHT.temperature, 1); } display.display(); } } }
One question - what results did you read from this sensor? I'm testing the same model and returned values are still around 420 ppm. I have another CO2 meter (commercial product) and results are completely different - when this one displays e.g. 800 ppm, MH-Z19 still returns around 420 ppm. Only when I breath out directly to MH-Z19, returned value slowly raises and after couple of seconds return back to "standard" ...
Thanks.
ppm = 5000 * (th - 2) / (th + tl - 4);
Change 5000 to 2000.
Arduino's RX will accept 3.3V TX from MH sensor without any conversion.
#include
SoftwareSerial mySerial(A0, A1); // RX, TX сенсора
byte cmd[9] = {0xFF,0x01,0x86,0x00,0x00,0x00,0x00,0x00,0x79};
// волшебная комманда - смотри мануал
char response[9]; // здесь будет ответ
void setup() {
Serial.begin(9600); //это наш монитор
mySerial.begin(9600); //а это датчик MH-Z19(14)
}
void loop()
{
mySerial.write(cmd,9);//запрос PPM CO2
mySerial.readBytes(response, 9);
int responseHigh = (int) response[2];
int responseLow = (int) response[3];
int ppm = (256*responseHigh)+responseLow;
// ну и по мануалу из ответа считаем PPM
Serial.println(ppm);
delay(10000);
}
Подсоединил датчик вот так https://yadi.sk/d/GodlSrCNqqJAy, скомпилил ваш скетч. В мониторе вижу PPM = 0
Подскажите, где косячу?
The same hardware setup, the same code (for pwm and uart) and the same room (air) for both detectors. And the results?
MH-Z19 - pwm: ~ 420 ppm, uart ~ 400 ppm
MH-Z14 - pwm: ~ 1320 ppm, uart ~ 1330 ppm
commercial CO2 detector ~ 1340 ppm
MH-Z19 I have on my desk seems to be defective upon this...
But my Z19 still returned values arund 400 ppm (even after 30 minutes). Only when I breath out directly to the sensor, returned values raises (up to 2000 - 2500 ppm) and slowly returns to ~ 400 ppm.
А вот у меня другая проблема - датчик очень странно себя порой ведет, то выдавая 5000 несколько минут, то снижая значения до 100-200. Контрольная сумма совпадает. Все выглядит так, словно он иногда рекалибруется сам по себе. Может быть пятый пин нужно подтянуть к HIGH? Или датчик плохо реагирует на ардуиновский пятивольтовый TX и нужно добавить резисторный делитель? Кто сталкивался с чем-то подобным?
Из-за чего значение PPM может быть всегда примерно ~ 400 ppm ?
Рядом стоящий прибор показывает 1200 ppm
Подключал и напрямую и через делитель напряжения.
2) Подключал напрямую и через делитель напряжения 10k + 22k
Во всех случаях показывает примерно 400.
Оставил работать до утра (примерно 5 часов), в итоге значение постепенно повысилось до ~550 ppm (хотя рядом стоящий датчик показывал значение 1200 ppm).
Если сильно подышать на него, то значение резко увеличивается (600, 900, 1000,..), а потом быстро спадает.
Т.е. все симптомы, которые были в переписке выше с "ice".
Есть ли нарисованная схема подключения, при котором обеспечивается условие "Interface level - 3.3 V"? Может, все-таки дело в этом и я просто неправильно подключаю...
Я так понимаю, простая подача LOW на Hd не откалибрует датчик.
Необходима процедура калибровки, при каких условиях и пункты действий. Иначе ручная калибровка по моему(может и не правильному) впечатлению, роняет чувствительность датчика. Короче ждём самокалибрующийся датчик по нормальной цене, пока модель S8 стоит порядка 10тр, А этот датчик хорош, но если его откалибровали. В условиях Китая, возвращать и переписываться - всё это тягомотина ненужная
Манипуляции с питанием ни к чему ни привели.
Решил сделать калибровку. Хорошо проветрил со сквозняком и сразу отправил на датчик команду Calibrate zero point = {0xFF,0x01,0x87,0x00,0x00,0x00,0x00,0x00,0x78} (она указана в datasheet).
После этого стало отображаться корректнее Стал постепенно повышаться PPM.
Попробую потом сбросить в ноль после долгого отсутствия.
Наверняка после коммента последовали ещё какие-то наблюдения?
Когда мы калибруем на свежем воздухе, то априори в среде 400-450 ppm. Итого - датчик получает ноль на 400ppm и может выводить данные с этого же уровня. То есть он думает, что 400ppm это ноль.
В итоге после калибровки, ниже 400ppm провала не наблюдаем, ибо это минимальная шкала (не путать со значением).
Корректное значение будет показания датчика + та самая нулевая среда, где вы калибровали (400-450).
И вы не увидите реальные значения ниже 800, поскольку упрётесь в нижнюю планку шкалы в 400.
Как вариант, это купить сифон, баллончики N20, запилить внуть шарика газ и датчик и там уже обнулить.
Так что думайте, прежде чем повторять мой опыт))
А на другой фотографии они (оба окна) закрыты чем-то волокнистым, вроде бумажки.
Хотел узнать, что-нибудь надо делать с этой бумажкой? Может это защита окна при транспортировке.
А SPAN это 5000 ppm?
зачем столько кода, когда его можно заменить одной строкой "man pulseIn";
tH = pulseIn(pwmPin,HIGH,2000000);
PPM = 5000 * (((tH)/1000.0)-2)/1000;
Но ещё лучше это сделать на прерываниях.
attachInterrupt(digitalPinToInterrupt(pwmPin), get_z19, CHANGE);
volatile unsigned long tC_last2=0,tC_last=0,tC=0;
volatile float z19_ppm;
void get_z19(){
cli();
tC_last2 = tC_last;
tC_last = tC;
tC = micros();
volatile int a = digitalRead(pwmPin);
sei();
if((tC-tC_last)>990000 && tC_last>0) return;
if(a == HIGH){
tH = tC_last - tC_last2;
tL = tC - tC_last;
} else {
tH = tC - tC_last;
tL = tC_last - tC_last2;
}
z19_ppm = 5000 * (tH/1000.0-2)/1000.0;
}
прежде всего поблагодарю автора за этот фантастический проект
хотел бы спросить или даже попросить помощи, у тех кто может быть сталкивался с CO2 датчиком MH-Z19.
Недавно преобрёл в этот датчик, подключил его с помощью этих страниц в интернете.
Но к сожалению, значения которые выдаё этот датчик я не могу вывести на экран. Разницы нет на какой 0.96 128х64 OLED или 16х2 I2C LCD. Просто ничего другого нету.
Может есть что-то подобное, или существует уже готовые решения?
К сожалению мои знания программирования на этом заканчиваются.
(что я использую в проекте: Arduino nano, Датчик CO2 MH-Z19, Display.)
Буду благодарен за все ваши предложения и варианты
Спасибо заранее.
Александр
Экстра под этот скечь купил все необходимые компоненты датчик MH-Z19, DHT11, Nokia 5110 диспей. Но у меня прблемка, подсоединил всё как на картинки, но к сожалению скопированный текст(Скечь) с вашей статьи, когда я вношу и начинаю проверять на ошибки, выдаёт на странице:
case DHTLIB_OK: ошибку
Nokia5110_MH-Z19_DHT11.ino: In function 'void loop()':
Nokia5110_MH-Z19_DHT11:57: error: 'DHTLIB_OK' was not declared in this scope
Nokia5110_MH-Z19_DHT11:59: error: 'DHTLIB_ERROR_CHECKSUM' was not declared in this scope
Nokia5110_MH-Z19_DHT11:62: error: 'DHTLIB_ERROR_TIMEOUT' was not declared in this scope
Хотел спросить, что делаю не так, может библиотеки како-то не хватает. Для Nokia 5110 вроде всё поставил. Скечь с подключениеи только датчика MH-Z19, работает. Что нетак???
:lol: Всё супер !!!
Всё пошло, ОГРОМНОЕ СПАСИБО.
Спасибо
// для тех кто любит цифру, как и я.
#include SoftwareSerial mySerial(A0, A1); // RX, TX сенсора
byte cmd[9] = {0xFF,0x01,0x86,0x00,0x00,0x00,0x00,0x00,0x79};
// волшебная комманда - смотри мануал
char response[9]; // здесь будет ответ
void setup() {
Serial.begin(9600); //это наш монитор
mySerial.begin(9600); //а это датчик MH-Z19(14)
}
void loop()
{
mySerial.write(cmd,9);//запрос PPM CO2
mySerial.readBytes(response, 9);
int responseHigh = (byte) response[2];
int responseLow = (byte) response[3];
int ppm = (256*responseHigh)+responseLow;
// ну и по мануалу из ответа считаем PPM
Serial.println(ppm);
delay(10000);
}
пока не поменял RX и TX местами, ничего не заработало
Я работаю с профессиональным оборудованием для измерения концентрации СО2 в научных целях. Во взрослом оборудовании обязательно есть встроенный корректировщик значений в зависимости от влажности воздуха т.к. инфракрасные спектры поглощения водяного пара и СО2 пересекаются, что может приводить к значительным отклонениям показаний.
Хотелось бы использовать ШИМ и скетч автора, чтобы спасти ситуацию.
Поясните, пожалуйста, как правильно назначить вывод Arduino в скетче: #define pwmPin 5 соответствующему выводу (GPIO) ESP8266?
На мониторе постоянный повтор - PPM = 5000. При разрыве цепи PPM - обновление на мониторе останавливаются. На датчик,подаю 3,3В.
Однозначно некондиционный датчик???
Черкните хотя бы на е-мейл: Да/Нет.
1. Его выходы имеют 3.3 вольта и ничего сжечь не могут - внутри встроен стабилизатор питания с 5 до 3.3 в.
2. Датчик чудит при низком питании. На него положено подавать 5 вольт.
похожая проблема. Я совсем новичок в Ардуино. Все собрал и скопировал как по схеме, но первую минуту выдает 410 (6 раз при считывании каждые 10 секунд) затем 5000 сплошняком. В чем проблема без понятия. Если узнали, могли бы подсказать решение?
Подскажите пожалуйста, на про мини к какому порту подключать VIN с датчика MH-Z19?
Кто-то сталкивался с таким поведением? Где искать проблему в первую очередь? Может ли кривая пайка так влиять?
Этот же сигнал выведен на нижннее отверстие в линейке возле разъёма, рядом с UART.