Отслеживаем содержание CO2 в воздухе при помощи WeMos D1 Mini Pro, ThingSpeak и MHZ-19

В проекте по подсчету и публикации в сети показаний газового счетчика, я уже упоминал, что хорошо бы иметь возможность измерять не только температуру, но и содержание различных газов в воздухе. Тогда, в проекте, я пытался использоваться электрохимические датчики серии MQ. Один для измерения содержания природного газа в воздухе, второй для отслеживания концентрации CO. И если бы не полное фиаско с сенсорами MQ, вполне возможно, что данный проект и не появился бы.

Тем не менее понимать и вести статистику по содержанию CO2 в воздухе — очень важно. Ведь за счет удаления углекислого газа из нашего тела, собственно, мы и живем. Высокие показатели содержания CO2 в воздухе ведут к потере работоспособности, плохому самочувствию, а при очень высоком содержании углекислого газа в воздухе, увы, наступает досрочное завершение жизненного цикла.

Итак, цель настоящего мини-проекта в том, чтобы опробовать связку из Arduino-совместимой платы WeMos D1 Mini Pro, датчика содержания углекислого газа в воздухе и сервиса по хранению показаний ThingSpeak. На самом деле, данный мини-проект — проба пера, технологии определения содержания CO2 в воздухе, перед началом другого, немного более крупного проекта. Хотя результатами проекта можно пользоваться без каких-либо ограничений. Собранное устройство делает именно то, что от него требуется.

Почему я использую WeMos D1 Mini Pro для этого проекта? Плата мною уже изучена, подводные камни понятны. Кроме того, чип ESP8266 имеет встроенный Wi-Fi, а на самой плате распаяно, ни много ни мало, а целых 16 мегабайт памяти. Поэтому можно как угодно изгаляться, места для вашего скетча будет достаточно, нет необходимости экономить память.

640 килобайт хватит надолго!

Уильям Гейтс, 1981

Плата D1 Mini Pro поставляется вместе с набором шпыньков, включая сквозные, позволяющие не только устанавливать ее на макетную плату, но и одновременно использовать различные шишлды. Но, кроме неоспоримых достоинств у D1 Mini Pro есть и некоторый недостаток. На плате использована керамическая антенна, которая вроде бы должна обеспечивать вполне уверенное соединение с Wi-Fi. Однако, на практике связь хуже, чем у той же D1 R2 с антенной разведенной на плате. Видимо, по этой причине производитель добавил гнездо для подключения внешней антенных. Впрочем, разница все равно не слишком существенная.

Почему я использую сервис ThingSpeak? Да все по той же причине. Он бесплатен, позволяет передавать, агрегировать, хранить и обрабатывать данные со всех моих устройств. Что, согласитесь, удобно. Более того, доступность мощнейшего аппарата MatLab прямо онлайн, делает сервис ThingSpeak очень привлекательной средой для любых домашних проектов.

А вот на датчике MHZ-19 остановится подробнее.

MH-Z19

Датчики серии MH-Z выпускаются китайской компанией Winsen. Сама компания специализируется именно на разработке и производстве датчиков для определения газов. В активе компании имеются как электрохимические, так и датчики, выполненные по другим технологиям. Собственно, датчики MH-Z, а среди них MH-Z14A, MH-Z19B, MH-Z14 и MH-Z19, как раз и относятся к группе сенсоров, выполненных по другим технологиям.

Датчик MHZ-19 с внешней стороны. Видны два пылевых фильтра корпуса.

Датчик MHZ-19 с внешней стороны. Видны два пылевых фильтра корпуса.

В основе процесса измерения в сенсорах MH-Z лежит принцип оптического измерения содержания определенного газа. В английском языке подобная технология называется Nondispersive infrared, что на русский язык переводится примерно как «недисперсионная инфракрасная технология» или сокращенно NDIR. Наличие IR и то, что сам сенсор используется инфракрасное излучение для анализа воздуха наводит некоторых «специалистов» на то, что в датчике используется PIR (Passive Infrared) технология. Да, в PIR, как и в NDIR, используется инфракрасный свет, а в остальном это две отличных технологий.

Датчик MHZ-19 с внутренней стороны

Датчик MHZ-19 с внутренней стороны

