Skip to content

Commit b41a0dc

Browse files
authored
Replace DHT11 implementation (#163)
1 parent c936376 commit b41a0dc

4 files changed

Lines changed: 131 additions & 23 deletions

File tree

.github/workflows/release.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ jobs:
3333
arduino-cli lib install "SerialCommandManager"
3434
arduino-cli lib install "SensorManager"
3535
arduino-cli lib install "SdFat"
36-
arduino-cli lib install "DHT11"
3736
3837
- name: Generate FirmwareVersion.h
3938
run: |

SmartFuseBox/Dht11SensorHandler.h

Lines changed: 127 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
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

5857
protected:
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);

SmartFuseBox/SmartFuseBox.vcxproj

Lines changed: 2 additions & 3 deletions
Large diffs are not rendered by default.

SmartFuseBox/SmartFuseBox.vcxproj.filters

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@
212212
<Filter>Source Files</Filter>
213213
</ClCompile>
214214
<ClCompile Include="OtaManager.cpp">
215-
<Filter>Source Files\BusinessLogic</Filter>
215+
<Filter>Source Files</Filter>
216216
</ClCompile>
217217
</ItemGroup>
218218
<ItemGroup>
@@ -462,11 +462,8 @@
462462
<ClInclude Include="SensorConfigCommandHandler.h">
463463
<Filter>Header Files\SerialCommandHandlers</Filter>
464464
</ClInclude>
465-
<ClInclude Include="FirmwareVersion.h">
466-
<Filter>Header Files</Filter>
467-
</ClInclude>
468465
<ClInclude Include="OtaManager.h">
469-
<Filter>Header Files\BusinessLogic</Filter>
466+
<Filter>Header Files</Filter>
470467
</ClInclude>
471468
</ItemGroup>
472469
<ItemGroup>

0 commit comments

Comments
 (0)