Jump to content
Sign in to follow this  
touch

Interfacing with DHT11 Humidty + Temp sensor

Recommended Posts

Anyone done anything with this sensor? They're insanely cheap at 3 dollars each (http://www.suntekstore.com/goods.php?id=14002854), but they use a really funky (imo) one wire (not dallas one wire) data line.

 

It's so frustrating!!! I can get them to read the data out, I can even read the data they are sending manually using my oscilloscope, but damn If I can write any code to read it.. Guess I just don't have enough knowledge yet.

 

This is the datasheet for it

http://www.micropik.com/PDF/dht11.pdf

 

Anyone want to give them a try? I have an extra one somewhere around here I could send out if someone wanted to try it.

Share this post


Link to post
Share on other sites

So you have a while loop that measures how long a pin is held high.. put a 5us delay (or 10us?) inside the loop and increment a counter every time it loops, then it's no longer high your counter is *delay = result, shift and append value. Rinse, repeat.... I'm too tired, but I know there's code on here for it...

Share this post


Link to post
Share on other sites

I'd post my code... But I managed to overwrite it accidentally...DERP.

 

I was actually doing that but could not get it to work out with the timings, it would randomly get stuck in the loop. I made a timeout for the while loop, but was still having problems.

 

I was actually thinking about having using a timer to increment 1us and then counting the length of pulses, but didn't have any luck with this.

Share this post


Link to post
Share on other sites

They also have a 10 pack for 16.37. That price was too good to refuse, so I ordered some.

 

The TimerA capture/compare could be used to accurately time the edges. That would be the easy way to do it in C, but it limits what pins can be used.

 

Polling or port interrupts can be used to allow the use of any pin. Just configure TimerA for continuous mode and read TAR when a transition is detected. Subtract the previous TAR value, and you know the elapsed time.

 

I will probably write some assembly code once I get the sensors.

Share this post


Link to post
Share on other sites

I got the DHT11 sensors from SunTek today and wrote some C code to read it. This uses TimerA for accurate time measurement, and polling to allow use on any pin. The internal pullup resistor is used, so an external one is not needed.

A six byte array passed to read_dht() will be filled with the start bit, 4 data bytes, and the checksum. Return is 0 for success, or a negative value for failure. This code can run at 1 MHz or higher.

The DHT11 seems to need about 300 ms between readings, less time will result in the sensor sometimes being non-responsive. The fractional bytes are always 0 for the DHT11.

 

