RSS блога
Подписка
MH-Z14A - датчик углекислого газа, сравнение с конкурентами.
- Цена: $22.99
- Перейти в магазин
В прошлом обзоре я сравнивал три датчика пыли. Сегодня в одном приборе четыре датчика CO2. Рассматриваем плюсы и минусы, изучаем подводные камни. Готовим простое и недорогое устройство для контроля воздуха дома.
Уровень СО2 влияет на наше самочувствие не меньше, чем пыль и аллергены в воздухе. Когда мы говорим, что нам душно, это не кислорода нам не хватает. Его уровень остается более-менее одинаковым. Это повышается уровень углекислого газа. В абсолютных значениях он повышается совсем незначительно. В относительных же диапазон от 0,04 до 0,5 процента — это расстояние от «чистейший горный воздух» до «тут опасно находиться, срочно покинуть помещение». В сети много полезной информации на эту тему, так что приступим сразу к делу.
У нас сегодня 4 датчика. Все они работают по одному принципу — измерению спектра инфра-красного излучения, проходящего через образец газа. Это довольно точный метод, но он дороже и сложнее другого, который основан на измерении электрических характеристик металло-оскидных пленок в среде анализируемого газа.
MH-Z14A
Это относительно старый датчик. Его характеристики:
Разрешающая способность в диапазоне измерения 0-2000 ppm: 5ppm;
Разрешающая способность в диапазоне измерения 2000-5000 ppm: 10ppm;
Разрешающая способность в диапазоне измерения 5000-10000 ppm: 20ppm;
Точность измерения: 50ppm ± 5%;
Повторяемость измерений: 30ppm;
Время измерения: < 30 сек.
Время прогрева: 3 мин.
Напряжение питания: 4-6В
Средний потребляемый ток: < 50mA
Максимальный потребляемый ток: < 100mA
Срок эксплуатации: > 5 лет.
Датчик выдает показания по трем каналам: UART, ШИМ-модуляция и аналоговый выход. Наиболее функциональный канал связи с датчиком — UART. Он позволяет не только получить точные измеренные значения, но и управлять датчиком. Производить калибровку. Это весьма важный момент, в описании рекомендуется калибровать датчик не реже чем раз в полгода.
MH-Z19A
Этот датчик выпускается тем же производителем, что и предыдущий, и должен был прийти ему на смену. Он заметно компактнее и экономичнее. Датчик выпускается в двух модификациях — «A» и «B». У нас более старая версия — «A». Его характеристики:
Диапазон измерения: 2000 или 5000 ppm;
Точность измерения: 50ppm ± 3%;
Время измерения: < 60 сек.
Средний потребляемый ток: < 18mA
Напряжение питания: 3,6 — 5,5В
Срок эксплуатации: > 5 лет.
Датчик выдает показания по двум каналам: UART и ШИМ-модуляция.
Senseair S8
Этот датчик считается наиболее точным. У нас версия 0053, вот ее характеристики:
Диапазоне измерения 400-2000 ppm;
Точность измерения: 40ppm ± 3%;
Напряжение питания: 4,5 — 5,25В
Средний потребляемый ток: < 18mA
Максимальный потребляемый ток: < 300mA
Срок эксплуатации: > 15 лет.
Датчик способен передавать измеренные значение по UART, шим-модуляцией, по протоколу Modbus, кроме того, имеется выход на внешние устройства, который включается при повышении концентрации CO2 до 1000 ppm и отключается при снижении до 800.
Как и у двух предыдущих датчиков, тут есть возможность калибровки и имеется автоматическая коррекция нулевого уровня.
LGAQS-HT01
Строго говоря, это не датчик углекислоты. Это металлооксидный датчик летучих органических соединений. Он определяет их концентрацию и подсчитывает абстрактный уровень углекислоты, эквивалентно влияющий на здоровье человека. Этот датчик представляет собой комбинацию двух датчиков — CCS811 (отвечает за ЛОС) и Si7021 — он измеряет температуру и влажность. Эти данные нужны для настройки датчика CCS811 для повышения точности измерений. Датчик подключается по I2C шине и доступен сразу по двум адресам — для снятия показаний с CCS811 и для Si7021. По каким-то причинам датчик влажности у меня все время показывает 50%, так что в прибор я добавил еще один датчик — BME280. Он более точно определяет температуру, влажность и давление.
Вот все датчики вместе.
А вот прочие элементы будущего устройства:
Нам потребуются: макетная плата, степдаун преобразователь 5->3,3 В, две платы интерфейса 5<->3,3 для согласования выводов датчиков и Ардуино, Ардуино Про Мини, экран, часы DS3221, «черный ящик» OpenLog, регистрирующий показания всех датчиков и разъем micro-usb, чтобы питаться от телефонной зарядки.
После установки всех разъемов и распайки проводов у меня получилось вот что:
Далее, была написана программа.
Первые данные, снятые с прибора, выглядят на графике вот так:
Датчик MH-Z14 включился впервые, так что он какое-то время потратил и еще потратит на калибровку. Видно, что его показания сильно коррелируют с показаниями датчика S8, но Z14 несколько шатает из стороны в сторону. Механизм автокалибровки состоит в следующем: датчик помнит минимальное значение измеренного уровня за какое-то время (около недели). Если минимум превышает 400 ppm, то скорее всего датчик завышает показания. Внутренние коэффициенты изменяются так, чтобы выдаваемое значение снижалось. За одну неделю вносимая поправка не может превышать 30-50 ppm, так что датчик необходимо «проветривать» раз в неделю для корректировки нулевых значений.
Вот еще пример графика снятых значений:
Видно, что теперь датчик слегка занижает уровень относительно S8. Но его более современный собрат Z14 занижает еще сильнее. Вероятно, в нем произошла калибровка нулевого значения. У этих датчиков, MH-Z14, есть специальный вывод для калибровки. При замыкании его на землю и удержании в течении 7 секунд датчик калибруется, рассматривая текущую атмосферу как нулевую точку.
Более свежая модификация «B» этого датчика отличается наличием аналогового выхода и возможностью калибровки не только по нулевому уровню, но и по произвольной точке, что особенно полезно, потому что газ, совершенно свободный от примеси углекислоты, в домашних условиях получить хлопотно. Вероятно, мой датчик случайно замкнул эту ногу и перекалибровался по 400ppm. Теперь могут уйти долгие недели на то, чтоб автокалибровка вернула более-менее правдоподобные показания. Или нужно искать жидкий азот, чтоб обеспечить для него безуглекислотную атмосферу.
Четвертый датчик, измеряющий eCO2, выполняет свою работу вполне неплохо. Всплеск его показаний в правой части графика — результат моей работы со спиртом. Я как раз протирал плату в этот момент, что он и зафиксировал.
Что в итоге:
Все датчики более-менее правдоподобно отражают изменения атмосферы в квартире. MH-Z19 нуждается в повторной калибровке. Ее механизм значительно усовершенствован в более поздней версии, MH-Z19B, так что я бы порекомендовал приобретать именно ее.
Датчик MH-Z14 оказался очень неплохим, его показания почти совпадают с более дорогим Senseair S8.
Датчик LGAQS-HT01 тоже правдоподобно показывает наличие примесей в атмосфере и послужит хорошим дополнением к датчикам углекислоты.
Теперь у меня в планах собрать одно устройство с лучшим из трех датчиков пыли из прошлого обзора и одним из датчиков углекислоты из этого обзора.
Пока готов код, отображающий на экране ситуацию с запылённостью. Подробности об этом проекте я добавил в обзор датчиков пыли, включая скетч для Ардуино. Показания прибора сегодня выглядят вот так:
Скоро туда добавится график измерения уровня углекислоты. Если нужно, объединенный скетч по мере готовности я выложу в этом обзоре.
MH-Z14A — рабочий датчик, рекомендую к покупке. Сейчас прилагается USB to RS485 TTL преобразователь, в хозяйстве пригодится.
Уровень СО2 влияет на наше самочувствие не меньше, чем пыль и аллергены в воздухе. Когда мы говорим, что нам душно, это не кислорода нам не хватает. Его уровень остается более-менее одинаковым. Это повышается уровень углекислого газа. В абсолютных значениях он повышается совсем незначительно. В относительных же диапазон от 0,04 до 0,5 процента — это расстояние от «чистейший горный воздух» до «тут опасно находиться, срочно покинуть помещение». В сети много полезной информации на эту тему, так что приступим сразу к делу.
У нас сегодня 4 датчика. Все они работают по одному принципу — измерению спектра инфра-красного излучения, проходящего через образец газа. Это довольно точный метод, но он дороже и сложнее другого, который основан на измерении электрических характеристик металло-оскидных пленок в среде анализируемого газа.
MH-Z14A
Это относительно старый датчик. Его характеристики:
Разрешающая способность в диапазоне измерения 0-2000 ppm: 5ppm;
Разрешающая способность в диапазоне измерения 2000-5000 ppm: 10ppm;
Разрешающая способность в диапазоне измерения 5000-10000 ppm: 20ppm;
Точность измерения: 50ppm ± 5%;
Повторяемость измерений: 30ppm;
Время измерения: < 30 сек.
Время прогрева: 3 мин.
Напряжение питания: 4-6В
Средний потребляемый ток: < 50mA
Максимальный потребляемый ток: < 100mA
Срок эксплуатации: > 5 лет.
Датчик выдает показания по трем каналам: UART, ШИМ-модуляция и аналоговый выход. Наиболее функциональный канал связи с датчиком — UART. Он позволяет не только получить точные измеренные значения, но и управлять датчиком. Производить калибровку. Это весьма важный момент, в описании рекомендуется калибровать датчик не реже чем раз в полгода.
MH-Z19A
Этот датчик выпускается тем же производителем, что и предыдущий, и должен был прийти ему на смену. Он заметно компактнее и экономичнее. Датчик выпускается в двух модификациях — «A» и «B». У нас более старая версия — «A». Его характеристики:
Диапазон измерения: 2000 или 5000 ppm;
Точность измерения: 50ppm ± 3%;
Время измерения: < 60 сек.
Средний потребляемый ток: < 18mA
Напряжение питания: 3,6 — 5,5В
Срок эксплуатации: > 5 лет.
Датчик выдает показания по двум каналам: UART и ШИМ-модуляция.
Senseair S8
Этот датчик считается наиболее точным. У нас версия 0053, вот ее характеристики:
Диапазоне измерения 400-2000 ppm;
Точность измерения: 40ppm ± 3%;
Напряжение питания: 4,5 — 5,25В
Средний потребляемый ток: < 18mA
Максимальный потребляемый ток: < 300mA
Срок эксплуатации: > 15 лет.
Датчик способен передавать измеренные значение по UART, шим-модуляцией, по протоколу Modbus, кроме того, имеется выход на внешние устройства, который включается при повышении концентрации CO2 до 1000 ppm и отключается при снижении до 800.
Как и у двух предыдущих датчиков, тут есть возможность калибровки и имеется автоматическая коррекция нулевого уровня.
LGAQS-HT01
Строго говоря, это не датчик углекислоты. Это металлооксидный датчик летучих органических соединений. Он определяет их концентрацию и подсчитывает абстрактный уровень углекислоты, эквивалентно влияющий на здоровье человека. Этот датчик представляет собой комбинацию двух датчиков — CCS811 (отвечает за ЛОС) и Si7021 — он измеряет температуру и влажность. Эти данные нужны для настройки датчика CCS811 для повышения точности измерений. Датчик подключается по I2C шине и доступен сразу по двум адресам — для снятия показаний с CCS811 и для Si7021. По каким-то причинам датчик влажности у меня все время показывает 50%, так что в прибор я добавил еще один датчик — BME280. Он более точно определяет температуру, влажность и давление.
Вот все датчики вместе.
А вот прочие элементы будущего устройства:
Нам потребуются: макетная плата, степдаун преобразователь 5->3,3 В, две платы интерфейса 5<->3,3 для согласования выводов датчиков и Ардуино, Ардуино Про Мини, экран, часы DS3221, «черный ящик» OpenLog, регистрирующий показания всех датчиков и разъем micro-usb, чтобы питаться от телефонной зарядки.
После установки всех разъемов и распайки проводов у меня получилось вот что:
Далее, была написана программа.
Код программы
///////////////////////////////////////////////////////////////////////
/// © tykhon, 2019
///////////////////////////////////////////////////////////////////////
/*
MH-Z19
TX: 3<->5 A0
RX: 3<->5 A1
PWM: 12
Gnd: Gnd
Vin: +5v
MH-Z14A
1_+5v: +5v
2_Gnd: Gnd
6_Pwm:
11_RX: 3<->5 7
10_TX: 3<->5 6
S8:
G+: +5v
G0: Gnd
Rx: 3<->5 A3
Tx: 3<->5 A2
LGAQS-HT01
SCL: A5
SDA: A4
Gnd: Gnd
VDD: 3.3v
BMP280
SCL: A5
SDA: A4
Gnd: Gnd
VDD: 3.3v
DS3231
SCL: A5
SDA: A4
Gnd: Gnd
VDD: 5v
TFT
Led: -> 150 Ohm -> 5v
SCK: -> 1 KOhm -> 13
SDA: -> 1 KOhm -> 11
A0: -> 1 KOhm -> 8
Reset: -> 1 KOhm -> 9
CS: -> 1 KOhm -> 10
Gnd: Gnd
Vcc: 5v
*/
#include <Adafruit_GFX.h> // for LCD Core graphics library
#include <Adafruit_ST7735.h> // for LCD Hardware-specific library
#include <CCS811.h> // for TVOC sensor
#include <Adafruit_Sensor.h> // for bme280
#include <Adafruit_BME280.h> // for bme280
#include <SoftwareSerial.h>
#include "Wire.h"
#define TFT_CS 10
#define TFT_RST 9
#define TFT_DC 8
Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST);
#define SEALEVELPRESSURE_HPA (1013.25) // for bme280
Adafruit_BME280 bme; // for bme280 I2C
#define CCS811_ADDR 0x5A // for LGAQS-HT01 or ccs811
#define SI7021_ADDR 0x40 // for LGAQS-HT01 or si7021
#define WAKE_PIN 5 // for LGAQS-HT01 or ccs811
#define DS1307_ADDR 0x68 // RTC address
CCS811 sensor;
float si7021_h = 0;
float si7021_t = 0;
unsigned int data[2];
int ccs188_co2 = 0;
int ccs188_tvoc = 0;
int z14_co2;
int z19_co2;
int z19_t;
int z19_ss;
int ccs188_co2_last;
int ccs188_tvoc_last;
int z14_co2_last;
int z19_co2_last;
unsigned long s8_co2_sum = 0;
unsigned long z14_co2_sum = 0;
unsigned long z19_co2_sum = 0;
unsigned long ccs188_co2_sum = 0;
unsigned long ccs188_tvoc_sum = 0;
unsigned long bme_t_sum = 0;
unsigned long bme_h_sum = 0;
unsigned long bme_p_mm_sum = 0;
unsigned int polls = 0;
unsigned long s8_co2 = 0;
unsigned long s8_co2_last = 0;
int bme_t;
int bme_h;
float bme_p;
int bme_p_mm;
const float hpa2mm = 133.3224;
SoftwareSerial mySerial_z19(A0, A1);
SoftwareSerial mySerial_s8(A2, A3);
SoftwareSerial mySerial_z14(7, 6);
byte cmd_z14[9] = {0xFF,0x01,0x86,0x00,0x00,0x00,0x00,0x00,0x79};
unsigned char response_z14[9];
String ppmString = " ";
byte cmd_z19[9] = {0xFF,0x01,0x86,0x00,0x00,0x00,0x00,0x00,0x79};
byte cmd_s8[7] = {0xFE,0x44,0x00,0x08,0x02,0x9F,0x25};
unsigned char response_z19[9];
byte response_s8[] = {0,0,0,0,0,0,0};
unsigned int current_minute, lastminute;
int second_delay = 30000; // delay between measurements, ms
int work_period = 3; // period for recording data, min
//////////////////////////////////////// functions //////////////////////////////////////////////
byte bcdToDec(byte val) {
return ( (val/16*10) + (val%16) );
}
String getdate(){
Wire.beginTransmission(DS1307_ADDR); // Reset the register pointer
byte zero = 0x00;
Wire.write(zero);
Wire.endTransmission();
Wire.requestFrom(DS1307_ADDR, 7);
int secondint = bcdToDec(Wire.read());
int minuteint = bcdToDec(Wire.read());
current_minute = minuteint;
int hour = bcdToDec(Wire.read() & 0b111111); //24 hour time
int weekDay = bcdToDec(Wire.read()); //0-6 -> sunday - Saturday
int monthDay = bcdToDec(Wire.read());
int month = bcdToDec(Wire.read());
int year = bcdToDec(Wire.read());
String second = String(secondint); if (secondint < 10) {second ="0"+second;};
String minute = String(minuteint); if (minuteint < 10) {minute ="0"+minute;};
return String(hour)+":"+minute+":"+second+" "+String(monthDay)+"/"+String(month)+"/"+String(year);
}
void sendRequest_s8(byte packet[]){
mySerial_s8.begin(9600);
while(!mySerial_s8.available()){ //keep sending request until we start to get a response
mySerial_s8.write(cmd_s8,7);
delay(50);
}
int timeout=0; //set a timeout counter
while(mySerial_s8.available() < 7 ) { //Wait to get a 7 byte response
timeout++;
if(timeout > 10) { //if it takes to long there was probably an error
while(mySerial_s8.available()) //flush whatever we have
mySerial_s8.read();
break; //exit and try again
}
delay(50);
}
for (int i=0; i < 7; i++) {
response_s8[i] = mySerial_s8.read();
}
mySerial_s8.end();
}
unsigned long getValue_s8(byte packet[])
{
int high = packet[3]; //high byte for value is 4th byte in packet in the packet
int low = packet[4]; //low byte for value is 5th byte in the packet
unsigned long val = high*256 + low; //Combine high byte and low byte with this formula to get value
return val;
}
void sendRequest_SI7021(){
Wire.beginTransmission(SI7021_ADDR);
Wire.endTransmission();
delay(500);
Wire.requestFrom(SI7021_ADDR, 2); // Request 2 bytes of data
if(Wire.available() == 2) // Read 2 bytes of data to get humidity
{
data[0] = Wire.read();
data[1] = Wire.read();
}
float hum = ((data[0] * 256.0) + data[1]); // Convert the data
si7021_h = ((125 * hum) / 65536.0) - 6;
Wire.begin();
Wire.beginTransmission(SI7021_ADDR); // Send temperature measurement command
Wire.write(0xF3);
Wire.endTransmission();
delay(500);
Wire.requestFrom(SI7021_ADDR, 2); // Request 2 bytes of data
if(Wire.available() == 2) // Read 2 bytes of data for temperature
{
data[0] = Wire.read();
data[1] = Wire.read();
}
float temp = ((data[0] * 256.0) + data[1]); // Convert the data
si7021_t = ((175.72 * temp) / 65536.0) - 46.85; //
}
void sendRequest_ccs188(){
sensor.compensate(si7021_t, si7021_h); // replace with t and rh values from sensor
sensor.getData();
ccs188_co2 = sensor.readCO2();
ccs188_tvoc = sensor.readTVOC();
}
void sendRequest_z14(){
mySerial_z14.begin(9600);
mySerial_z14.write(cmd_z14, 9);
memset(response_z14, 0, 9);
mySerial_z14.readBytes(response_z14, 9);
int i;
byte crc = 0;
for (i = 1; i < 8; i++) crc+=response_z14[i];
crc = 255 - crc;
crc++;
if ( !(response_z14[0] == 0xFF && response_z14[1] == 0x86 && response_z14[8] == crc) ) {
Serial.println("CRC error z14: " + String(crc) + " / "+ String(response_z14[8]));
} else {
unsigned int responseHigh = (unsigned int) response_z14[2];
unsigned int responseLow = (unsigned int) response_z14[3];
z14_co2 = (256*responseHigh) + responseLow;
};
mySerial_z14.end();
}
void sendRequest_z19(){
mySerial_z19.begin(9600);
mySerial_z19.write(cmd_z19, 9);
memset(response_z19, 0, 9);
mySerial_z19.readBytes(response_z19, 9);
int i;
byte crc = 0;
for (i = 1; i < 8; i++) crc+=response_z19[i];
crc = 255 - crc;
crc++;
if (response_z19[0] != 0xFF) {Serial.println("CRC error z19: response_z19[0] != 0xFF: " + String(response_z19[0]));};
if (response_z19[1] != 0x86) {Serial.println("CRC error z19: response_z19[1] != 0x86: " + String(response_z19[1]));};
if (response_z19[8] != crc) {Serial.println("CRC error z19: response_z19[8] != crc: " + String(response_z19[8]) + " / "+ String(crc));};
unsigned int responseHigh = (unsigned int) response_z19[2];
unsigned int responseLow = (unsigned int) response_z19[3];
unsigned int responseTT = (unsigned int) response_z19[4];
unsigned int responseSS = (unsigned int) response_z19[5];
z19_t = responseTT-40;
z19_ss = responseSS;
z19_co2 = (256*responseHigh) + responseLow;
mySerial_z19.end();
}
void tft_form(){
tft.setTextSize(2);
tft.setCursor(0, 4);
tft.print("ccs: ");
tft.setCursor(0, 30);
tft.print("z19: ");
tft.setCursor(0, 60);
tft.print("z14: ");
tft.setCursor(0, 90);
tft.print("s8: ");
}
void tft_output(){
tft.setTextSize(2);
tft.setTextColor(ST7735_BLACK);
tft.setCursor(60, 4);
tft.print(String(ccs188_co2_last));
tft.print(String("/"));
tft.print(String(ccs188_tvoc_last));
tft.setTextColor(ST7735_WHITE);
tft.setCursor(60, 4);
tft.print(String(ccs188_co2));
tft.print(String("/"));
tft.print(String(ccs188_tvoc));
ccs188_co2_last = ccs188_co2;
ccs188_tvoc_last = ccs188_tvoc;
tft.setCursor(60, 30);
tft.setTextColor(ST7735_BLACK);
tft.print(String(z19_co2_last));
tft.setCursor(60, 30);
tft.setTextColor(ST7735_WHITE);
tft.print(String(z19_co2));
z19_co2_last = z19_co2;
tft.setCursor(60, 60);
tft.setTextColor(ST7735_BLACK);
tft.print(String(z14_co2_last));
tft.setCursor(60, 60);
tft.setTextColor(ST7735_WHITE);
tft.print(String(z14_co2));
z14_co2_last = z14_co2;
tft.setCursor(60, 90);
tft.setTextColor(ST7735_BLACK);
tft.print(String(s8_co2_last));
tft.setCursor(60, 90);
tft.setTextColor(ST7735_WHITE);
tft.print(String(s8_co2));
s8_co2_last = s8_co2;
tft.setTextSize(1);
tft.fillRect(0, 117, 160, 120, ST7735_BLACK);
tft.setCursor(0, 117);
tft.print(String(getdate()));
}
/////////////////////////////////////// setup /////////////////////////////////////////
void setup(void) {
Serial.begin(9600);
bool status;
status = bme.begin();
if (!status) {
Serial.println("Could not find a valid BME280 sensor, check wiring!");
while (1);
}
delay(100); // let sensor boot up
tft.initR(INITR_BLACKTAB); // initialize a ST7735S chip, black tab
Serial.println("init");
if(!sensor.begin(uint8_t(CCS811_ADDR), uint8_t(WAKE_PIN)))
Serial.println("Init fail");
tft.setTextWrap(false); // Allow text to run off right edge
tft.setRotation(3);
tft.fillScreen(ST7735_BLACK);
tft_form();
}
//////////////////////////////////////////////// loop /////////////////////////////////////////////////////
void loop() {
sendRequest_SI7021();
sendRequest_ccs188();
sendRequest_z14();
sendRequest_z19();
sendRequest_s8(cmd_s8);
s8_co2 = getValue_s8(response_s8);
bme_p = bme.readPressure();
bme_p_mm = int(bme_p/hpa2mm);
bme_h = bme.readHumidity();
bme_t = bme.readTemperature();
s8_co2_sum += s8_co2;
z14_co2_sum += z14_co2;
z19_co2_sum += z19_co2;
ccs188_co2_sum += ccs188_co2;
ccs188_tvoc_sum += ccs188_tvoc;
bme_t_sum += bme_t;
bme_h_sum += bme_h;
bme_p_mm_sum += bme_p_mm;
polls ++;
getdate();
tft_output();
if (((current_minute)%work_period == 0)&&(lastminute != current_minute)) {
s8_co2 = s8_co2_sum/polls;
z14_co2 = z14_co2_sum/polls;
z19_co2 = z19_co2_sum/polls;
ccs188_co2 = ccs188_co2_sum/polls;
ccs188_tvoc = ccs188_tvoc_sum/polls;
bme_t = bme_t_sum/polls;
bme_h = bme_h_sum/polls;
bme_p_mm = bme_p_mm_sum/polls;
String dataString = getdate()+" s8: "+String(s8_co2)+" z14: "+String(z14_co2)+" z19: "+String(z19_co2)+" z19_t: "+String(z19_t)+" ccs: "+String(ccs188_co2)+ " TVOC: "+String(ccs188_tvoc)+" ccs_t: "+String(si7021_t)+" ccs_h: "+String(si7021_h)+" bme_t: "+String(bme_t)+" bme_h: "+String(bme_h);
dataString.replace(".",",");
dataString.replace(" ","\t");
Serial.println(dataString);
lastminute = current_minute;
polls = 0;
s8_co2_sum = 0;
z14_co2_sum = 0;
z19_co2_sum = 0;
ccs188_co2_sum = 0;
ccs188_tvoc_sum = 0;
bme_t_sum = 0;
bme_h_sum = 0;
bme_p_mm_sum = 0;
};
delay(second_delay);
}
Первые данные, снятые с прибора, выглядят на графике вот так:
Датчик MH-Z14 включился впервые, так что он какое-то время потратил и еще потратит на калибровку. Видно, что его показания сильно коррелируют с показаниями датчика S8, но Z14 несколько шатает из стороны в сторону. Механизм автокалибровки состоит в следующем: датчик помнит минимальное значение измеренного уровня за какое-то время (около недели). Если минимум превышает 400 ppm, то скорее всего датчик завышает показания. Внутренние коэффициенты изменяются так, чтобы выдаваемое значение снижалось. За одну неделю вносимая поправка не может превышать 30-50 ppm, так что датчик необходимо «проветривать» раз в неделю для корректировки нулевых значений.
Вот еще пример графика снятых значений:
Видно, что теперь датчик слегка занижает уровень относительно S8. Но его более современный собрат Z14 занижает еще сильнее. Вероятно, в нем произошла калибровка нулевого значения. У этих датчиков, MH-Z14, есть специальный вывод для калибровки. При замыкании его на землю и удержании в течении 7 секунд датчик калибруется, рассматривая текущую атмосферу как нулевую точку.
Более свежая модификация «B» этого датчика отличается наличием аналогового выхода и возможностью калибровки не только по нулевому уровню, но и по произвольной точке, что особенно полезно, потому что газ, совершенно свободный от примеси углекислоты, в домашних условиях получить хлопотно. Вероятно, мой датчик случайно замкнул эту ногу и перекалибровался по 400ppm. Теперь могут уйти долгие недели на то, чтоб автокалибровка вернула более-менее правдоподобные показания. Или нужно искать жидкий азот, чтоб обеспечить для него безуглекислотную атмосферу.
Четвертый датчик, измеряющий eCO2, выполняет свою работу вполне неплохо. Всплеск его показаний в правой части графика — результат моей работы со спиртом. Я как раз протирал плату в этот момент, что он и зафиксировал.
Что в итоге:
Все датчики более-менее правдоподобно отражают изменения атмосферы в квартире. MH-Z19 нуждается в повторной калибровке. Ее механизм значительно усовершенствован в более поздней версии, MH-Z19B, так что я бы порекомендовал приобретать именно ее.
Датчик MH-Z14 оказался очень неплохим, его показания почти совпадают с более дорогим Senseair S8.
Датчик LGAQS-HT01 тоже правдоподобно показывает наличие примесей в атмосфере и послужит хорошим дополнением к датчикам углекислоты.
Теперь у меня в планах собрать одно устройство с лучшим из трех датчиков пыли из прошлого обзора и одним из датчиков углекислоты из этого обзора.
Пока готов код, отображающий на экране ситуацию с запылённостью. Подробности об этом проекте я добавил в обзор датчиков пыли, включая скетч для Ардуино. Показания прибора сегодня выглядят вот так:
Скоро туда добавится график измерения уровня углекислоты. Если нужно, объединенный скетч по мере готовности я выложу в этом обзоре.
MH-Z14A — рабочий датчик, рекомендую к покупке. Сейчас прилагается USB to RS485 TTL преобразователь, в хозяйстве пригодится.
+145 |
29631
137
|
Самые обсуждаемые обзоры
+64 |
3653
95
|
Кмк проще сунуть датчик на проводах внутрь воздушного шарика и надуть гелием. Горловину перетянуть и откалибровать.
Даже простой шарик даст достаточно времени (для гелия стенки обрабатывают чтобы не травил).
За 400ppm а чистый лес идти)
Калибровку в диапазоне измерения проводят калибровочными газами с прецизионной концентрацией.
Но это в медицине.
Обозреваемые датчики имеют другую область использования и возможно описанные алгоритмы калибровки имеют право на жизнь, но я в них не уверен, imho
Но если уж очень хочется кустарно — то можно обойтись и без поисков жидкого азота или разговором со сварщиком на шиномонтаже. Достаточно пойти в магазин и купить брендовый баллончик газа для заправки зажигалок, или для туристических плиток. Там тоже углекислоты нет, пропан-бутан, довольно чистый…
Мне кажется такой лучше
P.S. Майнкрафт не люблю :)
Да и в коменте скорее всего разрешение дисплея выше и, соответственно, намутить шрифт можно посглаженнее.
Сам размышляю над этой темой и хотелось бы узнать какие управляемые заслонки кто использует.
Меня крайне одолевают сомнения, что в комнате возможно явное расслоение.
Можете сделать измерения CO2 на полу, в центре и под потолком? Для статистической верности — несколько раз, но в одинаковых условиях. Скажем, минут по 5 на каждом уровне и два-три-четыре измерения в течение дня — чтобы невозможно было списать на случайность и погрешности.
Мне просто не удалось нагуглить чётких данных. Все ограничиваются пространными рассуждениями про молекулярную массу или конвекцию воздуха. А циферок-то нет.
Как по мне — эти измерения на статью на Хабр потянули бы.
habr.com/ru/post/395755/
habr.com/ru/post/187210/
habr.com/ru/post/301296/
и ещё несколько там же можно нарыть.
www.youtube.com/user/Consulbud
Настолько системного и объёмного разжёвывания всяких строительных тонкостей я во всём рунете не встречал. Тем не менее, некоторое количество спорных тезисов есть и у него.
Ну а про молекулярный вес углекислого газа тут уже ответили. Это из школьного учебника — можете Рабиновича не трогать.
в медицине устройство приточно-вытяжной вентиляции предусматривает приток сверху, вытяжку снизу на высоте 0,3 метра от пола.
Дальше. Оптимальное место для приточки ещё и от температур зависит. У нас ведь сезоны разные — зимой мы подогреваем входящий холодный воздух, а летом — охлаждаем входящий с улицы тёплый. И тут либо делать полностью воздушное отопление-кондиционирование как у америкосов, либо соответствующим образом располагать дырки под приток.
Ну т.е. в типовой квартире с центральным отоплением приточка для зимы делается сразу над батареей, а приточка для лета — сразу под кондишеном.
Впрочем, кондишен и так агрессивно перемешивает воздух, значит можно для всех сезонов ограничиться подоконной приточкой.
А вот где забирать воздух при принудительной вентиляции зависит от того, где он «грязнее» — наверху из-за выдыхаемого пара или внизу из-за выдыхаемого CO2.
Поэтому и попросил сделать замеры на разных высотах. Ибо строить теоретические выкладки можно до посинения.
Это что, прям щели на улицу сделаны чтоль? =)
Если будет подпор, то система будет неустойчивой — может случиться картина, когда давление нагнетания будет равно разрежению вытяжки — тогда у вас в щели под окнами вообще перестанет поступать воздух — два вентилятора будут пахать исключительно друг на друга.
Тогда один общий вентилятор-вытяжка на кухне будет создавать общее разрежение на всю квартиру. Причём, скорость вентилятора (тобишь создаваемое разрежение) должно равняться степени открытия всех задвижек суммарно.
И в каждом помещении свой датчик СО2. Где больше нагазовано — там задвижка сильнее открывается (автоматикой) на улицу. А вентилятор тем сильнее крутится, чем больше задвижек открыто и чем они сильнее открыты.
В этой схеме исключено передавливание-перетягивание — когда несколько вентиляторов могут мешать друг другу и перенаправлять поток не так как надо.
Всё — ИМХО от диванного инженера, хоть и с дипломом :) Сам пока только планирую себе подобную схему — как там оно на практике встанет — посмотрим.
Вот бы обзор с фоточками.
Хотя бы уже реализованных узлов, в частности вот этой околобатарейной вентиляции.
И ещё такой вопрос интересует: а разве это легально, вентилировать прям на батарею?
Там наверняка какие-то нормативы же есть по кол-ву отнимаемого у соседей по стояку тепла.
Я, просто, хотел себе обдув батарей сделать, чтобы тёплый с холодным воздух перемешивался лучше, и чтобы теплее было дома.
Статистика по датчикам что говорит, критично это или норм?
Но вообще фильтр ведь для того и сделан, чтобы на нём что-то оседало.
Всё на пальцах — конкретных цифр и измерений не видел. Реальное воздействие нужно подкреплять обширными исследованиями. Только кому оно надо? Фирмачам — нафиг. Юзерам — да, но не потянут. Ждём может институт какой озабодится проблемой.
например, вполне легальный выжиматель Climer_sx_25 или, скажем, наоборот, Стеновой_клапан_Домвент тот же.
Ну, или когда помещение не имеет окон вообще, находится внутри здания.
+ Если людей больше 1, то и понятие душно у каждого может юыть разное.
P.S.
Для меня, к примеру, «душно» — это когда у большинства других это уже перебор. Дкмаю это зависит не только от самой предрасположенности к CO2, но и то как ты дышишь (частота/глуюина дыхания).
За обзор спасибо.
А то от жидкого-то, датчик кони двинет))
пс. Подскажите, где можно почитать про логгер «OpenLog»? Интересная штука.
судя по гуглу, это разработка sparkfun, так что плясать надо от его github'а.
Но станет очень холодным газом, что не отменяет возможного песца сенсору.
Плюс опять-же это будет не чистый азот, а смесь с воздухом, с ненормируемым содержанием других газов, что не позволит правильно откалибровать ноль/нуль.
Спасибо за ссылочку!
а вот фиг его знает, что будет с данным объектом, если он даже временно заморозится. Зависит от веществ, которые входят в его состав.
На состав этого азота — скорее всего, т.к. судя по вики точка кипения жидкого азота -195,75 °C. Сомневаюсь, что там азот при температуре ниже этой => какая смесь газов.
я делаю метеостанцию, взял 5000, только потому что в исходном проекте было озвучено что датчик используется 5000. Т.е я смогу переключить диапазон на 2000?
Вообщем он периодически сбивается на 500-1000 пунктов. И сам же возвращается назад…
Попробуйте проведите длительный тест в пару-тройку недель.
Вторая проблема этих датчиков была описана на хабре — предельный диапазон на цифре и аналоге может быть и 5000ppm и 2000ppm и узнать это без образцового прибора можно лишь косвенно, анализируя показания чистого воздуха и не очень.
зы: Только приехал такой, с индексом «B»…
Имею несколько MH-Z19B и несколько S8. Если MH-Z19B откалибровать и отключить автокалибровку — показывает ПОЛНОСТЬЮ идентичные данные с S8 (в рамках заявленной погрешности). Проверенно опытами в несколько месяцев непрерывной работы в квартире с показаниями 400-2500 (газовая плита).
Зеленая линия — MH-Z19, красная — MH-Z14, синяя — S8.
Глядя на график, можно лишь предположить, что Z19 откалиброван неверно — за 400 ppm он принимает некоторое большее значение (700?) и всё, что меньше него, обрубает. То же касается электрохимического. Z14 вообще оч странно колбасит, не очень похоже на калибровку. Возможно, глюки. S8 тоже один раз скакнул. Почему? Непонятно.
В общем, для начала надо откалибровать, а потом уже приводить графики.
У меня почему-то показания идут в диапазоне 1000 до 2000, меньше 1000 не хочет показывать. Версия датчика 0053.