[Revised]Working with STM32 and sensors: DHT11

In this article, we shall look at the dth11 temperature and humidity sensor and how does it work and interface it with STM32F4 Nucleo-64 board.

In this guide, we shall cover the following:

  • DHT11
  • Connection
  • Code
  • Demo

1. DHT11:

The DHT11 Temperature & Humidity Sensor features a temperature & humidity sensor complex with a calibrated digital signal output. By using the exclusive digital-signal-acquisition technique and temperature & humidity sensing technology, it ensures high reliability and excellent long-term stability. This sensor includes a resistive-type humidity measurement component and an NTC temperature measurement component, and connects to a high- performance 8-bit microcontroller, offering excellent quality, fast response, anti-interference ability and cost-effectiveness.

Each DHT11 element is strictly calibrated in the laboratory that is extremely accurate on humidity calibration. The calibration coefficients are stored as programmes in the OTP memory, which are used by the sensor’s internal signal detecting process. The single-wire serial interface makes system integration quick and easy. Its small size, low power consumption and up-to-20 meter signal transmission making it the best choice for various applications, including those most demanding ones. The component is 4-pin single row pin package. It is convenient to connect and special packages can be provided according to users’ request.

HOW THE DHT11 MEASURES HUMIDITY AND TEMPERATURE

The DHT11 detects water vapor by measuring the electrical resistance between two electrodes. The humidity sensing component is a moisture holding substrate with electrodes applied to the surface. When water vapor is absorbed by the substrate, ions are released by the substrate which increases the conductivity between the electrodes. The change in resistance between the two electrodes is proportional to the relative humidity. Higher relative humidity decreases the resistance between the electrodes, while lower relative humidity increases the resistance between the electrodes.

The DHT11 measures temperature with a surface mounted NTC temperature sensor (thermistor) built into the unit. 

With the plastic housing removed, you can see the electrodes applied to the substrate:

DHT11 Temperature and Humidity Sensor Inside Front with Cover Removed

An IC mounted on the back of the unit converts the resistance measurement to relative humidity. It also stores the calibration coefficients, and controls the data signal transmission between the DHT11 and the Arduino:

DHT11 Temperature and Humidity Sensor Inside Back with Cover Removed

The DHT11 uses just one signal wire to transmit data to the Arduino. Power comes from separate 5V and ground wires. A 10K Ohm pull-up resistor is needed between the signal line and 5V line to make sure the signal level stays high by default (see the datasheet for more info).

There are two different versions of the DHT11 you might come across. One type has four pins, and the other type has three pins and is mounted to a small PCB. The PCB mounted version is nice because it includes a surface mounted 10K Ohm pull up resistor for the signal line. Here are the pin outs for both versions:

Comparison of three pin DHT11 vs four pin DHT11

Technical Details

  • Low cost
  • 3 to 5V power and I/O
  • 2.5mA max current use during conversion (while requesting data)
  • Good for 20-80% humidity readings with 5% accuracy
  • Good for 0-50°C temperature readings ±2°C accuracy
  • No more than 1 Hz sampling rate (once every second)
  • Body size 15.5mm x 12mm x 5.5mm
  • 4 pins with 0.1″ spacing

Since the accuracy of the sensor is not that great, it is mostly used for temperature and humidity indicator in home application.

2. Connection:

In this guide, we will need the following:

  • STM32F411RE-Nucleo-64
  • DHT11 Sensor
  • 0.96″ OLED display
  • Small breadboard
  • Hookup wires

The connection shall be as shown in the image below

The DHT11 pin is connected to PA8.

In my case the module used has built-in pull resistor. Hence, no need to connect external one.

3. Code:

We start by creating the required delay using systick timer which is available in any ARM Cortex M based mcu.

Create new source and header file with name of delay.c and delay.h.

Within the header file:

#ifndef __delay__H__
#define __delay__H__

#include "stm32f4xx.h"                  // Device header

#include <stdint.h>
void delayuS(int us);
void delay(int ms);


#endif

Within the source file:

#include "delay.h"




void delayuS(int us)
{

	SysTick->LOAD=16-1;
	SysTick->VAL=0;
	SysTick->CTRL=0x5;
		for (int i=0;i<us;i++)
		{
			while(!(SysTick->CTRL &0x10000)){}
		}
	SysTick->CTRL=0;	
	

			 
	}

