Sunday, March 8, 2015

Using the MPL115A2 to read Temperature and Barometric Pressure

This post is a continuation of the series on my weather station system.

My weather station uses the MPL115A2, available from Adafruit.  It is an inexpensive sensor for measuring temperature and barometric pressure.  It is only moderately accurate - the MPL3115A2, which only slightly more expensive, would probably have been a better choice.  Both sensors are interfaced using an I2C bus and are fairly simple to use.

In order to compute the correct pressure, several coefficient values must be read from the device first.  These coefficients are unique to each device and provide the calibration necessary to arrive at an accurate reading.  Since they don't change, they only need to be read once.

After reading the coefficients, the data capture and conversion are started by writing a zero to register address 0x12.  The program must then pause briefly to allow the conversion to complete.  The results are stored in the device registers and are read with the standard I2C commands.  The temperature value is taken directly from the register values, with some simple adjustments to get it to degrees Celsius.  The pressure is then computed by a formula that applies the coefficients read earlier as well as the temperature.  The value is in kilo Pascals.  For use by an American like myself, these are finally converted to degrees Fahrenheit and inches of Mercury.

Below is an example, in C, of the code similar to what I use.

/*
    example to test mpl115a2 (temp/baro sensor) on i2c
 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 i, n, fd;
 // command string to get coefficients
 unsigned char coef_request[3] = {3, 4, 8};
 // command string to request conversion
 unsigned char conv_request[3] = {0, 0x12, 0};
 // variables for the final results
 float baro, celsius, farenheit;
 
 // variables to hold the integer values of coefficients
 int16_t a0coeff;
 int16_t b1coeff;
 int16_t b2coeff;
 int16_t c12coeff;
 // variables to hold the floating point coefficients
 float a0;
 float b1;
 float b2;
 float c12;
 // some intermediate values
 int pressure, temp;
 float pressureComp;
 
 // open a file descriptor to the device on the I2C bus
 fd = wiringPiI2CSetup(0x60);  // 0x60 is bus address of mpl115a2
 if (fd==-1)
 {
  printf("wiringPiI2CSetup failed\n");
  return 0;
 }

 // get the coefficients.  This only needs to be done once.
 // Note on C language: the << and >> operators perform bit shifting
 a0coeff = (( (uint16_t) wiringPiI2CReadReg8(fd,4) << 8) | wiringPiI2CReadReg8(fd,5));
 b1coeff = (( (uint16_t) wiringPiI2CReadReg8(fd,6) << 8) | wiringPiI2CReadReg8(fd,7));
 b2coeff = (( (uint16_t) wiringPiI2CReadReg8(fd,8) << 8) | wiringPiI2CReadReg8(fd,9));
 c12coeff = (( (uint16_t) (wiringPiI2CReadReg8(fd,10) << 8) | wiringPiI2CReadReg8(fd,11))) >> 2;
 printf("%d   %d   %d   %d\n",a0coeff,b1coeff,b2coeff,c12coeff);
 // convert coefficients to floating point
 a0 = (float)a0coeff / 8;
 b1 = (float)b1coeff / 8192;
 b2 = (float)b2coeff / 16384;
 c12 = (float)c12coeff;
 c12 /= 4194304.0;
 printf("%f   %f   %f   %f\n\n",a0,b1,b2,c12);
 
 // start conversion and wait a tiny bit
 wiringPiI2CWriteReg8(fd,0x12,0);
 delay(5);
 
 // get the results by reading the device registers
 pressure = (( (uint16_t) wiringPiI2CReadReg8(fd,0) << 8) | wiringPiI2CReadReg8(fd,1)) >> 6;
 temp = (( (uint16_t) wiringPiI2CReadReg8(fd,2) << 8) | wiringPiI2CReadReg8(fd,3)) >> 6;

 // compute temperature compensation for pressure
 pressureComp = a0 + (b1 + c12 * temp ) * pressure + b2 * temp;

 // get the pressure in kiloPascals
 baro = ((65.0F / 1023.0F) * pressureComp) + 50.0F;        // kPa
 // get the temperature in celsius degrees
 celsius = ((float) temp - 498.0F) / -5.35F +25.0F;        // C

 // convert kilo-Pascals to inches of mercury
 baro = baro * 0.295299830714;
 // convert Celsius to Farenheit
 farenheit = (celsius * 1.8) + 32.0;

 //show the results
 printf("%f    %f\n\n",baro,farenheit);
 
 return 0;
}

The device comes with a 6x1 header which allows it to be mounted simply on a circuit board.  I mounted it directly on the interface board that I built for the weather station.  This is in a weatherproof enclosure, but that should have negligible effect on the reading of  pressure since the enclosure is not completely air-tight.  This will, however, affect temperature reading.  Therefore, while I do record the temperature value, it is not the primary source for temperature that my weather station uses.  For that I use the AM2315 sensor, which is described in my previous post.

4 comments:

  1. Thank you for your informative blog, Ted. I work at a college, and we're trying to get our RaspberryPi B 512 with a DHT11 installed in an i2c to work properly. Right now, the humidity and temperature readings are off more than the expected +/- 2C and +/-5% accuracy.

    Is there a way to calibrate this for better accuracy? Your help is greatly appreciated.

    ReplyDelete
    Replies
    1. Glad to help. Isn't it great that the only datasheet for the DHT11 is in Chinese? I took a look at the datasheet for the DHT22 and it seems pretty simple to interface. There isn't any intrinsic method for calibrating the sensor.
      Your only option is to collect data comparing the output of the sensor to a reliable standard. Then you can adjust the data you get to be more accurate. It may be as simple as a fixed offset (y = x + B) Or it may have some linear variability (y = Ax + B) It may even be non-linear (y = Ax*x + Bx + C)
      You can enter the collected data into Microsoft Excel and use the Least Squares Fit function to give you the polynomial values for A B and C. You could also use MathWorks or some other software to do the same.
      I will bet that it just needs a simple offset value. Good luck.

      Delete
  2. Well cool! I just got a Pi 2, and was wondering if I could use it to replace my somewhat clunky barometer board to Arduino to Mac serial set up. This looks like I'll finally be able to get data rates up into the samples per second range. That Mac serial port is a real bottleneck.
    First time I've ever been to this site, and this shows up.
    Thanks!

    ReplyDelete