int read_dht(unsigned char *p)
{
                                                                // Note: TimerA must be continuous mode (MC_2) at 1 MHz
    const unsigned b = BIT4;                                    // I/O bit
    const unsigned char *end = p + 6;                           // End of data buffer
    register unsigned char m = 1;                               // First byte will have only start bit
    register unsigned st, et;                                   // Start and end times
                                                                //
    p[0] = p[1] = p[2] = p[3] = p[4] = p[5] = 0;                // Clear data buffer
                                                                //
    P1OUT &= ~b;                                                // Pull low
    P1DIR |= b;                                                 // Output
    P1REN &= ~b;                                                // Drive low
    st = TAR; while((TAR - st) < 18000);                        // Wait 18 ms
    P1REN |= b;                                                 // Pull low
    P1OUT |= b;                                                 // Pull high
    P1DIR &= ~b;                                                // Input
                                                                //
    st = TAR;                                                   // Get start time for timeout
    while(P1IN &  if((TAR - st) > 100) return -1;             // Wait while high, return if no response
    et = TAR;                                                   // Get start time for timeout
    do {                                                        //
        st = et;                                                // Start time of this bit is end time of previous bit
        while(!(P1IN & ) if((TAR - st) > 100) return -2;      // Wait while low, return if stuck low
        while(P1IN &  if((TAR - st) > 200) return -3;         // Wait while high, return if stuck high
        et = TAR;                                               // Get end time
        if((et - st) > 110) *p |= m;                            // If time > 110 us, then it is a one bit
        if(!(m >>= 1)) m = 0x80, ++p;                           // Shift mask, move to next byte when mask is zero
    } while(p < end);                                           // Do until array is full
                                                                //
    p -= 6;                                                     // Point to start of buffer
    if(p[0] != 1) return -4;                                    // No start bit
    if(((p[1] + p[2] + p[3] + p[4]) & 0xFF) != p[5]) return -5; // Bad checksum
                                                                //
    return 0;                                                   // Good read
}

Share this post


Link to post
Share on other sites

oPossum,

 

Great stuff!

 

Once again, this forum has allowed me to do things that I am not smart enough to do on my own!

 

So, I am trying to use oPossum's code and am having troubling displaying the values using the UART from a 2553.

 

dht_buffer should hold an array of 6 bytes, the start bit, the 4 data bytes, and the checksum.

 

It's my understanding the first data byte holds the left hand side of the decimal point of the humidity value, the next the right hand side, the third the left hand side of the temp in celsius and finally the right hand side of the temp all in binary.

 

I am trying to display via the hardware UART. The included function takes strings and is giving me jibberish. What's the best way to get the data to something I can display via the hardware UART?

 

Anything else I am doing wrong?

 

Thanks for help out a newbie who is learning!

 

/*
* ======== Standard MSP430 includes ========
*/
#include 

/*
* ======== Grace related includes ========
*/
#include 

/*
*	VARIABLES
*/

unsigned char dht_buffer[6];

/*
* PROTOTYPES
*/

int read_dht(unsigned char *p);
void TXString( char* string, int length );

/*
*  ======== main ========
*/
int main(int argc, char *argv[])
{
   CSL_init();                     // Activate Grace-generated configuration

   // >>>>> Fill-in user code here <<<<<
   while (1)
   {
   	read_dht(dht_buffer);

   	TXString(dht_buffer, 6);
    TXString("\r\n", 2);

    _delay_cycles(500000);                                       // 500 ms - delay

   }

   return (0);
}

int read_dht(unsigned char *p)
{
                                                               // Note: TimerA must be continuous mode (MC_2) at 1 MHz
   const unsigned b = BIT0;                                    // I/O bit
   const unsigned char *end = p + 6;                           // End of data buffer
   register unsigned char m = 1;                               // First byte will have only start bit
   register unsigned st, et;                                   // Start and end times
                                                               //
   memset(p, 0, 6);                                            // Clear data buffer
                                                               //
   P1OUT &= ~b;                                                // Pull low
   P1DIR |= b;                                                 // Output
   P1REN &= ~b;                                                // Drive low
   _delay_cycles(18000);                                       // 18 ms - change this for faster CPU clock
   P1REN |= b;                                                 // Pull low
   P1OUT |= b;                                                 // Pull high
   P1DIR &= ~b;                                                // Input
                                                               //
   st = TAR;                                                   // Get start time for timeout
   while(P1IN &  if((TAR - st) > 100) return -1;             // Wait while high, return if no response
   et = TAR;                                                   // Get start time for timeout
   do {                                                        //
       st = et;                                                // Start time of this bit is end time of previous bit
       while(!(P1IN & ) if((TAR - st) > 100) return -2;      // Wait while low, return if stuck low
       while(P1IN &  if((TAR - st) > 200) return -3;         // Wait while high, return if stuck high
       et = TAR;                                               // Get end time
       if((et - st) > 110) *p |= m;                            // If time > 110 us, then it is a one bit
       if(!(m >>= 1)) m = 0x80, ++p;                           // Shift mask, move to next byte when mask is zero
   } while(p < end);                                           // Do until array is full
                                                               //
   p -= 6;                                                     // Point to start of buffer
   if(p[0] != 1) return -4;                                    // No start bit
   if(((p[1] + p[2] + p[3] + p[4]) & 0xFF) != p[5]) return -5; // Bad checksum
                                                               //
   return 0;                                                   // Good read
}

void TXString( char* string, int length )
{
 int pointer;
 for( pointer = 0; pointer < length; pointer++)
 {
   volatile int i;
   UCA0TXBUF = string[pointer];
   while (!(IFG2&UCA0TXIFG));              // USCI_A0 TX buffer ready?
 }
}

Share this post


Link to post
Share on other sites

I'm also having some issues using this with the built in UART to display the values through the USB port. I have tried touch's code (here) without modifications and have a few times got output at the USB serial console. Normally it doesn't output anything at all, but if I switch the jumpers on the launchpad from software to hardware UART when it is already powered and connected to USB, it has a few times shown the output on the console just fine. Then if I closed the terminal on the pc, and opened it again, it didn't work anymore. Does anyone know what I could be missing..?

Share this post


Link to post
Share on other sites

This code will display the DHT11 hex data, and humidity and temperature in decimal.
It uses software UART so it will work on all versions of the Launchpad.
DHT11 is connect to P1.4

post-2341-135135546253_thumb.png

 


#include <msp430.h>
#include <stdlib.h>

volatile unsigned txdata = 0;

#pragma vector = TIMER0_A0_VECTOR
__interrupt void Timer0_A0(void)
{
    if(txdata) {
        (txdata & 1) ? (CCTL0 &= ~OUTMOD2) : (CCTL0 |=  OUTMOD2); 
        txdata >>= 1;
        CCR0 += 104;
    } else {
        CCTL0 &= ~CCIE;
    }
}

void putc(const char c)
{
    while(CCTL0 & CCIE);
    txdata = 0x0200 | (c << 1);
    CCR0 = TAR + 16;
    CCTL0 = OUTMOD0 | CCIE;
}

void print(const char *s)
{
    while(*s) putc(*s++);
}

void print_hex_digit(unsigned n)
{
    putc("0123456789ABCDEF"[n & 0x0F]); 
}

void print_hex_byte(unsigned n)
{
    print_hex_digit(n >> 4);
    print_hex_digit(n);
}

void print_dec2(int n)
{
    // Print 2 decimal digits  0 to 99
    const div_t d = div(n, 10);
    if(d.quot) putc('0' + d.quot);
    putc('0' + d.rem);
}

int read_dht(unsigned char *p)
{
                                                                // Note: TimerA must be continuous mode (MC_2) at 1 MHz
    const unsigned b = BIT4;                                    // I/O bit
    const unsigned char *end = p + 6;                           // End of data buffer
    register unsigned char m = 1;                               // First byte will have only start bit
    register unsigned st, et;                                   // Start and end times
                                                                //
    p[0] = p[1] = p[2] = p[3] = p[4] = p[5] = 0;                // Clear data buffer
                                                                //
    P1OUT &= ~b;                                                // Pull low
    P1DIR |= b;                                                 // Output
    P1REN &= ~b;                                                // Drive low
    st = TAR; while((TAR - st) < 18000);                        // Wait 18 ms
    P1REN |= b;                                                 // Pull low
    P1OUT |= b;                                                 // Pull high
    P1DIR &= ~b;                                                // Input
                                                                //
    st = TAR;                                                   // Get start time for timeout
    while(P1IN &  if((TAR - st) > 100) return -1;             // Wait while high, return if no response
    et = TAR;                                                   // Get start time for timeout
    do {                                                        //
        st = et;                                                // Start time of this bit is end time of previous bit
        while(!(P1IN & ) if((TAR - st) > 100) return -2;      // Wait while low, return if stuck low
        while(P1IN &  if((TAR - st) > 200) return -3;         // Wait while high, return if stuck high
        et = TAR;                                               // Get end time
        if((et - st) > 110) *p |= m;                            // If time > 110 us, then it is a one bit
        if(!(m >>= 1)) m = 0x80, ++p;                           // Shift mask, move to next byte when mask is zero
    } while(p < end);                                           // Do until array is full
                                                                //
    p -= 6;                                                     // Point to start of buffer
    if(p[0] != 1) return -4;                                    // No start bit
    if(((p[1] + p[2] + p[3] + p[4]) & 0xFF) != p[5]) return -5; // Bad checksum
                                                                //
    return 0;                                                   // Good read
}

void main(void)
{
    unsigned char dht[6];                           // Data from DHT11
    int err;                                        //
    unsigned n;                                     //
                                                    //
    WDTCTL = WDTPW | WDTHOLD;                       // Disable watchdog reset
    DCOCTL = 0;                                     // Run at 1 MHz
    BCSCTL1 = CALBC1_1MHZ;                          //
    DCOCTL  = CALDCO_1MHZ;                          //
    CCTL0 = OUT;                                    // Setup serial tx I/O
    P1SEL = BIT1;                                   //
    P1DIR = BIT1;                                   //
    TACTL = TASSEL_2 | MC_2;                        // TimerA SMCLK, continuous
    __enable_interrupt();                           //
                                                    //  
    for(; {                                       // for-ever
        while(CCTL0 & CCIE);                        // Wait for any serial tx to finish
        err = read_dht(dht);                        // Read DHT11
        if(err) {                                   // If error...
            print("Error -");                       //
            putc('0' - err);                        //
            print("\r\n");                          //                  
        } else {                                    // No error...
            for(n = 0; n < sizeof(dht); ++n) {      // Show hex data
                print_hex_byte(dht[n]);             //
                putc(' ');                          //
            }                                       //
            print(" ");                             //
            print_dec2(dht[1]);                     // Show humimidy
            print(" RH  ");                         //
            print_dec2(dht[3]);                     // Show temperature
            print(" C\r\n");                        //
        }                                           //
        __delay_cycles(1000000);                    // Wait a while
    }                                               //
}

 

 

Share this post


Link to post
Share on other sites

Hi,

 

I am a newbie and just started to learn things with my recently orered cheap Stellaris Launcpad EK-LM4F120XL, Googling for things to get started I found this excellent form. I have two questions :

 

1). Will the above DHT11 code work with my version of Launchpad ?

2). If yes, any pointer to how to connect it and read data on a PC ?

 

Thanks

Share this post


Link to post
Share on other sites

Got the DHT22 (actually AM2302, with built in 5.1K pullup on DATA) working with oPossum's TA0 code in conjunction with ladyada's code for the Arduino library.  Bravo!

retval = 0: 01 01 97 01 00 99 RH: 40%, Temp: 25 degC

Looks like my dehumidifiers are working correctly!

Share this post


Link to post
Share on other sites

For what it's worth, my exact code:

 

uartcli.c and uartcli.h from my mspuartcli lib -- https://github.com/spirilis/mspuartcli (just using the basic uart I/O primitives with no CLI input, someday I'll replace it with oPossum's printf() too)

 

dht22.c:

/* Code from oPossum @ 43oh */

#include <msp430.h>
#include <stdint.h>

int read_dht(uint8_t *p)
{
        const uint8_t b = BIT4;
        const uint8_t *end = p + 6;
        register uint8_t m = 1;
        register uint16_t st, et;

        p[0] = p[1] = p[2] = p[3] = p[4] = p[5] = 0;

        P1OUT &= ~b;     // Pull low
        P1DIR |= b;      // Output
        P1REN &= ~b;     // Drive low
        st = TA0R; while((TA0R - st) < 18000);  // Wait 18ms
        P1REN |= b;      // Pull low
        P1OUT |= b;      // Pull high
        P1DIR &= ~b;     // Input

        st = TA0R;       // Get start time for timeout
        while (P1IN &  {  // Wait while high, return if no response
                if ((TA0R - st) > 100)
                        return -1;
        }
        et = TA0R;       // Get start time for timeout
        do {
                st = et; // Start time of this bit is end time of previous bit
                while ( !(P1IN &  ) {        // Wait while low, return if stuck low
                        if ((TA0R - st) > 100)
                                return -2;
                }
                while (P1IN &  {             // Wait while high, return if stuck high
                        if ((TA0R - st) > 200)
                                return -3;
                }
                et = TA0R;                     // Get end time
                if ((et - st) > 110)           // If time > 110uS, then it is a "one" bit.
                        *p |= m;
                if (!(m >>= 1)) {              // Shift mask, move to next byte when mask is zero
                        m = 0x80;
                        ++p;
                }
        } while (p < end);                     // Do until array is full

        p -= 6;          // Point to start of buffer
        if (p[0] != 1)   // No start bit
                return -4;
        if ( ((p[1] + p[2] + p[3] + p[4]) & 0xFF) != p[5] )  // Bad checksum
                return -5;

        return 0;        // Good read
}

main.c:

#include <msp430.h>
#include <stdint.h>
#include "uartcli.h"
#include "dht22.h"


char inbuf[16];  // CLI input, not actually used...
volatile uint16_t sleep_counter;

int main()
{
        uint8_t dht22buf[6];
        int i;

        WDTCTL = WDTPW | WDTHOLD;
        DCOCTL = CALDCO_16MHZ;
        BCSCTL1 = CALBC1_16MHZ;
        BCSCTL2 = DIVS_1;
        BCSCTL3 = LFXT1S_2;

        TA0CTL = TASSEL_2 | MC_2 | ID_3;

        uartcli_begin(inbuf, 16);

        sleep_counter = 0;
        IFG1 &= ~WDTIFG;
        IE1 |= WDTIE;
        _EINT();

        while (1) {
                TA0CTL |= TACLR;
                i = read_dht(dht22buf);
                /* Buffer contents:
                 * dht22buf[0] = Start Bit (0x01)
                 * dht22buf[1:2] = Big-Endian 16-bit Unsigned Integer for Relative Humidity, Times 10
                 * dht22buf[3:4] = Big-Endian 16-bit Signed Integer for Temperature, Times 10
                 * dht22buf[5] = Checksum (dht22buf[1:4] summed and & 0xFF)
                 */
                uartcli_print_str("retval = ");
                uartcli_print_int(i);
                uartcli_print_str(": ");
                for (i=0; i < 6; i++) {
                        uartcli_printhex_byte(dht22buf[i]);
                        uartcli_print_str(" ");
                }
                uartcli_print_str("RH: ");
                uartcli_print_int( (dht22buf[1]*256+dht22buf[2]) / 10 );
                uartcli_print_str("%, Temp: ");
                uartcli_print_int( ((int) ((dht22buf[3] << 8) | dht22buf[4])) / 10 );
                uartcli_println_str(" degC");

                WDTCTL = WDT_ADLY_250; // VLOCLK = ~744ms per tick
                sleep_counter = 4; // 744ms * 4 = ~3 seconds

                LPM3;
                WDTCTL = WDTPW | WDTHOLD;
        }
}

#pragma vector=WDT_VECTOR
__interrupt void WDT_ISR(void)
{
        IFG1 &= ~WDTIFG;
        if (sleep_counter)
                sleep_counter--;
        else
                __bic_SR_register_on_exit(LPM3_bits);
}

Current output:

retval = 0: 01 01 A0 00 F9 9A RH: 41%, Temp: 24 degC
retval = 0: 01 01 A0 00 F9 9A RH: 41%, Temp: 24 degC
retval = 0: 01 01 9F 00 F9 99 RH: 41%, Temp: 24 degC
retval = 0: 01 01 9D 00 F9 97 RH: 41%, Temp: 24 degC

Share this post


Link to post
Share on other sites

I've noticed with this sensor, it does take a while for the humidity readings to change; breathing on it directly until it registers 99%, it took quite a while and a bit of blowing to get it to drop below 99%.  Probably something you want to sample every 10+ minutes in the field.

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Sign in to follow this  

×
×
  • Create New...