Кто пробовал пользоваться бюджетным датчиком вредных газов MQ-135 для анализа концентрации углекислого газа CO2 в воздухе, то сталкивались с неточностями в показаниях, с зависимостью от температуры окружающей среды, с трудностями калибровки и перевод в стандартные единицы PPM. Рассмотрим датчик  уровня углекислого газа MH-Z19, который и точнее и эргономичнее и удобнее.

Сперва просто подключим датчик к Arduino Nano, а ниже соберем прибор с дисплеем и датчиком температуры.

 

Необходимые материалы

 

Подключения к ШИМ выходу

Схема подключения датчика CO2 MH-Z19 к Arduino

 

Скетч

Чтение показаний датчика с выхода 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 conn

 

Скетч

Для переносного датчика 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();
    }
  }
}

 

Видео

Комментарии   

0 # ed 01.12.2015 01:26
Shldnt that formula be: ppm = 2000 * (th - 2) / (th + tl - 4);
Ответить | Ответить с цитатой | Цитировать
0 # Администратор 02.12.2015 12:48
Если использовать множитель "2000", то на свежем воздухе получается значение примерно 150ppm. Учитывая, что в настоящее время норма CO2 на планете примерно 400ppm и в описании датчика указан диапазон чувствительности до 5000ppm, то в формуле использовал множитель "5000".
Ответить | Ответить с цитатой | Цитировать
0 # ice 16.12.2015 17:00
First of all, thanks for the sample code.
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.
Ответить | Ответить с цитатой | Цитировать
0 # Администратор 16.12.2015 17:29
You can try to change this line to calibrate returning value.
ppm = 5000 * (th - 2) / (th + tl - 4);
Change 5000 to 2000.
Ответить | Ответить с цитатой | Цитировать
0 # ice 16.12.2015 17:46
I have tried it and results were even worse (sensor reads up to 5000 ppm, therefore value 2000 can not work).
Ответить | Ответить с цитатой | Цитировать
0 # Администратор 16.12.2015 17:51
Then you need to read the value via UART (tx/rx). I wasn't able to try this method, no appropriate Converter for the power levels.
Ответить | Ответить с цитатой | Цитировать
0 # ice 11.01.2016 13:59
As the simple converter you can use a voltage divider of two resistors (10k + 20k) on arduino's TX pin.
Arduino's RX will accept 3.3V TX from MH sensor without any conversion.
Ответить | Ответить с цитатой | Цитировать
+2 # Алексей 08.01.2016 13:52
// для тех кто любит цифру, как и я.

#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);
}
Ответить | Ответить с цитатой | Цитировать
0 # Анатолий 08.04.2016 14:46
Алексей, подскажите что делаю не так. С ардуино второй день и еще далеко не все понимаю, многое делаю копипастом.
Подсоединил датчик вот так https://yadi.sk/d/GodlSrCNqqJAy, скомпилил ваш скетч. В мониторе вижу PPM = 0
Подскажите, где косячу?
Ответить | Ответить с цитатой | Цитировать
0 # Анатолий 08.04.2016 18:01
Разобрался. Надо было как в коде подключаться к A0, A1. У меня в голове не могло уложиться что при работе по цифре надо подключать к контактам типа А :-?
Ответить | Ответить с цитатой | Цитировать
0 # ice 11.01.2016 13:54
Hi Alexey, code works fine. I have tested it with MH-Z19 mentioned in my previous posts and with a new MH-Z14.
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...
Ответить | Ответить с цитатой | Цитировать
0 # Алексей 11.01.2016 14:25
Hi, My z19 shows about 400ppm at start. It will not start immediately. You can try wait for real values about 1-3min.
Ответить | Ответить с цитатой | Цитировать
0 # ice 11.01.2016 18:26
MH-Z14 behaves as yours Z19.
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.
Ответить | Ответить с цитатой | Цитировать
0 # Алексей 11.01.2016 20:20
It's working! You need calibrate this sensor.
Ответить | Ответить с цитатой | Цитировать
0 # ice 13.01.2016 13:15
Any hint for the calibration procedure? Command is described in the datasheet, but is there any special requirement for the atmosphere. Somewhere I saw a note about nitrogen, but I can't find it now...
Ответить | Ответить с цитатой | Цитировать
0 # RRom 19.02.2016 14:48
Добрый день, при чтении данных по uart не сходится checksum хотя первый, второй байты как в таблице даташита, данные(второй и третий байт) очень странные, иногда верные( как и должно быть), иногда значительно выше. В четвертом байте вместо 0x47 у меня 0x40, в пятом, шестом и седьмом не 0x00, а всякое разное, иногда меняется. Подскажите пожалуйста, в чем возможна проблема. Спасибо!
Ответить | Ответить с цитатой | Цитировать
0 # Mike 23.02.2016 05:12
Думаю, что проблема в соединении - где-то плохой контакт. Для того контрольная сумма и придумана, собственно.

А вот у меня другая проблема - датчик очень странно себя порой ведет, то выдавая 5000 несколько минут, то снижая значения до 100-200. Контрольная сумма совпадает. Все выглядит так, словно он иногда рекалибруется сам по себе. Может быть пятый пин нужно подтянуть к HIGH? Или датчик плохо реагирует на ардуиновский пятивольтовый TX и нужно добавить резисторный делитель? Кто сталкивался с чем-то подобным?
Ответить | Ответить с цитатой | Цитировать
0 # Mike 23.02.2016 18:10
А да, там еще в примере Алексея косяк - везде нужно не char и int, а unsigned char и unsigned int, иначе легко получить кривые значения.
Ответить | Ответить с цитатой | Цитировать
0 # Андрей 26.04.2016 23:53
Добрый день.
Из-за чего значение PPM может быть всегда примерно ~ 400 ppm ?
Рядом стоящий прибор показывает 1200 ppm
Подключал и напрямую и через делитель напряжения.
Ответить | Ответить с цитатой | Цитировать
0 # Администратор 27.04.2016 08:35
Как только включается датчик, то значение 400 держится примерно 1 минуту. Время разогрева. Дальше начинает меняться. Значения снимаете с PWM или UART интерфейса ?
Ответить | Ответить с цитатой | Цитировать
0 # Андрей 27.04.2016 08:51
1) Подключал и по PWM и UART
2) Подключал напрямую и через делитель напряжения 10k + 22k
Во всех случаях показывает примерно 400.
Оставил работать до утра (примерно 5 часов), в итоге значение постепенно повысилось до ~550 ppm (хотя рядом стоящий датчик показывал значение 1200 ppm).
Если сильно подышать на него, то значение резко увеличивается (600, 900, 1000,..), а потом быстро спадает.
Т.е. все симптомы, которые были в переписке выше с "ice".
Есть ли нарисованная схема подключения, при котором обеспечивается условие "Interface level - 3.3 V"? Может, все-таки дело в этом и я просто неправильно подключаю...
Ответить | Ответить с цитатой | Цитировать
0 # Администратор 27.04.2016 08:56
Впечатление как будто неправильно откалиброван датчик. Думаю если уровни не соблюдены, то по UART вообще ничего стабильного не получили бы.
Ответить | Ответить с цитатой | Цитировать
0 # Андрей 27.04.2016 09:06
А калибровка должна происходить в специфичных условиях?
Я так понимаю, простая подача LOW на Hd не откалибрует датчик.
Ответить | Ответить с цитатой | Цитировать
0 # Администратор 27.04.2016 09:11
Производитель калибрует. Я бы с продавцом пообщался, насчет брака, или может у них есть решение.
Ответить | Ответить с цитатой | Цитировать
0 # Андрей 27.04.2016 09:48
Еще вопрос. Чем закрыты сенсоры (2 окошка), бумажкой или пластиком?
Ответить | Ответить с цитатой | Цитировать
0 # Андрей 27.04.2016 09:50
У вас на одной картинке продукта видна четкая решетка на сенсоре, а на другой картинке окошки закрыты чем-то матовым.
Ответить | Ответить с цитатой | Цитировать
0 # Администратор 27.04.2016 10:01
На ощупь непонятно, но чем-то волокнистым и гладким с рельефным узором закрыто.
Ответить | Ответить с цитатой | Цитировать
0 # Андрей 28.04.2016 19:17
Похоже и правда дело в калибровке.
Манипуляции с питанием ни к чему ни привели.
Решил сделать калибровку. Хорошо проветрил со сквозняком и сразу отправил на датчик команду Calibrate zero point = {0xFF,0x01,0x87,0x00,0x00,0x00,0x00,0x00,0x78} (она указана в datasheet).
После этого стало отображаться корректнее Стал постепенно повышаться PPM.
Попробую потом сбросить в ноль после долгого отсутствия.
Ответить | Ответить с цитатой | Цитировать
0 # Алексей 27.04.2016 08:59
Ребят, а датчик до 2000 или до 5000 ppm ???
Ответить | Ответить с цитатой | Цитировать
0 # Андрей 27.04.2016 10:08
Просто меня смутило в вашей карточке продукта на одной фотографии окно выглядит четко, с решеткой.
А на другой фотографии они (оба окна) закрыты чем-то волокнистым, вроде бумажки.
Хотел узнать, что-нибудь надо делать с этой бумажкой? Может это защита окна при транспортировке.
Ответить | Ответить с цитатой | Цитировать
0 # Администратор 27.04.2016 10:33
Снимать пленку думаю нельзя. Продавец про это не писал ничего, да и показания у меня корректные были.
Ответить | Ответить с цитатой | Цитировать
0 # Администратор 28.04.2016 17:18
В даташите заметил команды, посылаемые через UART, для калибровки.
Ответить | Ответить с цитатой | Цитировать
0 # Андрей 28.04.2016 19:28
Только непонятно, ZERO - это 0ppm или 400ppm ?
А SPAN это 5000 ppm?
Ответить | Ответить с цитатой | Цитировать
0 # Алексей 05.05.2016 12:51
8)
зачем столько кода, когда его можно заменить одной строкой "man pulseIn";

