In this tutorial we will go through on how to interface DHT11/DHT22 sensor with LPC2148 ARM7 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:
DHT11 & DHT22 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:
- 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 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:
- startTimer0() – Resets counter and Enables Timer0 block.
- unsigned int stopTimer0() – Disables counting and returns value in T0TC.
- checkResponse(…) – Used to check the response from DHT11. 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 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 - https://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: