OCFreaks!

DHT11 and DHT22 Interfacing with ARM LPC2148 Tutorial

In this tutorial we will go through on how to interface DHT11/DHT22 sensor with ARM7 LPC2148 microcontroller. In my previous tutorial I had discussed basics of DHT11/DHT22 interfacing – Please go through it if you are new to DHTxx Humidity and Temperature sensors.

Here is a quick recap of the communication process:

DHTxx Data format:

Now, lets see the algorithm or steps we need to implement for interfacing. Since only one DATA line(PIN) is used, we will use the same GPIO PIN first as OUTPUT to send START condition and then as INPUT to Receive data.

Steps For Programming DHT11/DHT22:

  1. Configure PIN to be used to communication as OUTPUT.
  2. Set the PIN O/P to LOW and wait for 18 milli-seconds.
  3. Configure the PIN as INPUT. The pull-up resistor will Pull the bus to HIGH.
  4. Wait until DHT responds and pulls the line to LOW after around 40 micro-seconds or else exit as timeout error.
  5. Next, check for 80us LOW followed by 80us HIGH from DHT. This condition denotes that DHT is ready to send data next.
  6. Now, check for 50us LOW followed by 26us to 28us HIGH denoting data bit ‘0’ or 50us LOW followed 70us HIGH denoting data bit ‘1’. Store the interpreted bit in an array. Repeat this for each of 40 bits.
  7. Extract the data bytes from array to record or display it. Optionally the checksum can be used to verify data integrity.
  8. Wait for at least 1 second before starting again to fetch new data.

Program for Interfacing DHT11/DHT22 with ARM LPC2148:

Now lets build a program for interfacing example. In this example we will us PIN P0.2 for communication with DHT11 sensor. For DHT22 you just need to make a slight modification in code to display the decimal part. I leave that to the reader. If you are using the bare 4 pin sensor than make sure that DATA pin is pulled up using 4.7K or 10K resistor. If you are using PCB mounted sensor which has 3 pins then external pullup resistor is not required since it is already present on the PCB. Timer0 block is used for generating delays and measuring timing of responses given by DHTxx. You can go through LPC2148 timer tutorial for reference.

We will also use UART0 to send data back to PC and display it using software terminal. Printf() has been retargetted such that its output gets redirected to UART0. Checkout my tutorial on how to retarget printf() in keil and LPC2148 UART tutorial for more. The schematic for connections between DHT11 and LPC214x is as given below:

Before going through to source code, first lets go through some the functions used:

DHT11 Interfacing Source Code in C++:


/*(C) Umang Gajera - www.ocfreaks.com
DHT11 Humidity and Temperature Sensor Interfacing with LCP2148 Example Source Code for KEIL ARM
More Embedded tutorials @ www.ocfreaks.com/cat/embedded/
License : GPL.*/
#include <lpc214x.h>
#include <stdio.h>
#include "lib_funcs.h" //Contains timer, uart and printf retarget code - http://www.ocfreaks.com/retarget-redirect-printf-scanf-uart-keil/

#define LOW 0
#define HIGH 1
void printError(const char * str);
void checkResponse(unsigned int waitTimeUS, unsigned int margin, bool pinValue);
char getDataBit(void);

//Using P0.2 for data communication

int main(void)
{
	bool dataBits[40] = {0};
	char dataBytes[5] = {0};
	
	initTimer0();
	initUART0();
	
	printf("OCFreaks.com - DHT11 Interfacing with LPC2148 example.\n");
	
	while(1)
	{
		//STEP 1: Set pin to output HIGH which represents idle state
		IO0DIR |= (1<<2);
		
		//STEP 2: Pull down pin for 18ms(min) to denote START
		IO0CLR |= (1<<2);
		delayUS(18000); //wait for 18ms
		
		//STEP 3: Pull HIGH and switch to input mode
		//pull-up will pull it HIGH after switching to input mode.
		IO0DIR &= ~(1<<2);
		
		//STEP 4: Wait between 20 to 40us for sensor to respond
		startTimer0();
		while( (IO0PIN & (1<<2)) != 0 )
		{
			if(T0TC > 40) break; //Timeout
		}
		unsigned int time = stopTimer0();
		
		if(time < 10 || time > 40) 
		{ 
			printError("Failed to communicate with sensor");
		}
		
		//STEP 5: Check for 80us LOW followed by 80us HIGH
		checkResponse(80,5,LOW);
		checkResponse(80,5,HIGH);
		
		//After this DHT sends data. Each bit has a preceding 50us LOW. After which 26-28us means '0' and 70us means '1'
		
		//STEP 6: Fetch data
		char data;
		for(int i=0; i < 40; i++)
		{
			data = getDataBit();
			
			if(data == 0 || data == 1)
			{
				dataBits[i] = data;
			}
			else printError("Data Error");
		}
		
		//STEP 7: Extract data bytes from array
		data = 0;
		for(int i=0; i<5; i++) // i is the BYTE counter
		{
			for(int j=0; j<8; j++) // j gives the current position of a bit in i'th BYTE
			{
				if( dataBits[ 8*i + j ] )
					data |= (1<<(7-j)); //we need to only shift 1's by ([BYTESZIE-1] - bitLocation) = (7-j)
			}
			dataBytes[i] = data;
			data = 0;
		}		
		
		printf("Humidity=%d%%, Temp=%d Deg. C\n",dataBytes[0], dataBytes[2]);
		
		//STEP8: Wait for atleast 1 second before probing again
		delayUS(1000000); 
	}
	//return 0;
}

void printError(const char * str)
{
	/*Print error and enter infinite loop to HALT program*/
	printf("%s\n",str);
	while(1);
}

void checkResponse(unsigned int waitTimeUS, unsigned int margin, bool pinValue)
{
	int time = 0;
	int maxwait = waitTimeUS + margin;
	
	startTimer0();
	if(pinValue)
	{
		while( IO0PIN & (1<<2) )
		{
			if(T0TC > (maxwait)) break; 
		}
	}
	else
	{
		while( !(IO0PIN & (1<<2)) )
		{
			if(T0TC > (maxwait)) break; 
		}
	}
	time = stopTimer0();
	
	if(time < (waitTimeUS-margin) || time > maxwait) 
	{
		//printf("Error for wait=%d,margin=%d,pinVal=%d,time=%d\n",waitTimeUS,margin,pinValue,time);
		printError("checkResponse() Error"); //Out of range, including error margin
	}
}

char getDataBit(void)
{
	int time = 0;
	
	checkResponse(50,5,LOW); //Each data bit starts with 50us low
	
	startTimer0();
	while( IO0PIN & (1<<2) )
	{
		if(T0TC > 75)
		{
			//printError("Data Error");
			return 2; //2 = Error (Timeout for 50us LOW)
		}
	}
	time = stopTimer0();
	
	if((time > (27-10)) && (time < (27+10))) //I am getting 21 for HIGH using my DHT11 sensor, so using higher margin
	{
		return 0;
	}
	else if((time > (70-5)) && (time < (70+5)))
	{
		return 1;
	}
	else 
	{ 
		//printError("Data Error");
		return 2; //Timeout for data pulse 
	}
}

Here is a screen of the above example in action:

KEIL ARM uV5 Project for example given above on GitHub @ DHT11 Interfacing with LPC2148 Example [Successfully tested on Keil uV5.23], Download Project Zip. You can find the HEX file inside objects folder.