STM32 Series Tutorial (13): Driving DHT11 to Obtain Temperature and Humidity

Table of Contents

1. Introduction to DHT11

2. Code Implementation

3. Summary & Precautions

01

Introduction to DHT11

The DHT11 is a composite sensor for temperature and humidity that features a calibrated digital signal output. It utilizes dedicated digital module acquisition technology and temperature and humidity sensing technology, ensuring high reliability and excellent long-term stability. It employs a single-wire communication protocol, requiring only one GPIO pin for data exchange with the microcontroller, making it cost-effective and resistant to interference. Its parameters are as follows:

Operating Voltage: 3-5.5V, Operating Current: 1mA, Measurement Resolution: 8 bit

Humidity Range: 20 – 90 %RH, Humidity Accuracy: ±5 %RH, Temperature Range: 0 – 50 ℃

Temperature Accuracy: ±2 ℃, Communication Protocol: Single Wire The DHT11 transmits 40 bits of data in one go, formatted as 8 bits of humidity integer data + 8 bits of humidity decimal data (usually 0); 8 bits of temperature integer data + 8 bits of temperature decimal data; 8 bits of checksum (the last 8 bits of the sum of the previous four 8-bit data);

The high bits of the data are sent first, and the checksum is used to verify the correctness of the data.

Hardware connection of DHT11 with STM32:

DHT11 Pin STM32 Pin Description
VCC 3.3V Power positive
GND GND Power negative
DATA PA5 Single wire data line, preferably with a pull-up resistor

02

Code Implementation

The system clock is set to 72MHz, using HSE clock, configuring APB1 and APB2, enabling the serial port and PA5 pin. Call the DHT11_Read_Data function to blockingly obtain temperature and humidity data. dht11.h

#ifndef __DHT11_H
#define __DHT11_H

#include "main.h"

// Pin definitions
#define DHT11_PORT GPIOA
#define DHT11_PIN GPIO_PIN_5
#define DHT11_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE()

// Pin operation macros
#define DHT11_DQ_OUT(x) do{ \
    if(x) HAL_GPIO_WritePin(DHT11_PORT, DHT11_PIN, GPIO_PIN_SET); \
    else HAL_GPIO_WritePin(DHT11_PORT, DHT11_PIN, GPIO_PIN_RESET); \
}while(0)

#define DHT11_DQ_IN() HAL_GPIO_ReadPin(DHT11_PORT, DHT11_PIN)

// Function declarations
void delay_init(uint8_t SYSCLK);
void delay_us(uint32_t nus);
void DHT11_GPIO_Input(void);
void DHT11_GPIO_Output(void);
uint8_t DHT11_Init(void);
void DHT11_Start(void);
uint8_t DHT11_Read_Bit(void);
uint8_t DHT11_Read_Byte(void);
uint8_t DHT11_Read_Data(uint8_t *temperature, uint8_t *humidity);

#endif

dht11.c

// dht11.c
#include "dht11.h"

static uint32_t fac_us = 0;

void delay_init(uint8_t SYSCLK)
{
    HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);
    fac_us = SYSCLK;
}

void delay_us(uint32_t nus)
{
    uint32_t ticks;
    uint32_t told, tnow, tcnt = 0;
    uint32_t reload = SysTick->LOAD;
    
    ticks = nus * fac_us;
    told = SysTick->VAL;
    
    while(1)
    {
        tnow = SysTick->VAL;
        if(tnow != told)
        {
            if(tnow < told)
                tcnt += told - tnow;
            else
                tcnt += reload - tnow + told;
            told = tnow;
            if(tcnt >= ticks)
                break;
        }
    }
}

// GPIO input mode setting
void DHT11_GPIO_Input(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    
    DHT11_CLK_ENABLE();
    GPIO_InitStruct.Pin = DHT11_PIN;
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(DHT11_PORT, &amp;GPIO_InitStruct);
}

// GPIO output mode setting
void DHT11_GPIO_Output(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    
    DHT11_CLK_ENABLE();
    GPIO_InitStruct.Pin = DHT11_PIN;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(DHT11_PORT, &amp;GPIO_InitStruct);
}

// DHT11 initialization
uint8_t DHT11_Init(void)
{
    DHT11_GPIO_Output();
    DHT11_DQ_OUT(1);
    HAL_Delay(1000); // Wait for DHT11 to stabilize
    return 0;
}

// Start signal
void DHT11_Start(void)
{
    // Set to output mode
    DHT11_GPIO_Output();
    
    // Pull down for at least 18ms
    DHT11_DQ_OUT(0);
    delay_us(18000);
    
    // Release the bus, pull high for 20-40μs
    DHT11_DQ_OUT(1);
    delay_us(30);
    
    // Set to input mode, wait for DHT11 response
    DHT11_GPIO_Input();
}

// Read a bit
uint8_t DHT11_Read_Bit(void)
{
    uint32_t timeout = 0;
    
    // Wait for low level start (about 50μs)
    while(DHT11_DQ_IN() == 1)
    {
        timeout++;
        if(timeout > 100) return 0;
        delay_us(1);
    }
    
    // Wait for low level end (about 54μs)
    timeout = 0;
    while(DHT11_DQ_IN() == 0)
    {
        timeout++;
        if(timeout > 100) return 0;
        delay_us(1);
    }
    
    // Delay 40μs to determine high level duration
    delay_us(40);
    
    // Read current level
    if(DHT11_DQ_IN() == 1)
    {
        // Long high level duration (about 70μs), indicates data 1
        timeout = 0;
        while(DHT11_DQ_IN() == 1)
        {
            timeout++;
            if(timeout > 100) return 1;
            delay_us(1);
        }
        return 1;
    }
    else
    {
        // Short high level duration (about 26μs), indicates data 0
        return 0;
    }
}

// Read a byte
uint8_t DHT11_Read_Byte(void)
{
    uint8_t i, data = 0;
    
    for(i = 0; i < 8; i++)
    {
        data <<= 1;
        data |= DHT11_Read_Bit();
    }
    
    return data;
}

// Read temperature and humidity data
uint8_t DHT11_Read_Data(uint8_t *temperature, uint8_t *humidity)
{
    uint8_t data[5] = {0};
    uint8_t i;
    
    // Send start signal
    DHT11_Start();
    
    // Wait for DHT11 response
    uint32_t timeout = 0;
    while(DHT11_DQ_IN() == 1)
    {
        timeout++;
        if(timeout > 100) return 0;
        delay_us(1);
    }
    
    timeout = 0;
    while(DHT11_DQ_IN() == 0)
    {
        timeout++;
        if(timeout > 100) return 0;
        delay_us(1);
    }
    
    timeout = 0;
    while(DHT11_DQ_IN() == 1)
    {
        timeout++;
        if(timeout > 100) return 0;
        delay_us(1);
    }
    
    // Read 40 bits of data
    for(i = 0; i < 5; i++)
    {
        data[i] = DHT11_Read_Byte();
    }
    
    // Set to output mode, pull high
    DHT11_GPIO_Output();
    DHT11_DQ_OUT(1);
    
    // Verify data
    if(data[4] == (data[0] + data[1] + data[2] + data[3]))
    {
        *humidity = data[0];    // Humidity integer
        *temperature = data[2]; // Temperature integer
        return 1; // Read successful
    }
    
    return 0; // Checksum failed
}

main.c

// main.c
#include "main.h"
#include "dht11.h"
#include "stdio.h"

UART_HandleTypeDef huart1;

void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART1_UART_Init(void);

int main(void)
{
    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();
    MX_USART1_UART_Init();
    
    // Initialize delay function
    delay_init(72);
    // Initialize DHT11
    DHT11_Init();
    
    uint8_t temperature = 0;
    uint8_t humidity = 0;
    char buffer[50];
    
    while(1)
    {
        if(DHT11_Read_Data(&amp;temperature, &amp;humidity) == 1)
        {
            // Output temperature and humidity data via serial
            sprintf(buffer, "Humidity: %d%%, Temperature: %d℃\r\n", humidity, temperature);
            HAL_UART_Transmit(&amp;huart1, (uint8_t*)buffer, strlen(buffer), 1000);
        }
        else
        {
            HAL_UART_Transmit(&amp;huart1, (uint8_t*)"Read failed\r\n", 10, 1000);
        }
        
        HAL_Delay(2500); // At least 2 seconds interval between readings
    }
}

// System clock configuration (adjust according to actual situation)
void SystemClock_Config(void)
{
    RCC_OscInitTypeDef RCC_OscInitStruct = {0};
    RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
    
    // Configure HSE
    RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
    RCC_OscInitStruct.HSEState = RCC_HSE_ON;
    RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
    RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
    RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
    RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
    HAL_RCC_OscConfig(&amp;RCC_OscInitStruct);
    
    // Configure system clock
    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_DIV2;
    RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
    HAL_RCC_ClockConfig(&amp;RCC_ClkInitStruct, FLASH_LATENCY_2);
}

// USART1 initialization (for debugging output)
static void MX_USART1_UART_Init(void)
{
    huart1.Instance = USART1;
    huart1.Init.BaudRate = 115200;
    huart1.Init.WordLength = UART_WORDLENGTH_8B;
    huart1.Init.StopBits = UART_STOPBITS_1;
    huart1.Init.Parity = UART_PARITY_NONE;
    huart1.Init.Mode = UART_MODE_TX_RX;
    huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
    huart1.Init.OverSampling = UART_OVERSAMPLING_16;
    HAL_UART_Init(&amp;huart1);
}

// GPIO initialization
static void MX_GPIO_Init(void)
{
    __HAL_RCC_GPIOA_CLK_ENABLE();
}

03

Summary & Precautions

The single-wire protocol is sensitive to delays, requiring precise calibration of microsecond-level delay functions; there should be at least a 2-second interval between two readings. The pull-up effect of STM32’s GPIO is relatively weak, so it is advisable to add a pull-up resistor between the GPIO and the DHT11 data pin. If data verification consistently fails, it is recommended to reduce the interval for reading GPIO data.

Leave a Comment