Skip to content

Commit 305918d

Browse files
committed
sns(dht): different number representations for dht22
sometimes sign-magnitude, sometimes twos-complement resolve #2638
1 parent ff6d86c commit 305918d

File tree

1 file changed

+101
-35
lines changed

1 file changed

+101
-35
lines changed

code/espurna/sensors/DHTSensor.h

+101-35
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@
88
#pragma once
99

1010
#include "../gpio.h"
11+
#include "../system_time.h"
1112
#include "../utils.h"
13+
1214
#include "BaseSensor.h"
1315

1416
enum class DHTChipType {
@@ -28,15 +30,20 @@ enum class DHTChipType {
2830
#define DHT_CHIP_AM2301 DHTChipType::AM2301
2931
#define DHT_CHIP_SI7021 DHTChipType::SI7021
3032

31-
int dhtchip_to_number(DHTChipType chip) {
32-
switch (chip) {
33+
namespace {
34+
35+
int dht_chip_to_number(DHTChipType type) {
36+
switch (type) {
3337
case DHTChipType::DHT11:
3438
return 11;
39+
3540
case DHTChipType::DHT12:
3641
return 12;
42+
3743
case DHTChipType::DHT21:
3844
case DHTChipType::AM2301:
3945
return 21;
46+
4047
case DHTChipType::DHT22:
4148
case DHTChipType::SI7021:
4249
return 22;
@@ -45,6 +52,91 @@ int dhtchip_to_number(DHTChipType chip) {
4552
return -1;
4653
}
4754

55+
float dht_humidity(DHTChipType type, std::array<uint8_t, 2> pair) {
56+
// binary representation varies between the original chip and its copies
57+
// but, its never negative, so no reason to do any conversions for signed numbers
58+
float out;
59+
60+
switch (type) {
61+
case DHT_CHIP_DHT11:
62+
out = pair[0];
63+
break;
64+
65+
case DHT_CHIP_DHT12:
66+
out = pair[0];
67+
out += pair[1] * 0.1f;
68+
break;
69+
70+
case DHT_CHIP_DHT21:
71+
case DHT_CHIP_DHT22:
72+
case DHT_CHIP_AM2301:
73+
case DHT_CHIP_SI7021:
74+
out = ((pair[0] << 8) | pair[1]) * 0.1f;
75+
break;
76+
}
77+
78+
return out;
79+
}
80+
81+
float dht_temperature(DHTChipType type, std::array<uint8_t, 2> pair) {
82+
// binary representation varies between the original chip and its copies
83+
// by default, check for generic sign-magnitude
84+
constexpr auto MagnitudeMask = uint8_t{ 0b1111111 };
85+
constexpr auto SignMask = uint8_t{ 0b10000000 };
86+
87+
// in case it is negative and looks like twos-complement, value can be c/p into memory as-is
88+
// plus, it is enough to only check the sign bit neighbour. possible values are around [0...800]
89+
constexpr auto NegativeTwoComplementMask = uint8_t{ 0b11000000 };
90+
91+
float out;
92+
93+
switch (type) {
94+
case DHT_CHIP_DHT11:
95+
out = pair[0];
96+
break;
97+
98+
case DHT_CHIP_DHT12:
99+
out = pair[0] & MagnitudeMask;
100+
if (pair[0] & SignMask) {
101+
out = -out;
102+
}
103+
104+
out = out * 0.1f;
105+
break;
106+
107+
case DHT_CHIP_DHT21:
108+
case DHT_CHIP_DHT22:
109+
case DHT_CHIP_AM2301:
110+
case DHT_CHIP_SI7021:
111+
if ((pair[0] & NegativeTwoComplementMask) == NegativeTwoComplementMask) {
112+
int16_t tmp;
113+
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
114+
std::swap(pair[0], pair[1]);
115+
#endif
116+
std::memcpy(&tmp, pair.data(), sizeof(tmp));
117+
out = tmp;
118+
} else {
119+
// positive numbers are the same, no conversion needed
120+
pair[0] &= MagnitudeMask;
121+
out = (pair[0] << 8) | pair[1];
122+
if (pair[0] & SignMask) {
123+
out = -out;
124+
}
125+
}
126+
127+
out *= 0.1f;
128+
break;
129+
}
130+
131+
return out;
132+
}
133+
134+
bool dht_checksum(const std::array<uint8_t, 5>& data) {
135+
return data[4] == ((data[0] + data[1] + data[2] + data[3]) & 0xFF);
136+
}
137+
138+
} // namespace
139+
48140
class DHTSensor : public BaseSensor {
49141

50142
public:
@@ -82,7 +174,7 @@ class DHTSensor : public BaseSensor {
82174
}
83175

84176
int getType() const {
85-
return dhtchip_to_number(_type);
177+
return dht_chip_to_number(_type);
86178
}
87179

88180
DHTChipType getChipType() const {
@@ -132,7 +224,7 @@ class DHTSensor : public BaseSensor {
132224
String description() const override {
133225
char buffer[20];
134226
snprintf_P(buffer, sizeof(buffer),
135-
"DHT%d @ GPIO%hhu", dhtchip_to_number(_type), _gpio);
227+
"DHT%d @ GPIO%hhu", dht_chip_to_number(_type), _gpio);
136228
return String(buffer);
137229
}
138230

@@ -244,49 +336,23 @@ class DHTSensor : public BaseSensor {
244336
espurna::duration::Milliseconds(250));
245337
}
246338

247-
Data dhtData{};
339+
Data data{};
248340

249341
noInterrupts();
250-
_read_critical(dhtData);
342+
_read_critical(data);
251343
interrupts();
252344

253345
if (_error != SENSOR_ERROR_OK) {
254346
return;
255347
}
256348

257-
// Verify checksum
258-
if (dhtData[4] != ((dhtData[0] + dhtData[1] + dhtData[2] + dhtData[3]) & 0xFF)) {
349+
if (!dht_checksum(data)) {
259350
_error = SENSOR_ERROR_CRC;
260351
return;
261352
}
262353

263-
// Get humidity from Data[0] and Data[1]
264-
if (_type == DHT_CHIP_DHT11) {
265-
_humidity = dhtData[0];
266-
} else if (_type == DHT_CHIP_DHT12) {
267-
_humidity = dhtData[0];
268-
_humidity += dhtData[1] * 0.1;
269-
} else {
270-
_humidity = dhtData[0] * 256 + dhtData[1];
271-
_humidity /= 10;
272-
}
273-
274-
// Get temp from Data[2] and Data[3]
275-
if (_type == DHT_CHIP_DHT11) {
276-
_temperature = dhtData[2];
277-
} else if (_type == DHT_CHIP_DHT12) {
278-
_temperature = (dhtData[2] & 0x7F);
279-
_temperature += dhtData[3] * 0.1;
280-
if (dhtData[2] & 0x80) {
281-
_temperature *= -1;
282-
}
283-
} else {
284-
_temperature = (dhtData[2] & 0x7F) * 256 + dhtData[3];
285-
_temperature /= 10;
286-
if (dhtData[2] & 0x80) {
287-
_temperature *= -1;
288-
}
289-
}
354+
_humidity = dht_humidity(_type, {data[0], data[1]});
355+
_temperature = dht_temperature(_type, {data[2], data[3]});
290356

291357
_last_ok = TimeSource::now();
292358

0 commit comments

Comments
 (0)