Читаем показания 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 таких положительных импульсов различной длительности.
Скетч прилагаю.
// Mega
uint8_t data[5]; 
const byte DHT1_pin = 22;
const byte DHT2_pin = 24;
const unsigned int interval = 1000;

void setup(){
  Serial.begin(9600); 
}

void loop(){
  int hum1  = 0, temp1 = 0;
  int hum2  = 0, temp2 = 0;
  static unsigned long prevMillis = 0;
  if(millis() - prevMillis > interval){
    prevMillis += 1000;
    if(readingDHT(DHT1_pin)){         //если контрольная сумма верна
      hum1 = (data[0] << 8) | data[1];//считаем Н и Т первого датчика
      temp1 = ((data[2] & 0x7F) << 8) | data[3];
      if(data[2] & 0x80)  temp1 *= -1;
    }else{
      //ошибка DHT1
    }
    if(readingDHT(DHT2_pin)){
      hum2 = (data[0] << 8) | data[1];
      temp2 = ((data[2] & 0x7F) << 8) | data[3];
      if(data[2] & 0x80)  temp2 *= -1;    
    }else{
      //ошибка DHT2
    }
    Serial.print("hum1 = "); Serial.print(hum1 /10);
    Serial.print('.'); Serial.println(hum1 %10);
    Serial.print("tem1 = ");Serial.print(temp1 /10);
    Serial.print('.'); Serial.println(temp1 %10);
    Serial.print("hum2 = ");Serial.print(hum2 /10);
    Serial.print('.'); Serial.println(hum2 %10);
    Serial.print("tem2 = ");Serial.print(temp2 /10);
    Serial.print('.'); Serial.println(temp2 %10);
    Serial.println();
  }
}
//=============================================== 
boolean readingDHT(byte pin) {
  boolean state = HIGH;   //состояние линии
  byte counter = 0, j = 0;
  const byte count = 5;
  for(byte i=0; i<5; i++){
    data[i] = 0;
  }
  pinMode(pin, OUTPUT);
  digitalWrite(pin, LOW);
  delay(1);           //более 800mks нужно чтобы рабудить датчик DHT21-22
  //delay(18);        //для DHT11 
  //cli();                    //отключить прерывания если нужно
  pinMode(pin, INPUT);     //начинаем слушать датчик
  for(byte i = 0; i < 83; i++){//читаем 83 сост линии
    counter = 0;
    while (digitalRead(pin) == state) {//state==1 первый проход
      counter++;
      delayMicroseconds(4);//if 4 mkc count==3-7
      if (counter > 20)  break;
    }//end while
    state = !state;//digitalRead(_pin);
    if (counter > 20) break;    
    if ((i > 3) && !(i & 1)) {//i%2 записать каждый бит в байты хранения
      data[j >> 3] <<= 1;  //читаем с 5(i=4) состояния(1,0,1,0,?,0,?,0,...?)
      if (counter > count)  data[j>>3] |= 1;//counter>3-10 дальше ошибки
      j++;
    }
  }//end for
  //sei();            //включить прерывания  
  uint8_t sum = data[0] + data[1] + data[2] + data[3];
  if(sum == data[4]) return 1;
  else               return 0;
}

 

 

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-магию:

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

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

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

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

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

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

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

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

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

SunX пишет:

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

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

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

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

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



boolean readingDHT(byte pin) {
  byte duration = 0;
  byte j = 0;
  for(byte i = 0; i < 5; i++){
    data[i] = 0;
  }
  pinMode(pin, OUTPUT);
  digitalWrite(pin, LOW);
  delay(1);           //более 800mks нужно чтобы рабудить датчик DHT21-22
  //delay(18);        //для DHT11 
  //cli();                    //отключить прерывания если нужно
  pinMode(pin, INPUT);     //начинаем слушать датчик
  delayMicroseconds(50);
  for(byte i = 0; i < 41; i++){//читаем 41 положительный импульс (первый неинформативный)
    duration = pulseIn(pin, HIGH, 200);
    if(!duration)  break;
    if (i){     //записываем сo 2 (i==1) импульса (1,?,?,...?)
      data[j/8] <<= 1; //пишем в младший бит "0"
      if(duration > 50)
        data[j/8] |= 1;//пишем в младший бит "1"
      j++;
    }
  }//end for
  //sei();            //включить прерывания  
  uint8_t sum = data[0] + data[1] + data[2] + data[3];
  if(sum == data[4]) return 1;
  else               return 0;
}

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

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

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

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

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

