Читаем показания DHT без библиотеки.

Pyotr
Offline
Зарегистрирован: 12.03.2014
При опросе двух датчиков DHT21 с помощью библиотек заметил, что скетч "тормозится". Чтение одного DHT занимает до 250 мс. По даташиту опрос датчика должен уложиться примерно в 5 мс. 
Как происходит общение МК с DHT21-DHT22. Для начинающих:)
Датчик молчит-на линии высокий уровень. МК прижимает линию к земле минимум на 800 мкс. Далее отпускает линию и подтягивающий резистор тянет её вверх. Заметьте, не МК тянет вверх!! МК переводит пин на ввод, а не устанавливает "1" на пине. В даташитах по этому поводу очень "скользко" . Здесь работает схемное "И": высокий уровень на линии если: И МК не тянет вниз ("1"), И DHT не тянет вниз ("1"). Низкий уровень на линии если: МК тянет вниз или DHT тянет вниз.
От импульса отрицательной полярности DHT "просыпается" и через 20-40 мкс (даже до 200 мкс) притягивает линию к земле на 80 мкс. Затем отпускает на 80 мкс. Снова прижимает на 50 мкс и отпускает. 
Начинается первый импульс положительной полярности, несущий информацию "0" или "1". Длительность в 26 мкс соответствует "0", а 70 мкс - "1". Всё что нужно это прочитать 40 таких положительных импульсов различной длительности.
Скетч прилагаю.

 

 

Pyotr
Offline
Зарегистрирован: 12.03.2014

Да, по даташиту DHT21-22 нужно опрашивать не чаще раза в 2сек. 
Поэтому в строке 5 нужно interval=2000;  а в 16 - prevMillis +=2000; или понятней - prevMillis +=interval;
Но и при опросе каждую сек показания датчиков не повторяются при изменении температуры и влажности.

SunX
SunX аватар
Offline
Зарегистрирован: 04.10.2014

Я читаю данные с DHT22 при помощи функции pulseIn, все выглядит гораздо лучше, понятнее и никакой магии. Если нужно - могу свою переделанную библиотеку оно Adafruit дать, вытащите оттуда функцию.

Pyotr
Offline
Зарегистрирован: 12.03.2014

SunX, выкладывайте свою библиотеку, раз уж лучше и понятней. Это замечательно, когда много примеров хороших и разных...
А про какую магию говорите?

SunX
SunX аватар
Offline
Зарегистрирован: 04.10.2014

Вот: https://github.com/SunAngel/DHT-sensor-library

Я вот про эту counter-магию:

01counter = 0;
02while (digitalRead(pin) == state) {//state==1 первый проход
03  counter++;
04  delayMicroseconds(4);//if 4 mkc count==3-7
05  if (counter > 20)  break;
06}//end while
07state = !state;//digitalRead(_pin);
08if (counter > 20) break;   
09if ((i > 3) && !(i & 1)) {//i%2 записать каждый бит в байты хранения
10  data[j >> 3] <<= 1;  //читаем с 5(i=4) состояния(1,0,1,0,?,0,?,0,...?)
11  if (counter > count)  data[j>>3] |= 1;//counter>3-10 дальше ошибки
12  j++;
13}

Оно все работает и наверное даже правильно, но зато совершенно непонятно, почему все так.

PS: Примеры в моей измененной библиотеке естественно не работают, так как их я не переписывал. Так же в этой либе возвращаются температура и влажность умноженные на 10, что бы не использовать float и для других нужных мне вещей, но, думаю, Вы с этим спокойно справитесь.

Pyotr
Offline
Зарегистрирован: 12.03.2014

Очень знакомая библиотека)) Если её ещё подправить, будет лучше,  меньше тормозить. Все отличия в pulseIn и заключаются.
Про "магию" раскажу как время будет.

SunX
SunX аватар
Offline
Зарегистрирован: 04.10.2014

Ну не удивлюсь, если Adafruit ее в свою очередь где-то еще потырили %) Ну или кто-то потырил у них.

Как эта counter-магия работает я вполне понимаю,  просто не понимаю, зачем так извращаться =).

Pyotr
Offline
Зарегистрирован: 12.03.2014

SunX пишет:

Я вот про эту counter-магию:

01counter = 0;
02while (digitalRead(pin) == state) {//state==1 первый проход
03  counter++;
04  delayMicroseconds(4);//if 4 mkc count==3-7
05  if (counter > 20)  break;
06}//end while
07state = !state;//digitalRead(_pin);
08if (counter > 20) break;   
09if ((i > 3) && !(i & 1)) {//i%2 записать каждый бит в байты хранения
10  data[j >> 3] <<= 1;  //читаем с 5(i=4) состояния(1,0,1,0,?,0,?,0,...?)
11  if (counter > count)  data[j>>3] |= 1;//counter>3-10 дальше ошибки
12  j++;
13}

Оно все работает и наверное даже правильно, но зато совершенно непонятно, почему все так.

В цикле while задержка складывается из delayMicrosecond(4) ==4mks (даже чуть больше) и чтение пина digitalRead(pin) == около 4-6 мкс, которые в сумме дают около 9мкс. За 6 проходов while получаем задержку около 50мкс. В это время положительный импульс 26мкс, соответствующий "0" уже закончился, а 70мкс импульс "1" ещё не закончился. 

С pulseIn функция  readingDHT(DHT_pin)  выглядит попроще. 

Коммент в строке 18 читать как: сдвигаем влево на 1, а в младший бит пишется "0"

Starter381
Offline
Зарегистрирован: 12.12.2020

Попробовал оба варианта кода — со старой функцией readingDHT, с упрощённым вариантом с pulseIn. В обоих случаях hum1 и temp1 (у меня один датчик) равны нулю. Пробовал использовать также библиотеку (скетч), с ней всё работает корректно. Самое странное, что даже если добавить в этот код посылку сообщения об ошибке, оно никогда не будет послано — код считает, что всё в порядке. Помогите!

nik182
Offline
Зарегистрирован: 04.05.2015

Если зарядить в блюпиле таймер

то можно получить аппаратно, без задержек вывод с датчика и его обработать.

Про этом получение данных не зависит от типа 11 или 22. Различается только обработка. Одна проблема - нога входа таймера фиксирована РА7.

Starter381
Offline
Зарегистрирован: 12.12.2020

У меня появилась мысль, что, возможно, у Петра в скетче есть ошибка контрольной суммы, которая проявляется тем, что humidity и temperature равны нулю.

nik182
Offline
Зарегистрирован: 04.05.2015

Мысль то откуда? Что выводить то если КС не сошлась? Некоторые выводят NaN.

Pyotr
Offline
Зарегистрирован: 12.03.2014

Давно это было... С тех пор и не использую эти датчики. Может прерывания мешают? Пробовали запрещать перед опросом датчика?

Starter381
Offline
Зарегистрирован: 12.12.2020

Пробовал, не помогло...

Joog
Offline
Зарегистрирован: 03.01.2020
01#include <delay.h>
02 
03#define DHT_PORT  PORTD
04#define DHT_DDR   DDRD
05#define DHT_PIN   PIND
06#define DHT_BIT   5
07 
08unsigned int  read_dht_hum()
09{
10int temp;
11unsigned char i,j;
12unsigned char data[5];
13 
14//=============MCU send START
15DHT_DDR|=(1<<DHT_BIT);   //pin as output
16DHT_PORT&=~(1<<DHT_BIT); //0
17delay_ms(18);
18DHT_PORT|=(1<<DHT_BIT); //1
19DHT_DDR&=~(1<<DHT_BIT); //pin as input
20//=============check DHT11 response
21delay_us(50);
22if (DHT_PIN&(1<<DHT_BIT))
23    return 0;
24delay_us(80);
25if (!(DHT_PIN&(1<<DHT_BIT)))
26    return 0;
27//===============receive 40 data bits
28while (DHT_PIN&(1<<DHT_BIT));
29for (j=0; j<5; j++)
30    {
31    data[j]=0;
32    for(i=0; i<8; i++)
33        {
34        while (!(DHT_PIN&(1<<DHT_BIT)));
35        delay_us (30);
36        if (DHT_PIN&(1<<DHT_BIT))
37            data[j]|=1<<(7-i);
38        while (DHT_PIN&(1<<DHT_BIT));
39        }
40    }
41if ((unsigned char)(data[0]+data[1]+data[2]+data[3])!=data[4]) //checksum
42    return 0;
43    temp = (data[0]<<8)+(data[2]); //data[0]-humidity, data[2]-temperature
44return temp;
45}