В NDIR используется две камеры, одна заполнена азотом, а такой газ содержится и в атмосфере нашей планеты. Более того, азота в воздухе, которым мы дышим аж 78%. Кислорода 21%, а углекислого газа всего 1%. Так вот, одна из камер содержит азот, а вторая тот воздух, который подвергается исследованию. Через обе камеры, одновременно пропускается пучок инфракрасного света. Его источником может быть, все что угодно. В подобных устройствах обычно используется полупроводниковый диод, обеспечивающий требуемую длину волны. Если же источник инфракрасного света недостаточно стабилизирован по спектральной характеристике, то применяется оптический фильтр, отсекающий все остальные длины волн.

Проходя через обе камеры, световой импульс подвергается рассеиванию на молекулах газа. Во время дисперсии происходит рассеивание (преломление) направленного пучка света и его частичное поглощение молекулами или стенками сенсора. Как правило, в качестве базовой отправной точки используется камера с азотом, а результаты из второй камеры сравниваются с результатами в камере с азотом. В результате прошедший через камеры свет анализируется фотоприемником и вычисляется содержание искомого газа. Следует заметить, что метод весьма точен и мало затратен в плане энергетики, а сам сенсор будет служить, по крайней мере так заявляет производитель, верой и правдой своему хозяину на протяжении ну никак не менее пяти лет. Деградация датчика будет происходить из-за забивания пылью фильтров, изменения спектральных характеристик у излучающего светодиода и деградацией фотоприемника. Но эти процессы небыстрые, поэтому можно считать, что такой датчик будет служить лет десять без видимых ухудшений показаний.

Несмотря на то что измерения сенсорами с технологией NDIR точны, могут наблюдаться и некоторые погрешности, особенно при малых концентрациях искомого газа, связанные с наличием в воздухе других газов. Так, у датчиков, используемых для определения CO2 есть некоторые, совсем небольшие, проблемы с H2O (паром), SO2 и NO2. В бытовом применении эти погрешности можно с легкостью скинуть со счетов, но для промышленного применения, где требуется высокая точность, видимо надо изобретать какие-то дополнительные механизмы. И да, когда вы дышите на датчик, стараясь проверить насколько он здорово работает, учтите, что в выдыхаемом вами воздухе содержится изрядное количество водяного пара, который так же окажет влияние на показания.

Как отметил выше, в настоящее время Winsen выпускает два вида датчиков, серию Z14 и Z19. Отличаются одна серия от другой внешним видом. Z19 это параллелепипед с двумя фильтрами, а Z14 — усеченный цилиндр на плате с одним фильтром. Есть различия между моделями и внутри серий связанные с дополнительными настройками по диапазонам работы. В целом же обе серии можно считать идентичными, а различия несущественными.

Калибровка

Датчик Z19 имеет возможность калибровки. Причем возможна калибровка двумя способами. Первый и самый точный способ: калибровка абсолютного нуля. Для этого датчик необходимо поместить в атмосферу из чистого азота, подождать пока азот не вытеснит все остальные газы из корпуса датчика, примерно пять минут, и запустить калибровку командой. Результаты будут записаны в пять микроконтроллера сенсора, и они будут использоваться для последующих определений значений измерений. Азот, кстати, можно приобрести на предприятиях занимающимися реализацией газов. Правда, самый маленький баллон будет стоить как два, а то и четыре датчика MH-Z19, но искусство требует жертв.

Второй способ, так называемый span calibration применяется для калибровки в бытовых условиях. Выполняется он точно так же, как и в первом способе, но датчик нет необходимости помещать в атмосферу азота. Достаточно вынести его на чистый и свежий воздух и произвести калибровку. Тут делается упор на то, что в атмосферном воздухе содержится порядка 400 ppm CO2. И значение это постоянно на протяжении длительного времени и более-менее однородно по всему земному шару. Но при подобной калибровке следует учитывать, что значение в 400 ppm весьма условно, поскольку концентрация углекислого газа колеблется, и в весьма ощутимых пределах, в зависимости от внешних факторов. В крупном городе концентрация будет выше, чем на селе. Летней ночью в лесу углекислого газа будет больше, чем в том же лесу, тем же летом, но в солнечную погоду днем.

