Well tonight has been productive! I’ve managed to get the DS1307 working with my STM32F0 and now the AM2302! I thought that I’d actually done a previous post on interfacing the DHT11 humidity and temperature sensor but it turns out I hadn’t! The method of operation and data transfer is pretty much the same so it shouldn’t be too hard to modify the code to work between the two.
So! Where to begin? The AM2302 is a cheap integrated humidity and temperature sensor which has 4 pins, VCC, Data, Ground and… oh, ground. The AM2302 (or DHT22, I don’t think there is a difference…) is considered the higher quality big brother of the DHT11 and can be found easily on the good ol’ eBay (http://goo.gl/WaJW16 – Link will expire!) The data is transmitted on a bi-directional open drain one wire bus. Unlike the Maxim one wire bus, the sensor isn’t phantom powered and only data is transferred down this line, as opposed to power and data. To ensure that a change in logic state can actually be detected on the data line, I’ve used a 1k pullup, as recommended on the data sheet (http://goo.gl/OgUUG9). The DHT22 has a quite odd protocol but one that I consider quite cool in how effectively it transfers data to the host. The data sent from the sensor to the microcontroller is 16bit relative humidity data, 16bit temperature and an 8bit checksum.
The actual sensing range of the DHT22 is -40 to 80 degC and 0 to 100% relative humidity (defined in the datasheet). If the temperature is negative, the most significant bit of the temperature data will be set. If the temperature is positive, the most significant bit will be cleared. In my program, I take account for negative temperatures. The sensor can only be polled for data every two seconds and after throwing successive data attempts at it, I managed to crash it to take note!
To start a transmission, the MCU must pull the data line low for >1ms then release the bus (allowing the pull up resistor to pull the bus up) for 20-40us. Once this has happened, the sensor will then pull the bus low for 80us as an acknowledgement signal then release the bus for another 80us to signal the start of a transfer.
The actual bit value is defined by on time. In my code example, I use the whole wave period to determine the piece of data. As the low time is defined at 50us, I decided that this is essentially constant. When a 1 is transmitted, the bus is low for 50us then high for 70us. This gives a total period of 120us for a high condition. When a 0 is transmitted, the bus is also low for 50us but it then becomes high for 26-28us giving a period of 76-78us. I’m using external interrupts and a timer to time the period then storing the period lengths in an array and processing them further in my program.
Once 40 periods have been measured, the timer is disabled and the external interrupt exits. Inside of the data processing section, the time periods are compared to a constant value and if the period is less than this constant, a 0 is placed in a seperate bit keeping array. If the period is more than this constant, a 1 is places in the bit keeping array. Once the bits have been placed in the bit keeping array, the bits are packed into individual unsigned 16bit words, apart from the checksum. The sum of the temperature and humidity (its actually the sum of the top 8 bits of the temperature, bottom 8 bits of the temperature, top 8 bits of the humidity and the bottom 8 bits of the humidity, executed under 8 bit addition allowing for overflows.) is then calculated and compared to the received checksum. If the data is correct, a data correct flag is set. The temperature is then checked for negativity and both temperature and humidity are divided by 10 and stored in floating point variables.
After programming this in ~200 lines, I managed to get the temperature and humidity of my room! For those who are interested, while the sensor is asleep, it only consumes ~18uA! During conversion, this shoots up to about 840uA (measured crudely with my multimeter). These value exclude the pullup resistor current. This is pretty close to what the datasheet mentions. During sleep time, current should be between 40uA and 50uA. During acquisition, this current should then be ~1mA which seems to fit with my measurements! I am actually undervolting the sensor by running it at 3v instead of the specified 3.3v minimum but it works fine.
As ever, you can find my code on Github. Happy coding!