static void MX_TIM3_Init(void)
{

  /* USER CODE BEGIN TIM3_Init 0 */

  /* USER CODE END TIM3_Init 0 */

  LL_TIM_InitTypeDef TIM_InitStruct = {0};

  LL_GPIO_InitTypeDef GPIO_InitStruct = {0};

  /* Peripheral clock enable */
  LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM3);
  
  LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_GPIOA);
  /**TIM3 GPIO Configuration  
  PA7   ------> TIM3_CH2 
  */
  GPIO_InitStruct.Pin = LL_GPIO_PIN_7;
  GPIO_InitStruct.Mode = LL_GPIO_MODE_FLOATING;
  LL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  /* TIM3 interrupt Init */
  NVIC_SetPriority(TIM3_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),0, 0));
  NVIC_EnableIRQ(TIM3_IRQn);

  /* USER CODE BEGIN TIM3_Init 1 */

  /* USER CODE END TIM3_Init 1 */
  TIM_InitStruct.Prescaler = 71;
  TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP;
  TIM_InitStruct.Autoreload = 65535;
  TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1;
  LL_TIM_Init(TIM3, &TIM_InitStruct);
  LL_TIM_DisableARRPreload(TIM3);
  LL_TIM_SetClockSource(TIM3, LL_TIM_CLOCKSOURCE_INTERNAL);
  LL_TIM_SetTriggerInput(TIM3, LL_TIM_TS_TI2FP2);
  LL_TIM_SetSlaveMode(TIM3, LL_TIM_SLAVEMODE_RESET);
  LL_TIM_CC_DisableChannel(TIM3, LL_TIM_CHANNEL_CH2);
  LL_TIM_IC_SetFilter(TIM3, LL_TIM_CHANNEL_CH2, LL_TIM_IC_FILTER_FDIV1);
  LL_TIM_IC_SetPolarity(TIM3, LL_TIM_CHANNEL_CH2, LL_TIM_IC_POLARITY_FALLING);
  LL_TIM_DisableIT_TRIG(TIM3);
  LL_TIM_DisableDMAReq_TRIG(TIM3);
  LL_TIM_SetTriggerOutput(TIM3, LL_TIM_TRGO_RESET);
  LL_TIM_EnableMasterSlaveMode(TIM3);
  LL_TIM_IC_SetActiveInput(TIM3, LL_TIM_CHANNEL_CH1, LL_TIM_ACTIVEINPUT_INDIRECTTI);
  LL_TIM_IC_SetPrescaler(TIM3, LL_TIM_CHANNEL_CH1, LL_TIM_ICPSC_DIV1);
  LL_TIM_IC_SetFilter(TIM3, LL_TIM_CHANNEL_CH1, LL_TIM_IC_FILTER_FDIV1);
  LL_TIM_IC_SetPolarity(TIM3, LL_TIM_CHANNEL_CH1, LL_TIM_IC_POLARITY_RISING);
  LL_TIM_IC_SetActiveInput(TIM3, LL_TIM_CHANNEL_CH2, LL_TIM_ACTIVEINPUT_DIRECTTI);
  LL_TIM_IC_SetPrescaler(TIM3, LL_TIM_CHANNEL_CH2, LL_TIM_ICPSC_DIV1);
  /* USER CODE BEGIN TIM3_Init 2 */

  /* USER CODE END TIM3_Init 2 */

}

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

