Вся страна на самоизоляции (ну или не вся) и удалёнке, в Сети обсуждают нашествие печенегов и половцев и то, как мы победим коронавирус, а я продолжаю серию постов #МастеримДома. На третий день работы с датчиком MQ135 я сказал, что для более менее корректной калибровки датчика необходимо учитывать температуру и влажность среды в которой происходит калибровка. И вот на прошлой неделе мне подвезли датчик температуры и влажности DHT22, благодаря использованию которого теперь можно реализовать несколько иной подход к измерению содержания CO2 в помещении и даже собрать что-то типа домашней мини-метеостанции.
Итак, что у нас есть на данный момент:
- Скетч для чтения данных с датчика MQ135 (концентрации CO2 и сопротивления R0)
- Небольшая программка на Delphi для чтения данных с COM-порта к которому подключается наша Arduino.
Сегодня разберемся как подключить к Arduino датчик температуры и влажности DHT22 и скорректировать по полученным от DHT22 данным показания с датчика MQ135.
Модуль датчика DHT-22
Модуль датчика DHT22 я заказывал вот на этом сайте. В отличие от «голого» датчика DHT-22 модуль уже готов к использованию и оснащен всем необходимым для работы. Выглядит модуль следующим образом:
- «+» — подключается к пину «5V» на Arduino
- ‘-‘ — подключается к пину «GND»
- «S» — подключается к любому свободному цифровому пину.
Для работы с модулем DHT-22 нам потребуется дополнительная библиотека, которую можно скачать вот по этой ссылке. О том, как подключаются библиотеки в Arduino IDE я рассказывал в третьей части статей про Arduino.
Собственно, сам скетч, демонстрирующий чтение температуры и влажности с DHT-22 выглядит следующим образом:
//Подключение библиотеки для работы с датчиком #include //определение постоянных значений #define DHTPIN 8 //пин получения данных с датчика #define DHTTYPE DHT22 //Значение типа датчика DHT dhtSensor(DHTPIN, DHTTYPE); //инициализация датчика void setup() { dhtSensor.begin(); //запуск датчика Serial.begin(9600); //запуск Serial соединения } void loop() { delay(2000); //время чтобы датчик прогрузился float h = dhtSensor.readHumidity(); //получение данных по влажности float t = dhtSensor.readTemperature(); //получение данных по температуре if(isnan(h) || isnan(t)) return; //возврат если данные по температуре или влажности некорректно считались //Вывод значений Serial.print("Temperature: "); Serial.print(t); Serial.print(" humidity: "); Serial.println(h); }
Задержка в две секунды в loop() необходима, так как в соответствии с документацией на датчик, время его отклика составляет как раз те самые две секунды.
Как «подружить» MQ-135 и DHT-22?
В принципе, если использовать библиотеку для MQ-135 о которой я рассказывал, то в этой библиотеке определены следующие методы:
float getPPM(); float getCorrectedPPM(float t, float h); float getRZero(); float getCorrectedRZero(float t, float h);
Методы «getCorrected…» как раз и учитывают при расчётах температуру и влажность среды. Соответственно, всё, что от нас требуется — это прочитать значения температуры и влажности и передать эти значения в соответствующие функции библиотеки для MQ-135. Выглядеть это может, например, вот так:
#include //подключаем библиотеку для MQ135 #include //подключаем библиотеку для DHT22 //определение постоянных значений #define analogPin A0 // аналоговый выход MQ135 подключен к пину A0 Arduino #define DHTPIN 8 //пин получения данных с датчика #define DHTTYPE DHT22 //Значение типа датчика //датчики MQ135 gasSensor = MQ135(analogPin); DHT dhtSensor(DHTPIN, DHTTYPE); //инициализация датчика //переменные для работы bool Zero = false; bool Conc = true; String ComStr = "C"; float Value = 0; //значение без учёта температуры и влажности float Value2 = 0; //значение с учётом температуры и влажности float h = 0; //температура float t = 0; //относительная влажность void setup() { Serial.setTimeout(1000); Serial.begin(9600); // инициализация последовательного порта pinMode(analogPin, INPUT); // режим работы аналогового пина dhtSensor.begin(); //запуск датчика delay(1000); // даём датчикам небольшое время на разогрев } void loop() { delay(2000); // задержка перед выводом следующего значения h = dhtSensor.readHumidity(); //получение данных по влажности t = dhtSensor.readTemperature(); //получение данных по температуре if(isnan(h) || isnan(t)) return; //возврат если данные по температуре или влажности некорректно считались if (Serial.available() > 0) { //если есть доступные данные // считываем строку String incomingStr = Serial.readString(); //определяем какая команды пришла int Command = incomingStr.toInt(); switch (Command) { case 1: Zero = true; Conc = false; ComStr = "R"; break; case 2: Zero = false; Conc = false; ComStr = ""; break; default: Zero = false; Conc = true; ComStr = "C"; } } if (Zero) { Value = gasSensor.getCorrectedRZero(t, h); // чтение R0 Value2 =gasSensor.getRZero(); } else if (Conc) { Value = gasSensor.getCorrectedPPM(t,h); Value2 =gasSensor.getPPM(); } if (Zero || Conc) { //вывод в порт Serial.print(ComStr); Serial.print("|"); Serial.print(Value); Serial.print("|"); Serial.print(Value2); Serial.print("|"); Serial.print(t); Serial.print("|"); Serial.print(h); Serial.println("#"); }
Разберемся, что здесь и как работает. Итак, подключены датчики следующим образом:
- MQ-135 подключен к аналоговому пину A0. Цифровой пин не используется
- DHT-22 подключен к цифровому пину 8.
Вся эта адова конструкция в натуре выглядит вот так:
Далее работа строится следующим образом:
- пробуем считать с датчика DHT-22 значения температуры и влажности;
- если чтение прошло успешно — смотрим, какие данные должны передать (об этом — смотри тут);
- если надо передать значения сопротивления ил концентрации, то в порт передается следующая строка:
Команда|корректное_значение|обычное_значение|температура|влажность#
где
- Команда — один из двух символов: R — чтение сопротивления; C — чтение концентрации;
- корректное_значение — это значение с датчика MQ-135 с учётом температуры и влажности;
- обычное_значение — это, соответственно, значения без учёта температуры и влажности
- температура и влажность — это данные с датчика DHT-22.
Теперь, учитывая произведенные в скетче Arduino изменения, нам необходимо немного переписать приложение для работы с Arduino в Delphi.
Дописываем Delphi-приложение для работы с Arduino
О том, что делала предыдущая версия программы, вы можете узнать в этой статье. Учитывая то, что формат передаваемых с Arduino данных у нас теперь изменился (вместо одного числа теперь посылается строка), первое, что необходимо сделать — это написать функцию разбора принимаемой строки с показаниями датчиков. Функция будет такая:
function TfrmMain.ParseString(const AStr: string; var ADataType: TArduinoData; var AValue, AValue2, AT, AH: double): boolean; var List: TStringList; S: string; begin //R|корректное_значение|обычное_значение|температура|влажность S:=Trim(AStr); if not EndsText('#', S) then exit(False); List:=TStringList.Create; try List.Delimiter:='|'; List.DelimitedText:=Copy(S, 1, length(S)-2); if List[0]='C' then ADataType:=adPPM else if List[0]='R' then ADataType:=adRo else ADataType:=adNone; AValue:=StrToFloat(List[1], FS); AValue2:=StrToFloat(List[2], FS); AT:=StrToFloat(List[3], FS); AH:=StrToFloat(List[4], FS); Result:=True; finally FreeAndNil(List) end; end;
Соответственно, немного изменится и подсчёт средних значений. Для этой цели я, опять же, написал простенькую функцию, которая на входе принимает предыдущее среднее значение и количество итераций, а на выходе выдает очередное среднее значение:
function GetAvgValue(const PredAvg, NextVal: double; var Count: integer):double; begin if Count=0 then Result:=NextVal else Result:=PredAvg*(Count/(Count+1))+NextVal/(Count+1); inc(Count); end;
Изменился немного и интерфейс приложения:
При работе с COM-портом обновление данных осуществляется теперь следующим образом:
type TfrmMain = class(TForm) [...] private FS: TFormatSettings; DataType: TArduinoData; Value, Value2, T, H: double; aCount: integer; aAvgVal: double; aAvgVal2: double; procedure UpdateForm; function ParseString(const AStr: string; var ADataType: TArduinoData; var AValue, AValue2, AT, AH: double): boolean; public { Public declarations } end; procedure TfrmMain.ArduinoComRxChar(Sender: TObject; Count: Integer); var Str: string; aNext: double; begin ArduinoCom.ReadStr(Str, Count); memData.Text:=memData.Text+Str; if ParseString(Str, DataType, Value, Value2, T, H) then begin aAvgVal:=GetAvgValue(aAvgVal, Value, aCount); aAvgVal2:=GetAvgValue(aAvgVal2, Value2, aCount); UpdateForm; end; end; procedure TfrmMain.UpdateForm; const cValStr = '%s (без корректировки: %s)'; begin lbAvg.Caption:=Format(cValStr, [FloatToStrF(aAvgVal, fffixed, 6,2), FloatToStrF(aAvgVal2, fffixed, 6,2)]); lbCount.Caption:=IntToStr(aCount); lbTemp.Caption:=FloatToStrF(T, fffixed, 6,1); lbHum.Caption:=FloatToStrF(H, fffixed, 6,1); end;
Таким образом, теперь можно получать данные по температуре и влажности в помещении, откалибровать датчик с учётом этих значений и попытаться получить хоть какое-то более менее точное значение концентрации CO2 в доме.
Новые калибровочные данные с датчика MQ-135
Для получения новых калибровочных данных с MQ-135 я сделал следующее: установил драйвер для Arduino на ноутбук, прицепил к ноуту всю получившуюся конструкцию и вытащил всё это богатство на балкон на 20 минут. В результате я получил следующие калибровочные данные:
R0 = 251,35 (без корректировки 242,62)
В прошлый раз значение R0 (без учёта температуры и влажности) было равно 70,83. Различие более чем в три раза нового значения и предыдущего заставляет задуматься…При этом стоит отметить, что датчик температуры и влажности DHT-22 точно не лажал, по крайней мере с температурой — показания датчика и обычного термометра на балконе полностью совпали и показывали 190С.
Что ж, вносим изменения в библиотеку для MQ-135, как это делалось в прошлый раз и смотрим, что теперь будет показывать MQ-135 у меня в комнате, опять же, усредняя показатели за 20-ти минутный интервал времени.
Показания датчика MQ-135: концентрация CO2 в помещении: 576,3 ppm. Опять же, следуя имеющейся статистике, показания датчика вроде бы соответствуют реальности. Но как знать, как знать…Видимо придётся где-то искать газоанализатор на CO2 и проверять работу датчика более детально.
Вот, собственно, так, имя под рукой два датчика можно собрать небольшую домашнюю метеостанцию
Теперь интерфейс программы можно настраивать «под себя» и баловаться с Arduino дальше.