Библиотека для CVAVR, но в принципе несложно перевести в Arduino

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

Интересно, что оно вот здесь возвращает?  И почему так? 

43    temp = (data[0]<<8)+(data[2]); //data[0]-humidity, data[2]-temperature

    return temp;

если temp обьявлен как int

sadman41
Offline
Зарегистрирован: 19.10.2016

DetSimen пишет:

Интересно, что оно вот здесь возвращает?  И почему так? 

43    temp = (data[0]<<8)+(data[2]); //data[0]-humidity, data[2]-temperature

Если ардуина оригинальная, то она сама разберется, а если китайская - то...

Joog
Offline
Зарегистрирован: 03.01.2020

Давно работал, забыл уже. Это для DHT11, он дает только целую часть процента влажности , а дробная равно 0.

Процесс коммуникации

  1. Микроконтроллер подаёт стартовый сигнал — прижимает шину к земле на 18-20 мс;
  2. После этого контроллер отпускает линию и следить за уровнем на ней, примерно 20-40 мкс;
  3. Датчик, обнаружив сигнал и подождав пока уровень снова станет высоким, сам прижимает шину к земле на 80 мкс, за это время делаются измерения и преобразование результатов;
  4. Затем DHT11 отпускает линию на 80 мкс, что указывает на то, что он готов отправить данные;
  5. Затем он отправляет 40 бит данных. Перед отправкой каждого бита датчик прижимает шину к земле на 50 мкс, за которым следует 26-28 мкс для «0» или 70 мкс для «1»;
  6. По завершении связи линия вытягивается подтягивающим резистором и переходит в состояние ожидания.

DHT11/DHT22 - Процесс коммуникации (protocol)

Формат данных DHT11/DHT22

Когда датчик влажности и температуры отправляет данные, он сначала отправляет MSb (Most Significant Bit) — старший значащий бит. Данные от датчика передаются в виде посылки, состоящих из 40 бит данных — это 5 байт из которых первых два влажность, следующие 2 температура и байт четности. Байт четности равен сумме предыдущих байт. 1 и 2 байт содержат соответственно целую и дробную часть информации о влажности, 3 и 4 байт содержат целую и дробную часть информации о температуре. Для датчика DHT11 2-й и 4-й байты всегда ноль. Значение этих байтов заключается в следующем:

  • 1-й байт: относительная влажность — целая часть в %;
  • 2-й байт: десятая часть относительной влажности в % (ноль для DHT11);
  • 3-й байт: целая часть температуры в °C;
  • 4-й байт: десятая часть температуры в °C (ноль для DHT11);
  • 5-й байт: контрольная сумма (последние 8 бит {1-й байт + 2-й байт + 3-й байт + 4-й байт})
b707
Offline
Зарегистрирован: 26.05.2017

Joog - как обычно...

К чему эти цитаты, вы сами головой думать умеете?

Вас спрашивают, что получится, если сложить влажность и температуру ? :)

Joog
Offline
Зарегистрирован: 03.01.2020

b707 пишет:

Joog - как обычно...

К чему эти цитаты, вы сами головой думать умеете?

Вас спрашивают, что получится, если сложить влажность и температуру ? :)

Кому нужно разобраться, приведённого выше достаточно. Но вы из той породы кому надо на "пальцах" всё показывать и ещё и несколько раз.

unsigned char temp=0, hum=0;
int  ca=0;

        ca = read_dht_hum();                      //читаем влажность и температуру c DHT11 из подпрограммы
        hum = ((ca >>8) & 0xFF);               // Разрезаем на байты старший байт влажность
        temp = (ca & 0xFF);                       // Разрезаем на байты младший байт температура

Можете просто из подпрограммы забрать char data[5]; если работаете с DHT22  и вам надо цифры после запятой

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Там не сложение температуры с влажностью, там возвращается двухбайтовая величина, в старшем байте которой находится однобайтная влажность, а в младшем - однобайтная температура. Непонятно только, почему переменная описана как знаковая величина.

b707
Offline
Зарегистрирован: 26.05.2017

andriano пишет:

Непонятно только, почему переменная описана как знаковая величина.