void DHT11(void)
{
   LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
  LL_GPIO_ResetOutputPin(GPIOA, LL_GPIO_PIN_7);
  LL_GPIO_ResetOutputPin(GPIOA, LL_GPIO_PIN_1);

  GPIO_InitStruct.Pin = GPIO_PIN_7;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);*/
  GPIO_InitStruct.Pin = LL_GPIO_PIN_7 | LL_GPIO_PIN_1;
  GPIO_InitStruct.Mode = LL_GPIO_MODE_OUTPUT;
  GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW;
  GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
  LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
  HAL_Delay(20);
  GPIO_InitStruct.Mode = LL_GPIO_MODE_FLOATING;
  LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
  

 // GPIO_InitStruct.Mode = GPIO_MODE_AF_INPUT;
 // HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
  
  nt=0;nt2=0;
  LL_TIM_EnableIT_CC2(TIM3); //HAL_TIM_IC_Start_IT(&htim3, TIM_CHANNEL_2);
  LL_TIM_CC_EnableChannel(TIM3,LL_TIM_CHANNEL_CH1); //HAL_TIM_IC_Start(&htim3, TIM_CHANNEL_1);
  LL_TIM_CC_EnableChannel(TIM3,LL_TIM_CHANNEL_CH2);
  LL_TIM_EnableCounter(TIM3);

  LL_TIM_EnableIT_CC2(TIM2); //HAL_TIM_IC_Start_IT(&htim3, TIM_CHANNEL_2);
  LL_TIM_CC_EnableChannel(TIM2,LL_TIM_CHANNEL_CH2);
  LL_TIM_EnableCounter(TIM2);

};

void TimerCaptureCompare_Callback(void)
{
  period[nt] = LL_TIM_IC_GetCaptureCH2(TIM3); //HAL_TIM_ReadCapturedValue(htim3, TIM_CHANNEL_2);
  pulse[nt] = LL_TIM_IC_GetCaptureCH1(TIM3);// HAL_TIM_ReadCapturedValue(htim3, TIM_CHANNEL_1);
  nt++;
  if (nt >41) 
  {
  LL_TIM_DisableCounter(TIM3);
  LL_TIM_SetCounter(TIM3,0);
  LL_TIM_DisableIT_CC2(TIM3);//HAL_TIM_IC_Stop_IT(htim3, TIM_CHANNEL_2);
  LL_TIM_CC_DisableChannel(TIM3,LL_TIM_CHANNEL_CH1);//HAL_TIM_IC_Stop(htim3, TIM_CHANNEL_1);
  LL_TIM_CC_DisableChannel(TIM3,LL_TIM_CHANNEL_CH2);//HAL_TIM_IC_Stop(htim3, TIM_CHANNEL_2);
/* */
  b1=0;for(i=0;i<8;i++) if ((period[9-i] / 100) > 0) b1|= (1 << i);
  b2=0;for(i=0;i<8;i++) if ((period[17-i] / 100) > 0) b2|= (1 << i);
  b3=0;for(i=0;i<8;i++) if ((period[25-i] / 100) > 0) b3|= (1 << i);
  b4=0;for(i=0;i<8;i++) if ((period[33-i] / 100) > 0) b4|= (1 << i);
  b5=0;for(i=0;i<8;i++) if ((period[41-i] / 100) > 0) b5|= (1 << i);
/** /
  b1=0;for(i=0;i<8;i++) if ((period[i+1] / 100) > 0) b1|= (1 << i);
  b2=0;for(i=0;i<8;i++) if ((period[i+9] / 100) > 0) b2|= (1 << i);
  b3=0;for(i=0;i<8;i++) if ((period[i+17] / 100) > 0) b3|= (1 << i);
  b4=0;for(i=0;i<8;i++) if ((period[i+25] / 100) > 0) b4|= (1 << i);
  b5=0;for(i=0;i<8;i++) if ((period[i+33] / 100) > 0) b5|= (1 << i);
*/
  float ft,fh;
  ft=(b3 & 0x80 ? -1 : 1)*0.1*(((uint16_t)(b3 & 0x7F)) << 8 | b4);
  fh=0.1*(((uint16_t)(b1 & 0x7F)) << 8 | b2);
  bs=b1+b2+b3+b4;
  if (bs==b5) {hum=(uint16_t)fh;tmp=(uint16_t)ft;}
  else {hum=0;tmp=0;};
  };
};


Про этом получение данных не зависит от типа 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
#include <delay.h>

#define DHT_PORT  PORTD
#define DHT_DDR   DDRD
#define DHT_PIN   PIND
#define DHT_BIT   5

