From 5719d5badf4fb3343604b7d39de350b8fbdb5fe5 Mon Sep 17 00:00:00 2001 From: Yurii Date: Thu, 12 Feb 2026 13:00:26 +0300 Subject: [PATCH] feat: Added support DHT11/DHT22 sensors --- platformio.ini | 6 +- src/MqttTask.h | 16 ++++++ src/Sensors.h | 2 + src/SensorsTask.h | 110 ++++++++++++++++++++++++++++++++++++ src/strings.h | 1 + src/utils.h | 9 ++- src_data/locales/cn.json | 2 + src_data/locales/en.json | 2 + src_data/locales/it.json | 2 + src_data/locales/nl.json | 2 + src_data/locales/ru.json | 2 + src_data/pages/sensors.html | 16 +++++- 12 files changed, 164 insertions(+), 6 deletions(-) diff --git a/platformio.ini b/platformio.ini index 51f59d1..626ed01 100644 --- a/platformio.ini +++ b/platformio.ini @@ -4,7 +4,7 @@ extra_configs = secrets.default.ini core_dir = .pio [env] -version = 1.6.0-async +version = 1.6.0-async-dht framework = arduino lib_deps = ESP32Async/AsyncTCP@^3.4.10 ESP32Async/ESPAsyncWebServer@^3.9.4 @@ -19,9 +19,10 @@ lib_deps = ESP32Async/AsyncTCP@^3.4.10 pstolarz/OneWireNg@^0.14.1 ;milesburton/DallasTemperature@^4.0.5 https://github.com/Laxilef/Arduino-Temperature-Control-Library#fix_85c + https://github.com/Laxilef/esp32DHT#idf5 ;laxilef/TinyLogger@^1.1.1 https://github.com/Laxilef/TinyLogger#custom_handlers -lib_ignore = paulstoffregen/OneWire +lib_ignore = OneWire build_type = ${secrets.build_type} build_flags = -mtext-section-literals -Wno-deprecated-declarations @@ -33,6 +34,7 @@ build_flags = -mtext-section-literals -D ARDUINOJSON_USE_DOUBLE=0 -D ARDUINOJSON_USE_LONG_LONG=0 -D TINYLOGGER_GLOBAL + -D DHT_TASK_STACK_SIZE=4096 -D DEFAULT_SERIAL_ENABLED=${secrets.serial_enabled} -D DEFAULT_SERIAL_BAUD=${secrets.serial_baud} -D DEFAULT_WEBSERIAL_ENABLED=${secrets.webserial_enabled} diff --git a/src/MqttTask.h b/src/MqttTask.h index f7f780e..cfb9a05 100644 --- a/src/MqttTask.h +++ b/src/MqttTask.h @@ -292,6 +292,14 @@ protected: this->haHelper->deleteDynamicSensor(prevSettings, Sensors::ValueType::RSSI); break; + case Sensors::Type::DHT11: + case Sensors::Type::DHT22: + this->haHelper->deleteConnectionDynamicSensor(prevSettings); + this->haHelper->deleteSignalQualityDynamicSensor(prevSettings); + this->haHelper->deleteDynamicSensor(prevSettings, Sensors::ValueType::TEMPERATURE); + this->haHelper->deleteDynamicSensor(prevSettings, Sensors::ValueType::HUMIDITY); + break; + case Sensors::Type::DALLAS_TEMP: this->haHelper->deleteConnectionDynamicSensor(prevSettings); this->haHelper->deleteSignalQualityDynamicSensor(prevSettings); @@ -319,6 +327,14 @@ protected: this->haHelper->publishDynamicSensor(sSettings, Sensors::ValueType::RSSI, settings.system.unitSystem, false); break; + case Sensors::Type::DHT11: + case Sensors::Type::DHT22: + this->haHelper->publishConnectionDynamicSensor(sSettings); + this->haHelper->publishSignalQualityDynamicSensor(sSettings, false); + this->haHelper->publishDynamicSensor(sSettings, Sensors::ValueType::TEMPERATURE, settings.system.unitSystem); + this->haHelper->publishDynamicSensor(sSettings, Sensors::ValueType::HUMIDITY, settings.system.unitSystem); + break; + case Sensors::Type::DALLAS_TEMP: this->haHelper->publishConnectionDynamicSensor(sSettings); this->haHelper->publishSignalQualityDynamicSensor(sSettings, false); diff --git a/src/Sensors.h b/src/Sensors.h index 71b9ac3..bd9b524 100644 --- a/src/Sensors.h +++ b/src/Sensors.h @@ -39,6 +39,8 @@ public: NTC_10K_TEMP = 50, DALLAS_TEMP = 51, BLUETOOTH = 52, + DHT11 = 53, + DHT22 = 54, HEATING_SETPOINT_TEMP = 253, MANUAL = 254, diff --git a/src/SensorsTask.h b/src/SensorsTask.h index 156842b..0124feb 100644 --- a/src/SensorsTask.h +++ b/src/SensorsTask.h @@ -1,6 +1,7 @@ #include #include #include +#include #if USE_BLE #include @@ -50,19 +51,27 @@ protected: class SensorsTask : public LeanTask { public: SensorsTask(bool _enabled = false, unsigned long _interval = 0) : LeanTask(_enabled, _interval) { + // OneWire this->owInstances.reserve(2); this->dallasInstances.reserve(2); this->dallasSearchTime.reserve(2); this->dallasPolling.reserve(2); this->dallasLastPollingTime.reserve(2); + + // DHT + this->dhtLastPollingTime.reserve(2); } ~SensorsTask() { + // OneWire this->dallasInstances.clear(); this->owInstances.clear(); this->dallasSearchTime.clear(); this->dallasPolling.clear(); this->dallasLastPollingTime.clear(); + + // DHT + this->dhtLastPollingTime.clear(); } protected: @@ -70,17 +79,26 @@ protected: const unsigned int wirelessDisconnectTimeout = 600000u; const unsigned short dallasSearchInterval = 60000; const unsigned short dallasPollingInterval = 10000; + const unsigned short dhtPollingInterval = 15000; const unsigned short globalPollingInterval = 15000; #if USE_BLE const unsigned int bleSetDtInterval = 7200000; #endif + // OneWire std::unordered_map owInstances; std::unordered_map dallasInstances; std::unordered_map dallasSearchTime; std::unordered_map dallasPolling; std::unordered_map dallasLastPollingTime; + + // DHT + DHT dhtInstance; + bool dhtIsPolling = false; + std::unordered_map dhtLastPollingTime; + #if USE_BLE + // Bluetooth std::unordered_map bleClients; std::unordered_map bleSubscribed; std::unordered_map bleLastSetDtTime; @@ -116,6 +134,9 @@ protected: this->yield(); } + pollingDhtSensors(); + this->yield(); + if (millis() - this->globalLastPollingTime > this->globalPollingInterval) { cleanDallasInstances(); makeDallasInstances(); @@ -399,6 +420,95 @@ protected: } } + void pollingDhtSensors() { + if (this->dhtIsPolling) { + // busy + return; + } + + for (uint8_t sensorId = 0; sensorId <= Sensors::getMaxSensorId(); sensorId++) { + auto& sSensor = Sensors::settings[sensorId]; + + if (!sSensor.enabled || sSensor.purpose == Sensors::Purpose::NOT_CONFIGURED) { + continue; + } + + if (sSensor.type != Sensors::Type::DHT11 && sSensor.type != Sensors::Type::DHT22) { + continue; + } + + if (this->dhtLastPollingTime.count(sSensor.gpio) && millis() - this->dhtLastPollingTime[sSensor.gpio] < this->dhtPollingInterval) { + continue; + } + + const auto sensorGpio = static_cast(sSensor.gpio); + if (this->dhtInstance.getGpio() != sensorGpio) { + this->dhtInstance.reset(); + this->dhtInstance.onData([this, sensorId](float humidity, float temperature) { + auto& sSensor = Sensors::settings[sensorId]; + + Log.straceln( + FPSTR(L_SENSORS_DHT), F("GPIO %hhu, sensor #%hhu '%s', temp: %.2f, humidity: %.2f%%"), + sSensor.gpio, sensorId, sSensor.name, temperature, humidity + ); + + // set temperature + Sensors::setValueById(sensorId, temperature, Sensors::ValueType::TEMPERATURE, true, true); + + // set humidity + Sensors::setValueById(sensorId, humidity, Sensors::ValueType::HUMIDITY, true, true); + + auto& rSensor = Sensors::results[sensorId]; + if (rSensor.signalQuality < 100) { + rSensor.signalQuality++; + } + + this->dhtLastPollingTime[sSensor.gpio] = millis(); + this->dhtIsPolling = false; + }); + + this->dhtInstance.onError([this, sensorId](DHT::Status status) { + auto& sSensor = Sensors::settings[sensorId]; + + Log.swarningln( + FPSTR(L_SENSORS_DHT), F("GPIO %hhu, sensor #%hhu '%s': failed receiving data (err: %s)"), + sSensor.gpio, sensorId, sSensor.name, DHT::statusToString(this->dhtInstance.getStatus()) + ); + + auto& rSensor = Sensors::results[sensorId]; + if (rSensor.signalQuality > 0) { + rSensor.signalQuality--; + } + + this->dhtLastPollingTime[sSensor.gpio] = millis(); + this->dhtIsPolling = false; + }); + + DHT::Type sType; + if (sSensor.type == Sensors::Type::DHT11) { + sType = DHT::Type::DHT11; + + } else if (sSensor.type == Sensors::Type::DHT11) { + sType = DHT::Type::DHT22; + + } else { + sType = DHT::Type::DHT22; + } + + if (this->dhtInstance.setup(sensorGpio, sType)) { + Log.sinfoln(FPSTR(L_SENSORS_DHT), F("Started on GPIO %hhu"), sSensor.gpio); + + } else { + Log.swarningln(FPSTR(L_SENSORS_DHT), F("Failed to start on GPIO %hhu"), sSensor.gpio); + } + } + + this->dhtIsPolling = this->dhtInstance.poll(); + + break; + } + } + void pollingNtcSensors() { for (uint8_t sensorId = 0; sensorId <= Sensors::getMaxSensorId(); sensorId++) { auto& sSensor = Sensors::settings[sensorId]; diff --git a/src/strings.h b/src/strings.h index 6b3f362..4b44f5c 100644 --- a/src/strings.h +++ b/src/strings.h @@ -24,6 +24,7 @@ const char L_OT_CH2[] PROGMEM = "OT.CH2"; const char L_SENSORS[] PROGMEM = "SENSORS"; const char L_SENSORS_SETTINGS[] PROGMEM = "SENSORS.SETTINGS"; const char L_SENSORS_DALLAS[] PROGMEM = "SENSORS.DALLAS"; +const char L_SENSORS_DHT[] PROGMEM = "SENSORS.DHT"; const char L_SENSORS_NTC[] PROGMEM = "SENSORS.NTC"; const char L_SENSORS_BLE[] PROGMEM = "SENSORS.BLE"; const char L_REGULATOR[] PROGMEM = "REGULATOR"; diff --git a/src/utils.h b/src/utils.h index 0ec8225..6c39e1d 100644 --- a/src/utils.h +++ b/src/utils.h @@ -1927,6 +1927,8 @@ bool jsonToSensorSettings(const uint8_t sensorId, const JsonVariantConst src, Se case static_cast(Sensors::Type::NTC_10K_TEMP): case static_cast(Sensors::Type::DALLAS_TEMP): case static_cast(Sensors::Type::BLUETOOTH): + case static_cast(Sensors::Type::DHT11): + case static_cast(Sensors::Type::DHT22): case static_cast(Sensors::Type::HEATING_SETPOINT_TEMP): case static_cast(Sensors::Type::MANUAL): case static_cast(Sensors::Type::NOT_CONFIGURED): @@ -1943,7 +1945,8 @@ bool jsonToSensorSettings(const uint8_t sensorId, const JsonVariantConst src, Se // gpio if (!src[FPSTR(S_GPIO)].isNull()) { - if (dst.type != Sensors::Type::DALLAS_TEMP && dst.type != Sensors::Type::NTC_10K_TEMP) { + if (dst.type != Sensors::Type::DALLAS_TEMP && dst.type != Sensors::Type::NTC_10K_TEMP && + dst.type != Sensors::Type::DHT11 && dst.type != Sensors::Type::DHT22) { if (dst.gpio != GPIO_IS_NOT_CONFIGURED) { dst.gpio = GPIO_IS_NOT_CONFIGURED; changed = true; @@ -2084,6 +2087,10 @@ void sensorResultToJson(const uint8_t sensorId, JsonVariant dst) { dst[FPSTR(S_BATTERY)] = roundf(rSensor.values[static_cast(Sensors::ValueType::BATTERY)], 1); dst[FPSTR(S_RSSI)] = roundf(rSensor.values[static_cast(Sensors::ValueType::RSSI)], 0); + } else if (sSensor.type == Sensors::Type::DHT11 || sSensor.type == Sensors::Type::DHT22) { + dst[FPSTR(S_TEMPERATURE)] = roundf(rSensor.values[static_cast(Sensors::ValueType::TEMPERATURE)], 3); + dst[FPSTR(S_HUMIDITY)] = roundf(rSensor.values[static_cast(Sensors::ValueType::HUMIDITY)], 3); + } else { dst[FPSTR(S_VALUE)] = roundf(rSensor.values[static_cast(Sensors::ValueType::PRIMARY)], 3); } diff --git a/src_data/locales/cn.json b/src_data/locales/cn.json index 2e74d48..3aebecf 100644 --- a/src_data/locales/cn.json +++ b/src_data/locales/cn.json @@ -248,6 +248,8 @@ "ntcTemp": "NTC 传感器", "dallasTemp": "DALLAS 传感器", "bluetooth": "BLE 传感器", + "dht11": "DHT11 传感器", + "dht22": "DHT22 传感器", "heatSetpointTemp": "Heating, setpoint temp", "manual": "通过 MQTT/API 手动配置", "notConfigured": "未配置" diff --git a/src_data/locales/en.json b/src_data/locales/en.json index 9739e7d..ca9367d 100644 --- a/src_data/locales/en.json +++ b/src_data/locales/en.json @@ -248,6 +248,8 @@ "ntcTemp": "NTC sensor", "dallasTemp": "DALLAS sensor", "bluetooth": "BLE sensor", + "dht11": "DHT11 sensor", + "dht22": "DHT22 sensor", "heatSetpointTemp": "Heating, setpoint temp", "manual": "Manual via MQTT/API", "notConfigured": "Not configured" diff --git a/src_data/locales/it.json b/src_data/locales/it.json index 7e2c9e5..b423e5b 100644 --- a/src_data/locales/it.json +++ b/src_data/locales/it.json @@ -248,6 +248,8 @@ "ntcTemp": "Sensore NTC", "dallasTemp": "Sensore DALLAS", "bluetooth": "Sensore BLE", + "dht11": "Sensore DHT11", + "dht22": "Sensore DHT22", "heatSetpointTemp": "Riscaldamento, temp impostata", "manual": "Manuale via MQTT/API", "notConfigured": "Non configurato" diff --git a/src_data/locales/nl.json b/src_data/locales/nl.json index 8d978be..8140b95 100644 --- a/src_data/locales/nl.json +++ b/src_data/locales/nl.json @@ -227,6 +227,8 @@ "ntcTemp": "NTC-sensor", "dallasTemp": "DALLAS-sensor", "bluetooth": "BLE-sensor", + "dht11": "DHT11-sensor", + "dht22": "DHT22-sensor", "heatSetpointTemp": "Verwarming, insteltemperatuur", "manual": "Handmatig via MQTT/API", "notConfigured": "Niet geconfigureerd" diff --git a/src_data/locales/ru.json b/src_data/locales/ru.json index 3f3a036..5c90269 100644 --- a/src_data/locales/ru.json +++ b/src_data/locales/ru.json @@ -248,6 +248,8 @@ "ntcTemp": "NTC датчик", "dallasTemp": "DALLAS датчик", "bluetooth": "BLE датчик", + "dht11": "DHT11 датчик", + "dht22": "DHT22 датчик", "heatSetpointTemp": "Отопление, температура уставки", "manual": "Вручную через MQTT/API", "notConfigured": "Не сконфигурировано" diff --git a/src_data/pages/sensors.html b/src_data/pages/sensors.html index 47f9986..8d7e435 100644 --- a/src_data/pages/sensors.html +++ b/src_data/pages/sensors.html @@ -118,6 +118,8 @@ + + @@ -271,27 +273,35 @@ } switch(parseInt(event.target.value)) { - // ntc + // NTC10K case 50: parentGpio.classList.remove("hidden"); parentAddress.classList.add("hidden"); address.removeAttribute("pattern"); break; - // dallas + // OneWire case 51: parentGpio.classList.remove("hidden"); parentAddress.classList.remove("hidden"); address.setAttribute("pattern", "([A-Fa-f0-9]{2}:){7}[A-Fa-f0-9]{2}"); break; - // ble + // Bluetooth case 52: parentGpio.classList.add("hidden"); parentAddress.classList.remove("hidden"); address.setAttribute("pattern", "([A-Fa-f0-9]{2}:){5}[A-Fa-f0-9]{2}"); break; + // DHT + case 53: + case 54: + parentGpio.classList.remove("hidden"); + parentAddress.classList.add("hidden"); + address.removeAttribute("pattern"); + break; + // other default: parentGpio.classList.add("hidden");