чтобы полной грудью ощутить разницу битового сдвига между знаковым и беззнаковым инт

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

Да ладно бы они в старшую часть температуру пхали, она тоже знаковая, потом её только выкусить и использовать, так нет жеж, там беззнаковая влажность. 

Starter381
Offline
Зарегистрирован: 12.12.2020

Я также пробовал использовать эту библиотеку, и обсуждаю сейчас проблему с её разработчиком. Ни одна библиотека, которая читает DHT22 функцией, не работает.

Joog
Offline
Зарегистрирован: 03.01.2020

Starter381 загрузите мой скетч, и посмотрите ошибки от датчика где происходят? Обязательно проверьте резистор от вывода Data к +5в, должен быть 10 килоом. Чем он меньше тем больше ошибок чтения. Data DHT на 5 пин Arduino/

01unsigned char temp=0, hum=0;
02int  ca=0;
03unsigned char data[5];
04 
05unsigned int  read_dht_hum()    //подпрограмма работы с DHT
06{
07#define DHT_PORT  PORTD
08#define DHT_DDR   DDRD
09#define DHT_PIN   PIND
10#define DHT_BIT   5              //по ардуиновски D5 
11int temp;
12unsigned char i,j;
13ReLoad:                           //метка для ошибок
14//=============MCU send START
15DHT_DDR|=(1<<DHT_BIT);            //выход
16DHT_PORT&=~(1<<DHT_BIT);          //низкий уровень, подтягиваем линию, разбудим датчик
17delay(18);                        //18 мс по условиям документации.
18DHT_PORT|=(1<<DHT_BIT);           //отпускаем линию
19DHT_DDR&=~(1<<DHT_BIT);           //пин как выход
20//============= инциализация DHT
21delayMicroseconds(50);            //задержка по условию
22if (DHT_PIN&(1<<DHT_BIT)){Serial.println("NO init "); goto ReLoad;} //датчик должен ответить 0  
23delayMicroseconds(80);
24if (!(DHT_PIN&(1<<DHT_BIT))){Serial.println("Creazy DHT ");goto ReLoad;} //по истечению 80 мкс, датчик должен отпустить шину
25//===============Приём 40 бит данных
26while (DHT_PIN&(1<<DHT_BIT));    //ждем пока на шине появится 1 
27for (j=0; j<5; j++)
28    {
29    data[j]=0;
30    for(i=0; i<8; i++)
31        {
32        while (!(DHT_PIN&(1<<DHT_BIT))); //ждем когда датчик отпустит шину
33        delayMicroseconds(30);           //задержка высокого уровня при 0 30 мкс
34        if (DHT_PIN&(1<<DHT_BIT))        //если по истечению времени сигнал на линии высокий, значит передается 1
35            data[j]|=1<<(7-i);           //тогда i-й бит устанавливаем 1
36        while (DHT_PIN&(1<<DHT_BIT));    // ждем окончание 1
37        }
38    }
39if ((unsigned char)(data[0]+data[1]+data[2]+data[3])!=data[4]) //checksum
40    {Serial.println("checksum Error ");goto ReLoad;}
41    temp = (data[0]<<8)+(data[2]); //data[0]-humidity, data[2]-temperature
42return temp; //двухбайтовая величина, в старшем байте которой находится однобайтная влажность, а в младшем - однобайтная температура.
43}            //конец подрограммы
44 
45 
46void setup()
47{
48  Serial.begin(9600);
49}
50 
51void loop() {
52   
53        ca = read_dht_hum();      //читаем влажность и температуру c DHT11 из подпрограммы
54        hum = ((ca >>8) & 0xFF);  // Разрезаем на байты старший байт влажность
55        temp = (ca & 0xFF);       // Разрезаем на байты младший байт температура
56  Serial.print(ca, HEX);          //вывод полученного результата в шеснадцатеричном формате
57  Serial.print(" ");
58  Serial.print(data [0], HEX);    //относительная влажность — целая часть в %;
59  Serial.print(" ");             
60  Serial.print(data [1], HEX);    //десятая часть относительной влажности в % (ноль для DHT11);
61  Serial.print(" ");
62  Serial.print(data [2], HEX);    //целая часть температуры в °C;
63  Serial.print(" ");
64  Serial.print(data [3], HEX);    //десятая часть температуры в °C (ноль для DHT11);
65  Serial.print(" "); 
66  Serial.println(data [4], HEX);  //контрольная сумма (последние 8 бит {1-й байт + 2-й байт + 3-й байт + 4-й байт})
67   
68  delay(1000);   //хватает задержки в 1 сек    
69}

 

