Notes About Working with Various Arduino & Netduino Microcontroller Boards

Monday, May 5, 2014

Reading Barometric Pressure with an Arduino Galileo & BMP085 Pressure Sensor

I have an Adafruit BMP085 (now replaced by BMP180) pressure sensor that I wanted to use with an Arduino Galileo, but I couldn't get the code that I had used with an Uno to work with the Arduino Galileo - the readings were wildly wrong. The libraries from Adafruit would not compile for the Galileo.

Starting with the sample BMP085 Arduino code from Sparkfun, I have come up with the following code that reads the adjusted barometric pressure.  To keep this example simple,   the code prints the pressure (in inches of Mercury) to the serial console.

The key to getting correct readings with to change the numeric data types to portable C numeric types (int16_t, uint16_t, etc...).

This example also uses C++ style output streaming (to the serial console), as suggested by Mikal Hart in this post at the Arduino Playground.

This code also works with the Arduino Yun.

Connections


Adafruit BMP085  Galileo
3Vo              3.3V
SCL              SCL
SDA              SDA
GND              GND

Code


#include <Wire.h>

#define BMP085_ADDRESS 0x77  // I2C address of BMP085

// Provides C++ style output streaming
template<class T> inline Print &operator <<(Print &obj, T arg) { 
  obj.print(arg); return obj; 
}

const uint8_t OSS = 0;  // Oversampling Setting

// Calibration values
int16_t ac1;
int16_t ac2; 
int16_t ac3; 
uint16_t ac4;
uint16_t ac5;
uint16_t ac6;
int16_t b1; 
int16_t b2;
int16_t mb;
int16_t mc;
int16_t md;
int32_t b5; 
int16_t t;
int32_t p;
double tempF;

void setup()
{
  Serial.begin(9600);
  Wire.begin();
  bmp085Calibration();
}

void loop()
{
  t = bmp085GetTemperature(bmp085ReadUT());
  p = bmp085GetPressure(bmp085ReadUP());
  double pInHg = p * 0.000295333727; // convert to inches of mercury
  // Now adjust to sea level: Add 1200 Pa / 100 meters in elev.
  // Minneapolis is 264m above sea level. Converted to inches Hg this is 0.935
  pInHg += 0.935;
  pInHg = trunc(pInHg * 100)/100;
  
  // Print output to serial console
  Serial << "Pressure: " << pInHg << " in. Hg\n";
  
  delay(1000);
}

// Stores all of the bmp085's calibration values into global variables
// Calibration values are required to calculate temp and pressure
// This function should be called at the beginning of the program
void bmp085Calibration()
{
  ac1 = bmp085ReadInt(0xAA);
  ac2 = bmp085ReadInt(0xAC);
  ac3 = bmp085ReadInt(0xAE);
  ac4 = bmp085ReadInt(0xB0);
  ac5 = bmp085ReadInt(0xB2);
  ac6 = bmp085ReadInt(0xB4);
  b1 = bmp085ReadInt(0xB6);
  b2 = bmp085ReadInt(0xB8);
  mb = bmp085ReadInt(0xBA);
  mc = bmp085ReadInt(0xBC);
  md = bmp085ReadInt(0xBE);
}

// Calculate temperature given unadjusted temp ut.
// Value returned will be in units of 0.1 deg C
short bmp085GetTemperature(uint16_t ut)
{
  int32_t x1, x2;
  x1 = (((int32_t)ut - (int32_t)ac6) * (int32_t)ac5) >> 15;
  x2 = ((int32_t)mc << 11) / (x1 + md);
  b5 = x1 + x2;

  return ((b5 + 8)>>4);  
}