tH = pulseIn(pwmPin,HIGH,2000000);
PPM = 5000 * (((tH)/1000.0)-2)/1000;
Ответить | Ответить с цитатой | Цитировать
0 # Алексей 20.08.2016 12:52
:-*
Но ещё лучше это сделать на прерываниях.

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;
}
Ответить | Ответить с цитатой | Цитировать
0 # Alex 05.06.2016 23:44
Приветствую всех друзья, :lol:
прежде всего поблагодарю автора за этот фантастический проект
хотел бы спросить или даже попросить помощи, у тех кто может быть сталкивался с CO2 датчиком MH-Z19.
Недавно преобрёл в этот датчик, подключил его с помощью этих страниц в интернете.
Но к сожалению, значения которые выдаё этот датчик я не могу вывести на экран. Разницы нет на какой 0.96 128х64 OLED или 16х2 I2C LCD. Просто ничего другого нету.
Может есть что-то подобное, или существует уже готовые решения?
К сожалению мои знания программирования на этом заканчиваются.
(что я использую в проекте: Arduino nano, Датчик CO2 MH-Z19, Display.)
Буду благодарен за все ваши предложения и варианты
Спасибо заранее.
Александр
Ответить | Ответить с цитатой | Цитировать
0 # Alex 21.06.2016 16:03
Приветствую автора статьи CO2 датчиком MH-Z19. А именно: "Для переносного датчика MH-Z19 с дисплеем Nokia 5110"
Экстра под этот скечь купил все необходимые компоненты датчик 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, работает. Что нетак???
Ответить | Ответить с цитатой | Цитировать
0 # Администратор 22.06.2016 12:03
Перед скетчем выложил необходимые библиотеки. Попробовал с ними на чистой Arduino IDE 1.6.4 - скомпилировалось без ошибок.
Ответить | Ответить с цитатой | Цитировать
0 # Alex 23.06.2016 00:09
Приветствую автора статьи CO2 датчиком MH-Z19.
:lol: Всё супер !!!
Всё пошло, ОГРОМНОЕ СПАСИБО.
Ответить | Ответить с цитатой | Цитировать

Добавить комментарий


Защитный код
Обновить