1818#pragma once
1919
2020
21- #include < DHT11 .h>
21+ #include < Arduino .h>
2222
2323#include " Local.h"
2424#include " SystemDefinitions.h"
@@ -42,7 +42,6 @@ class Dht11SensorHandler : public BaseSensor, public BroadcastLoggerSupport
4242 SensorCommandHandler* _sensorCommandHandler;
4343 WarningManager* _warningManager;
4444 const uint8_t _sensorPin;
45- DHT11 _dht11Sensor;
4645 float _humidityOffset;
4746 float _temperatureOffset;
4847 float _humidity;
@@ -56,37 +55,152 @@ class Dht11SensorHandler : public BaseSensor, public BroadcastLoggerSupport
5655#endif
5756
5857protected:
58+ // ── Return codes ─────────────────────────────────────────────────────────
59+ static constexpr int Dht11Ok = 0 ;
60+ static constexpr int Dht11Timeout = -1 ;
61+ static constexpr int Dht11Checksum = -2 ;
62+
63+ // ── DHT11 one-wire protocol constants ────────────────────────────────────
64+
65+ // Spec: host pulls bus LOW for a minimum of 18 ms to trigger the sensor.
66+ static constexpr uint8_t Dht11StartLowMs = 18 ;
67+
68+ // Spec: host drives HIGH for 20–40 µs before releasing to INPUT so the
69+ // sensor can detect the rising edge. 40 µs satisfies the upper bound.
70+ static constexpr uint8_t Dht11StartHighUs = 40 ;
71+
72+ // Spec: 40 bits (5 bytes) per reading, transmitted MSB-first within each byte.
73+ static constexpr uint8_t Dht11BitCount = 40 ;
74+ static constexpr uint8_t Dht11ByteCount = 5 ;
75+ static constexpr uint8_t Dht11BitsPerByte = 8 ;
76+
77+ // Spec: each bit is preceded by a 50 µs LOW separator, then a HIGH pulse:
78+ // bit '0' → 26–28 µs HIGH
79+ // bit '1' → 70 µs HIGH
80+ // 40 µs sits unambiguously between the two ranges and is the standard threshold.
81+ static constexpr uint8_t Dht11BitOneThresholdUs = 40 ;
82+
83+ // Spec: byte layout of the 5 transmitted data bytes.
84+ static constexpr uint8_t Dht11HumIntIdx = 0 ; // humidity integer part
85+ static constexpr uint8_t Dht11HumDecIdx = 1 ; // humidity decimal part (always 0 on DHT11)
86+ static constexpr uint8_t Dht11TmpIntIdx = 2 ; // temperature integer part
87+ static constexpr uint8_t Dht11TmpDecIdx = 3 ; // temperature decimal part (always 0 on DHT11)
88+ static constexpr uint8_t Dht11ChecksumIdx = 4 ; // checksum = (byte0+byte1+byte2+byte3) & 0xFF
89+
90+ // Spec: decimal byte encodes tenths (e.g. decimal byte 5 → +0.5 °C / +0.5 %).
91+ static constexpr float Dht11DecimalScale = 0 .1f ;
92+
93+ // Iteration cap for every bounded spin-wait in readDht11().
94+ // digitalRead() on ESP32 at 240 MHz takes ~0.5–1 µs, so 10 000 iterations
95+ // gives a ~5–10 ms hard ceiling per transition — well above any legitimate
96+ // DHT11 signal period (max 80 µs response, 70 µs bit-HIGH) yet fast enough
97+ // to recover from a fault without stalling the main loop.
98+ static constexpr unsigned int Dht11MaxLoopCount = 10000 ;
99+
100+ /* *
101+ * @brief Read temperature and humidity from a DHT11 sensor.
102+ *
103+ * Every spin-wait is bounded by Dht11MaxLoopCount so the function can never
104+ * hang, regardless of sensor state or electrical faults. The 18 ms host
105+ * start-signal uses delay() which on ESP32/FreeRTOS calls vTaskDelay(),
106+ * yielding to the scheduler — not a hard block.
107+ *
108+ * Adapted from https://github.com/adidax/dht11 which is distributed under GPL v3
109+ *
110+ * @param pin GPIO data pin connected to the DHT11.
111+ * @param outTemperature Receives temperature in °C on success.
112+ * @param outHumidity Receives relative humidity (%) on success.
113+ * @return Dht11Ok, Dht11Timeout, or Dht11Checksum.
114+ */
115+ static int readDht11 (uint8_t pin, float & outTemperature, float & outHumidity)
116+ {
117+ uint8_t bits[Dht11ByteCount] = {};
118+ uint8_t cnt = Dht11BitsPerByte - 1 ; // start at MSB (bit 7)
119+ uint8_t idx = 0 ;
120+
121+ // Host start signal: pull LOW ≥ Dht11StartLowMs then drive HIGH briefly before releasing
122+ pinMode (pin, OUTPUT);
123+ digitalWrite (pin, LOW);
124+ delay (Dht11StartLowMs);
125+ digitalWrite (pin, HIGH);
126+ delayMicroseconds (Dht11StartHighUs);
127+ pinMode (pin, INPUT);
128+
129+ // Sensor acknowledgement (80 µs LOW + 80 µs HIGH) — every loop is bounded
130+ unsigned int loopCnt = Dht11MaxLoopCount;
131+ while (digitalRead (pin) == LOW)
132+ if (loopCnt-- == 0 ) return Dht11Timeout;
133+
134+ loopCnt = Dht11MaxLoopCount;
135+ while (digitalRead (pin) == HIGH)
136+ if (loopCnt-- == 0 ) return Dht11Timeout;
137+
138+ // Read Dht11BitCount bits; each bit = 50 µs LOW separator + variable-width HIGH
139+ for (int i = 0 ; i < Dht11BitCount; i++)
140+ {
141+ loopCnt = Dht11MaxLoopCount;
142+ while (digitalRead (pin) == LOW)
143+ if (loopCnt-- == 0 ) return Dht11Timeout;
144+
145+ unsigned long t = micros ();
146+
147+ loopCnt = Dht11MaxLoopCount;
148+ while (digitalRead (pin) == HIGH)
149+ if (loopCnt-- == 0 ) return Dht11Timeout;
150+
151+ // HIGH pulse longer than Dht11BitOneThresholdUs µs decodes as logic '1'
152+ if ((micros () - t) > Dht11BitOneThresholdUs)
153+ bits[idx] |= (1 << cnt);
154+
155+ if (cnt == 0 )
156+ {
157+ cnt = Dht11BitsPerByte - 1 ;
158+ idx++;
159+ }
160+ else cnt--;
161+ }
162+
163+ // Verify checksum: low byte of the sum of the four data bytes
164+ uint8_t sum = (bits[Dht11HumIntIdx] + bits[Dht11HumDecIdx]
165+ + bits[Dht11TmpIntIdx] + bits[Dht11TmpDecIdx]) & 0xFF ;
166+
167+ if (bits[Dht11ChecksumIdx] != sum) return Dht11Checksum;
168+
169+ outHumidity = bits[Dht11HumIntIdx] + (bits[Dht11HumDecIdx] * Dht11DecimalScale);
170+ outTemperature = bits[Dht11TmpIntIdx] + (bits[Dht11TmpDecIdx] * Dht11DecimalScale);
171+ return Dht11Ok;
172+ }
173+
59174 void initialize () override
60175 {
61176 }
62177
63178 unsigned long update () override
64179 {
65180 sendDebug (" Reading DHT11 sensor..." , _name);
66- int rawTemp = 0 ;
67- int rawHumidity = 0 ;
68- int result = _dht11Sensor.readTemperatureHumidity (rawTemp, rawHumidity);
69181
70- if (result != 0 )
182+ float temperature = 0 .0f ;
183+ float humidity = 0 .0f ;
184+ int result = readDht11 (_sensorPin, temperature, humidity);
185+
186+ if (result != Dht11Ok)
71187 {
72188 if (_warningManager && !_warningManager->isWarningActive (WarningType::TemperatureSensorFailure))
73189 {
74190 _warningManager->raiseWarning (WarningType::TemperatureSensorFailure);
75191 char buffer[48 ];
76- snprintf (buffer, sizeof (buffer), " DHT11 read error: %s" , DHT11::getErrorString (result).c_str ());
192+ const char * errStr = (result == Dht11Checksum) ? " checksum failure" : " timeout" ;
193+ snprintf (buffer, sizeof (buffer), " DHT11 read error: %s" , errStr);
77194 sendError (buffer, " DHT11 Error" );
78195 }
79-
80196 return TempHumidityCheckMs;
81197 }
82198
83199 if (_warningManager && _warningManager->isWarningActive (WarningType::TemperatureSensorFailure))
84- {
85200 _warningManager->clearWarning (WarningType::TemperatureSensorFailure);
86- }
87201
88- _humidity = static_cast < float >(rawHumidity) + _humidityOffset;
89- _celsius = static_cast < float >(rawTemp) + _temperatureOffset;
202+ _humidity = humidity + _humidityOffset;
203+ _celsius = temperature + _temperatureOffset;
90204
91205 if (_messageBus)
92206 {
@@ -116,10 +230,9 @@ class Dht11SensorHandler : public BaseSensor, public BroadcastLoggerSupport
116230 Dht11SensorHandler (MessageBus* messageBus, BroadcastManager* broadcastManager, SensorCommandHandler* sensorCommandHandler,
117231 WarningManager* warningManager, uint8_t sensorPin, float humidityOffset, float temperatureOffset, const char * name = " Dht11" )
118232 : BaseSensor(name), BroadcastLoggerSupport(broadcastManager), _messageBus(messageBus), _sensorCommandHandler(sensorCommandHandler),
119- _warningManager (warningManager), _sensorPin(sensorPin), _dht11Sensor(sensorPin), _humidityOffset(humidityOffset),
233+ _warningManager (warningManager), _sensorPin(sensorPin), _humidityOffset(humidityOffset),
120234 _temperatureOffset(temperatureOffset), _humidity(0 .0f ), _celsius(0 .0f )
121235 {
122- _dht11Sensor.setDelay (0 );
123236#if defined(MQTT_SUPPORT)
124237 snprintf (_slugTemp, sizeof (_slugTemp), " %s_temperature" , _safeSlug);
125238 snprintf (_slugHumidity, sizeof (_slugHumidity), " %s_humidity" , _safeSlug);
0 commit comments