sadman41
Offline
Зарегистрирован: 19.10.2016

Зачем клеить, а затем резать влажность и температуру, если она никуда не выводится? 

"goto ReLoad" вообще за гранью моего понимания.

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

Думаю, это Северныйветер в другой реинкарнации

b707
Offline
Зарегистрирован: 26.05.2017

со знаковыми и беззнаковыми стало еще интереснее...

Из функции с возвращаемым значением unsigned int

1unsigned int  read_dht_hum()

возвращаем знаковый int temp, который в loop() преобразуем в переменную опять же с именем temp, но уже типа байт :)

Но с точки зрения синтаксиса ошибок нет ... :)))) Хотя несоответвие типа функции и возвращаемого значения должно, по идее, подсвечиваться предупреждением

 

Starter381
Offline
Зарегистрирован: 12.12.2020

Вот что получил:

Joog
Offline
Зарегистрирован: 03.01.2020

Значит датчик отвечает, работает. Он у вас на улице? Влажность 1,3% и температура +0,25. Контрольная сумма совпадает.

первые 16 бит - влажность, следующие 16 бит - температура, последние 8 бит - контрольная сумма, где
- значения влажности в %, значения температуры в °C.
- первый бит температуры это её знак («0» значит «+», «1» значит «-»);
 
 

 

Starter381
Offline
Зарегистрирован: 12.12.2020

Это чрезвычайно странные показания датчика. У меня датчик расположен в тёплой комнате; другой скетч сообщает температуру 24.3°C, влажность 24.6%!

Joog
Offline
Зарегистрирован: 03.01.2020

Starter381 Нашел в этой статье, оказывается всё таки они разные DHT11 и DHT22 в плане расчета конечных показаний. Немного переделал, у меня только DHT11, попробуйте.

01unsigned char data[5];
02float temperature, humidity;
03 
04unsigned int  read_dht_hum()    //подпрограмма работы с DHT
05{
06#define DHT_PORT  PORTD
07#define DHT_DDR   DDRD
08#define DHT_PIN   PIND
09#define DHT_BIT   5              //по ардуиновски D5 
10int temp;
11unsigned char i,j;
12ReLoad:                           //метка для ошибок
13//=============MCU send START
14DHT_DDR|=(1<<DHT_BIT);            //выход
15DHT_PORT&=~(1<<DHT_BIT);          //низкий уровень, подтягиваем линию, разбудим датчик
16delay(18);                        //18 мс по условиям документации.
17DHT_PORT|=(1<<DHT_BIT);           //отпускаем линию
18DHT_DDR&=~(1<<DHT_BIT);           //пин как выход
19//============= инциализация DHT
20delayMicroseconds(50);            //задержка по условию
21if (DHT_PIN&(1<<DHT_BIT)){Serial.println("NO init "); goto ReLoad;} //датчик должен ответить 0  
22delayMicroseconds(80);
23if (!(DHT_PIN&(1<<DHT_BIT))){Serial.println("Creazy DHT ");goto ReLoad;} //по истечению 80 мкс, датчик должен отпустить шину
24//===============Приём 40 бит данных
25while (DHT_PIN&(1<<DHT_BIT));            //ждем пока на шине появится 1 
26for (j=0; j<5; j++)                      //цикл для 0-4 байт
27    {
28    data[j]=0;
29    for(i=0; i<8; i++)                   //приём битов и укладка их в байты
30        {
31        while (!(DHT_PIN&(1<<DHT_BIT))); //ждем когда датчик отпустит шину
32        delayMicroseconds(30);           //задержка высокого уровня при 0 30 мкс
33        if (DHT_PIN&(1<<DHT_BIT))        //если по истечению времени сигнал на линии высокий, значит передается 1
34            data[j]|=1<<(7-i);           //тогда i-й бит устанавливаем 1
35        while (DHT_PIN&(1<<DHT_BIT));    // ждем окончание 1
36        }
37    }
38if ((unsigned char)(data[0]+data[1]+data[2]+data[3])!=data[4]) //checksum
39    {Serial.println("checksum Error ");goto ReLoad;}
40}            //конец подрограммы
41 
42void setup()
43{
44  Serial.begin(9600);
45}
46 
47void loop() {
48   read_dht_hum();                                           //вызов подпрограммы DHT11
49   temperature= (data [3]*0.1) + ((data [2] & 0b01111111)*25.6); //нюанс расчета температуры для DHT22
50    if (data [2] & 0b10000000)  temperature*= -1;            //если отрицательная температура
51    humidity=  (data [1]*0.1) + (data [0]*25.6);                //нюанс расчета влажности для DHT22
52     
53  Serial.print(humidity);                                  //относительная влажность в %;
54  Serial.print("%  ");             
55  Serial.print(temperature);                              //температура в °C;
56  Serial.println("C");
57  delay(200);   //хватает задержки в 1 сек , для этого скетча хватает скорости 180   
58}

 