void delay(int ms)
{

	SysTick->LOAD=16000-1;
	SysTick->VAL=0;
	SysTick->CTRL=0x5;
		for (int i=0;i<ms;i++)
		{
			while(!(SysTick->CTRL &0x10000)){}
		}
	SysTick->CTRL=0;	

}

For the OLED display, please refer to this guide here.

Now, create new source and header file with name of dht11.c and dht11.h respectively.

Within the header file:

Declare the header guard:

#ifndef DHT11_H_
#define DHT11_H_


#endif /* DHT11_H_ */

Include the stdint library:

#include "stdint.h"

declare an enum to handle if there is a response from the sensor or not as following:

typedef enum
{
	Response_OK=0,
	Response_ERR=1

}DHT11_Response_Typedef;

Declare the following function:

void dht11_PinA8_Init(void);

This function will enable clock access to GPIOA and take no argument and returns nothing.

void dht11_start(void);

This function will start the dht11 to start measuring the temperature and humidity. It Takes no argument and returns nothing.

DHT11_Response_Typedef Check_Response();

This function will check the status of the response and return if the response is ok or error.

void Get_DHT_Data(float * TEMP, float *RH);

This function will get the DHT11 data, it takes temperature and Relative Humidity as float pointer and returns nothing.

Hence, the header file as following:

#ifndef DHT11_H_
#define DHT11_H_

#include "stdint.h"


typedef enum
{
	Response_OK=0,
	Response_ERR=1

}DHT11_Response_Typedef;

void dht11_PinA8_Init(void);
void dht11_start(void);
DHT11_Response_Typedef Check_Response();
void Get_DHT_Data(float * TEMP, float *RH);


#endif /* DHT11_H_ */

In source file:

We start by including the required header files:

#include "dht11.h"
#include "stm32f4xx.h"
#include "delay.h"

First function is the enable clock access to GPIOA:

void dht11_PinA8_Init(void)
{
	/*Enable clock access to GPIOA*/
	RCC->AHB1ENR|=RCC_AHB1ENR_GPIOAEN;
}

For the start of the sensor measurement, the followings are the step:

  • Set PA8 to output.
  • Set the pin to low for 18 milliseconds.
  • Set PA8 to input.
void dht11_start(void)
{
	/*Set PA8 as output*/
	GPIOA->MODER|=GPIO_MODER_MODE8_0;
	GPIOA->MODER&=~GPIO_MODER_MODE8_1;

	/*Set the pin low*/
	GPIOA->BSRR=GPIO_BSRR_BR8;

	/*Wait for 18ms*/

	delay(18);

	/*Set the pin to input*/
	GPIOA->MODER&=~GPIO_MODER_MODE8;
}

To check for the response:

In order to indicate it’s presence, after receiving the start signal, DHT11 will send a response signal. To do so, it will pull the data line low for 80 us, and than high for another 80 us. To read this response, we will do the following

  • Wait for 40 us
  • Read the pin, it must be low at this point
  • wait for 80 us
  • Read the pin, this time it should be HIGH

If the above conditions are satisfied, that means the sensor is present, and we can proceed with reading the data.

DHT11_Response_Typedef Check_Response()
{
	uint8_t Response = 0;
	delayuS(40);
	if ((GPIOA->IDR & GPIO_IDR_ID8)!=GPIO_IDR_ID8)
	{
		delayuS (80);
		if ((GPIOA->IDR & GPIO_IDR_ID8)==GPIO_IDR_ID8)
		{
			Response = Response_OK;
		}
		else
		{
			Response = Response_ERR;
		}
	}
	while ((GPIOA->IDR & GPIO_IDR_ID8)==GPIO_IDR_ID8);   // wait for the pin to go low
	return Response;
}

READ DATA

  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

static uint8_t DHT11_Read (void)
{
	uint8_t i,j;
	for (j=0;j<8;j++)
	{
		while ((GPIOA->IDR & GPIO_IDR_ID8)!=GPIO_IDR_ID8);   // wait for the pin to go high
		delayuS (40);   // wait for 40 us
		if ((GPIOA->IDR & GPIO_IDR_ID8)!=GPIO_IDR_ID8)   // if the pin is low
		{
			i&= ~(1<<(7-j));   // write 0
		}
		else i|= (1<<(7-j));  // if the pin is high, write 1
		while ((GPIOA->IDR & GPIO_IDR_ID8)==GPIO_IDR_ID8);  // wait for the pin to go low
	}
	return i;
}

