Thursday, February 26, 2015

Using the AM2315 Temperature/Humidity Sensor

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:

  • 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.
/*
    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 ;
}
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.

2 comments:

  1. After less than six months, the AM2315 has failed. When I opened it up, quite a bit of water poured out and it no longer responds on the i2c bus. Adafruit never claimed that it was weatherproof. I probably won;t replace it since I really don't care about humidity and temperature is trivial to read using a DS18B20 over 1-wire. If you decide to use one, cover it to keep the rain out.

    ReplyDelete
  2. Thank you for the article !!
    I would like to use it with a RPi2 and W10T, could you please post a C# porting too..?

    Thanks

    ReplyDelete