RobTillaart / DHTNew

Arduino library for DHT11 and DHT22 with automatic sensor recognition
MIT License
96 stars 15 forks source link

Problem: wrong values at negative temperatures an ESP8266 and DHT22 #52

Closed argltuc closed 3 years ago

argltuc commented 3 years ago

I use the DHT22 in combination with an ESP8266 and this lib. When having negative temperatures, the resulting value of getTemperature() is -3278.6 at -0.1°C To fix negatives values a recalculation is nessecary:

#include <stdint.h>
if(temperature < 0)
 {
   temperature = (temperature = (INT16_MAX - temperature*-10)*-0.1; - temperature*-10)*-0.1;
 }

This was tested with Arduino IDE against lib version 0.4.3, installed via Arduino library manager.

RobTillaart commented 3 years ago

Thanks, will come back to this later this week.

RobTillaart commented 3 years ago

Quick question: is it only with esp8266? Or also other platforms? I need to check datasheet on the math.

argltuc commented 3 years ago

I don't have another platform available at the moment. But I did a little analysis. Here my logs, based on your debug hex dump in read() function:

22:39:25.851 -> F2    0000    0.0    01F1    49.7
22:39:27.858 -> 74    8000    0.0    01F3    49.9
22:39:33.835 -> F4    FFFF    -3276.7    01F5    50.1
22:39:49.890 -> F8    FFFD    -3276.5    01FB    50.7
22:40:09.937 -> FF    FFFC    -3276.4    0202    51.4

lets take a look at the bit stream and what should the result:

0000 0000 0000 0000 => 0.0°C
1000 0000 0000 0000 => -0.0°C
1111 1111 1111 1111 => -0.1°C
1111 1111 1111 1110 => -0.2°C
1111 1111 1111 1101 => -0.3°C

But as you see above, the result is -3276.7 and so on.

My interpretation is, that the output of the sensor (and of _bits[]) is in ones complement, while the interpretation of int in the Arduino IDE seems a BCD interpretation.

RobTillaart commented 3 years ago

Thought I had a fix for this in https://github.com/RobTillaart/DHTNew/commit/7a4fd3f49e7e13ea9d07af0fdb181186a9f71a82 Needs investigation...


update 1 - problem confirmed

RobTillaart commented 3 years ago

Test solution ESP32 + DHT22 Looks OK.

17.9    4.0
17.9    3.8
18.0    3.6
18.1    3.5
18.1    3.3
18.2    3.1
18.3    2.9
18.4    2.8
18.4    2.5
18.5    2.4
18.5    2.2
18.7    2.1
18.7    1.9
18.8    1.7
18.9    1.5
18.9    1.3
19.0    1.2
19.0    1.0
19.2    0.9
19.2    0.6
19.3    0.5
19.3    0.3
19.4    0.2
19.5    0.0
19.6    -0.2
19.7    -0.3
19.8    -0.5
19.9    -0.6
20.0    -0.7
20.0    -0.9
20.1    -1.1
20.2    -1.2
20.2    -1.4
20.4    -1.5
21.1    -1.7
23.1    -1.7
25.8    -1.8
28.3    -1.9
30.7    -1.9
33.9    -1.8
36.7    -1.8
39.4    -1.8
42.5    -1.7
45.8    -1.6
48.8    -1.5
52.6    -1.1
RobTillaart commented 3 years ago

@argltuc Please verify solution works for you - https://github.com/RobTillaart/DHTNew/tree/develop

argltuc commented 3 years ago

Yeah, your solution works @ Arduino ESP8266 / DHT22.

another approach, which could have a better performance would be to shift the _bits[] value: instead of

int16_t t    = (_bits[2] * 256 + _bits[3]);

you could use

int16_t t    = ((int16_t)_bits[2] << 8) | _bits[3];

If you like i can test this behavior on Arduino / EPS32 by end of this week.

RobTillaart commented 3 years ago

Compiler takes care of multiplications by integer powers of two. This is the code as tested, so changing it would imply doing all my tests again.

So I will wait after you did your tests, and if faster I will change it.

argltuc commented 3 years ago

Ah, don't worry. i think this is nothing time critical. so i think we just take your solution. But i will do another compatibility test during the next days.

argltuc commented 3 years ago

Just tested latest lib 0.4.4 against ESP32 (ESP-WROOM-32) on Arduino IDE 1.8.13 / win10 with DHT22 and it works perfectly. I did also a time measurment with arduino integrated time function micros(), which lead to no noticeable differences between bit shiftig or multiplication.

So thanks a lot for your bug fix!

RobTillaart commented 3 years ago

You're welcome!

argltuc commented 3 years ago

Hmmm, i put my sensor in a real life scenario, where this issue came back in a specific situation: the transition from positive to negative values. (0.1C ... 0C ... -0C ... -0.1C, Where -0C issued against -3276.8°C as output)

So I check some documents regarding DHT22 once again. As it is described in this document: https://cdn-shop.adafruit.com/datasheets/Digital+humidity+and+temperature+sensor+AM2302.pdf Sensor output should be a sign-magnitude value, so 15 bits as absolute value and MSB as sign bit.

But if this document is right, why is the output of _bits[2]/_bits[3] not in this way?

So lets take a look back to my logs, only the print of _bits[2]/_bits[3] as hex from _read() function:

22:39:25.851 -> F2    0000 // 0°C
22:39:27.858 -> 74    8000 // -0°C
22:39:33.835 -> F4    FFFF // -0.1°C

Reagarding to the linked datasheet, shouldn't value of _bits[2..3] for -0.1°C 0x8001?

RobTillaart commented 3 years ago

Are you saying that the fix is not correct? Or are there 2 different sensors that only differ in the way they code negative values?


You have tested the code and a DHT22, I did the same here, so I am confident the code is correct. In case of conflict between code and comment (= documentation) code always wins.

It might be that the implementation inside the sensor has changed and that the documentation wasn't upgraded accordingly?

The DHT12 (same manufacturer) uses even another scheme - https://robototehnika.ru/file/DHT12.pdf for negative values it sets the MSB of the "decimal" byte not of the "integral" byte

It looks like the aosong does nto have the DHT22 (under that name) in their product range

I expect some day I will need a flag to switch between two different negative temperature algorithms.

argltuc commented 3 years ago

I tested with 3 DHT22 sensors with your fix. they quite work es expected, with one exception: if the sensor measurement is below zero but stil not -0.1°C, then it will fail, since the incomming data is 0x8000 To get this measurement point during testing with a cold pack or something similar, is quite difficult and very randomly. But in a outdoor usecase, where temps change slowly, good measurably.

I can't tell you, if there are still DHT22 or equivalent sensors outside, which use the encoding of negativ values, which will fit to your code before the latest fix.

How ever, for that "-0°C" Problem, i will push a PR.

argltuc commented 3 years ago

The idea with the flag for negative temp algorithms sounds good. Otherweise it could also detected with _bits[2]:

if _bits[2] is 0x80 or 0x81, then incomming value is in sign-magnitude (0x82 would be outside of temp spec of DHT22). But if _bits[2] is 0xFF or 0xFE then incomming negative value is in two's complement.

ada3000 commented 7 months ago

I use the DHT22 in combination with an ESP8266 and this lib. When having negative temperatures, the resulting value of getTemperature() is -3278.6 at -0.1°C To fix negatives values a recalculation is nessecary:

#include <stdint.h>
if(temperature < 0)
 {
   temperature = (temperature = (INT16_MAX - temperature*-10)*-0.1; - temperature*-10)*-0.1;
 }

This was tested with Arduino IDE against lib version 0.4.3, installed via Arduino library manager.

This is a magic of C++, real transform equivalent:

temperature = -temperature - 3276.7

It because AM2301 has inside not DHT22(1) but AHT10.

Home assistant correction:

value_template: "{% if value_json.temp_c <3000 %}{{ (-value_json.temp_c - 3276.7) | round(1)}}{% else %}{{ value_json.temp_c | round(1)}}{% endif %}"