unsigned int  read_dht_hum()
{
int temp;
unsigned char i,j;
unsigned char data[5];

//=============MCU send START
DHT_DDR|=(1<<DHT_BIT);   //pin as output
DHT_PORT&=~(1<<DHT_BIT); //0
delay_ms(18);
DHT_PORT|=(1<<DHT_BIT); //1
DHT_DDR&=~(1<<DHT_BIT); //pin as input
//=============check DHT11 response
delay_us(50);
if (DHT_PIN&(1<<DHT_BIT))
    return 0;
delay_us(80);
if (!(DHT_PIN&(1<<DHT_BIT)))
    return 0;
//===============receive 40 data bits
while (DHT_PIN&(1<<DHT_BIT));
for (j=0; j<5; j++)
    {
    data[j]=0;
    for(i=0; i<8; i++)
        {
        while (!(DHT_PIN&(1<<DHT_BIT)));
        delay_us (30);
        if (DHT_PIN&(1<<DHT_BIT)) 
            data[j]|=1<<(7-i);
        while (DHT_PIN&(1<<DHT_BIT));
        }
    }
if ((unsigned char)(data[0]+data[1]+data[2]+data[3])!=data[4]) //checksum
    return 0;
    temp = (data[0]<<8)+(data[2]); //data[0]-humidity, data[2]-temperature 
return temp; 
}

Библиотека для 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/

unsigned char temp=0, hum=0;
int  ca=0;
unsigned char data[5];

unsigned int  read_dht_hum()    //подпрограмма работы с DHT
{
#define DHT_PORT  PORTD 
#define DHT_DDR   DDRD
#define DHT_PIN   PIND
#define DHT_BIT   5              //по ардуиновски D5  
int temp;
unsigned char i,j;
ReLoad:                           //метка для ошибок
//=============MCU send START
DHT_DDR|=(1<<DHT_BIT);            //выход
DHT_PORT&=~(1<<DHT_BIT);          //низкий уровень, подтягиваем линию, разбудим датчик
delay(18);                        //18 мс по условиям документации.
DHT_PORT|=(1<<DHT_BIT);           //отпускаем линию
DHT_DDR&=~(1<<DHT_BIT);           //пин как выход
//============= инциализация DHT
delayMicroseconds(50);            //задержка по условию
if (DHT_PIN&(1<<DHT_BIT)){Serial.println("NO init "); goto ReLoad;} //датчик должен ответить 0   
delayMicroseconds(80);
if (!(DHT_PIN&(1<<DHT_BIT))){Serial.println("Creazy DHT ");goto ReLoad;} //по истечению 80 мкс, датчик должен отпустить шину 
//===============Приём 40 бит данных
while (DHT_PIN&(1<<DHT_BIT));    //ждем пока на шине появится 1  
for (j=0; j<5; j++)
    {
    data[j]=0;
    for(i=0; i<8; i++)
        {
        while (!(DHT_PIN&(1<<DHT_BIT))); //ждем когда датчик отпустит шину
        delayMicroseconds(30);           //задержка высокого уровня при 0 30 мкс
        if (DHT_PIN&(1<<DHT_BIT))        //если по истечению времени сигнал на линии высокий, значит передается 1
            data[j]|=1<<(7-i);           //тогда i-й бит устанавливаем 1
        while (DHT_PIN&(1<<DHT_BIT));    // ждем окончание 1 
        }
    }
if ((unsigned char)(data[0]+data[1]+data[2]+data[3])!=data[4]) //checksum
    {Serial.println("checksum Error ");goto ReLoad;}
    temp = (data[0]<<8)+(data[2]); //data[0]-humidity, data[2]-temperature 
return temp; //двухбайтовая величина, в старшем байте которой находится однобайтная влажность, а в младшем - однобайтная температура. 
}            //конец подрограммы


void setup()
{
  Serial.begin(9600);
}

