From 2235d5aa3860da2c59be9a5379f9175063147f41 Mon Sep 17 00:00:00 2001 From: IoT Manager <67171972+IoTManagerProject@users.noreply.github.com> Date: Thu, 23 Nov 2023 18:34:05 +0100 Subject: [PATCH 1/5] =?UTF-8?q?=D0=B8=D1=81=D0=BF=D1=80=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=B1=D0=B0=D0=B3=D0=B0=20=D0=B2?= =?UTF-8?q?=D1=80=D0=B5=D0=BC=D0=B5=D0=BD=D0=B8=20=D0=B3=D0=B5=D0=B9=D1=82?= =?UTF-8?q?=D0=B0=20mysensors?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data_svelte/flashProfile.json | 12 +++++++++ platformio.ini | 4 +-- src/modules/exec/MySensors/MySensorsGate.cpp | 26 +++++++------------- 3 files changed, 23 insertions(+), 19 deletions(-) diff --git a/data_svelte/flashProfile.json b/data_svelte/flashProfile.json index 79eca748..dea9e970 100644 --- a/data_svelte/flashProfile.json +++ b/data_svelte/flashProfile.json @@ -80,6 +80,14 @@ "path": "src/modules/sensors/Ble", "active": false }, + { + "path": "src/modules/sensors/Ble_part1", + "active": false + }, + { + "path": "src/modules/sensors/Ble_part2", + "active": false + }, { "path": "src/modules/sensors/Bme280", "active": true @@ -312,6 +320,10 @@ "path": "src/modules/display/Lcd2004", "active": true }, + { + "path": "src/modules/display/NextionUpload", + "active": false + }, { "path": "src/modules/display/Oled128", "active": false diff --git a/platformio.ini b/platformio.ini index 26e8006c..beb5f904 100644 --- a/platformio.ini +++ b/platformio.ini @@ -597,7 +597,6 @@ lib_deps = adafruit/Adafruit BMP280 Library beegee-tokyo/DHT sensor library for ESPx https://github.com/milesburton/Arduino-Temperature-Control-Library - https://github.com/tremaru/iarduino_RTC robtillaart/SHT2x@^0.1.1 WEMOS SHT3x@1.0.0 plerup/EspSoftwareSerial @@ -618,9 +617,9 @@ build_src_filter = + + + + + + + - + + + + @@ -645,6 +644,7 @@ build_src_filter = + + + + + + + + diff --git a/src/modules/exec/MySensors/MySensorsGate.cpp b/src/modules/exec/MySensors/MySensorsGate.cpp index 8e4aebde..5e147812 100644 --- a/src/modules/exec/MySensors/MySensorsGate.cpp +++ b/src/modules/exec/MySensors/MySensorsGate.cpp @@ -3,11 +3,6 @@ #include "Arduino.h" #include "MySensorsGate.h" -// временное решение -unsigned long currentMillis; -unsigned long prevMillis; -unsigned long difference; - #ifdef MYSENSORS // callback библиотеки mysensors void receive(const MyMessage& message) { @@ -58,16 +53,11 @@ String parseToString(const MyMessage& message) { class MySensorsGate : public IoTItem { private: public: - MySensorsGate(String parameters) : IoTItem(parameters) { - SerialPrint("i", "MySensors", "Gate initialized"); - } + MySensorsGate(String parameters) : IoTItem(parameters) { SerialPrint("i", "MySensors", "Gate initialized"); } - void doByInterval() { - } + void doByInterval() {} - void loop() { - loopMySensorsExecute(); - } + void loop() { loopMySensorsExecute(); } ~MySensorsGate(){}; @@ -356,6 +346,10 @@ class MySensorsNode : public IoTItem { int _minutesPassed = 0; String json = "{}"; bool dataFromNode = false; + // временное решение + unsigned long currentMillis; + unsigned long prevMillis; + unsigned long difference; public: MySensorsNode(String parameters) : IoTItem(parameters) { @@ -393,9 +387,7 @@ class MySensorsNode : public IoTItem { } // событие когда пользователь подключается приложением или веб интерфейсом к усройству - void onMqttWsAppConnectEvent() { - setNewWidgetAttributes(); - } + void onMqttWsAppConnectEvent() { setNewWidgetAttributes(); } void setNewWidgetAttributes() { if (dataFromNode) { @@ -431,4 +423,4 @@ void* getAPI_MySensorsGate(String subtype, String param) { } else { return nullptr; } -} +} \ No newline at end of file From ee72108f738a24be9fe2106b5f479230ae7cd044 Mon Sep 17 00:00:00 2001 From: Al <44e4ee@gmail.com> Date: Fri, 24 Nov 2023 21:14:46 +0100 Subject: [PATCH 2/5] Added MQgas --- src/modules/sensors/MQgas/MQgas.cpp | 552 ++++++++++++++++++ src/modules/sensors/MQgas/modinfo.json | 271 +++++++++ src/modules/sensors/MQgas/mq135alone.json | 135 +++++ .../sensors/MQgas/mq135withBME280.json | 176 ++++++ 4 files changed, 1134 insertions(+) create mode 100644 src/modules/sensors/MQgas/MQgas.cpp create mode 100644 src/modules/sensors/MQgas/modinfo.json create mode 100644 src/modules/sensors/MQgas/mq135alone.json create mode 100644 src/modules/sensors/MQgas/mq135withBME280.json diff --git a/src/modules/sensors/MQgas/MQgas.cpp b/src/modules/sensors/MQgas/MQgas.cpp new file mode 100644 index 00000000..1ac82aad --- /dev/null +++ b/src/modules/sensors/MQgas/MQgas.cpp @@ -0,0 +1,552 @@ +#include "Global.h" +#include "classes/IoTItem.h" +#include "NTP.h" + +extern IoTGpio IoTgpio; + +#ifdef ESP8266 +#define ADC_BIT 10 +#endif +#ifdef ESP32 +#define ADC_BIT 12 +// #define analogWrite ledcWrite +#endif + +#define ADC_VALUE_MAX pow(2, ADC_BIT) + +// Это файл сенсора, в нем осуществляется чтение сенсора. +// для добавления сенсора вам нужно скопировать этот файл и заменить в нем текст AnalogAdc на название вашего сенсора +// Название должно быть уникальным, коротким и отражать суть сенсора. + +// ребенок - родитель +class MQgas : public IoTItem +{ +private: + //======================================================================================================= + // Секция переменных. + // Это секция где Вы можете объявлять переменные и объекты arduino библиотек, что бы + // впоследствии использовать их в loop и setup + byte _pin; + int currentSensor; + float _ro = 0; // расчетное сопротивления датчика. переменная для текущих расчетов + float ro_TH = 0; + float rlBoard = 0; + float R0CleanAir; // расчетное сопротивления датчика, полученное из настроек элемента + float R0CleanAirDefault; + float RlR0CleanAir; + float RlR0CleanAirDefault; + float ppmCleanAir; + float ppmCleanAirDefault; + double aLimit; + double bLimit; + byte sampleTimes = 10; + byte sampleInterval = 20; + byte intensity = 5; + bool autoCalibrationEnable = true; + unsigned int autoCalibPeriod = 24; // часы + unsigned long autoCalibTimer = 0; // мс + unsigned int warmUpTime = 60; // сек + bool _stateCalibrate = false; + bool _stateCalibrateTH = false; + String lastCalibration; + float highRs = 0; + bool enableTempHumCorrection = false; + float k1; + float k2; + float b1; + float b2; + double Hum; + double Temp; + double tempHumCorrection; + String _idTempSensor; + String _idHumSensor; + float operatingVoltage = 3.3; + double ppmResult = 0; + bool debug = true; + +public: + //======================================================================================================= + // setup() + // это аналог setup из arduino. Здесь вы можете выполнять методы инициализации сенсора. + // Такие как ...begin и подставлять в них параметры полученные из web интерфейса. + // Все параметры хранятся в перемененной parameters, вы можете прочитать любой параметр используя jsonRead функции: + // jsonReadStr, jsonReadBool, jsonReadInt + MQgas(String parameters) : IoTItem(parameters) + { + +#ifdef ESP8266 + _pin = 0; +#endif +#ifdef ESP32 + _pin = jsonReadInt(parameters, "pin-Esp32"); + // adcAttachPin(_pin); +#endif + + currentSensor = jsonReadInt(parameters, "Series"); // пока не используется + jsonRead(parameters, F("Rl on board"), rlBoard, false); + jsonRead(parameters, F("aLimit"), aLimit, false); + jsonRead(parameters, F("bLimit"), bLimit, false); + jsonRead(parameters, F("Ro in clean air"), R0CleanAir, false); // + R0CleanAirDefault = R0CleanAir; + jsonRead(parameters, F("Rl/Ro in clean air"), RlR0CleanAir, false); // соотношение + RlR0CleanAirDefault = RlR0CleanAir; + jsonRead(parameters, F("PPM in clean air"), ppmCleanAir, false); // + ppmCleanAirDefault = ppmCleanAir; + warmUpTime = jsonReadInt(parameters, "Warm up time"); + sampleInterval = jsonReadInt(parameters, "Sample interval"); + sampleTimes = jsonReadInt(parameters, "Sample times"); + intensity = jsonReadInt(parameters, "Calibtation intensity"); + autoCalibrationEnable = jsonReadBool(parameters, "autoCalibration"); + autoCalibPeriod = jsonReadInt(parameters, "autoCalib.Period"); + + enableTempHumCorrection = jsonReadBool(parameters, "TempHum correction"); + jsonRead(parameters, F("k1"), k1, false); + jsonRead(parameters, F("k2"), k2, false); + jsonRead(parameters, F("b1"), b1, false); + jsonRead(parameters, F("b2"), b2, false); + jsonRead(parameters, F("temperature"), Temp, false); + jsonRead(parameters, F("humidity"), Hum, false); + jsonRead(parameters, "idTempSensor", _idTempSensor); + jsonRead(parameters, "idHumSensor", _idHumSensor); + + jsonRead(parameters, F("operating voltage"), operatingVoltage, false); + + debug = jsonReadBool(parameters, "Debug"); + if (debug) + { + Serial.print("ADC_VALUE_MAX ="); + Serial.println(ADC_VALUE_MAX); + Serial.print("R0CleanAir ="); + Serial.println(R0CleanAir); + Serial.print("Rl/R0CleanAir ="); + Serial.println(RlR0CleanAir); + Serial.print("ppmCleanAir ="); + Serial.println(ppmCleanAir); + } + + calibrate(); // быстрая калибровка, по холодному + warmUpTime = millis() / 1000 + warmUpTime; + _stateCalibrate = false; // чтобы сделать еще одну калибровку через warmUpTime + lastCalibration = NAN; + } + + //======================================================================================================= + // это аналог loop из arduino, но вызываемый каждые int секунд, заданные в настройках. Здесь вы должны выполнить чтение вашего сенсора + void doByInterval() + { + + if (!_stateCalibrate && millis() > warmUpTime * 1000) // повторная калибровка после минимального прогрева + { + calibrate(); + } + + if (((millis() - autoCalibTimer) > autoCalibPeriod * 3600 * 1000) && autoCalibrationEnable) // автомастическа калибровка + { + autoCalibration(); + } + + ppmResult = readSensor(); + + if (_stateCalibrate) // без калибровки не выводим ничего + { + value.valD = ppmResult; + + regEvent(value.valD, "MQgas"); + } + else + { + value.valD = NAN; + regEvent(value.valD, "MQgas"); + } + } + + // получаем Temp и Hum из других элементов + void onRegEvent(IoTItem *eventItem) + { + if (eventItem) + { + if (_idTempSensor != "") + { + if (_idTempSensor == eventItem->getID()) + { + String _idTempSensorString = eventItem->getValue(); + Temp = _idTempSensorString.toFloat(); + if (debug) + { + String output = " got via eventItem: Temp = " + String(Temp); + SerialPrint("I", "MQgas", output, _id); + } + } + } + + if (_idHumSensor != "") + { + if (_idHumSensor == eventItem->getID()) + { + String _idHumSensorSting = eventItem->getValue(); + Hum = _idHumSensorSting.toFloat(); + if (debug) + { + String output = " got via eventItem: Hum = " + String(Hum); + SerialPrint("I", "MQgas", output, _id); + } + } + } + } + } + + // получаем вызовы из сценария + IoTValue execute(String command, std::vector ¶m) + { + if (command == "calibrate") // калибровка значениями по умолчанию (из настроек) + { + R0CleanAir = R0CleanAirDefault; + RlR0CleanAir = RlR0CleanAirDefault; + ppmCleanAir = ppmCleanAirDefault; + calibrate(); + SerialPrint("I", "MQgas", "calibrate() with default values", _id); + } + else if (command == "calibrateR0") + { + R0CleanAir = param[0].valD; + calibrate(); + String output = "calibrateR0(), R0CleanAir = " + String(R0CleanAir); + SerialPrint("I", "MQgas", output, _id); + } + else if (command == "calibrateRlRo") + { + R0CleanAir = 0; + ppmCleanAir = 0; + RlR0CleanAir = param[0].valD; + calibrate(); + String output = "calibrateRlRo(), RlR0CleanAir = " + String(RlR0CleanAir); + SerialPrint("I", "MQgas", output, _id); + } + else if (command == "calibratePPM") + { + R0CleanAir = 0; + RlR0CleanAir = 0; + ppmCleanAir = param[0].valD; + calibrate(); + String output = "calibratePPM(), ppmCleanAir = " + String(ppmCleanAir); + SerialPrint("I", "MQgas", output, _id); + } + else if (command == "setAutoCalibration") + { + if (param[0].isDecimal) + { + autoCalibrationEnable = param[0].valD; + } + String output = "setAutoCalibration = " + String(autoCalibrationEnable); + SerialPrint("I", "MQgas", output, _id); + } + else if (command == "runAutoCalibration") + { + autoCalibration(); + } + else if (command == "lastCalibration") // оправляем время послежней калибровки в сценарий + { + IoTValue valTmp; + valTmp.isDecimal = false; + valTmp.valS = lastCalibration; + String output = "By request: lastCalibration = " + String(valTmp.valS); + SerialPrint("I", "MQgas", output, _id); + return valTmp; + } + /* + else if (command == "enabledAutoCalibration") // оправляем время послежней калибровки в сценарий + { + IoTValue valTmp; + valTmp.isDecimal = true; + valTmp.valD = autoCalibrationEnable; + String output = "By request: enabledAutoCalibration = " + String(valTmp.valD); + SerialPrint("I", "MQgas", output, _id); + return valTmp; + } + */ + else if (command == "TempHumCorrection") // получаем Temp и Hum из сценария + { + if (param[0].isDecimal) + Temp = param[0].valD; + if (param[1].isDecimal) + Hum = param[1].valD; + String output = "TempHumCorrection() temperature = " + String(Temp) + " humidity = " + String(Hum); + SerialPrint("I", "MQgas", output, _id); + } + + return {}; // команда поддерживает возвращаемое значения. Т.е. по итогу выполнения команды или общения с внешней системой, можно вернуть значение в сценарий для дальнейшей обработки + } + + // получиение соотношения Rl/Ro на чистом воздухе + float getRlRoInCleanAir() + { + float RlR0CleanAirCalc; + + if (ppmCleanAir) // или расчитываем по ppm + { + RlR0CleanAirCalc = exp((log(ppmCleanAir) * aLimit) + bLimit); + } + else // или берем готовое их настроек + { + RlR0CleanAirCalc = RlR0CleanAir; + } + + return RlR0CleanAirCalc; + } + + // калибровка датчика + void calibrate() + { + float ro = 0; + if (R0CleanAir) // при знании сопративления датчика на чистом воздухe + { // фиксированая калибровка датчика + ro = R0CleanAir; + RlR0CleanAir = rlBoard / R0CleanAir; // просто пересчитываем для вывода в дебаг + } + else // расчет ro через RlRoInCleanAir + { + float rs = readRs(sampleTimes * intensity); // считывания показаний сопративление датчика на чистом воздухе!! + ro = rs / getRlRoInCleanAir(); + } + + if (ro && ro == ro) // проверка что не NAN + { + _ro = ro; // значение сопративления сенсора на воздухе + _stateCalibrate = true; + lastCalibration = getDateTimeDotFormated(); + String output = "Calibration successful! Ro in Clean Air = " + String(_ro); + Serial.println(); + SerialPrint("I", "MQgas", output, _id); + calibrationTH(); + } + else + { + Serial.println(); + SerialPrint("E", "MQgas", " Calibration failed! Fill one of Setting 'in clean air', check wiring ", _id); + } + } + + // выполнение автоматической калибровки + void autoCalibration() + { + _ro = highRs / getRlRoInCleanAir(); // значение сопративления сенсора на воздухе + + if (_ro && _ro == _ro) + { + _stateCalibrate = true; + lastCalibration = getDateTimeDotFormated(); + String output = "autoCalibration successful!, R0 = " + String(_ro); + Serial.println(); + SerialPrint("I", "MQgas", output, _id); + calibrationTH(); + autoCalibTimer = millis(); + highRs = 0; + } + else + { + Serial.println(); + SerialPrint("E", "MQgas", " autoCalibration failed! Fill one of Setting 'in clean air', check wiring ", _id); + } + } + + // калибровка с учетом температуры и влажности + void calibrationTH() + { + if (enableTempHumCorrection && goodReadTH()) // нормализуем к текущей температуре и влажности + { + tempHumCorrection = TempHumCorrection(); + ro_TH = _ro * tempHumCorrection; + _stateCalibrateTH = true; + String output = "TH Calibration successful! Ro_TH in Clean Air = " + String(ro_TH); + Serial.println(); + SerialPrint("I", "MQgas", output, _id); + } + else + { + Serial.println(); + SerialPrint("E", "MQgas", "TH Calibration failed! Fill Temp and Hum at Settings or add Temp and Hum sensors!", _id); + } + } + + // расчет сопротивление датчика + float CalculateResistance(int sensorADC) + { + float sensorVoltage = sensorADC * (operatingVoltage / ADC_VALUE_MAX); + float sensorResistance = (operatingVoltage - sensorVoltage) / sensorVoltage * rlBoard; + return sensorResistance; + } + + // циклическое считывание сопративления датчика + float readRs(byte sampleTimes) + { + float rs = 0; + int sensorADC; + byte error0 = 0; + byte errorMax = 0; + + for (int i = 0; i < sampleTimes; i++) + { + sensorADC = analogRead(_pin); + if (sensorADC >= ADC_VALUE_MAX - 1) + { + errorMax = 1; + if (debug) + { + String output = "Check wiring, analogRead(_pin) = " + String(sensorADC); + SerialPrint("E", "MQgas", output, _id); + } + } + else if (sensorADC == 0) + { + error0 = 1; + if (debug) + SerialPrint("E", "MQgas", "Check sensor, analogRead(_pin) = 0", _id); + } + else + { + rs += CalculateResistance(sensorADC); + delay(sampleInterval); + } + } + + if (!error0 && !errorMax) + { + rs = rs / (sampleTimes); + + if (rs > highRs && millis() > warmUpTime * 1000) // запоминаем максимальное значение сопротивления сенсора для автокалибровки + highRs = rs; + } + else + { + rs = NAN; + if (errorMax) + { + String output = "Check wiring, analogRead(_pin) = " + String(sensorADC); + SerialPrint("E", "MQgas", output, _id); + } + if (error0) + SerialPrint("E", "MQgas", "Check sensor, analogRead(_pin) = 0", _id); + } + + if (debug) + { + Serial.print("Analog: "); + Serial.print(analogRead(_pin)); + Serial.print("\tRo: "); + Serial.print(_ro); + Serial.print("\treadRs: "); + Serial.print(rs); + Serial.print("\thighRs: "); + Serial.print(highRs); + } + return rs; + } + + // расчет соотношения Rs/Ro + float readRatio() + { + float readRatio = NAN; + if (_ro) + { + float rs = readRs(sampleTimes); + if (rs) + { + readRatio = rs / _ro; // getRo(); + } + } + if (debug) + { + Serial.print(" \tRs/RoRatio: "); + Serial.print(readRatio); + } + return readRatio; + } + + // расчет значения концентрации газа + double readSensor() + { + double readSensor = NAN; + double ratio = readRatio(); + + if (ratio != 0 && ratio == ratio) // also not NAN + { + readSensor = exp((log(ratio) - bLimit) / aLimit); + if (debug) + { + Serial.print("\tGas: "); + Serial.println(readSensor, 10); + } + + if (enableTempHumCorrection && _stateCalibrateTH && goodReadTH()) // поправка на температуру и влажность + { + tempHumCorrection = TempHumCorrection(); + + ratio = ratio * tempHumCorrection; + readSensor = exp((log(ratio * _ro / ro_TH) - bLimit) / aLimit); + if (debug) + { + Serial.print("\tTHCor: "); + Serial.print(tempHumCorrection, 10); + Serial.print("\tGasTHCorrected: "); + Serial.println(readSensor, 10); + } + } + } + + return readSensor; + } + + // расчет поправки на температуру и влажность + double TempHumCorrection() + { + double correction = 1; + + double k_hum = k1 * Hum / 100 + k2; + double b_hum = b1 * Hum / 100 + b2; + correction = k_hum * Temp + b_hum; + + if (debug) + { + Serial.print("\tk_hum: "); + Serial.print(k_hum, 10); + Serial.print("\tb_hum: "); + Serial.print(b_hum, 10); + Serial.print("\tcor: "); + Serial.print(correction, 10); + } + + return correction; + } + + // проверка значний температуры и влажности + bool goodReadTH() + { + if (Hum > 0 && Hum < 95 && Temp > 0 && Temp < 70) + { + return true; + } + else + { + if (debug) + { + Serial.println(); + SerialPrint("E", "MQgas", "Wrong data from Temperature and Humidity sensor or from Settings", _id); + } + return false; + } + } + + ~MQgas(){}; +}; + +void *getAPI_MQgas(String subtype, String param) +{ + if (subtype == F("MQgas")) + { + return new MQgas(param); + } + else + { + return nullptr; + } +} diff --git a/src/modules/sensors/MQgas/modinfo.json b/src/modules/sensors/MQgas/modinfo.json new file mode 100644 index 00000000..bcaa1fd6 --- /dev/null +++ b/src/modules/sensors/MQgas/modinfo.json @@ -0,0 +1,271 @@ +{ + "menuSection": "sensors", + "configItem": [ + { + "global": 0, + "name": "MQ газовые анализаторы", + "type": "Reading", + "subtype": "MQgas", + "id": "MQ", + "widget": "anydataPpm", + "page": "Сенсоры", + "descr": "MQ-135", + "Series": 135, + "Gas": "CO2", + "Rl on board": 10, + "Ro in clean air": 0, + "Rl/Ro in clean air": 0, + "PPM in clean air": 397.13, + "aLimit": -0.42, + "bLimit": 1.92, + "Warm up time": 60, + "Sample interval": 20, + "Sample times": 10, + "Calibtation intensity": 5, + "autoCalibration": 1, + "autoCalib.Period": 24, + "TempHum correction" : 1, + "temperature": 20, + "idTempSensor": "", + "humidity": 50, + "idHumSensor": "", + "k1": 0.00672096284322792, + "k2":-0.0159038179354688, + "b1":-0.741244323718154, + "b2":1.77535862501753, + "Debug": 1, + "plus": 0, + "multiply": 1, + "round": 1, + "pin-Esp32": 34, + "operating voltage": 3.3, + "int": 15 + } + ], + "about": { + "authorName": "Alex K", + "authorContact": "https://t.me/cmche", + "authorGit": "https://github.com/CHE77/MQ-sensors_IotManager", + "exampleURL": "https://iotmanager.org/wiki", + "specialThanks": "https://github.com/amperka/TroykaMQ", + "specialThanks2": "https://forum.amperka.ru/threads/%D0%94%D0%B0%D1%82%D1%87%D0%B8%D0%BA%D0%B8-%D1%81%D0%B5%D1%80%D0%B8%D0%B8-mq-%D0%B8-%D0%B1%D0%B8%D0%B1%D0%BB%D0%B8%D0%BE%D1%82%D0%B5%D0%BA%D0%B0-troykamq.16377/page-7#post-220009", + "moduleName": "MQgas", + "moduleVersion": "1.0", + "usedRam": { + "esp32_4mb": 15, + "esp8266_4mb": 15 + }, + "title": "MQ газовые анализаторы", + "moduleDesc": "Позволяет получить концентрации газов с сенсоров серии MQ, подключаемых на аналоговый вход. Заполните свойства элемента значениями для каждого сенсора и типа газа. ‘Rl on board’, ‘aLimit’, ‘bLimit’ – обязательные для заполнения. И как минимум одно из - ‘Ro in clean air’, ‘Rl/Ro in clean air’, ‘PPM in clean air’. При запуске будет сразу запущена предварительная калибровка для определения базовой характеристики сенсора - сопротивления в чистом воздухе - Ro. Через ‘Warm up time’ будет произведена повторная калибровка уже слегка прогретого сенсора. Полный прогрев занимает сутки. Дополнительную калибровку можно вызвать из сценария, либо с помощью автоматической калибровки.", + "propInfo": { + "Series": "Номер серии из линейки сенсоров MQ- (только для обозначения для себя)", + "Gas": "Тип исследуемого газа. (только для обозначения для себя) Один сенсор может регистировать концентрацию нескольких газов", + "Rl on board": "Фиксированое сопротивление резистора делителя напряжение на плате. [кОм]", + "Ro in clean air": "Известное номинальное сопротивление сенсора на чистом воздухе или референсной среде. [кОм]", + "Rl/Ro in clean air": "Известное соотношенее сопротивления сенсора делителя напряжения к номинальному на чистом воздухе.", + "PPM in clean air": "Известное значение концентрации газа в чистом воздухе. [ppm]", + "aLimit": "Табличный коэффициент нижнего предела диапзона", + "bLimit": "Табличный коэффициент верхнего предела диапзона", + "Warm up time": "Время прогрева для дополнительной калибровки. [сек]", + "Sample interval": "Задержка между замерами на аналоговом пине.[мс]", + "Sample times": "Количестов замеров в серии для поледующего усреднения", + "Calibtation intensity": "Во сколько раз увеличить количество замеров в серии для калибровки", + "autoCalibration": "1 - включить Автокалибровку. Для переодически проветриваемых помещений", + "autoCalib.Period": "Период Автоколибровки в часах. Будет выбиратся минимальное значение ppm для рачета Сопротивления датчика на чистом воздухе.[ч]", + "TempHum correction" : "Включить (1), выключить(0) коррекцию по темературе и влажности", + "temperature": "Температутра по умолчанию [*C]. Если нет сенсора, то можно откалибровать с одними значениями, потом использовать с другими", + "idTempSensor": "id сенсора Температуры. Значения сенсора в приоритете перед значением по умолчанию", + "humidity": "Влажность по умолчанию [%]. Если нет сенсора, то можно откалибровать с одними значениями, потом использовать с другими", + "idHumSensor": "id сенсора Влажности. Значения сенсора в приоритете перед значением по умолчанию", + "k1":"коэффициент для расчета k_hum = k1 * Hum / 100 + k2 и correction = k_hum * Temp + b_hum", + "k2":"коэффициент для расчета k_hum = k1 * Hum / 100 + k2 и correction = k_hum * Temp + b_hum", + "b1":"коэффициент для расчета b_hum = b1 * Hum / 100 + b2 и correction = k_hum * Temp + b_hum", + "b2":"коэффициент для расчета b_hum = b1 * Hum / 100 + b2 и correction = k_hum * Temp + b_hum", + "Debug": "1 - для вывода промежуточных рачетных значений в Лог", + "plus": "поправочный коэффиент +c", + "multiply": "поправочный коэффиент k*", + "round": "округление", + "pin-Esp32": "Esp32: Аналоговый GPIO номер, к которому подключен датчик:32, 33, 34, 35, 36, 39. Для Esp8266 указывать не надо.", + "operating voltage": "3.3 - если используете согласование уровней на аналоговый вход. 5 - если подключаете датчик напрямую. MQ датчики 5-вольтовые, но диапазон реальных измерений как павило не выходит за 3.3В И даже если и будет превышение (что конечно не рекомендуются) то из практики известно что аналоговый пин это выдерживает. Поэтому можно подкючать напрямую и при этом даже несколько повышается разрешающая способность датчика", + "int": "Количество секунд между опросами датчика" + }, + "settings": { + "MQ-2": { + "Rl on board": 5, + "Ro in clean air": 0, + "Rl/Ro in clean air": 9.83, + "PPM in clean air": 0, + "LPG": { + "aLimit": -0.45, + "bLimit": 2.95 + }, + "Methane": { + "aLimit": -0.38, + "bLimit": 3.21 + }, + "Smoke": { + "aLimit": -0.42, + "bLimit": 3.54 + }, + "Hydrogen": { + "aLimit": -0.48, + "bLimit": 3.32 + } + }, + "MQ-3": { + "Gas": "Alcohol", + "Rl on board": 200, + "Ro in clean air": 0, + "Rl/Ro in clean air": 60, + "PPM in clean air": 0, + "aLimit": -0.66, + "bLimit": -0.62, + "multiply": 2.2 + }, + "MQ-4": { + "Gas": "Methane", + "Rl on board": 20, + "Ro in clean air": 0, + "Rl/Ro in clean air": 4.4, + "PPM in clean air": 0, + "aLimit": -0.36, + "bLimit": 2.54 + }, + "MQ-5": { + "Rl on board": 20, + "Ro in clean air": 0, + "Rl/Ro in clean air": 6.5, + "PPM in clean air": 0, + "LPG": { + "aLimit": -0.39, + "bLimit": 1.73 + }, + "Methane": { + "aLimit": -0.38, + "bLimit": 1.79 + } + }, + "MQ-6": { + "Gas": "LPG", + "Rl on board": 20, + "Ro in clean air": 0, + "Rl/Ro in clean air": 10, + "PPM in clean air": 0, + "aLimit": -0.42, + "bLimit": 2.91 + }, + "MQ-7": { + "Gas": "CO", + "Rl on board": 10, + "Ro in clean air": 0, + "Rl/Ro in clean air": 27, + "PPM in clean air": 0, + "aLimit": -0.77, + "bLimit": 3.38 + }, + "MQ-8": { + "Gas": "H2", + "Rl on board": 10, + "Ro in clean air": 0, + "Rl/Ro in clean air": 27, + "PPM in clean air": 0, + "aLimit": -1.52, + "bLimit": 10.49 + }, + "MQ-9": { + "Rl on board": 10, + "Ro in clean air": 0, + "Rl/Ro in clean air": 9.8, + "PPM in clean air": 0, + "LPG": { + "aLimit": -0.48, + "bLimit": 3.33 + }, + "Methane": { + "aLimit": -0.38, + "bLimit": 3.21 + }, + "CO": { + "aLimit": -0.48, + "bLimit": 3.10 + } + }, + "MQ-135": { + "Gas": "CO2", + "Rl on board": 10, + "aLimit": -0.42, + "bLimit": 1.92, + "Ro in clean air": 0, + "Rl/Ro in clean air": 0, + "PPM in clean air": 397.13 + } + }, + "funcInfo": [ + { + "name": "calibrate", + "descr": "Калибровка с параметрами по умолчанию" + }, + { + "name": "calibrateR0", + "descr": "Калибровка с установкой номинального сопротивления сенсора на чистом воздухе ", + "params": [ + "Ro in clean air" + ] + }, + { + "name": "calibrateRlRo", + "descr": "Калибровка с установкой соотношения сопротивления делителя напряжения к номинальному на чистом воздухе.", + "params": [ + "Rl/Ro in clean air" + ] + }, + { + "name": "calibratePPM", + "descr": "Калибровка с установкой значения концентрации газа в чистом воздухе", + "params": [ + "PPM in clean air" + ] + }, + { + "name": "setAutoCalibration", + "descr": "Включение/Выключение автокалибровки", + "params": [ + "0 - Выкл. 1 - Вкл." + ] + }, + { + "name": "runAutoCalibration", + "descr": "Принудительная автоматическая калибровка (с обнулением максимального сопротивления сенсора и перезапуском счетчика автокалибровки)" + }, + { + "name": "lastCalibration", + "descr": "Возврат время последней калибровки" + }, + { + "name": "TempHumCorrection", + "descr": "Передача значений темературы и влажности для расчета поправки", + "params": [ + "Temperature", + "Humidity" + ] + } + + ] + }, + "defActive": true, + "usedLibs": { + "esp32_4mb": [], + "esp32_4mb3f": [], + "esp32cam_4mb": [], + "esp32_16mb": [], + "esp32s2_4mb": [], + "esp8266_4mb": [], + "esp8266_16mb": [], + "esp8266_1mb": [], + "esp8266_1mb_ota": [], + "esp8285_1mb": [], + "esp8285_1mb_ota": [], + "esp8266_2mb": [], + "esp8266_2mb_ota": [] + } +} diff --git a/src/modules/sensors/MQgas/mq135alone.json b/src/modules/sensors/MQgas/mq135alone.json new file mode 100644 index 00000000..76e9eaf2 --- /dev/null +++ b/src/modules/sensors/MQgas/mq135alone.json @@ -0,0 +1,135 @@ +{ + "mark": "iotm", + "config": [ + { + "global": 0, + "type": "Reading", + "subtype": "MQgas", + "id": "MQ135", + "widget": "anydataPpm", + "page": "Сенсоры", + "descr": "MQ-135", + "Series": 135, + "Gas": "CO2", + "Rl on board": "10", + "Ro in clean air": 0, + "Rl/Ro in clean air": "0", + "PPM in clean air": "397.13", + "aLimit": "-0.42", + "bLimit": "1.92", + "Warm up time": "60", + "Sample interval": 20, + "Sample times": "10", + "Calibtation intensity": 5, + "autoCalibration": 1, + "autoCalib.Period": 1, + "TempHum correction": 1, + "k1": 0.00672096284322792, + "k2": -0.0159038179354688, + "b1": -0.741244323718154, + "b2": 1.77535862501753, + "temperature": "20", + "idTempSensor": "Tmp", + "humidity": "50", + "idHumSensor": "Hum", + "Debug": 1, + "plus": 0, + "multiply": 1, + "round": 1, + "pin-Esp32": 34, + "operating voltage": 3.3, + "int": 15 + }, + { + "global": 0, + "type": "Writing", + "subtype": "Loging", + "id": "log", + "widget": "chart1", + "page": "Графики", + "descr": "CO2", + "int": "1", + "logid": "MQ135", + "points": 300 + }, + { + "global": 0, + "type": "Reading", + "subtype": "VButton", + "id": "vbtn", + "needSave": 0, + "widget": "toggle", + "page": "Калибровка", + "descr": "Быстрая Калибровка", + "int": "0", + "val": "0" + }, + { + "global": 0, + "type": "Reading", + "subtype": "Variable", + "id": "vout", + "needSave": 0, + "widget": "anydataDef", + "page": "Калибровка", + "descr": "Последняя калибровка", + "int": "0", + "val": "0.0", + "map": "1024,1024,1,100", + "plus": 0, + "multiply": 1, + "round": 0 + }, + { + "global": 0, + "type": "Reading", + "subtype": "VButton", + "id": "vbtn13", + "needSave": 0, + "widget": "toggle", + "page": "Калибровка", + "descr": "Автокалибровка выкл/вкл", + "int": "0", + "val": "1" + }, + { + "global": 0, + "type": "Reading", + "subtype": "VButton", + "id": "vbtn50", + "needSave": 0, + "widget": "toggle", + "page": "Калибровка", + "descr": "Накопительная Калибровка", + "int": "0", + "val": "0" + } + ] +} + +scenario=>#запускаем разово калибровку без параметрова или с ними +if vbtn then { +MQ135.calibrate(); +#MQ135.calibrateR0(42); +#MQ135.calibrateRlRo(0.60); +#MQ135.calibratePPM(400); +#MQ135.TempHumCorrection(20,55); + vbtn = 0;} + +#обновляем время последней калибровки +if MQ135 then { +vout = MQ135.lastCalibration(); +} + + +#влючаем/выключаем автокалибровку +if vbtn13 == 1 then MQ135.setAutoCalibration(1) +if vbtn13 == 0 then MQ135.setAutoCalibration(0) + + +#принудительно запускаем автокалибровку +if vbtn50 then { +MQ135.runAutoCalibration(); +vbtn50 = 0; +} + diff --git a/src/modules/sensors/MQgas/mq135withBME280.json b/src/modules/sensors/MQgas/mq135withBME280.json new file mode 100644 index 00000000..fc88e799 --- /dev/null +++ b/src/modules/sensors/MQgas/mq135withBME280.json @@ -0,0 +1,176 @@ +{ + "mark": "iotm", + "config": [ + { + "global": 0, + "type": "Reading", + "subtype": "MQgas", + "id": "MQ135", + "widget": "anydataPpm", + "page": "Сенсоры", + "descr": "MQ-135", + "Series": 135, + "Gas": "CO2", + "Rl on board": "10", + "Ro in clean air": 0, + "Rl/Ro in clean air": "0", + "PPM in clean air": "397.13", + "aLimit": "-0.42", + "bLimit": "1.92", + "Warm up time": "60", + "Sample interval": 20, + "Sample times": "10", + "Calibtation intensity": 5, + "autoCalibration": 1, + "autoCalib.Period": 1, + "TempHum correction": 1, + "k1": 0.00672096284322792, + "k2": -0.0159038179354688, + "b1": -0.741244323718154, + "b2": 1.77535862501753, + "temperature": "20", + "idTempSensor": "Tmp", + "humidity": "50", + "idHumSensor": "Hum", + "Debug": 1, + "plus": 0, + "multiply": 1, + "round": 1, + "pin-Esp32": 34, + "operating voltage": 3.3, + "int": 15 + }, + { + "global": 0, + "type": "Writing", + "subtype": "Loging", + "id": "log", + "widget": "chart1", + "page": "Графики", + "descr": "CO2", + "int": "1", + "logid": "MQ135", + "points": 300 + }, + { + "global": 0, + "type": "Reading", + "subtype": "VButton", + "id": "vbtn", + "needSave": 0, + "widget": "toggle", + "page": "Калибровка", + "descr": "Быстрая Калибровка", + "int": "0", + "val": "0" + }, + { + "global": 0, + "type": "Reading", + "subtype": "Bme280t", + "id": "Tmp", + "widget": "anydataTmp", + "page": "Сенсоры", + "descr": "Температура", + "int": 15, + "addr": "0x76", + "round": 1 + }, + { + "global": 0, + "type": "Reading", + "subtype": "Bme280h", + "id": "Hum", + "widget": "anydataHum", + "page": "Сенсоры", + "descr": "Влажность", + "int": 15, + "addr": "0x76", + "round": 1 + }, + { + "global": 0, + "type": "Writing", + "subtype": "Timer", + "id": "timer0", + "widget": "nil", + "page": "", + "descr": "Таймер", + "int": "1", + "countDown": 15, + "ticker": "0", + "repeat": "1", + "needSave": 0 + }, + { + "global": 0, + "type": "Reading", + "subtype": "Variable", + "id": "vout", + "needSave": 0, + "widget": "anydataDef", + "page": "Калибровка", + "descr": "Последняя калибровка", + "int": "0", + "val": "0.0", + "map": "1024,1024,1,100", + "plus": 0, + "multiply": 1, + "round": 0 + }, + { + "global": 0, + "type": "Reading", + "subtype": "VButton", + "id": "vbtn13", + "needSave": 0, + "widget": "toggle", + "page": "Калибровка", + "descr": "Автокалибровка выкл/вкл", + "int": "0", + "val": "1" + }, + { + "global": 0, + "type": "Reading", + "subtype": "VButton", + "id": "vbtn50", + "needSave": 0, + "widget": "toggle", + "page": "Калибровка", + "descr": "Накопительная Калибровка", + "int": "0", + "val": "0" + } + ] +} + +scenario=>#запускаем разово калибровку без параметрова или с ними +if vbtn then { +MQ135.calibrate(); +#MQ135.calibrateR0(42); +#MQ135.calibrateRlRo(0.60); +#MQ135.calibratePPM(400); +#MQ135.TempHumCorrection(20,55); +#MQ135.TempHumCorrection(Tmp,Hum); + vbtn = 0;} + +#обновляем время последней калибровки +if MQ135 then { +vout = MQ135.lastCalibration(); +} + + +#влючаем/выключаем автокалибровку +if vbtn13 == 1 then MQ135.setAutoCalibration(1) +if vbtn13 == 0 then MQ135.setAutoCalibration(0) + + +#принудительно запускаем автокалибровку +if vbtn50 then { +MQ135.runAutoCalibration(); +vbtn50 = 0; +} + +#можно по таймеру передавать температуру и влажность в модуль. Но проще сразу вписать id этих элементов в настройки MQ +#if timer0 == 0 then MQ135.TempHumCorrection(Tmp,Hum); From 2c53e28b9ad78df55e66676f899db9fd80f50fd1 Mon Sep 17 00:00:00 2001 From: Al <44e4ee@gmail.com> Date: Tue, 28 Nov 2023 02:00:26 +0100 Subject: [PATCH 3/5] Add files via upload --- src/modules/sensors/MQgas/MQgas.cpp | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/src/modules/sensors/MQgas/MQgas.cpp b/src/modules/sensors/MQgas/MQgas.cpp index 1ac82aad..416b56e2 100644 --- a/src/modules/sensors/MQgas/MQgas.cpp +++ b/src/modules/sensors/MQgas/MQgas.cpp @@ -37,8 +37,8 @@ private: float RlR0CleanAirDefault; float ppmCleanAir; float ppmCleanAirDefault; - double aLimit; - double bLimit; + float aLimit; + float bLimit; byte sampleTimes = 10; byte sampleInterval = 20; byte intensity = 5; @@ -49,14 +49,15 @@ private: bool _stateCalibrate = false; bool _stateCalibrateTH = false; String lastCalibration; + unsigned long lastCalibrationMillis; float highRs = 0; bool enableTempHumCorrection = false; float k1; float k2; float b1; float b2; - double Hum; - double Temp; + float Hum; + float Temp; double tempHumCorrection; String _idTempSensor; String _idHumSensor; @@ -248,6 +249,21 @@ public: { IoTValue valTmp; valTmp.isDecimal = false; + + if (lastCalibration == "00.00.00 00:00:00") + { + unsigned long miliSeconds = millis() - lastCalibrationMillis; + unsigned long seconds = miliSeconds / 1000; + unsigned long minutes = seconds / 60; + unsigned long hours = minutes / 60; + unsigned long days = hours / 24; + // miliSeconds %= 1000; + seconds %= 60; + minutes %= 60; + hours %= 24; + lastCalibration = String(days) + "d " + String(hours) + ":" + String(minutes) + ":" + String(seconds); + } + valTmp.valS = lastCalibration; String output = "By request: lastCalibration = " + String(valTmp.valS); SerialPrint("I", "MQgas", output, _id); @@ -273,7 +289,7 @@ public: String output = "TempHumCorrection() temperature = " + String(Temp) + " humidity = " + String(Hum); SerialPrint("I", "MQgas", output, _id); } - + return {}; // команда поддерживает возвращаемое значения. Т.е. по итогу выполнения команды или общения с внешней системой, можно вернуть значение в сценарий для дальнейшей обработки } @@ -314,6 +330,7 @@ public: _ro = ro; // значение сопративления сенсора на воздухе _stateCalibrate = true; lastCalibration = getDateTimeDotFormated(); + lastCalibrationMillis = millis(); String output = "Calibration successful! Ro in Clean Air = " + String(_ro); Serial.println(); SerialPrint("I", "MQgas", output, _id); @@ -335,6 +352,7 @@ public: { _stateCalibrate = true; lastCalibration = getDateTimeDotFormated(); + lastCalibrationMillis = millis(); String output = "autoCalibration successful!, R0 = " + String(_ro); Serial.println(); SerialPrint("I", "MQgas", output, _id); From f2d49a31de541acd3480df65e0f5238ce778e6ac Mon Sep 17 00:00:00 2001 From: Al <44e4ee@gmail.com> Date: Tue, 28 Nov 2023 02:07:10 +0100 Subject: [PATCH 4/5] Update mq135alone.json --- src/modules/sensors/MQgas/mq135alone.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/sensors/MQgas/mq135alone.json b/src/modules/sensors/MQgas/mq135alone.json index 76e9eaf2..a638da71 100644 --- a/src/modules/sensors/MQgas/mq135alone.json +++ b/src/modules/sensors/MQgas/mq135alone.json @@ -88,7 +88,7 @@ "needSave": 0, "widget": "toggle", "page": "Калибровка", - "descr": "Автокалибровка выкл/вкл", + "descr": "Автокалибровка", "int": "0", "val": "1" }, From 7fe92efbe7abd34cf4e5d8d73081e79fb8afad35 Mon Sep 17 00:00:00 2001 From: Al <44e4ee@gmail.com> Date: Tue, 28 Nov 2023 02:08:04 +0100 Subject: [PATCH 5/5] Update mq135withBME280.json --- src/modules/sensors/MQgas/mq135withBME280.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/sensors/MQgas/mq135withBME280.json b/src/modules/sensors/MQgas/mq135withBME280.json index fc88e799..d9b0e238 100644 --- a/src/modules/sensors/MQgas/mq135withBME280.json +++ b/src/modules/sensors/MQgas/mq135withBME280.json @@ -126,7 +126,7 @@ "needSave": 0, "widget": "toggle", "page": "Калибровка", - "descr": "Автокалибровка выкл/вкл", + "descr": "Автокалибровка", "int": "0", "val": "1" },