UPDATE: 19.07.2015 – after 2 years, out of nowhere i decided to rewrite most of the code to make it more user friendly

DHT11 is humidity & temperature sensor made by Aosong (Guangzhou) Electronics. What make it really interesting is 1-wire interface, which – as you can imagine – use only 1 data pin to communicate with MCU.

I wrote simple library to test this sensor and later on I even used it in some projects. It is written is pure C, so I made tons of macros if someone would like to port this lib to any other MCU than STM32 (in my case I tested it with F103ZE).

The most important defines are GPIO  and DELAY_US. In this example DHT11 data pin is connected to GPIOA pin 1 with STM32f103ze.

/*
File: dht11.h
Description: DHT11 library
Author: Adam Orcholski, tath@o2.pl, www.tath.eu
Log (day/month/year):
- (07.04.2013) Initial
- (19.07.2015) Refactored
*/
#ifndef _DHT11_DEF_
#define _DHT11_DEF_

#include <stdint.h>
#include "stm32f10x.h"
#include "gpio.h"
#include "timer.h"

/* configuration macros: */
/* enable Delay setup */
#define DELAY_INIT          TIM2_Init();
#define DELAY_DEINIT        TIM2_Deinit()
#define DELAY_ENABLE        TIM2_Enable()
#define DELAY_DISABLE       TIM2_Disable()
#define DELAY_US(__time__)  TIM2_DelayUs((__time__))

/* GPIO configuration defines for 1-wire data input/output pin*/
#define GPIO_SET_AS_OUTPUT  Gpio_SetPinAsOutput(GPIOA, GPIO_PIN1, GPIO_SPEED_2MHZ, GPIO_GP_PUSH_PULL)
#define GPIO_SET_AS_INPUT   Gpio_SetPinAsInput(GPIOA, GPIO_PIN1, GPIO_FLOATING_IN)
#define GPIO_OUPUT_CLEAR    Gpio_ClearOutputPin(GPIOA, GPIO_PIN1)   /* clear port state */
#define GPIO_OUTPUT_SET     Gpio_SetOutputPin(GPIOA, GPIO_PIN1)     /* set port state to 1 */
#define GPIO_INPUT_GET      Gpio_GetInputPinValue(GPIOA, GPIO_PIN1) /* should return 0 or 1 */

/* Optional critical section (because of delays slow as 30us) */
#define CRITICAL_SECTION_INIT
#define CRITICAL_SECTION_DEINIT
#define CRITICAL_SECTION_ENTER
#define CRITICAL_SECTION_LEAVE

/* optional timeouts for while() loops (in case of hardware failure) */
#define ENABLE_TIMEOUTS     /* comment to not perform timeout checks */
#define TIMEOUT_VALUE       100000

/* Return codes */
typedef enum DHT11_ERROR_CODE_t
{
    DHT11_OK = 0,
    DHT11_TIMEOUT,
    DHT11_WRONG_CHCKSUM
} DHT11_ERROR_CODE_t;

/* Interface function declarations */
void               DHT11_Init(void);
void               DHT11_Denit(void);
DHT11_ERROR_CODE_t DHT11_Read(uint8_t * const pData);
#endif

 

/*
File: dht11.c
Description: DHT11 library
Author: Adam Orcholski, tath@o2.pl, www.tath.eu
Log (day/month/year):
- (07.04.2013) Initial
- (19.07.2015) Refactored
Note:
 - refer to official documentation to understand what is happening here
*/
#include <dht11.h>

/* Public function definitions */
void DHT11_Init(void)
{
    CRITICAL_SECTION_INIT;
    DELAY_INIT;
}

void DHT11_Denit(void)
{
    DELAY_DEINIT;
    CRITICAL_SECTION_DEINIT;
}

DHT11_ERROR_CODE_t DHT11_Read(uint8_t * const pData)
{
    int i = 0;
    int j = 0;
    DHT11_ERROR_CODE_t errorCode = DHT11_OK;
    
    #ifdef ENABLE_TIMEOUTS
    int timeout = TIMEOUT_VALUE;
    #endif

    GPIO_SET_AS_OUTPUT;
    
    CRITICAL_SECTION_ENTER;
    
    DELAY_ENABLE;
    
    /* start sequence */
    GPIO_OUPUT_CLEAR;    
    DELAY_US(18000);

    GPIO_OUTPUT_SET;
    DELAY_US(40);

    GPIO_SET_AS_INPUT;

    while(0 == GPIO_INPUT_GET) /* 80us on '0' */
    {
        #ifdef ENABLE_TIMEOUTS
        if (--(timeout) <= 0)
        {
            errorCode = DHT11_TIMEOUT;
            break;
        }
        #endif
    };
    
    #ifdef ENABLE_TIMEOUTS
    timeout = TIMEOUT_VALUE;
    #endif
    if (DHT11_OK == errorCode)
    {
        while(1 == GPIO_INPUT_GET) /* 80us on '1' */
        {
            #ifdef ENABLE_TIMEOUTS
            if (--(timeout) <= 0)
            {
                errorCode = DHT11_TIMEOUT;
                break;
            }
            #endif
        };
    }        
    /* start sequence - end */

    /* read sequence */
    if (DHT11_OK == errorCode)
    {
        for(j=0;j<5;j++)
        {
            for(i=0;i<8;i++)
            {
                #ifdef ENABLE_TIMEOUTS
                timeout = TIMEOUT_VALUE;
                #endif
                while(0 == GPIO_INPUT_GET)
                {
                    #ifdef ENABLE_TIMEOUTS
                    if (--(timeout) <= 0)
                    {
                        errorCode = DHT11_TIMEOUT;
                        break;
                    }
                    #endif
                }; /* 50 us on 0 */

                if (1 == GPIO_INPUT_GET)
                {
                    DELAY_US(30);
                }

                pData[j] <<= 1;
                
                if(1 == GPIO_INPUT_GET)
                {
                    DELAY_US(40); /* wait 'till 70us */
                    pData[j] |= 1;
                }
                else
                {
                    pData[j] &= 0xfe;
                }
            }
        }
    }
    /* read sequence - end */
    
    DELAY_DISABLE
    CRITICAL_SECTION_LEAVE;

    /* checksum check */
    if (DHT11_OK == errorCode)
    {
        if ((pData[0] + pData[2]) != pData[4])
        {
            errorCode = DHT11_WRONG_CHCKSUM;
        }
        else
        {
            errorCode = DHT11_OK;
        }
    }

    return errorCode;
}

 