// Calculate pressure given unadjusted pressure up
// calibration values must be known
// b5 is also required so bmp085GetTemperature(...) must be called first.
// Value returned will be pressure in units of Pa.
int32_t bmp085GetPressure(uint32_t up)
{
  int32_t x1, x2, x3, b3, b6, p;
  uint32_t b4, b7;
  
  b6 = b5 - 4000;
  // Calculate B3
  x1 = (b2 * (b6 * b6)>>12) >> 11;
  x2 = (ac2 * b6) >> 11;
  x3 = x1 + x2;
  b3 = (((((int32_t)ac1) * 4 + x3) << OSS) + 2) >> 2;
  
  // Calculate B4
  x1 = (ac3 * b6) >> 13;
  x2 = (b1 * ((b6 * b6) >> 12)) >> 16;
  x3 = ((x1 + x2) + 2) >> 2;
  b4 = (ac4 * (uint32_t)(x3 + 32768)) >> 15;
  
  b7 = ((uint32_t)(up - b3) * (50000 >> OSS));
  if (b7 < 0x80000000)
    p = (b7 << 1) / b4;
  else
    p = (b7 / b4) << 1;
    
  x1 = (p >> 8) * (p >> 8);
  x1 = (x1 * 3038) >> 16;
  x2 = (-7357 * p) >> 16;
  p += (x1 + x2 + 3791) >> 4;
  
  return p;
}

// Read 1 byte from the BMP085 at 'address'
int8_t bmp085Read(uint8_t address)
{
  uint8_t data;
  
  Wire.beginTransmission(BMP085_ADDRESS);
  Wire.write(address);
  Wire.endTransmission();
  
  Wire.requestFrom(BMP085_ADDRESS, 1);
  while(!Wire.available())
    ;
    
  return Wire.read();
}

// Read 2 bytes from the BMP085
// First byte will be from 'address'
// Second byte will be from 'address'+1
int16_t bmp085ReadInt(uint8_t address)
{
  uint8_t msb, lsb;
  
  Wire.beginTransmission(BMP085_ADDRESS);
  Wire.write(address);
  Wire.endTransmission();
  
  Wire.requestFrom(BMP085_ADDRESS, 2);
  while(Wire.available()<2)
    ;
  msb = Wire.read();
  lsb = Wire.read();
  
  return (int16_t) msb<<8 | lsb;
}

// Read the uncompensated temperature value
uint16_t bmp085ReadUT()
{
  uint16_t ut;
  
  // Write 0x2E into Register 0xF4
  // This requests a temperature reading
  Wire.beginTransmission(BMP085_ADDRESS);
  Wire.write(0xF4);
  Wire.write(0x2E);
  Wire.endTransmission();
  
  // Wait at least 4.5ms
  delay(5);
  
  // Read two bytes from registers 0xF6 and 0xF7
  ut = bmp085ReadInt(0xF6);
  return ut;
}

// Read the uncompensated pressure value
uint32_t bmp085ReadUP()
{
  unsigned char msb, lsb, xlsb;
  unsigned long up = 0;
  
  // Write 0x34+(OSS<<6) into register 0xF4
  // Request a pressure reading w/ oversampling setting
  Wire.beginTransmission(BMP085_ADDRESS);
  Wire.write(0xF4);
  Wire.write(0x34 + (OSS << 6));
  Wire.endTransmission();
  
  // Wait for conversion, delay time dependent on OSS
  delay(2 + (3 << OSS));
  
  // Read register 0xF6 (MSB), 0xF7 (LSB), and 0xF8 (XLSB)
  Wire.beginTransmission(BMP085_ADDRESS);
  Wire.write(0xF6);
  Wire.endTransmission();
  Wire.requestFrom(BMP085_ADDRESS, 3);
  
  // Wait for data to become available
  while(Wire.available() < 3)
    ;
  msb = Wire.read();
  lsb = Wire.read();
  xlsb = Wire.read();
  
  up = (((uint32_t) msb << 16) | ((uint32_t) lsb << 8) | (uint32_t) xlsb) >> (8-OSS);
  
  return up;
}

No comments:

Post a Comment