To get the data:

void Get_DHT_Data(float * TEMP, float *RH)
{
	  uint8_t Rh_byte1 = DHT11_Read ();

	  uint8_t Rh_byte2 = DHT11_Read ();

	  uint8_t Temp_byte1 = DHT11_Read ();

	  uint8_t Temp_byte2 = DHT11_Read ();

	  uint8_t SUM = DHT11_Read();

	  *TEMP = (float)Temp_byte1;
	  *RH = (float)Rh_byte1;

}

Hence, the source code as following:

#include "dht11.h"
#include "stm32f4xx.h"
#include "delay.h"

void dht11_PinA8_Init(void)
{
	/*Enable clock access to GPIOA*/
	RCC->AHB1ENR|=RCC_AHB1ENR_GPIOAEN;
}

void dht11_start(void)
{
	/*Set PA8 as output*/
	GPIOA->MODER|=GPIO_MODER_MODE8_0;
	GPIOA->MODER&=~GPIO_MODER_MODE8_1;

	/*Set the pin low*/
	GPIOA->BSRR=GPIO_BSRR_BR8;

	/*Wait for 18ms*/

	delay(18);

	/*Set the pin to input*/
	GPIOA->MODER&=~GPIO_MODER_MODE8;
}

DHT11_Response_Typedef Check_Response()
{
	uint8_t Response = 0;
	delayuS(40);
	if ((GPIOA->IDR & GPIO_IDR_ID8)!=GPIO_IDR_ID8)
	{
		delayuS (80);
		if ((GPIOA->IDR & GPIO_IDR_ID8)==GPIO_IDR_ID8)
		{
			Response = Response_OK;
		}
		else
		{
			Response = Response_ERR;
		}
	}
	while ((GPIOA->IDR & GPIO_IDR_ID8)==GPIO_IDR_ID8);   // wait for the pin to go low
	return Response;
}

static uint8_t DHT11_Read (void)
{
	uint8_t i,j;
	for (j=0;j<8;j++)
	{
		while ((GPIOA->IDR & GPIO_IDR_ID8)!=GPIO_IDR_ID8);   // wait for the pin to go high
		delayuS (40);   // wait for 40 us
		if ((GPIOA->IDR & GPIO_IDR_ID8)!=GPIO_IDR_ID8)   // if the pin is low
		{
			i&= ~(1<<(7-j));   // write 0
		}
		else i|= (1<<(7-j));  // if the pin is high, write 1
		while ((GPIOA->IDR & GPIO_IDR_ID8)==GPIO_IDR_ID8);  // wait for the pin to go low
	}
	return i;
}

void Get_DHT_Data(float * TEMP, float *RH)
{
	  uint8_t Rh_byte1 = DHT11_Read ();

	  uint8_t Rh_byte2 = DHT11_Read ();

	  uint8_t Temp_byte1 = DHT11_Read ();

	  uint8_t Temp_byte2 = DHT11_Read ();

	  uint8_t SUM = DHT11_Read();

	  *TEMP = (float)Temp_byte1;
	  *RH = (float)Rh_byte1;

}

In main.c code:

#include "oled.h"
#include "dht11.h"
#include "delay.h"

float temp,hum;

char oled_buff[20];


int main(void)
{
	SSD1306_Init();
	SSD1306_GotoXY (0,0);
	SSD1306_Puts ("DHT11 STM32", &Font_11x18, 1);
	SSD1306_UpdateScreen(); //display

	dht11_PinA8_Init();

	GPIOA->MODER|=GPIO_MODER_MODE5_0;
	GPIOA->MODER&=~GPIO_MODER_MODE5_1;

	while(1)
	{
		dht11_start();
		if(Check_Response()==Response_OK)
		{
			Get_DHT_Data(&temp,&hum);

			sprintf(oled_buff,"Temp=%0.1f",temp);
			SSD1306_GotoXY (0,20);
			SSD1306_Puts(oled_buff,&Font_11x18, 1);

			sprintf(oled_buff,"Humi=%0.1f",hum);
			SSD1306_GotoXY (0,40);
			SSD1306_Puts(oled_buff,&Font_11x18, 1);

			SSD1306_UpdateScreen();

		}

		delay(1000);
		GPIOA->ODR ^=GPIO_ODR_OD5;
	}
}

You may download the entire project from here:

4. Demo:

You should see this once you uploaded the code:

Happy coding 🙂

Add Comment

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