/*
File: main.c
Description: Example use of my DHT11 library
Author: Adam Orcholski, tath@o2.pl, www.tath.eu
Log (day/month/year):
- (07.04.2013) Initial
- (19.07.2015) Refactored
Note:
    - in this example i use GPIOA pin 1 to communicate with DHT11
    - some compilers does not support retargeting (usart through printf),
      so some changes might be requried in this case
*/

#include <stdio.h>     /* printf declaration */
#include <stm32f10x.h> /* Official STM register definitions */
#include <gpio.h>
#include <usart.h>
#include <dht11.h>

/* Entry point */
int main (void)
{
    uint8_t         dht11Data[5] = {0};     /* dht11 data container */
    volatile int    i = 0;                  /* Keil compiler doesn't like delay loops */

    /* Setup USART */
    RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;     /* GPIOA clock enable (USART and DHT11) */
    RCC->APB2ENR |= RCC_APB2ENR_USART1EN;   /* USART1 clock enable */
    Gpio_SetPinAsOutput(GPIOA, GPIO_PIN9, GPIO_SPEED_50MHZ, GPIO_AF_PUSH_PULL);
    USART_Init(USART1);
    USART_Enable(USART1);
    
    /* setup DHT11 */
    DHT11_Init();

    printf("All initialization done\r\n");

    while(1)
    {
        if (DHT11_OK == DHT11_Read(dht11Data))
        {
            printf("DHT11 data: %d%%, %dC\r\n", dht11Data[0], dht11Data[2]);
        }
        else /* wrong checksum or timeout */
        {
            printf("DHT11: ERROR on read");
        }
        
        /* delay loop :D */
        for (i=0; i<2000000;i++);
    }
}

Because DHT11 is reading only integral part of the measurements, only data[0] and data[2] are read. Please refer to the official documentation for more information.

This driver should also work with other DHTxx sensors, like DHT21 or DHT22.

The project has been compiled in Keil uVision using armcc and flashed/debugged with Keil Ulink2.

Project files (25.07.2015): dht11_driver
Old Project files (07.04.2013): dht11_driver_old

11 Comments


  1. hi,
    i am from turkey. i work in stm32f4 and i wanna use dht 11. i am implment cortexm4 but not work. ( i’m using coide and GCC arm embedded )
    best regards.

    Reply

    • dht11 code is compiler independent. All you have to do is to fill defines (timer and gpio) for it to work

      Reply
  2. Bùi Hoàng Thịnh

    HOW to set different baudrate 256000, 9600baud—>#define UART_BAUD_RATE_96000 CORE_FREQUENCY/96000L ….true or fail ????

    Reply

    • #define UART_BAUD_RATE_9600 CORE_FREQUENCY/9600L
      for baud rate 9600 should do. I will test it later

      Reply
  3. Bùi Hoàng Thịnh

    and 1 question.we can use source code for stm32f103RBT6 ??? ..i use board stm32rbt6 ?? Is there a problem ? If “yes” how fix…..sr you im newbie arm :D

    Reply

    • as you can see on official ST site: http://www.st.com/web/catalog/mmc/FM141/SC1169/SS1031/LN1565
      stm32103rb has less memory (128k) and has smaller package (64 pins).
      Some GPIO pins might differ between RB and ZE so you should check stm32103rb datasheet if these are available.
      Except that, you should be able to use my code without problems.

      Reply
  4. Bùi Hoàng Thịnh

    thank you

    Reply
  5. LuKi

    thank you for update but i can qs ??
    why use DELAY_US = TIM2 , but dont use = delay_us normal
    ex : void delay_us(unsigned int value)
    {
    while (value!=0)
    value –;
    }

    Reply

    • I don’t like delay loops, especially where time precision is at stake (micro seconds or less). Timer configuration is described, defined and is architecture dependant – if set correctly it will always behave the same. On the other hand, delay loop is compiler dependant – it might work differently with other compilers or optimization selected. You might also not predict how cpu pipeline will affect such delays.

      Reply
  6. Ayn

    How do I join Interrupt in usart.h
    Thank you

    Reply
  7. krishna

    I am using stm32f407vg. Any one have the code for interfacing DHT11. Please send to me.

    Reply

Leave a Reply

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