This post is a continuation of my series on the weather station that I built. On e of the sensors that it uses is the AM2315 temperature/humidity sensor
The AM2315 is very poorly documented. This leads to several problems when attempting to use it. Thankfully, Sopwith has provided details of his experience with this device http://sopwith.ismellsmoke.net/wp-content/uploads/2014/03/PI-How-To-AM2315.pdf so we can move on more quickly to making use of it. Sopwith provides a more detailed explanation of how to set up and use this device. If you are using Python, then you must read his article, since he explains some additional problems caused by the way the Python library works.
The first problem you will run into is that it doesn't show up on the i2c bus. That is because the device stays in sleep mode until it is woken up to prevent generating heat that would affect the humidity sensor accuracy. If you run the command i2cdetect twice quickly, then the second time it will show up.
The second problem is that the i2c address is wrong in the datasheet. It is no longet at 0xB8, but is instead at 0x5c. This is not a big problem since you will see the correct address show up in the second i2cdetect results.
The third problem is really a side effect of the first - the device does not keep updated values in registers like most i2c devices. You will have to issue a read request command and then read the response.
Even though I use the i2c support provided by the WiringPi library, it is best to do raw reads and writes to the device because it does not follow the i2c standard very well. This is the sequence to follow:
Here is an example program in C with comments that give more detailed explanation.
The AM2315 is very poorly documented. This leads to several problems when attempting to use it. Thankfully, Sopwith has provided details of his experience with this device http://sopwith.ismellsmoke.net/wp-content/uploads/2014/03/PI-How-To-AM2315.pdf so we can move on more quickly to making use of it. Sopwith provides a more detailed explanation of how to set up and use this device. If you are using Python, then you must read his article, since he explains some additional problems caused by the way the Python library works.
The first problem you will run into is that it doesn't show up on the i2c bus. That is because the device stays in sleep mode until it is woken up to prevent generating heat that would affect the humidity sensor accuracy. If you run the command i2cdetect twice quickly, then the second time it will show up.
The second problem is that the i2c address is wrong in the datasheet. It is no longet at 0xB8, but is instead at 0x5c. This is not a big problem since you will see the correct address show up in the second i2cdetect results.
The third problem is really a side effect of the first - the device does not keep updated values in registers like most i2c devices. You will have to issue a read request command and then read the response.
Even though I use the i2c support provided by the WiringPi library, it is best to do raw reads and writes to the device because it does not follow the i2c standard very well. This is the sequence to follow:
- Write a zero byte to it twice to wake it up
- Write a read request to make it update the temperature and humidity values
- Read the response to the read request
- Do some manipulations on the data to get the values in degress Celcius and %RH
Here is an example program in C with comments that give more detailed explanation.
The AM2315 is supposed to be a very accurate device, but after using it a while, I am very suspicious of the humidity values. The plots of my data show the humidity doing things that are not possible. I suspect that it sometimes gets saturated with moisture from fog, dew or rain and then it reads 100% for a long time. Mine is mounted so that it is directly exposed to the outside environment. Best practice says that I should have a radiation shield protecting the temperature sensor from direct sunlight. I plan on creating one for the AM2315 and this may also allow the humidity sensor to be more accurate.
/*
example code to test am2315 (temp/humid sensor) on i2c bus
ted.b.hale@gmail.com
*/
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdint.h>
#include <wiringPi.h>
#include <wiringPiI2C.h>
int main(int argc, char *argv[])
{
int n, fd;
// read request - 3 is the read register command
// 0 is the address to start at
// 4 is the number of bytes to read
unsigned char read_request[3] = {3, 0, 4};
// buffer for the response: command byte, length byte, 4 bytes data, 2 bytes of checksum
unsigned char response[8];
// dummy data sent to wake up device
unsigned char dummy[1] = {0};
// the final results
float humidity, celsius;
// open the am2315 device using WiringPi
// 0x5C is bus address of am2315
// fd is the "file descriptor" used in later read and writes
fd = wiringPiI2CSetup(0x5c);
if (fd==-1)
{
printf("wiringPiI2CSetup failed\n");
return 0;
}
// run until killed with Ctrl-C
while (1)
{
// send some data to wake it up
n = write(fd, dummy, 1);
n = write(fd, dummy, 1);
// send the read request
n = write(fd, read_request, 3);
printf("write returned %d bytes\n",n);
// very short delay to allow device to do data conversion
delay(2);
// read the reaponse
n = read(fd, response, 8);
printf("read returned %d bytes\n",n);
// sanity check on data returned
// first byte should echo the read requst byte (3)
// second byte should indicate 4 bytes of data returned
// I don't bother verifying the checksum
if ((response[0]!=3) || (response[1]!=4))
{
printf("i2c response invalid\n");
for (n=0; n<8; n++)
printf("%02x ",response[n]);
}
else
{
// (high byte * 256) + low byte
// divide by 10
humidity = (256*response[2] + response[3])/10.0;
// same as above but mask out the sign bit on the high byte
celsius = (256 * (response[4] & 0x7F) + response[5]) / 10.0;
// make result negative if the sign bit is set
if ((response[4]&0x80)!=0)
celsius *= -1.0;
printf(" humidity = %5.1f%%\n",humidity);
printf("temperature = %5.1f\n",celsius);
}
printf("\n\n\n");
// wait two second and loop again
delay(2000);
}
return 0 ;
}