How to use DHT22 with STM32

UPDATE

If you are not able to get DHT11 or DHT22 values, Here is another method you can use. This one is unified for both the sensors. No setup needed for timer and all. Just select the data pin as output and you are done. you need to select the DHT TYPE in DHT.c

https://controllerstech.com/wp-content/uploads/2020/06/DHT_11_22_DWT.zip

In my last post, I mentioned the use of microseconds delay, check HERE, and this post is the application of that. Today I am going to interface DHT22 with STM32 microcontroller, which utilizes delay in microseconds.

The DHT-22  is a digital-output relative humidity and temperature sensor. It uses a capacitive humidity sensor and a thermistor to measure the surrounding air, and sends out a digital signal on the data pin. You can download the datasheet HERE.

I am not going to waste more time by going into more details, I am sure you already know what this is and that’s why you are here. So let’s jump to the point. If you look at the datasheet, you will find the signal pattern to initialize the sensor as shown in the picture below

INITIALIZATION

dht22 start

Here the black line is the signal from the microcontroller and the white line is the signal from the DHT22. In order to initialize the sensor, we have to pull the data line LOW for at least 1ms, and pull it HIGH for around 20-40 us.
On receiving the start signal, DHT22 will indicate it’s presence by pulling the line low for 80us and than high for 80us.

NOTE:- You might need to connect pull-up resistance to the data line or else DHT22 will not be able to pull it HIGH.

To initialize the sensor, the steps are as follows:-

  • Set the pin (data) as output
  • Pull the pin low and wait for > 1 ms
  • Pull the pin high and wait for 30 us
  • Release the pin by setting it as input

DHT22 will now send the response as you can see in the figure above. To check the response, steps are as follows:-

  • wait for 40us
  • Check if the pin is low, than wait for 80 us. This will totally be a delay of 120 us and the pin should be high now
  • Check if the pin is high. If it is, than the response is OK
  • Now wait for the pin to go LOW

DATA Transmission

DHT11 Data transmission
DHT11 Data transmission

Now DHT22 will send 40 bits of data.  Each bit’s transmission begins with low-voltage-level that last 50 us, the following high-voltage-level signal’s length decides whether the bit is “1” or “0”.

  • If the length of high-voltage-level is around 26-28 us, the bit is “0”
  • And if the length is around 70 us, than the bit is “1”

The 40 bits sent by DHT22 are as follows DATA = 8 bit integral RH data + 8 bit decimal RH data + 8 bit integral T data+8 bit decimal T data + 8 bit checksum
If the data transmission is right, check-sum should be the last 8 bit of “8 bit integral RH data+8 bit decimal RH data+8 bit integral T data+8 bit decimal T data”.

Following are the steps to READ DATA from the sensor

  1. Wait for the pin to go high
  2. Wait for 40us. This is because the length of “0” bit is 26-28us, and if the pin is high after 40us, it indicates that the bit is “1”
  3. write the respective values to the variable



Connection

dht22_connection


Some Insight into the CODE

INITIALIZATION

void DHT22_Start (void)
{
	Set_Pin_Output(DHT22_PORT, DHT22_PIN); // set the pin as output
	HAL_GPIO_WritePin (DHT22_PORT, DHT22_PIN, 0);   // pull the pin low
	HAL_Delay(1200);   // wait for > 1ms

	HAL_GPIO_WritePin (DHT22_PORT, DHT22_PIN, 1);   // pull the pin high
	delay (20);   // wait for 30us

	Set_Pin_Input(DHT22_PORT, DHT22_PIN);   // set as input
}
  1. Set the pin (data) as output
  2. Pull the pin low and wait for > 1 ms
  3. set the pin as input for receiving the data

RESPONSE

uint8_t DHT22_Check_Response (void)
{
	Set_Pin_Input(DHT22_PORT, DHT22_PIN);   // set as input
	uint8_t Response = 0;
	delay (40);  // wait for 40us
	if (!(HAL_GPIO_ReadPin (DHT22_PORT, DHT22_PIN))) // if the pin is low
	{
		delay (80);   // wait for 80us

		if ((HAL_GPIO_ReadPin (DHT22_PORT, DHT22_PIN))) Response = 1;  // if the pin is high, response is ok
		else Response = -1;
	}

	while ((HAL_GPIO_ReadPin (DHT22_PORT, DHT22_PIN)));   // wait for the pin to go low
	return Response;
}
  1. wait for 40 us
  2. check if the pin is low, than wait for 80 us. This will totally be a delay of 120 us and the pin should be high now
  3. Check if the pin is high. If it is, than the response is OK

READ DATA

uint8_t DHT22_Read (void)
{
	uint8_t i,j;
	for (j=0;j<8;j++)
	{
		while (!(HAL_GPIO_ReadPin (DHT22_PORT, DHT22_PIN)));   // wait for the pin to go high
		delay (40);   // wait for 40 us

		if (!(HAL_GPIO_ReadPin (DHT22_PORT, DHT22_PIN)))   // if the pin is low
		{
			i&= ~(1<<(7-j));   // write 0
		}
		else i|= (1<<(7-j));  // if the pin is high, write 1
		while ((HAL_GPIO_ReadPin (DHT22_PORT, DHT22_PIN)));  // wait for the pin to go low
	}

	return i;
}
  1. Wait for the pin to go high
  2. Wait for 40 us. This is because the length of “0” bit is 26-28 us  and if the pin is high after 40 us, it indicates that the bit is “1”
  3. write the respective values to the variable


Result

dht22 result

Check out the Video Below









Info

You can help with the development by DONATING
To download the code, click DOWNLOAD button and view the Ad. The project will download after the Ad is finished.