Поэтому, если уж и заниматься калибровкой, то лучше разориться на литр другой азота и провести полноценную процедуру.

Чем плох CO2?

Чем же плохо повышенное содержание углекислого газа в воздухе? А тем, что углекислый газ есть не что иное, как естественный результат нашей с вами жизнедеятельности. И не только нашей, кстати. Вдыхаем мы воздух, в котором преобладает азот и кислород, а выдыхаем все тот же азот, мало кислорода и много углекислого газа. Процесс миграции углекислого газа из крови, через альвеолы в легких происходит из-за парциального давления газов по разные стороны мембраны альвеолы. Суть парциального давления в том, что при одинаковом атмосферном давлении, если различается состав газов по разную сторону мембраны, то тот газ, которого больше с одной стороны и меньше с другой, будет пытаться проникнуть на другую сторону и уравновесить свою концентрацию. По такому же принципу работает и наполнение газом датчика MH-Z, где нет никакой принудительной вентиляции. Поэтому результаты он показывает с некоторой задержкой. Пока парциальное давление газов уравновесится.

Ну так вот, при дыхании, давление углекислого газа в капиллярах альвеолы, читай его процентное соотношение к другим газам, выше, чем во вдыхаемом воздухе, а содержание кислорода, наоборот, ниже. Углекислый газ старается выбраться из тела в атмосферу, а кислород наоборот, проникнуть в него. Для успешной работы этой системы обмена, во вдыхаемом воздухе должно быть много кислорода и мало углекислого газа. Иначе, вывод отработки из организма работать не будет. Собственно, этим и плохо высокое содержание CO2 в воздухе. Чем выше его содержание, тем хуже будет выводиться ненужный результат химических реакций наших тех. От переизбытка СО2 в воздухе и как следствие в самом теле, ухудшается самочувствие, страдает работоспособность, да и в целом идет интоксикация организма теми самыми шлаками. Именно по этой причине мамы стараются выгнать своих детей на улицу, подышать свежим воздухом, поиграть на морозце. Тем самым происходит очищение организма от ненужного ему СО2.

Во взрослом состоянии организма увещевание мам уже не действует и человек готов просидеть 12 часов в душном офисе имитируя бурную деятельность. Именно имитируя, ведь работать в атмосфере с повышенным содержанием углекислого газа нормально невозможно. Даже имитация работы будет жиденькой и вялой.

На подводных лодках и космических станция борются с повышенным содержанием СО2 в локальных атмосферах своих замкнутых миров при помощи химических фильтров, улавливающих и связывающих диоксид углерода. Но большинство людей дышат все же атмосферным воздухом планеты и для понижения концентрации углекислого газа достаточно только проветрить помещение. Строго говоря, проветривать нужно постоянно, хотя многие предпочитают жить в своих квартирах как в бетонных подводных лодках и ни под какими предлогами не открывают окна. В административных же зданиях, должна работать приточно-вытяжная вентиляция, обеспечивающая должный приток чистого и свежего воздуха.

co2soderzhanie

Подключение

Подключаются датчики серии MH-Z к микроконтроллерам двумя способами. Доступно либо использование PWM (ШИМ), либо подключение по UART (последовательному порту). Мы уже знаем, что чипы ESP8266 не очень дружат с PWM под средой Android, поэтому для подключения будем использовать только и исключительно UART. Более того, подключение по последовательному порту еще и предпочтительнее, поскольку не только снижает нагрузку на сам микроконтроллер, но и позволяет использовать команды калибровки. Да и разве не здорово получать значения содержания CO2 сразу в ppm прямо с датчика, а не заниматься длительными и мучительными вычислениями? Вот именно, что стоит.

Диаграмма подключения: нога 6 питание, 7 - земля, 2 - RXD, 3 - TXD.

Диаграмма подключения: нога 6 питание, 7 - земля, 2 - RXD, 3 - TXD.

