I picked up a nice compact little temperature and relative humidity sensor called the
RHT03 for a project from Little Bird Electronics. It and very similar parts appear to go by the names
AM2302. You can find the part at SparkFun, Adafruit, etc too.
It took a lot more work to get it working than I expected, so I thought I'd write it up here for anyone else who is looking into it. There's sample code at the end of this post, but you should probably read the details because this is a quirky beast.
UPDATE: I since found a library on GitHub: nethoncho/Arduino-DHT22 that does a better job more simply and compactly. It works fine with my sensor. It needed some changes for Arduino 1.0 and some further tweaks to work how I wanted, so I've uploaded a fork here: https://github.com/ringerc/Arduino-DHT22.
Given that it's clearly a pretty commonplace part, I thought reading it couldn't be too tricky. Foolish me!Its datasheet almost in engrish and the sample code (warning: Word document) is pretty horrid. The sensor communicates over a custom (non-Dallas-compatible) 1-wire interface where the lengths of low and high pulses convey information. Parts of the datasheet are practically fiction.
Particular things to note when working with this sensor are:
- When requesting a sensor read, you may have to actively pull the signal line up to +5V for 20-40μs after your initial 1 - 10ms low pulse.
- The datasheet is a lie. In particular, instead of a neat 50μs low signal before sending a bit, my sensor may pull the line low for anything from 35 to 75μs. Similarly, 0-bit high pulses may be from 10-40μs instead of the documented 22-26μs, and 1-bit high pulses may be 60-85μs instead of 70μs. Actually, surprise surprise, this was probably mostly my code not masking the timer interrupt while conversing with the sensor, thus losing time when the timer ISR ran. Surprise, newbie code buggy. I still measure significant timing variation from the sensor with NetHoncho's code, but nothing like as bad.
- Even when you're being tolerant of timing weirdness, you'll probably get the odd bad read. Adding a cap across the +5VDC input (as mentioned in the datasheet) doesn't seem to help, so it's probably not power fluctuation. Be prepared to re-try reads and remember to wait at least 2s between reads. ("The odd bad read" was probably the timer issue mentioned above, but you should be ready for them anyway)
- (Didn't trouble me, but): Remember to let the sensor settle for at least 2s after power-on before attempting a read, and at least 2s between reads.
- Your code must be extremely fast to avoid missing state transitions when capturing the bit stream, whether you're using a looping or interrupt-driven capture method. I landed up buffering the stream to an array of pulse lengths, then interpreting it after the full stream was buffered. This would be less of an issue if the timings from the sensor weren't so erratic to start with.
Having finally got it working, I thought I'd post some sample code. It's very much that - sample code - not a library suitable for direct re-use. In particular, this code does not handle millisecond counter roll-over at all. Reads during which the millisecond counter rolls over will fail. That said, at least the checksum is verified and negative temperatures are handled correctly. (By the way, a USB cable coming out of the freezer door seal looks really weird).
You should really look at the GitHub project for DHT-22 linked to above rather than using this code.
To try the code with an Arduino Uno R3 (ATMEGA328 based), connect the sensor's I/O wire (pin 2) to digital pin 2 on the Arduino. Connect sensor pin 1 to +5V and sensor pin 3 and 4 to GND. Theoretically you should be able to let pin 3 float, but I've seen reports that some sensors have their internal ground on pin 3 instead of pin 4, so it won't hurt to ground both.
I use pin 2 on the Arduino because it's one of the two pins that support external interrupts on the basic Arduino boards. Pin 3 will also work, just change
sensorPin in the example code.
You could make the code work without requiring interrupts, either by busy-waiting in a loop to detect level changes or by using the
pulseIn function to read pulse lengths. I haven't implemented that, but it shouldn't be hard. You can re-use all the code except
readSensor, which must be rewritten to capture the
timings array a non-interrupt-driven way. The trick is making it fast enough.