b707
Offline
Зарегистрирован: 26.05.2017

Joog пишет:

Starter381 Нашел в этой статье,

вместо того чтоб цитировать безграмотные статьи, может лучше в даташит заглянуть?

вот нахера эти гланды через жопу?

1temperature= (data [3]*0.1) + ((data [2] & 0b01111111)*25.6);

Температура и влажность выдаются в виде 16-битного целого, содержащего нужную величину, умноженную на 10. Старший бит температуры дополнительно несет знак.

И все!!

Пример

Влажность 00000010  10000100 = 644 = 64.4%

Starter381
Offline
Зарегистрирован: 12.12.2020

Я скажу так, Ваш код теперь работает, НО! Ваша логика в этом коде совершенно не понятна. Для начала, расскажите подробнее, зачем вам столько #define, которые ссылаются непонятно на что: 

1#define DHT_PORT  PORTD
2#define DHT_DDR   DDRD
3#define DHT_PIN   PIND
4#define DHT_BIT   5

Во-вторых, что означают конкретно эти строки, и что это за ошибки, которые обрабатываются в этом месте:

1if (DHT_PIN&(1<<DHT_BIT)){Serial.println("NO init "); goto ReLoad;} //датчик должен ответить 0  
2delayMicroseconds(80);
3if (!(DHT_PIN&(1<<DHT_BIT))){Serial.println("Creazy DHT ");goto ReLoad;} //по истечению 80 мкс, датчик должен отпустить шину

 

b707
Offline
Зарегистрирован: 26.05.2017

Starter381 пишет:

 Ваша логика в этом коде совершенно не понятна. Для начала, расскажите подробнее, зачем вам столько #define, которые ссылаются непонятно на что: 

1#define DHT_PORT  PORTD
2#define DHT_DDR   DDRD
3#define DHT_PIN   PIND
4#define DHT_BIT   5

может стоит что-то почитать про микроконтроллеры, прежде чем задавать детские вопросы? А то беретесь работать с датчиком напрямую, без библиотек - а даже элементарные регистры МК ставят вас в тупик.

Напомните, что за цель у вас была изобретать велосипеды, чем не устроила готовая библиотека DHT.h ? Разобраться самому, как оно работает? - так разбирайтесь

Сейчас вы с Joog с грехом пополам повторили код стандартной библиотеки... если вы ничего не поняли, к чему была вся эта эпопея?

Starter381
Offline
Зарегистрирован: 12.12.2020

Я по специальности изначально фронтендист, так что не будь таким злым. Насчёт #define`ов вопрос снимаю.

Green
Offline
Зарегистрирован: 01.10.2015

Это же простые define, без наворотов.
А вот уже с такими у некоторых начинается непонимание:

1#define RESET             C,6,L
2// когда в тексте встречается:
3  out(RESET);
4  on(RESET);
5  off(RESET);

Хотя всё тривиально. И использовалось ещё с тех пор, когда Ардуино ещё и в помине не было.)

Starter381
Offline
Зарегистрирован: 12.12.2020

Обнаружил проблему, что данный код возвращает неадекватные значения в диапазоне от -3200 до -3300 (чаще всего значения колеблются около -3275) при отрицательной температуре. При этом я проверил, что проблема лежит в коде, а не в самом датчике —с библиотекой DHT.h значения считаются нормально.