Interfacing with DHT11 temperature + RH sensor

Posted on
  • The DHT11 (and higher-spec'ed DHT22 - I've got one on it's way from china to try out) are widely available temperature+relative humidity sensors that communicate with the controller via an (apparently non-standard) single wire protocol, as (inaccurately) described in the datasheet: http://www.micro4you.com/files/sensor/DH­T11.pdf The most appealing feature of these sensors is that they are cheaper than dirt (like, $1.50 a pop, shipped cheap) - and their accuracy is good enough for lots of cases.

    After some experimentation, I was able to get correct date out of the DHT11 with the code provided below. I'm sure this code is pretty far from ideal, and I'd like to clean it up - if anyone has any advice on that, I'd love to hear it. I know the fact that I'm doing setTimeout() with a string to be evaluated is bad, particularly since it makes it awkward to select the pin to use - but I can't figure out how to make it work otherwise. I figure I should also put all of those global variables inside of the dht object. Also - is there a better way than making an array of bits and accessing them like I do?

    I found a few caveats when interfacing with these:

    • The spec sheet says it returns 40 bits - 32 data bits plus an 8 bit checksum. My DHT11's are only returning 32 bits. Also, 16 of those bits are always zero, as the DHT11 has no decimal data to return (resolution is only 1 degree, 1% RH).
    • Sometimes it doesn't seem to recognize the start signal. Not sure why - so I have dht.onread() make sure we actually got data, and try again if we didnt.

    usage: dht.read(function());

    ex: dht.read(function(a){print("Data: "+a.rh.toString()+" %RH "+a.temp.toString()+" C");});

    Get data from the DHT11, and then call the supplied function when the data is available. Function is called with one argument, an object with properties 'temp' and 'rh'.

    var dht={};
    var watch;
    var i=0;
    var bits=[];
    var pstart=0;
    dht.read = function (a) {
    	dht.onreadf=a;
    	i=0;
    	bits=[];
    	digitalPulse(C6,1,0.08);
    	setTimeout("pinMode(C6,'input_pullup');w­atch=setWatch(function(t) {dht.onwatch(t);},C6,{repeat:true});",0.­07);
    	setTimeout("dht.onread(dht.endRead());",­50);
    }
    dht.onread= function(d) {
    	if (d.temp==-1) {
    		dht.read(dht.onreadf);
    	} else {
    		dht.onreadf(d);
    	}
    }
    dht.onwatch = function(t) {
    	//console.log(t.time+" "+t.state);
    	if (t.state) {
    		pstart=t.time
    	} else {
    		var tt=t.time-pstart;
    		if (tt > 0.000044) {
    			bits[i]=1;
    		} else {
    			bits[i]=0;
    		}
    		i++;
    	}
    }
    dht.endRead = function() {
    	clearWatch(watch);
    	if (i==34 | i==35 | i==36) {
    		var rh=eval("0b"+bits[2]+bits[3]+bits[4]+bit­s[5]+bits[6]+bits[7]+bits[8]+bits[9]);
    		var temp=eval("0b"+bits[18]+bits[19]+bits[20­]+bits[21]+bits[22]+bits[23]+bits[24]+bi­ts[25]);
    		//console.log("Data Obtained: "+rh.toString()+" %RH "+temp.toString()+" C");
    		return {"temp":temp,"rh":rh};
    	} else {
    		//console.log("Bad data: "+i);
    		return {temp:-1,rh:-1};
    	}
    }
    
  • I was unaware that setTimeout supported timeouts of less than 1 ms, you have 0.07. I don't yet know whether this is an Espruino feature.

    As for the eval'd strings, you can always pass a function that returns what you want to run, take a look at line 51.

  • @randunel, Espruino can support timeouts relatively accurately, but at much lower than 0.5ms it's very unlikely to hit the target. The point is to delay the pin mode setting until after the digitalPulse though - because the digitalPulse returns immediately and pulses the pin on an IRQ.

    Just a note on the code though - if you define a function (rather than a string) to be called from setWatch/setTimeout then when it executes, it has access to the variables in the function it was defined in:

    function a() {
     b=5;
     setTimeout(function() { print(b); }, 100);
    }
    

    I think that could let you tidy the code up quite a bit. It might also make sense to set up setWatch before the digital pulse, and to just write code to ignore the first pulse (as this could be why you're not seeing some of the bits?).

    As far as evaluating the final value, I'd suggest just using binary arithmetic. You've got 64 bit numbers, so you should easily have enough space for all 40 bits. So change:

    var bits = [];
    ...
            if (tt > 0.000044) {
                bits[i]=1;
            } else {
                bits[i]=0;
            }
            i++;
    ...
    var rh=eval("0b"+bits[2]+bits[3]+bits[4]+bit­s[5]+bits[6]+bits[7]+bits[8]+bits[9]);
    

    to

    var bits = 0;
    ...
            bits=(bits<<1) | (tt > 0.000044);
    ...
    var rh=(bits>>2)&0xFF;
    

    It'll be a bit faster too...

  • Thanks. Yeah, setTimeout() definitely works with values less than 1 ms, though I'm sure the timing isn't perfect.

    Even if I were to setWatch() before the pulse, wouldn't I still need to do pinMode() after the pulse to set it back to 'input_pullup'? I'm already discarding some of the bits at the beginning (namely, the first two, from the handshake).

    This delay is not the cause of the missing checksum bits, in any event - the missing bits should be at the end, while an incorrect delay would chop off the beginning.

    Oh! I didn't realize you could access the variables from a function defined in setTimeout/setWatch - that makes life a lot easier.

    I'll look into the bitwise operators - they're something I'd never really known existed, since I approach javascript from web development. These aren't used much in web design (for example, W3S doesn't even cover them). This looks so much more graceful than what I was doing.

  • This looks much better:

    var dht={};
    var watch;
    var i=0;
    var out=0;
    var pstart=0;
    var badbits=0;
    dht.read = function (a,pin) {
    	dht.onreadf=a;
    	if (pin===undefined) {pin=C6;}
    	i=0;
    	out=0;
    	badbits=0;
    	digitalPulse(pin,1,0.08);
    	setTimeout(function() {pinMode(pin,'input_pullup');watch=setWa­tch(function(t) {dht.onwatch(t);},pin,{repeat:true});},0­.07);
    	setTimeout(function() {dht.onread(dht.endRead());},50);
    }
    dht.onread= function(d) {
    	if (d.temp==-1) {
    		dht.read(dht.onreadf);
    	} else {
    		dht.onreadf(d);
    	}
    }
    dht.onwatch = function(t) {
    	if (t.state) {
    		pstart=t.time
    	} else {
    		var tt=t.time-pstart;
    		if (tt < 0.000044) {
    			badbits = 1;
    		}
    		if (badbits) {
    			out=(out<<1) | ((tt > 0.000044) && (tt < 0.0001));
    		}
    		i++;
    	}
    }
    dht.endRead = function() {
    	clearWatch(watch);
    	if (badbits && i > 32) {
    		rh=(out>>(i-10))&0xFF;
    		temp=(out>>(i-26))&0xFF;
    		return {"temp":temp,"rh":rh};
    	} else {
    		return {temp:-1,rh:-1};
    	}
    }
    

    Any other cleanup suggestions?

  • Thanks! It looks good... if you wanted, you could try formatting it like a module, and then we could add it so others can do var d = require("DHT11").connect(A0); d.read(callback). There's some info on that here.

    I've quickly hacked it up but haven't tested so it's almost certainly buggy I'm afraid :)

    The only strange bit is having to set var dht=this; because when you're in a timeout, this is set to something else.

    function DHT11(pin) {
      this.pin = pin;
    }
    
    DHT11.prototype.read = function (a) {
        this.onreadf=a;
        this.i=0;
        this.out=0;
        this.badbits=0;
        digitalPulse(this.pin,1,0.08);
        var dht = this;
        setTimeout(function() {pinMode(dht.pin,'input_pullup');dht.wat­ch=setWatch(function(t) {dht.onwatch(t);},dht.pin,{repeat:true})­;},0.07);
        setTimeout(function() {dht.onread(dht.endRead());},50);
    };
    DHT11.prototype.onread= function(d) {
        if (d.temp==-1) {
            dht.read(dht.onreadf);
        } else {
            dht.onreadf(d);
        }
    };
    DHT11.prototype.onwatch = function(t) {
        if (t.state) {
            this.pstart=t.time;
        } else {
            var tt=t.time-pstart;
            if (tt < 0.000044) {
                this.badbits = 1;
            }
            if (this.badbits) {
                this.out=(this.out<<1) | ((tt > 0.000044) && (tt < 0.0001));
            }
            this.i++;
        }
    };
    DHT11.prototype.endRead = function() {
        clearWatch(this.watch);
        if (this.badbits && this.i > 32) {
            rh=(this.out>>(this.i-10))&0xFF;
            temp=(this.out>>(this.i-26))&0xFF;
            return {"temp":temp,"rh":rh};
        } else {
            return {temp:-1,rh:-1};
        }
    };
    
    var d = new DHT11(C6);
    d.read(function(a) { print(a); });
    

    Looking at it, if it's not vital that you get exactly a 0.08ms pulse, I'd consider just writing:

    pinMode(dht.pin,'output');
    digitalWrite(dht.pin, 1);
    setTimeout(function() {
      pinMode(dht.pin,'input_pullup');
      ...
    }, 0.1);
    

    I'm not sure if that does quite what you want - but it's not guaranteed that the setTimeout will happen at exactly the same time as the digitalPulse ends (because another task might jump in there first)

  • I've got this working as a module now. Will issue pull request shortly.

    Thanks for that bit of conversion into module-style code - not only does it now work as a module, I think I now understand how to make my code into modules in the future.

  • No problem! Thanks for doing the work to modulify it :)

    The whole var foo = this; thing for timeouts is a bit strange - but once you've got that everything usually falls into place quite nicely.

  • Yup - I'd have never figured that var foo=this; bit out on my own.

    I think it would be nice to have an example of non-modulized code, and then the same code, in the form of a module, as part of the Module Writing tutorial. Some people (myself included) learn much better with an "example problem" so to speak.

    I'm excited to submit a module. I can't wait to get some other sensor in the mail that we have no module for yet :-P

  • I've written module for DHT22 now. Nifty little sensors these are...

    The DHT22 and DHT11's disagree about the humidity, though. Both of my 11's say 34%, and the 22 says 24%... They agree about the temperature at least....

    I think I'll take data from them over a period of time and see if they track eachother...

  • Hi DrAzzy

    Thanks for your effort. When i receive my espruino, i will try to use your module too.

    Sacha

  • Thanks! I'll update the website with the new modules today. I got a DHT11 in the post so I'll get to try it out now ;)

  • how to interfacing serial communication of single wire two way in mplab.sensor is DHT11 please reply this message

  • If you're using MPLab, my guess is that you don't have an Espruino board but have a PIC Microcontroller. If I were you I'd ask on the Microchip forums

  • Post a reply
    • Bold
    • Italics
    • Link
    • Image
    • List
    • Quote
    • code
    • Preview
About

Interfacing with DHT11 temperature + RH sensor

Posted by Avatar for DrAzzy @DrAzzy

Actions