Во избежание ненужной гибели человеческих жертв, хочу обратить внимание воспитанного читателя на то, что для питания датчика MH-Z19 требуется напряжение от 3.6 и до 5 вольт, а для обмена данными по последовательному порту (равно как и для PWM) требуется 3.3 вольтовая логика. Поэтому при подключении датчика к оригинальным Arduino потребуется согласование уровней логики, иначе возможно повреждение устройства. Все эти параметры указаны в официальной спецификации на устройство, поэтому пренебрегать ими не стоит. Итак, подключаем питание, благо на плате WeMos есть выводы как для 5 вольт, так и для 3.3 вольт, а заодно подключаем линии передачи и приема на пины D1 и D2. И собственно после этого все должно заработать, в аппаратном понимании понятно.

Теперь, что касается программной стороны. Для того чтобы WeMos начал работать с UART необходимо использовать библиотеку для программного последовательного порта (SoftwareSerial). Библиотека позволяет организовывать UART порт на любых пинах устройства. В стандартной Arduino поставке уже есть такая библиотека, но для чипов ESP8266 нужна своя, особая библиотека. Ее необходимо будет скачать и установить в систему, если ранее подобная операция не была произведена. Правда, после этого возможны коллизии с обычными Arduino скетчами. Нужно просто помнить по SoftwareSerial и отключать ее в случае проблем.

Для работы с MH-Z19 не требуется какая-то отдельная библиотека, весь требуемый код весьма и весьма компактен, и доступен для написания программистом самостоятельно.

Небольшое замечание по частоте опроса датчика. В спецификации не указывается максимальная частота опроса, но в некоторых обзорах упоминается, что дескать, не стоит дергать датчик чаще, чем раз в 10 секунд. Мол помрет раньше времени. Не могу согласиться с таким утверждением. Если в темноте присмотреться к датчику, то будут заметны световые импульсы, примерно каждые 3 секунды. Это сенсор проводит измерения уровня газа. Обработка показаний занимает тоже не космические величины, поэтому лично я не вижу никакой проблемы по считыванию значений не чаще чем 4-5 секунд. Тут важно попасть между вспышками, дабы получать каждый раз новый результат, а не копию старого. Хотя в моем скетче показания считываются вообще раз в 10 минут, мне важна статистика и динамика, а не моментальный результат.

В скетче, помимо всего прочего, осуществляется отправка показаний измерения CO2 и температуры на сервер ThingSpeak. Да-да, вы не должны не доверять своим глазам. Датчик MH-Z19 измеряет еще и температуру. Для получения более точного результата измерений содержания углекислого газа нужно проводить корректировку измерений по температуре самого датчика. Именно отсюда измеряемая температура слегка выше температуры окружающего воздуха и, пожалуй, может быть использована только в академических целях. Но как же получить измерения температуры? В официальной спецификации, полученной с сайта Winsen и многих других, действительно нет никакого упоминания по поводу температуры. А вот в спецификации на датчик MH-Z14 температура есть и передается она в ответе от сенсора вместе с остальными данными (байт 4).

При работе все это дело успешно подглюкивает. ThingSpeak может быть недоступен ввиду некоторого несовершенства передачи данных посредством керамической антенны, а данные, поступающие с сенсора, могут содержать ошибки. Во-первых, данные могут просто не передаваться между микроконтроллером и датчиком. Во-вторых, в данных могут быть ошибки контрольной суммы. В-третьих, значения измерений могут выходить за пределы: сильно меньше 400 или же больше или близко к границе 5000 ppm. Во всех этих случаях помогает повторная отправка или повторный запрос сенсора. Хотя в плане работы с сенсором отлично помогает отключение и повторное подключение контактов для передачи и получения данных. Возможно, что тут виноваты «сопли» в виде временных проводков.

#include <SoftwareSerial.h>
#include <Wire.h>
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
#include <ArduinoOTA.h>
#include <ESP8266WebServer.h>
#include <ESP8266HTTPClient.h>

const char* ssid = "YourWiFiNetWork";
const char* password = "YourWiFiPasswird";
WiFiClient client; // All fucntions https://www.arduino.cc/en/Reference/WiFi & http://esp8266.github.io/Arduino/versions/2.0.0/doc/libraries.html#wifi-esp8266wifi-library
#define ledPin D4         // the onboard LED
unsigned long CO2Channel = ThingSpeakChannelNumber;
const char * CO2WriteAPIKey = "ThingSpeakWriteAPIKey";
unsigned long CO2Millis = 600000;
unsigned long MillisLast = 0;
ESP8266WebServer server(80); // Simple web server
String webArray[100]; // Keeps messages
// CO2 sensor:
SoftwareSerial mySerial(D1, D2); // RX,TX
byte cmd[9] = {0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79};
unsigned char response[9];

void setup() {
  pinMode(ledPin, OUTPUT);
  Serial.begin(115200);
  Serial.println("");
  mySerial.begin(9600);
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  while (WiFi.waitForConnectResult() != WL_CONNECTED) {
    Serial.println("Connection Failed! Rebooting...");
    delay(5000);
    ESP.restart();
  }
  Serial.println("WiFi is ready...");
  int lo = WiFi.RSSI();
  Serial.print("WiFi signal: ");
  Serial.println(lo);
  { // Ota
    ArduinoOTA.onStart([]() {
      String type;
      if (ArduinoOTA.getCommand() == U_FLASH)
        type = "sketch";
      else // U_SPIFFS
        type = "filesystem";

      // NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
      Serial.println("Start updating " + type);
    });
    ArduinoOTA.onEnd([]() {
      Serial.println("\nEnd");
    });
    ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
      Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
    });
    ArduinoOTA.onError([](ota_error_t error) {
      Serial.printf("Error[%u]: ", error);
      if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
      else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
      else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
      else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
      else if (error == OTA_END_ERROR) Serial.println("End Failed");
    });
    ArduinoOTA.begin();
    Serial.println("OTA is ready");
  }
  { // Web-server init
    server.on ("/", EhternetWebServer);
    server.begin(); // Starts Web-Server
    Serial.println("Web-server is Ready...");
  }
  digitalWrite(ledPin, HIGH);
  Serial.println("Let us go....");
  delay(3000);
}
void getCO2Data() {
  mySerial.write(cmd, 9);
  memset(response, 0, 9);
  mySerial.readBytes(response, 9);

  // CRC check
  int i;
  byte crc = 0;
  for (i = 1; i < 8; i++) crc += response[i];
  crc = 255 - crc;
  crc++;
  // End of CRC check
  if ( !(response[0] == 0xFF && response[1] == 0x86 && response[8] == crc) ) {
    //Serial.println("CRC error: " + String(crc) + " / " + String(response[8]));
    char raw[32];
    sprintf(raw, "RAW: %02X %02X %02X %02X %02X %02X %02X %02X %02X", response[0], response[1], response[2], response[3], response[4], response[5], response[6], response[7], response[8]);
    //Serial.println(raw);
    printWeb(raw);    
    printWeb("CRC error: " + String(crc) + " / " + String(response[8]));
    MillisLast = CO2Millis-10000;
  } else {
    unsigned int responseHigh = (unsigned int) response[2];
    unsigned int responseLow = (unsigned int) response[3];
    int ppm = (256 * responseHigh) + responseLow;
    int temp = response[4] - 20;
    Serial.println(String("-=-"));
    char raw[32];
    sprintf(raw, "RAW: %02X %02X %02X %02X %02X %02X %02X %02X %02X", response[0], response[1], response[2], response[3], response[4], response[5], response[6], response[7], response[8]);
    printWeb(raw);
    if (ppm <= 400 || ppm > 4900) {
      //Serial.println("CO2: no valid data");
      printWeb("CO2: no valid data");
      MillisLast = CO2Millis;
    } else {
      printWeb("CO2 PPM:" + String(ppm) + "; Temp:" + String(temp));
      digitalWrite(ledPin, LOW);
      String sURL = "http://api.thingspeak.com/update?api_key=";
      sURL += CO2WriteAPIKey;
      sURL += "&field1=" + String(ppm) + "&field2=" + String(temp);
      printWeb(sURL);
      HTTPClient http;
      http.begin(sURL);
      int httpCode = http.GET();
      if (httpCode == HTTP_CODE_OK) {
        //Serial.printf("ThingSpeak responce code: %d\n", httpCode);
        printWeb("ThingSpeak responce code: " + String(httpCode));
      } else {
        printWeb("ThingSpeak failed, error: " + String(http.errorToString(httpCode).c_str()));
        MillisLast = CO2Millis-10000;
      }
      digitalWrite(ledPin, HIGH);
    }
  }
}
void loop() {
  unsigned long CurrentMillis = millis();
  if (CurrentMillis - MillisLast > CO2Millis || MillisLast == 0) {
    MillisLast = CurrentMillis;
    getCO2Data();
  }
  server.handleClient(); // Check Web-Server requests
  ArduinoOTA.handle(); // Handle Over The Air update
  delay(1000);
}
void EhternetWebServer() {
  Serial.println("WebServer responce request");
  String responce = "<!DOCTYPE HTML>";
  responce += "<html>";
  for (int i = 0; i < 100; i++) {
    responce += i + 1;
    responce += ". ";
    responce += webArray[i];
    responce += "
";
  }
  responce += "
<hr>

</html>";
  server.send (200, "text/html", responce);
}
void printWeb(String s) {
  Serial.println(s);
  for (int i = 99; i > 0; i--) {
    webArray[i] = webArray[i - 1];
  }
  String sS = "" + String(MillisLast) + ": " + s;
  webArray[0] = sS;
}

Помимо чтения значений датчика и отправки их в сеть к ThingSpeak, в скетче реализован механизм обновления прошивки по воздуху (OTA), и веб-сервер выводящий 100 последних сообщений, в целях отладки, разумеется.

ThingSpeak

Как видно из приведенного ниже графика, содержание диоксида углерода плавает в течение 12 часов. Датчик установлен в комнате с одним окном. В комнате периодически обитает двое взрослых и два мелких хищника. Окно постоянно приоткрыто для проветривания. Данные подаются на сервер каждые 10 минут, а затем происходит усреднение по 10 значениям. В какие-то периоды времени отправка данных невозможна, и линия графика выглядит как просто сплайн.

Показания датчика CO2 за 12 часов

Показания датчика CO2 за 12 часов

По графику хорошо видно, что в ночное время содержание CO2 колеблется на приемлемом уровне в районе 500 ppm, а около 8 утра наблюдается небольшой всплеск до 600. Ночью, когда любая активность минимальна, проветривания оказалось достаточно для поддержания оптимального уровня свежести воздуха. А вот в тот момент, когда все люди и звери становятся активными, содержание диоксида увеличивается. Хотя и в такой момент оно находится на вполне неплохом уровне. Затем же идет плавное снижение содержания вплоть до его номинального значения. Мелкие хищники днем спят, а их мощности метаболизма недостаточно для поддержания высокого уровня содержания CO2 в атмосфере комнаты.

График изменений содержания CO2 на протяжении нескольких суток.

График изменений содержания CO2 на протяжении нескольких суток.

Графики на ThingSpeak хороши, но не гибки. Но я не поленился и выгрузил данные сразу за несколько дней для анализа в Excel. Благо подобная функция, функция выгрузки, присутствует в ThingSpeak. Несколько несложных манипуляций и готов весьма подробный график готов. Поскольку значений выгружено много, то без дополнительных манипуляций провести усреднение непросто, но, с другой стороны, так даже интереснее. При анализе графика следует учитывать временный сдвиг в 3 часа из-за выгруженных данных в часовом поясе UTC+0.

Как видно, после начала сбора данных значения CO2 весьма и весьма высоки. Я и сам был удивлен столь неоднозначным показаниям. Но ответ нашелся быстро: в те дни на улице стояла прохладная температура с сильным ветром. И чтобы не понижать температуру в помещении до некомфортных значений, пластиковые окошки прикрыли. В результате домашние сумели надышать высокую концентрацию углекислого газа. В целом после анализа все пики были, так или иначе, связаны с постоянным пребыванием людей в помещении с датчиком и прикрытое окно. Но в общем, заметно постепенное понижение концентрации, что отображается линией тренда в виде зеленой точечной линии.

Выводы

В целом я могу заметить, что применение датчика MH-Z19 вполне оправдано в домашних проектах в качестве одного из действенных инструментов по контролю качества воздуха. Вторым по значимости будет датчик влажности, которой так не хватает в зимние периоды, когда отопление иссушивает воздух в помещении. Ну и третий из необходимых датчиков — датчик температуры. А как их соединить все в месте в одном проекте — поговорим в одной из последующих статей.



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