void loop() {
  
        ca = read_dht_hum();      //читаем влажность и температуру c DHT11 из подпрограммы
        hum = ((ca >>8) & 0xFF);  // Разрезаем на байты старший байт влажность
        temp = (ca & 0xFF);       // Разрезаем на байты младший байт температура
  Serial.print(ca, HEX);          //вывод полученного результата в шеснадцатеричном формате
  Serial.print(" "); 
  Serial.print(data [0], HEX);    //относительная влажность — целая часть в %;
  Serial.print(" ");              
  Serial.print(data [1], HEX);    //десятая часть относительной влажности в % (ноль для DHT11);
  Serial.print(" ");
  Serial.print(data [2], HEX);    //целая часть температуры в °C;
  Serial.print(" ");
  Serial.print(data [3], HEX);    //десятая часть температуры в °C (ноль для DHT11);
  Serial.print(" ");  
  Serial.println(data [4], HEX);  //контрольная сумма (последние 8 бит {1-й байт + 2-й байт + 3-й байт + 4-й байт})
  
  delay(1000);   //хватает задержки в 1 сек     
}

 

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

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

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

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

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

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

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

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

unsigned 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, попробуйте.


unsigned char data[5];
float temperature, humidity;

unsigned int  read_dht_hum()    //подпрограмма работы с DHT
{
#define DHT_PORT  PORTD 
#define DHT_DDR   DDRD
#define DHT_PIN   PIND
#define DHT_BIT   5              //по ардуиновски D5  
int temp;
unsigned char i,j;
ReLoad:                           //метка для ошибок
//=============MCU send START
DHT_DDR|=(1<<DHT_BIT);            //выход
DHT_PORT&=~(1<<DHT_BIT);          //низкий уровень, подтягиваем линию, разбудим датчик
delay(18);                        //18 мс по условиям документации.
DHT_PORT|=(1<<DHT_BIT);           //отпускаем линию
DHT_DDR&=~(1<<DHT_BIT);           //пин как выход
//============= инциализация DHT
delayMicroseconds(50);            //задержка по условию
if (DHT_PIN&(1<<DHT_BIT)){Serial.println("NO init "); goto ReLoad;} //датчик должен ответить 0   
delayMicroseconds(80);
if (!(DHT_PIN&(1<<DHT_BIT))){Serial.println("Creazy DHT ");goto ReLoad;} //по истечению 80 мкс, датчик должен отпустить шину 
//===============Приём 40 бит данных
while (DHT_PIN&(1<<DHT_BIT));            //ждем пока на шине появится 1  
for (j=0; j<5; j++)                      //цикл для 0-4 байт
    {
    data[j]=0;
    for(i=0; i<8; i++)                   //приём битов и укладка их в байты
        {
        while (!(DHT_PIN&(1<<DHT_BIT))); //ждем когда датчик отпустит шину
        delayMicroseconds(30);           //задержка высокого уровня при 0 30 мкс
        if (DHT_PIN&(1<<DHT_BIT))        //если по истечению времени сигнал на линии высокий, значит передается 1
            data[j]|=1<<(7-i);           //тогда i-й бит устанавливаем 1
        while (DHT_PIN&(1<<DHT_BIT));    // ждем окончание 1 
        }
    }
if ((unsigned char)(data[0]+data[1]+data[2]+data[3])!=data[4]) //checksum
    {Serial.println("checksum Error ");goto ReLoad;}
}            //конец подрограммы

void setup()
{
  Serial.begin(9600);
}

void loop() { 
   read_dht_hum();                                           //вызов подпрограммы DHT11
   temperature= (data [3]*0.1) + ((data [2] & 0b01111111)*25.6); //нюанс расчета температуры для DHT22
    if (data [2] & 0b10000000)  temperature*= -1;            //если отрицательная температура
    humidity=  (data [1]*0.1) + (data [0]*25.6);                //нюанс расчета влажности для DHT22
    
  Serial.print(humidity);                                  //относительная влажность в %;
  Serial.print("%  ");              
  Serial.print(temperature);                              //температура в °C;
  Serial.println("C");
  delay(200);   //хватает задержки в 1 сек , для этого скетча хватает скорости 180    
}

 

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

Joog пишет:

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

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

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

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

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

И все!!

Пример

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

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

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

#define DHT_PORT  PORTD 
#define DHT_DDR   DDRD
#define DHT_PIN   PIND
#define DHT_BIT   5

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

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

 

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

Starter381 пишет:

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

#define DHT_PORT  PORTD 
#define DHT_DDR   DDRD
#define DHT_PIN   PIND
#define DHT_BIT   5

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

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

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

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

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

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

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

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

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

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

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