38 Comments. Leave new

  • Hamza Alibi
    May 24, 2024 9:22 PM

    please help me , i used an stm32 nucleo F439ZI with dht22 (AM2302).
    clock config : clock freq = 180MHZ, prescale = 179 , counter period = 0xffff .

    result of debugging : Error: check_response
    Checksum error: SUM = 43, checksum = 389

    #include “main.h”
    #include “stdio.h”

    /* Private variables ———————————————————*/
    TIM_HandleTypeDef htim6;

    /* Private function prototypes ———————————————–*/
    void SystemClock_Config(void);
    static void MX_GPIO_Init(void);
    static void MX_TIM6_Init(void);

    int _write(int file, char *ptr, int len) {
    int i;
    for (i = 0; i < len; i++) {
    ITM_SendChar((*ptr++));
    }
    return len;
    }

    void delay_us(uint16_t us)
    {
    __HAL_TIM_SET_COUNTER(&htim6, 0); // Set the counter value to 0
    HAL_TIM_Base_Start(&htim6); // Start the timer
    while (__HAL_TIM_GET_COUNTER(&htim6) 1ms

    HAL_GPIO_WritePin(DHT22_PORT, DHT22_PIN, GPIO_PIN_SET); // pull the pin high
    delay_us(20); // wait for 20-40us

    Set_Pin_Input(DHT22_PORT, DHT22_PIN); // set as input
    }

    uint8_t DHT22_Check_Response(void)
    {
    uint8_t Response = 0;
    delay_us(40); // wait for 40us
    if (!(HAL_GPIO_ReadPin(DHT22_PORT, DHT22_PIN))) // if the pin is low
    {
    delay_us(80); // wait for 80us
    if (HAL_GPIO_ReadPin(DHT22_PORT, DHT22_PIN)) Response = 1; // if the pin is high, response is ok
    else Response = -1;
    }
    while (HAL_GPIO_ReadPin(DHT22_PORT, DHT22_PIN)); // wait for the pin to go low
    return Response;
    }

    uint8_t DHT22_Read(void)
    {
    uint8_t i, j;
    for (j = 0; j < 8; j++)
    {
    while (!(HAL_GPIO_ReadPin(DHT22_PORT, DHT22_PIN))); // wait for the pin to go high
    delay_us(40); // wait for 40 us

    if (!(HAL_GPIO_ReadPin(DHT22_PORT, DHT22_PIN))) // if the pin is low
    {
    i &= ~(1 << (7 – j)); // write 0
    }
    else i |= (1 << (7 – j)); // if the pin is high, write 1
    while (HAL_GPIO_ReadPin(DHT22_PORT, DHT22_PIN)); // wait for the pin to go low
    }
    return i;
    }

    /* Main Function */
    int main(void)
    {
    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();
    MX_TIM6_Init();

    while (1)
    {
    DHT22_Start();
    Presence = DHT22_Check_Response();
    if (Presence == 1)
    {
    Rh_byte1 = DHT22_Read();
    Rh_byte2 = DHT22_Read();
    Temp_byte1 = DHT22_Read();
    Temp_byte2 = DHT22_Read();
    SUM = DHT22_Read();
    uint16_t checksum;
    checksum = Rh_byte1 + Rh_byte2 + Temp_byte1 + Temp_byte2;

    if (SUM == checksum)
    {
    TEMP = ((Temp_byte1 << 8) | Temp_byte2);
    RH = ((Rh_byte1 << 8) | Rh_byte2);

    Temperature = (float)(TEMP / 10.0);
    Humidity = (float)(RH / 10.0);
    printf("Temperature: %.1f°C, Humidity: %.1f%%\n", Temperature, Humidity);
    }
    else
    {
    printf("Checksum error: SUM = %d, checksum = %d\n", SUM, checksum);
    }
    }
    else
    {
    printf("Error: check_response\n");
    }
    HAL_Delay(2000); // augmenter le délai entre les lectures pour éviter les lectures erronées
    }
    }

    /**
    * @brief System Clock Configuration
    * @retval None
    */
    void SystemClock_Config(void)
    {
    RCC_OscInitTypeDef RCC_OscInitStruct = {0};
    RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

    /** Configure the main internal regulator output voltage
    */
    __HAL_RCC_PWR_CLK_ENABLE();
    __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);

    /** Initializes the RCC Oscillators according to the specified parameters
    * in the RCC_OscInitTypeDef structure.
    */
    RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
    RCC_OscInitStruct.HSEState = RCC_HSE_ON;
    RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
    RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
    RCC_OscInitStruct.PLL.PLLM = 8;
    RCC_OscInitStruct.PLL.PLLN = 360;
    RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
    RCC_OscInitStruct.PLL.PLLQ = 7;
    if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
    {
    Error_Handler();
    }

    /** Activate the Over-Drive mode
    */
    if (HAL_PWREx_EnableOverDrive() != HAL_OK)
    {
    Error_Handler();
    }

    /** Initializes the CPU, AHB and APB buses clocks
    */
    RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
    |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
    RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
    RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
    RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
    RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;

    if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK)
    {
    Error_Handler();
    }
    }

    /**
    * @brief TIM6 Initialization Function
    * @param None
    * @retval None
    */
    static void MX_TIM6_Init(void)
    {

    /* USER CODE BEGIN TIM6_Init 0 */

    /* USER CODE END TIM6_Init 0 */

    TIM_MasterConfigTypeDef sMasterConfig = {0};

    /* USER CODE BEGIN TIM6_Init 1 */

    /* USER CODE END TIM6_Init 1 */
    htim6.Instance = TIM6;
    htim6.Init.Prescaler = 179;
    htim6.Init.CounterMode = TIM_COUNTERMODE_UP;
    htim6.Init.Period = 0xffff;
    htim6.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
    if (HAL_TIM_Base_Init(&htim6) != HAL_OK)
    {
    Error_Handler();
    }
    sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
    sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
    if (HAL_TIMEx_MasterConfigSynchronization(&htim6, &sMasterConfig) != HAL_OK)
    {
    Error_Handler();
    }
    /* USER CODE BEGIN TIM6_Init 2 */

    /* USER CODE END TIM6_Init 2 */

    }

    /**
    * @brief GPIO Initialization Function
    * @param None
    * @retval None
    */
    static void MX_GPIO_Init(void)
    {
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    /* USER CODE BEGIN MX_GPIO_Init_1 */
    /* USER CODE END MX_GPIO_Init_1 */

    /* GPIO Ports Clock Enable */
    __HAL_RCC_GPIOH_CLK_ENABLE();
    __HAL_RCC_GPIOA_CLK_ENABLE();
    __HAL_RCC_GPIOB_CLK_ENABLE();

    /*Configure GPIO pin Output Level */
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET);

    /*Configure GPIO pin : PA5 */
    GPIO_InitStruct.Pin = GPIO_PIN_5;
    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);

    /* USER CODE BEGIN MX_GPIO_Init_2 */
    /* USER CODE END MX_GPIO_Init_2 */
    }

    Reply
  • Hey man,
    You got small bug here in the initialization code (just here on the website, source code in zip file is OK thought).

    HAL_Delay(1200); // wait for > 1ms

    should be
    delay (1200); // wait for > 1ms

    Reply
  • Dhamodharan Krishnan
    September 5, 2021 5:18 PM

    Checksum requires fix in the function DHT_GetData below as it is failing.

    if (SUM == ( (Rh_byte1 + Rh_byte2 + Temp_byte1 + Temp_byte2) & 0x00FF) )
        {
    #if defined(TYPE_DHT11)
            DHT_Data->Temperature = Temp_byte1;
            DHT_Data->Humidity = Rh_byte1;
    #endif
    
    #if defined(TYPE_DHT22)
            DHT_Data->Temperature = ( ((Temp_byte1 << 8) | Temp_byte2) * 0.1f);
            DHT_Data->Humidity = ( ((Rh_byte1 << 8) | Rh_byte2) * 0.1f);
    #endif
        }
    
    
    Reply
  • Hello,
    Where can i found i2c-led.h and .c files?

    Reply
  • Bilgehan Kargin
    October 16, 2019 2:50 PM

    I’ve practiced everything you’ve told us. But, while ((HAL_GPIO_ReadPin (GPIOA, GPIO_PIN_1))); // wait for the pin to go low
    in this line, waiting forever, I can not go to next step. What could be the reason ?

    Reply
  • SUMIT MISHRA
    June 4, 2019 4:27 PM

    Hi, why you used separate delay function instead of HAL Delay? Any specific reason?

    Reply
  • Hi! Thank you for your tuto! For the right checksum you shound do:
    if (sum == ((Rh_byte1+Rh_byte2+Temp_byte1+Temp_byte2) & 0xFF))

    Best regards

    Reply
  • In line No. 78 if ((HAL_GPIO_ReadPin (GPIOA, GPIO_PIN_1))) check = 1;
    Variable “check” is not used anywhrere. Could you explain Why the “check” need?

    Reply
  • hello thanks for the code, i want to read values from dht22 but without using an LCD how could to modify your code to be useful in my case

    Reply
    • just use the following code
      DHT22_start ();
      check_response ();
      Rh_byte1 = read_data ();
      Rh_byte2 = read_data ();
      Temp_byte1 = read_data ();
      Temp_byte2 = read_data ();
      sum = read_data();
      //if (sum == (Rh_byte1+Rh_byte2+Temp_byte1+Temp_byte2))
      {
      TEMP = ((Temp_byte1<<8)|Temp_byte2); RH = ((Rh_byte1<<8)|Rh_byte2); } No LCD related functions... that's it

      Reply
  • is it same and works for dht11 module?

    Reply
  • Hello, I just followed your youtube video here.
    This code is different with another I2C code,
    First your codes on this project are:
    void lcd_send_cmd (char cmd)
    {
    char data_u, data_l;
    uint8_t data_t[4];
    data_u = cmd&0xf0;
    data_l = (cmd<<4)&0xf0;
    data_t[0] = data_u|0x04; //en=1, rs=0
    data_t[1] = data_u; //en=0, rs=0
    data_t[2] = data_l|0x04; //en=1, rs=0
    data_t[3] = data_l; //en=0, rs=0
    HAL_I2C_Master_Transmit (&hi2c1, 0x4E,(uint8_t *) data_t, 4, 100);
    }

    void lcd_send_data (char data)
    {
    char data_u, data_l;
    uint8_t data_t[4];
    data_u = data&0xf0;
    data_l = (data<<4)&0xf0;
    data_t[0] = data_u|0x05; //en=1, rs=0
    data_t[1] = data_u|0x01; //en=0, rs=0
    data_t[2] = data_l|0x05; //en=1, rs=0
    data_t[3] = data_l|0x01; //en=0, rs=0
    HAL_I2C_Master_Transmit (&hi2c1, 0x4E,(uint8_t *) data_t, 4, 100);
    }

    But in another project, the lcd_send_cmd and lcd_send_data used different numbers for the data_t[ ] calculation. Why and which one is correct?

    void lcd_send_cmd (char cmd)
    {
    char data_u, data_l;
    uint8_t data_t[4];
    data_u = (cmd&0xf0);
    data_l = ((cmd<<4)&0xf0);
    data_t[0] = data_u|0x0C; //en=1, rs=0
    data_t[1] = data_u|0x08; //en=0, rs=0
    data_t[2] = data_l|0x0C; //en=1, rs=0
    data_t[3] = data_l|0x08; //en=0, rs=0
    HAL_I2C_Master_Transmit (&hi2c1, SLAVE_ADDRESS_LCD,(uint8_t *) data_t, 4, 100);
    }

    void lcd_send_data (char data)
    {
    char data_u, data_l;
    uint8_t data_t[4];
    data_u = (data&0xf0);
    data_l = ((data<<4)&0xf0);
    data_t[0] = data_u|0x0D; //en=1, rs=0
    data_t[1] = data_u|0x09; //en=0, rs=0
    data_t[2] = data_l|0x0D; //en=1, rs=0
    data_t[3] = data_l|0x09; //en=0, rs=0
    HAL_I2C_Master_Transmit (&hi2c1, SLAVE_ADDRESS_LCD,(uint8_t *) data_t, 4, 100);
    }

    Reply
    • The down one is a fix for lcd backlight so I recommend you use that.

      Reply
      • Thank you for your response.
        I have another question:
        What commands should I send to turn on/off the backlight? I know P3 is the backlight control pin.
        I just can’t figure out a way to change only one bit of the register.

        Reply
  • Do I need to change the Master Transmit address from 0x4E to whatevery my I2C is putting out? For Arduino I needed to use a scanner and check to see what it was and change that over before transmitting? Just wondering because I used everything here for my STM32F4 and it’s not working…thanks.

    Reply
    • 0x4E is the address of the slave device PCF8574. If you are using any other variant, you need to change the address.

      Reply
  • Hamza ELGUEZZAR
    February 16, 2018 3:14 PM

    where is dwt_stm32_delay.h/dwt_stm32_delay.c please !

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

Fill out this field
Fill out this field
Please enter a valid email address.

keyboard_arrow_up

Adblocker detected! Please consider reading this notice.

We've detected that you are using AdBlock Plus or some other adblocking software which is preventing the page from fully loading.

We don't have any banner, Flash, animation, obnoxious sound, or popup ad. We do not implement these annoying types of ads!

We need money to operate the site, and almost all of it comes from our online advertising.

Please add controllerstech.com to your ad blocking whitelist or disable your adblocking software.

×