In this tutorial we learn how to interface DHT11 and DHT22 Humidity and Temperature sensor with ARM Cortex-M3 LPC1768 microcontroller. I had discussed basics of DHT11/DHT22 interfacing in my previous tutorial. Please go through it if you are new to DHTxx Humidity and Temperature sensors.
A quick recap of the communication process:
DHT11 & DHT22 Data format:
Now, lets have a look at steps we need to implement for Programming DTH11/DHT22. 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 required for DHT11/DHT22 Interfacing:
- Configure PIN to be used to communication as OUTPUT.
- Set the PIN O/P to LOW and wait for 18 milli-seconds.
- Configure the PIN as INPUT. The pull-up resistor will Pull the bus to HIGH.
- Wait until DHT11 responds and pulls the line to LOW after around 40 micro-seconds or else exit as timeout error.
- Next, check for 80us LOW followed by 80us HIGH from DHT11. This condition denotes that DHT11 is ready to send data next.
- 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.
- Extract the data bytes from array to record or display it. Optionally the checksum can be used to verify data integrity.
- Wait for at least 1 second before starting again to fetch new data.
Program for Interfacing DHT11/DHT22 with ARM LPC1768/LCP1769:
In this example we will use PIN P0.0 (marked as P9 on mbed board) for communication with DHT11 sensor. For DHT22 you just need to make a small change 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 module is used for generating delay and measuring timing of responses given by DHTxx. You can go through LPC1768 timer tutorial for reference.
We will also use UART0 to send data back to PC and display it using software terminal. printf() has been targeted such that its output gets redirected to UART0. Checkout my tutorial on how to retarget printf() in keil and LPC1768 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:
- startTimer0() – Resets counter and Enables Timer0 block.
- unsigned int stopTimer0() – Disables counting and returns value in T0TC.
- checkResponse(…) – Used to check the response from DHTxx. It has 3 parameters viz. unsigned int waitTimeUS, unsigned int margin, bool pinValue. waitTimeUS is simply the expected wait time in micro-seconds. Margin is additional timing error or offset we can account for, since the sensor might have intrinsic timing offets. pinValue is the current expected state of pin which it must hold until the given waitTime.
- char getDataBit() – This checks the pulses for a ‘0’ or a ‘1’ and returns the value accordingly. It will return 2 in case of an error.
- printError(const char * str) – Prints error message and halts program execution by entering infinite while loop. You must reset board in this situation or you can extend the code to handle errors on the fly.
DHT11 Humidity & Temperature sensor Interfacing Source Code Snippet
/*(C) Umang Gajera - www.ocfreaks.com
DHT11 Humidity and Temperature Sensor Interfacing with LPC1768/LPC1769 Example Source Code for KEIL ARM
More Embedded tutorials @ www.ocfreaks.com/cat/embedded/
License : GPL.*/
#include <lpc17xx.h>
#include <stdio.h> //For retargeted printf() - https://www.ocfreaks.com/retarget-redirect-printf-scanf-uart-keil/
#include "ocf_lpc176x_lib.h" //contains initUART0(), initTimer0() & putc() to retarget printf()
#define LOW 0
#define HIGH 1
void printError(const char * str);
void checkResponse(unsigned int waitTimeUS, unsigned int margin, unsigned char pinValue);
char getDataBit(void);
#define DATA_PIN (1<<0) //Using P0.0 for data communication
int main(void)
{
unsigned char dataBits[40] = {0};
char dataBytes[5] = {0};
initTimer0();
initUART0();
printf("OCFreaks.com - Interfacing DHT11 with LPC1768 Example.\n");
while(1)
{
//STEP 1: Set pin to output HIGH which represents idle state
LPC_GPIO0->FIODIR |= DATA_PIN;
//STEP 2: Pull down pin for 18ms(min) to denote START
LPC_GPIO0->FIOCLR |= DATA_PIN;
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.
LPC_GPIO0->FIODIR &= ~(DATA_PIN);
//STEP 4: Wait between 20 to 40us for sensor to respond
startTimer0();
while((LPC_GPIO0->FIOPIN & DATA_PIN) != 0)
{
if(LPC_TIM0->TC > 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 DHTxx 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, unsigned char pinValue)
{
int time = 0;
int maxwait = waitTimeUS + margin;
startTimer0();
if(pinValue)
{
while(LPC_GPIO0->FIOPIN & DATA_PIN)
{
if(LPC_TIM0->TC > (maxwait)) break;
}
}
else
{
while( !(LPC_GPIO0->FIOPIN & DATA_PIN) )
{
if(LPC_TIM0->TC > (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(LPC_GPIO0->FIOPIN & DATA_PIN)
{
if(LPC_TIM0->TC > 75)
{
return 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
{
return 2; //Error - Timeout for data pulse
}
}
Here is a screenshot of the above example in action: