/*
© Tykhon, 2019.
MH-Z19
TX: 3<->5 A0
RX: 3<->5 A1
Gnd: Gnd
Vin: +5v
MH-Z19b
TX: 3<->5 4
RX: 3<->5 A1
Gnd: Gnd
Vin: +5v
MH-Z14A
1_+5v: +5v
2_Gnd: Gnd
11_RX: 3<->5 7
10_TX: 3<->5 6
S8:
G+: +5v
G0: Gnd
Rx: 3<->5 A3
Tx: 3<->5 A2
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 <SoftwareSerial.h>
#include "Wire.h"
/////////////////////// for LCD //////////////////////////////
#define TFT_CS 10
#define TFT_RST 9 // you can also connect this to the Arduino reset in which case, set this #define pin to -1!
#define TFT_DC 8
Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST); //for LCD
#define DS1307_ADDR 0x68 // RTC address
int s8_co2;
int z14_co2;
int z19_co2;
int z19b_co2;
int z19_t;
int z19_ss;
int z19b_t;
int z19b_ss;
int s8_co2_last;
int z14_co2_last;
int z19_co2_last;
int z19b_co2_last;
int s8_co2_mean_last;
int z14_co2_mean_last;
int z19_co2_mean_last;
int z19b_co2_mean_last;
int s8_co2_mean;
int z14_co2_mean;
int z19_co2_mean;
int z19b_co2_mean;
int s8_co2_mean2;
int z14_co2_mean2;
int z19_co2_mean2;
int z19b_co2_mean2;
SoftwareSerial mySerial_z19(A0, A1);
SoftwareSerial mySerial_z19b(4, 5);
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];
unsigned char response_z19b[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 = 2; // period for recording data, min
float smoothing_factor = 0.5; // coefficient for Kalman filter
float smoothing_factor2 = 0.15; // coefficient for Kalman filter
//////////////////////////////////////// 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_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));};
if ( !(response_z19[0] == 0xFF && response_z19[1] == 0x86 && response_z19[8] == crc) ) {
Serial.println("CRC error z19: " + String(crc) + " / "+ String(response_z19[8]));
} else {
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 sendRequest_z19b(){
mySerial_z19b.begin(9600);
mySerial_z19b.write(cmd_z19, 9);
memset(response_z19b, 0, 9);
mySerial_z19b.readBytes(response_z19b, 9);
int i;
byte crc = 0;
for (i = 1; i < 8; i++) crc+=response_z19b[i];
crc = 255 - crc;
crc++;
if (response_z19b[0] != 0xFF) {Serial.println("CRC error z19b: response_z19b[0] != 0xFF: " + String(response_z19b[0]));};
if (response_z19b[1] != 0x86) {Serial.println("CRC error z19b: response_z19b[1] != 0x86: " + String(response_z19b[1]));};
if (response_z19b[8] != crc) {Serial.println("CRC error z19b: response_z19b[8] != crc: " + String(response_z19b[8]) + " / "+ String(crc));};
if ( !(response_z19b[0] == 0xFF && response_z19b[1] == 0x86 && response_z19b[8] == crc) ) {
Serial.println("CRC error z19b: " + String(crc) + " / "+ String(response_z19b[8]));
} else {
unsigned int responseHigh = (unsigned int) response_z19b[2];
unsigned int responseLow = (unsigned int) response_z19b[3];
unsigned int responseTT = (unsigned int) response_z19b[4];
unsigned int responseSS = (unsigned int) response_z19b[5];
z19b_t = responseTT-40;
z19b_ss = responseSS;
z19b_co2 = (256*responseHigh) + responseLow;
};
mySerial_z19b.end();
}
void tft_form(){
tft.setTextSize(2);
tft.setTextColor(0x177F);
tft.setCursor(2, 4);
tft.print("z14");
tft.setCursor(2, 33);
tft.print("z19");
tft.setCursor(2, 62);
tft.print("z19b");
tft.setCursor(2, 91);
tft.print("s8");
}
void tft_output(){
tft.setTextSize(2);
tft.setCursor(56, 4);
tft.setTextColor(ST7735_BLACK);
tft.print(String(z14_co2_mean_last));
tft.setCursor(56, 4);
tft.setTextColor(ST7735_WHITE);
tft.print(String(z14_co2_mean));
z14_co2_mean_last = z14_co2_mean;
tft.setCursor(56, 33);
tft.setTextColor(ST7735_BLACK);
tft.print(String(z19_co2_mean_last));
tft.setCursor(56, 33);
tft.setTextColor(ST7735_WHITE);
tft.print(String(z19_co2_mean));
z19_co2_mean_last = z19_co2_mean;
tft.setCursor(56, 62);
tft.setTextColor(ST7735_BLACK);
tft.print(String(z19b_co2_mean_last));
tft.setCursor(56, 62);
tft.setTextColor(ST7735_WHITE);
tft.print(String(z19b_co2_mean));
z19b_co2_mean_last = z19b_co2_mean;
tft.setCursor(56, 91);
tft.setTextColor(ST7735_BLACK);
tft.print(String(s8_co2_mean_last));
tft.setCursor(56, 91);
tft.setTextColor(ST7735_WHITE);
tft.print(String(s8_co2_mean));
s8_co2_mean_last = s8_co2_mean;
tft.setTextSize(1);
tft.setCursor(110, 4);
tft.setTextColor(ST7735_BLACK);
tft.print(String(z14_co2_last));
tft.setCursor(110, 4);
tft.setTextColor(ST7735_WHITE);
tft.print(String(z14_co2));
z14_co2_last = z14_co2;
tft.setCursor(110, 33);
tft.setTextColor(ST7735_BLACK);
tft.print(String(z19_co2_last));
tft.setCursor(110, 33);
tft.setTextColor(ST7735_WHITE);
tft.print(String(z19_co2));
z19_co2_last = z19_co2;
tft.setCursor(110, 62);
tft.setTextColor(ST7735_BLACK);
tft.print(String(z19b_co2_last));
tft.setCursor(110, 62);
tft.setTextColor(ST7735_WHITE);
tft.print(String(z19b_co2));
z19b_co2_last = z19b_co2;
tft.setCursor(110, 91);
tft.setTextColor(ST7735_BLACK);
tft.print(String(s8_co2_last));
tft.setCursor(110, 91);
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()));
}
void z19setup() {
/* 2 3 6 7
{0x86,0x00,0x00,0x00}, // mhz_cmnd_read_ppm
{0x79,0xA0,0x00,0x00}, // mhz_cmnd_abc_enable
{0x79,0x00,0x00,0x00}, // mhz_cmnd_abc_disable
{0x87,0x00,0x00,0x00}, // mhz_cmnd_zeropoint
{0x8D,0x00,0x00,0x00}, // mhz_cmnd_reset
{0x99,0x00,0x03,0xE8}, // mhz_cmnd_set_range_1000
{0x99,0x00,0x07,0xD0}, // mhz_cmnd_set_range_2000
{0x99,0x00,0x0B,0xB8}, // mhz_cmnd_set_range_3000
{0x99,0x00,0x13,0x88}}; // mhz_cmnd_set_range_5000
*/
// byte setrangeA_cmd[9] = {0xFF, 0x01, 0x99, 0x00, 0x00, 0x00, 0x07, 0xD0, 0x8F}; // set range 0 - 2000ppm
byte setrangeA_cmd[9] = {0xFF, 0x01, 0x99, 0x00, 0x00, 0x00, 0x13, 0x88, 0xCB}; // set range 0 - 5000ppm
// byte setrangeA_cmd[9] = {0xFF, 0x01, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86}, // mhz_cmnd_abc_disable
// byte setrangeA_cmd[9] = {0xFF, 0x01, 0x79, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xE6}; // mhz_cmnd_abc_enable
// byte setrangeA_cmd[9] = {0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79}; // data request
unsigned char setrangeA_response[9];
mySerial_z19.begin(9600);
mySerial_z19.write(setrangeA_cmd,9);
mySerial_z19.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);
mySerial_z19.end();
}
/////////////////////////////////////// setup /////////////////////////////////////////
void setup(void) {
Serial.begin(38400);
Wire.begin();
// z19setup();
tft.initR(INITR_BLACKTAB); // initialize a ST7735S chip, black tab
Serial.println("init");
tft.setTextWrap(false); // Allow text to run off right edge
tft.setRotation(3);
tft.fillScreen(ST7735_BLACK);
tft_form();
}
//////////////////////////////////////////////// loop /////////////////////////////////////////////////////
void loop() {
sendRequest_z14();
sendRequest_z19();
sendRequest_z19b();
sendRequest_s8(cmd_s8);
s8_co2 = getValue_s8(response_s8);
if (!s8_co2_mean) s8_co2_mean = s8_co2;
if (!z14_co2_mean) z14_co2_mean = z14_co2;
if (!z19_co2_mean) z19_co2_mean = z19_co2;
if (!z19b_co2_mean) z19b_co2_mean = z19b_co2;
s8_co2_mean = s8_co2_mean - smoothing_factor*(s8_co2_mean - s8_co2);
z14_co2_mean = z14_co2_mean - smoothing_factor*(z14_co2_mean - z14_co2);
z19_co2_mean = z19_co2_mean - smoothing_factor*(z19_co2_mean - z19_co2);
z19b_co2_mean = z19b_co2_mean - smoothing_factor*(z19b_co2_mean - z19b_co2);
if (!s8_co2_mean2) s8_co2_mean2 = s8_co2;
if (!z14_co2_mean2) z14_co2_mean2 = z14_co2;
if (!z19_co2_mean2) z19_co2_mean2 = z19_co2;
if (!z19b_co2_mean2) z19b_co2_mean2 = z19b_co2;
s8_co2_mean2 = s8_co2_mean2 - smoothing_factor2*(s8_co2_mean2 - s8_co2);
z14_co2_mean2 = z14_co2_mean2 - smoothing_factor2*(z14_co2_mean2 - z14_co2);
z19_co2_mean2 = z19_co2_mean2 - smoothing_factor2*(z19_co2_mean2 - z19_co2);
z19b_co2_mean2 = z19b_co2_mean2 - smoothing_factor2*(z19b_co2_mean2 - z19b_co2);
getdate();
tft_output();
// if (((current_minute)%work_period == 0)&&(lastminute != current_minute)) {
String dataString = getdate()+" s8~~: "+String(s8_co2_mean2)+" s8~: "+String(s8_co2_mean)+" s8: "+String(s8_co2)+" z14~~: "+String(z14_co2_mean2)+" z14~: "+String(z14_co2_mean)+" z14: "+String(z14_co2)+" z19~~: "+String(z19_co2_mean2)+" z19~: "+String(z19_co2_mean)+" z19: "+String(z19_co2)+" z19b~~: "+String(z19b_co2_mean2)+" z19b~: "+String(z19b_co2_mean)+" z19b: "+String(z19b_co2);
// +" z19_t: "+String(z19_t)+" z19b_t: "+String(z19b_t);
dataString.replace(".",",");
dataString.replace(" ","\t");
Serial.println(dataString);
lastminute = current_minute;
// };
delay(second_delay);
}
Все эти датчики за 400 принимают самое низкое значение которые видели в период автокалибровки. Если у вас на улице (и в помещении соответсвенно) никогда не опускается ниже 500, то через какое-то время датчики примут эти 500 за 400 и будут показывать именно 400.
В таком случае помогут лишь Double NDIR — у них есть «референсный» обьем воздуха с которым происходит сравение.
В африканских джунглях
В лесах на юге Австралии
и тд.
согласно википедии содержание CO2 еще 100 лет назад составляло около 300ppm.
Вам сколько лет? )))
И ссылками я вас тоже могу завалить. Только я не о средней температуре по больнице, а о конкретных измерениях, собственноручно сделанных калиброванными датчиками.
Я вам даже могу рассказать, как распределяется уровень углекислоты по высоте конкретной пятнадцатиметровой берёзы в берёзовом лесу под окном.
А также про то, с какой скоростью изменяется уровень углекислоты, например, в изолированном помещении, после открытия заслонки на улицу (без принудительной вентиляции). У диффузии есть скорость. И углекислый газ тяжелее воздуха.
не получилось у вас завалить.
а других нет? ну интересно же почитать не анонима из интернета, а что то более научное
да и про то, что CO2 тяжелее кислорода в 1,5 раза и собирается в низинах я надеюсь всем известный факт и то что в некоторых таких низинах кислород вообще вытесняется CO2 тоже должны знать.
Это все физика.
А вот то что береза возле дома(надеюсь на солнечной стороне и только днем) работает как насос CO2 и на выхлопе получается О2 слышу впервые.
Какие же конкретно калиброванными датчиками вы измерили этот факт?
Ночью я не измерял. Cамое меньшее, что видел — 290ppm.
Растения все в той или иной степени поглощают углекислоту и выделяют кислород. Это биология.
По поводу низин. Диффузии плюс малейших перемещений воздуха обычно достаточно для равномерного перемешивания углекислоты.
Город, где я живу, находится в кольце гор. Иногда (пару раз за зиму) возникает некая климатическая аномалия, когда на доступных мне высотах несколько дней держится до 800ppm. Я не знаю, почему.
Если вам сильно важны эти факты, попробую в этом году запруфить. Только лето, к сожалению, закончилось.
Конкретно эти измерения делались прибором iCeeO2-700. Датчик внутри их собственный, двухкамерный.
Я тоже с 2014.
Если точно уверены что помещение хотя бы раз в сутки проветривается — можно брать 19B. Если нет — лучше все таки доплатить до S8.
На работе рядом стоят S8 и MH-Z19B. Если ночью нормально проветрилось — показания совпадают до 10ppm. Но если вдруг ночью на работе кто-то был или по какой-то причине не проветрили, то MH-Z19B легко может занижать хоть на 200ppm. Вплоть до следующей автокалибровки (раз в сутки).
S8 калибруется по показаниям за 8 дней. Раз в неделю то точно помещение проветривается.
(хоят на форумах встречались случаи когда помещение до «уличного» состояния не проветривалось месяцами, в этом случае и S8 показывал неадекватно низкие показания)
Ну или как вариант — брать MH-Z19B и отключать автокалибровку после одного нормального проветривания.
И да, у всех показания зависят от температуры. По-этому калбировать на улице тоже нужно аккуратно.
A = A — x *(A-A`)
, где:
А — сглаженное значение
А` — только что измеренное
x — коэффициент сглаживания.
Вот и вся математика. Очень просто и эффективно.
В любом случае познавательная и отличная работа
Судя по графику, как раз таки MH-Z19B показывал все верно. В помещении у вас не опускается ниже 500. И вот когда автокалибровка прошла, эти 500 он принял за 400 :)
(по крайне мере по этому одному графику я бы сделал именно такой вывод)
По собственному опыту, MH-Z19B откалиброван правильно если вообще нет показаний 399 и ниже. Даже если он видит показание условно 380, то показывать он все равно будет 399.
От 19B отказался, стоят S8.
По датчикам Co2 система решает в какую комнату гнать воздух.
Беру на вооружение(-в полезное..).
Здесь подключение, документация и апроксимация:
wiki.amperka.ru/%D0%BF%D1%80%D0%BE%D0%B4%D1%83%D0%BA%D1%82%D1%8B:mq135#%D1%80%D0%B5%D1%81%D1%83%D1%80%D1%81%D1%8B
Вот так, имея датчиков больше чем газов, можно составить систему линейных уравнений из их графиков чувствительности, и если повезет (если матрица окажется невырожденной), то найти концентрацию всех газов более-менее точно.
Но идея пока не реализована и не уверен, что будет. Все-таки в этих датчиках измерения скачут, их еще прогревать надо, да еще и неэкономичны они, не хочется вкладывать столько сил в такой проект.
К примеру вот https://aliexpress.com/item/item/33030441543.html (первая попавшася, сам у него не покупал)
Про калибровку «улицей» в городах и 400 ппм.
Это как бэ прорыв в метрологии.
Я могу представить (кратко, на пальцах) результаты двухсуточных исследований городского воздуха в одном месте, расположенном в городской черте на предмет объемной доли СО2. Городок куда меньше города-героя Ленинграда (откуда ТС) и города-героя Москвы (откуда Надёжин).
У меня только 1 вопрос — на показания сабжевых датчиков как-то влияет концентрация углеводородов в воздухе? Для домохозяек — ключевое слово «метан».
Ежели — да, то все может оказаться еще интереснее…
Не плохой, а дебильный.
То можно получить какие-то цифры.
Получены цыфры: 5,6,7,8,9,0,1,2,3,4,5… И, внезапно — 9,4,7,3!!!
Вот это поворот!
Надо просто открывать форточку/окно в комнате при любой возможности.
В лаборатории: тоже самое, либо включать имеющеюся приточно-вытяжную вентиляцию.
Это открытие сделано лет 200 тому как. Не благодарите.
MH-Z14 — хороший, но старый (много потребляет, капризен по питанию, горячий). SenseAir S8 — ОЕМ китайская штуковина, ничего общего с легендарными К30 и К33 не имеющая.
Мы сейчас в своих изделиях используем Т6703.
И да, альтернативы NDIR датчикам для целей вентиляции помещений нет (они быстрее и точнее). Все эти eCO2 (например, CCS811) — маркетинговый шлак, показывающий погоду на Марсе. Есть ещё древние датчики накаливания (например, MQ-135) — тоже убожество (очень долго выходит на режим, отравляется разными парами, часто врёт).
Ну и про Т6703 тоже, благо цена у него вполне адекватная (на али по крайне мере).
Я бы не сказал, что S8 плохой датчик. Просто он «среднекитайский», и по критериям цена-качество несколько проигрывает Т6703.
Преимущества Т6703:
— дешевле,
— штатная версия 5000ppm (S8 теперь тоже показывает, после обновления прошивки, но на PWM только 2000ppm осталось),
— наличие интерфейса I2C,
— лучше работает АВС (тут надо сказать, что этот алгоритм, видимо, самая большая тайна производителей этих датчиков, подробно не расскажу, но Т6703 может обходиться без проветривания минимум две-три недели).
MH-Z19B — это довольно неудачный датчик, как я уже сказал. Хотите дёшево и аналоговый выход — купите лучше MH-Z14A.
Зачем вам, кстати, геморрой и танцы с бубном при разнице в 500 рублей с Т6703?
Вверху мой заказ и цена за которую я его купил, внизу страница поиска, причем я выбрал кусочек с самой дешевой ценой+доставка
Только за минимальную цену я бы покупать не стал, это часто отбраковка из отбраковки.
А 103.03 + 9.04 = таки 112.07
что уже дает нам 56.6% от цены такого же точно «отбракованного» (самого дешевого) Т6703
Если вы считаете что 56.6% и 100% это не близкое к «в два раза» — то хз, вам виднее…
Да, и в рублях это не 500, а 800 разница, ну так, на всяк случай.
За сим откланиваюсь, надоело уже вас поправлять в каждой фразе.
Цена T6703 выше цены MH-Z19B на 35%
или
цена MH-Z19B ниже цены T6703 на 26%
Да и в рублях на текущий момент разница следующая 1702,11 — 1259,56 = 442,55.
ну и добавлю ссылки что бы каждый мог проверить для своей страны
T6703
MH-Z19B
хосспади… сплошные фейспалмы…
PS, но хотя бы T6703 нашли дешевле, уже плюс
ну ок, давайте вашу цену возьмем за основу, а это к моей -$1,74, специально возьму вчерашний курс $ — 68.08руб. Получается в рублях ваш лот дешевле моего на 118,45руб, итого разница в рублях 561.
Ну и проценты
Цена T6703 выше цены MH-Z19B на 49%
или
цена MH-Z19B ниже цены T6703 на 33%
Получается на разницу в цене максимум можно купить только половину MH-Z19B, но ни как не два. Т.е. при покупке двух T6703 можно купить три MH-Z19B. И в рублях разница не 800 рублей, ну так, на всякий случай.
Цена двух шт в РФ — $31,20
$15,6 каждый.
Ну и проценты
Цена T6703 выше цены MH-Z19B на 60%
или
цена MH-Z19B ниже цены T6703 на 37,6%
Ну и в рублях.
1702,11 — 1062,115 = 640
Что и требовалось доказать — два MH-Z19B на цену T6703 в $25 купить не возможно. Как и разницы в цене в 800 рублей тут нет.
зы: ссылка на лот с ценой в $16 с бесплатной доставкой в РФ — https://aliexpress.com/item/item/33012363245.html
Правд много, на всех хватит.
По ABC достаточно мануалы посмотреть :)
T6703 — период 21 день
S8 — период 8 дней.
19B — период один день (это вообще за гранью разумного)
То есть если не важны 5000 (мои S8 кстати показывают, 2600 точно) и i2C то вполне себе и S8 неплох (с учетом использования дома где раз в неделю таки проветриваем).
По ABC достаточно мануалы посмотреть :)
T6703 — период 21 день
S8 — период 8 дней.
19B — период один день (это вообще за гранью разумного)
Для показометра лучше иметь предел 5000ppm, чтобы не зашкаливало. А для целей управления вентиляцией лучше 2000ppm, так как у такого сенсора разрешение в целевом диапазоне в два раза выше (и, соответственно, он быстрее и точнее работает).
По поводу I2C. Это сейчас стандарт для всех видов сенсоров. И, если делать комбинированный датчик (как в нашем случае), то I2C однозначно удобнее. Опять же, если у вас уже есть сеть Модбас, то вам это неважно.
С периодом ABC не всё так просто, как в мануале. S8\K30\T6703 тоньше подходят к вопросу. Если ошибка (несоответствие неким ожидаемым профилям показаний) большая, то могут перекалиброваться раньше. Если ошибки по профилям нет, но уровень в 400ppm за указанный период они не видели, то будут дрейфовать понемногу, но сразу на несколько сотен не скакнут, как MH-Z19.
Кстати, MH-Z14 — это копия T6613, вплоть до мельчайших подробностей. Есть версия, что Winsen был производителем T6613\T6603 для Telaire. А потом что-то у них не срослось. Но вот MH-Z19 — это их собственная разработка, а это совсем другое дело.
Ещё хочу сказать, что и S8, и T6703 в цивилизованных странах стоят сильно других денег. Так что то, что все покупают на Али, это либо «третья смена», либо неплохая подделка. Есть и хуже варианты, например, все продающиеся на Али датчики Бош Сенсортек (ie BME280) — довольно плохая подделка, которую использовать нереально.
использую BME280 с Али уже полгода. Да, самый дешевый лот :D
Температура совпадает с 3мя DS18B20 (в пределах документированных погрешностей естественно), влажность в калибровочной среде показывает с точностью до 1% (хотя ДЩ регламентирует 3%) Альтиметр показывает адекватную моей географической местности высоту над уровнем моря. Давление проверить не могу, но по скольку оно присутствует основным параметром в формуле расчета альтиметра, результат которого совпадает с данными для моей местности, нету оснований сомневаться в этом измерении.
Специально «убивал» датчик — мочил в ИПА, использовал в среде предельной влажности (90-100%), заливал водой, замораживал, создавал условия конденсата — работает гад такой «поддельный» и кажет от 74.5 до 75.6% влажности в «солевой» калибровочной среде.
(длительность теста, и соответственно «ширина» графика — 6 часов)
Так что, «святой отец, или крестик снимите, или штаны оденьте» ©… Ну или с руками у вас что-то не так…
А вот по поводу сенсора ВМЕ280 могу рассказать следующее.
Сначала измерьте его потребление и сравните с Бошевским даташитом. Увидите, что у подделки оно больше в разы.
Затем возьмите тепловизор и посмотрите температуру сенсора. Высокое потребление приводит к разогреву. И тот факт, что создатели учли это при калибровке температуры/влажности, никак не помогает в целом ряде практических случаев.
Ну и наконец, сравните показания датчика давления (тут-то Бош непревзойдённым должен быть) на высоте земли и, скажем. второго этажа. И опять же сравните с даташитом.
DS18B20 также очень много неоригинальных, но это отдельная история.
Если вы используете такие сенсоры для поделок на фанерке при помощи соплемёта, ещё не значит, что их можно ставить в изделия и продавать людям. Как-то так.
Очередной вброс: не менее 100% силовых компонентов некитайских марок, которые продаются на Алиэкспресс — подделка. А также много популярных микросхем, например, микроконтроллеров Атмел.
Все мои ардуины с Али работают отлично, датчики — отлично, БМЕ280 отслеживает и определяет высоту на столе и на уровне пола (перепад чуть более 0.7м).
Силовые мосфеты — соглашусь, тут все грустно. Но во-первых есть и другие способы, во-вторых арбитраж Али. К тому же феты относительно легко проверить и сравнить с ДЩ. Остальная силовуха — очень грустная песня. Для любителей пощекотать нервы, это факт.
Для коммерческих проектов народ заказывает непосредственно у производителя или на специализированных площадках, за цену, часто, х10 от цены на Али\Тао. И это оправдано! Для «домашнего хобби» — кому как, у каждого свои критерии рентабельности и свои коэффициенты конвертации время\деньги.
digikey и mouser — из США,
farnell — из Европы,
lcsc — из Китая.
Правильных магазинов на самом деле больше, я указал наиболее крупные. Кроме того, некоторые российские поставщики также проверяют то, что продают. И, если контрафакт, так прямо и пишут. Например — Промэлектроника.
Ещё мы иногда заказываем прямо на сайте производителя компонентов. Например — Микрочип.
За остальную ифну спасибо. Полезно.
Имею T6703, заказанный с Али для теста.
Датчик был установлен на макетную плату совместно с esp8266 и экраном, на который выводятся показания.
После первичного включения он показывал порядка 1500ppm, но через сутки показания пришли в норму.
Пару недель он работал нормально, но недавно начались какие-то приколы — то 0ppm показывает, то около 200, то 700-800. Помещение оборудовано вентиляцией и нормально проветривается, за предыдущее время я показаний выше 600ppm не видел. А тут после этих глюков его вообще понесло — неделю показывал 700+ ppm и дальше все хуже и хуже, постепенно и за 1000ppm перевалило.
Прочитал тут, что это может быть из-за нестабильного питания (при срабатывании датчика яркость экрана проседала), установил по питанию доп. конденсаторы, просадки исчезли. Но это никак не повлияло на работу датчика. Он как показывал 1000+ ppm, так и кажет.
Собсно вопрос, в чем тут дело?
Может можно как-то его принудительно заново откалибровать? Выключение питания на сутки не помогает.
У вас либо что-то где-то окислилось, либо датчик повредился такими скачками питания (что маловероятно, но всё-таки возможно).
Похоже, в нем из-за провалов по питанию ABC Logic сошла с ума.
Как раз все ходил вокруг да около этих «коробочек», но домой поставил таки CCS811 (CJMCU-8128) — за миниатюрность. Впихнул на платку рядом с D1-mini — все получилось не больше спичечного коробка. Работает.
Правда, выяснилось, что библиотек на CCS811 на lua нет — сам морочился. Но зарабюотало :)
Конечно, это «маркетинговый шлак и игрушки», но чисто по ощущениям вполне себе коррелирует со спертым воздухом и перегаром ;)
Думал уже брать что-то посерьезнее. Но раз такая штука на подходе — подожду. )
Зеленый график — MH-Z19
Синий — CCS811
Датчики находятся в 2 см друг от друга.
Так, для сравнения и сделать вывод.
Есть вопрос по поводу самокалибровки и обхода этой гадости с помощью «простых костылей»
А именно… К сожалению, ЕСЛИ брать ответ от датчика по аналогу или PWM — то нету возможности ему давать по UART команду отключения калибровки (которая должна подаваться при каждом его включении). С другой стороны вроде как период калибровки у него 24ч. В связи с этим вопрос к Вам — можете ли Вы проверить точный интервал времени через которое он калибруется, и, что на много важнее, если незадолго до этого времени его принудительно выкл-вкл (ну скажем сделать ему обесточивание на 30 сек) — начнет ли он отсчет периода для калибровки с нуля или нет.
Т.е. развивая мысль до конца — если приделать ему костыльный таймер на отключение его от питания каждые условно 23ч50м на 30 сек — будет ли он пытаться калибровать себя или нет.
Заранее спасибо, очень надеюсь на Вас (проведение эксперимента и ответ в теме)
Дело в том, что источником ИК-излучения внутри датчика является по сути лампа накаливания. Её характеристики со временем неизбежно «уходят». Прошивка датчика, кстати, может учитывать факт старения лампочки по некоторой кривой. Материалы отражателя также стареют и это влияет на показания.
Бредятина №2 — это не нормально вбивать ежесуточную поправку на 1% (минимум, а судя по постам Андрея — вообще на 4%) шкалы, при лайфтайме 5 лет (значок огромного фейспалма)
Бредятина №3 — ЧТО может стареть в лампочке накаливания, которая работает в режиме недокала (для смещения в ИК диапазон) да еще и в импульсном режиме?! (значок тройного фейспалма)
А про лампочку — так вы откройте, посмотрите. Она, конечно, не простая, но спираль вполне себе видна.
Про старение ламп накаливания вот так сходу не нашёл документов. Но был такой прибор — спектрофотометр ГретагМакбет Спектролино. И производитель довольно доходчиво обосновывал сроки калибровки и скорость ухода параметров этой самой лампочкой накаливания, работающей в недокале.
Про какие 4% вы пишете — не очень понял.
200ппм=4% от 5000ппм шкалы.
в доках я встречал о максимально 50ппм ежедневной калибровке, т.е. 1% полной шкалы.
В любом случае калибровка на такие огромные величины без серьёзных причин каждые 24 часа — это бред и вредительство.
ЗЫ Важный совет: датчик должен быть поближе к блоку питания и желательно припаян. Любые потери питания в цепи — неадекватность показаний. Из опыта должно быть стабильное напряжение.
Это всё-таки сенсоры, и обеспечить в точке их обычной установки (на стене, на высоте 1,7-2,5 метра от пола) наличие близости к блоку питания — несколько затруднительно.
Правильно — до датчика вести 12-24 вольта, а возле него устанавливать импульсный преобразователь. Дроссель и конденсаторы преобразователя должны иметь достаточные номиналы, чтобы сенсор работал нормально.
Спасибо!
Еще момент — ровно через 24 часа после старта «таймера», т.е. после 62/72 минут, или включительно с этим «предразогревом»?
Можете понаблюдать, а то я уже начинаю путаться в них. Сегодня была первая автокалибровка после сброса и выставления «нуля». ссылка
Короче надо его тушить раз в 20-23 часа для гарантированного обнуления таймера.
еще раз спасибо!
255 134 2 30 65 0 27 3 251
Отсчет слева направо с нулевого байта. В данном случае 27+256*3=795.
Тройка на 7-й позиции показывает количество «сбросов» 6-го байта.
Это для датчика 2019 года. В датчике 2018 года 7-й байт всегда 0.
С моей точки зрения логично ориентироватсья по критерию наполнения статистики. Сделал столько-то измерений — калибровка. Потому что иначе нужно либо встраивать в датчик часы реального времени и их питание, либо вовсе лишить датчик возможности калибровки при эпизодической его работе, когда статистика сбрасывается при отключении питания. Но это мои домыслы.
Точно могу прислать таблицу показаний датчика, если нужно. Когда накопится достаточно данных.
Дата выпуска датчика 05.08.2019
Но смотрю и у Вас на второй калибровке тоже подпрыгнуло на 400 и после этого ниже 600 за 2,5 суток не опустилось ни разу…
Если подключить к питанию +5вольт, то перестает совсем отвечать.
Может кто подскажет что может быть?
А сделав калибровку ты загубил все.
Если у тебя версия В, то дай нормальное питание и откалибруй.
Переключил снова питание датчика на +5.
Датчик не отвечает. Причем при питании +3 датчик мигает, а на +5 не мигает совсем.
Может дело в несовпадении уровня питания и уровня Tx и Rx?
Но везде подключают напрямую.
Подключил напрямую, датчик вроде ожил, но данные читаются через раз. Иногда выдает ошибку контрольной суммы.
Однако, повторюсь, MH-Z19B — не самый лучший вариант.
А вот разборка: forum.mysensors.org/topic/7761/mh-z19-teardown
Подскажите чем заменить защитный фильтр для датчика MH-Z14, у меня он пришел в негодность после чистки. Почему -то при измерениях без фильтра датчик стал более резвым.
Спасибо!
— примерно так:
Подсоединить датчик к контроллеру, программой отслеживать его показания. При благоприятной помеховой обстановке достаточно анализировать аналоговый выход датчика. При длинных проводах от датчика к контроллеру лучше цепляться к выходу ШИМ. При необходимости крутить настройки датчика, снимать с него температуру — коммуницироваться через последовательный порт. Показания датчика сравнивать с пороговыми значениями, При превышении подать сигнал на реле или мосфет для исполнительного устройства. Если нужна плавная регулировка — то только мосфет, а регулировать ШИМом. Для более стабильной работы ввести гистерезис, чтоб прибор не дергал почем зря вентилятор или заслонку.
Если есть конкретные вопросы по какому-нибудь из этапов — задавайте, отвечу. Но если вообще ничего не понятно, то лучше доверить эту работу кому-нибудь.
взять D1 mini, или NodeMcu, датчик S8 (дороже) или MH-Z19В и поставить прошивку ESP EASY
как прошить, мануалов полно
для отображения значений датчика CO2 я использовал семисегментный tm1637
0,56 достаточно крупный и яркий (доступно 5 цветов, зеленый самый тусклый почему то)
7dn,[s8#ppm]
endon
заслонки у меня нет) просто открывал окно пошире, когда загорался красным трехцветныйсветодиод
if [s8#ppm] > 600 and s8#ppm] < 900
gpio,15,1 // горит желтый светофор
else
gpio,15,0
endif
endon
On s8#ppm do
if [s8#ppm] > 900
gpio,14,1 // горит красный светофор — срочно проветрить
else
gpio,14,0
endif
endon
On s8#ppm do
if [s8#ppm] < 600
gpio,12,1 // горит зеленый светофор
else
gpio,12,0
endif
endon
соответственно на срабатывание GPIO можно повесить реле заслонки — электромеханическое или твердотельное
зы на идеальность кода не претендую
В примерах даташита обращение идет по адресу «любой сенсор», хотя в том же даташите пишут, что это значение не должно использоваться в продакшине. И как быть с адресом датчика?
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.