編號GY-65的BMP085
如果要同時使用多個BMP085模塊,必須使用XCLR,LOW(turn off),HIGH(turn on)
How can I connect two of these sensors on my Arduino, I can only use A5 and A4?
“There is an easy way to connect two BMP085 to the same i2c bus: You can use the XCLR input of BMP085 to set one BMP085 part silent while you communicate with the other BMP085 part via i2c and vice versa. The signals can be provided by two digital outputs of the microcontroller, or one digital output and one inverter.”
如果需要計算高度,詳細資料請參考原文
data from https://www.sparkfun.com/tutorials/253
BMP085 Quickstart Guide
Bosch's BMP085 is a rock-solid barometric pressure sensor. It features a measuring range of anywhere between 30,000 and 110,000 Pa. 'Pa' meaning the Pascal unit, which you'll probably more often see converted to hPa (hectoPascal), equal to 100 Pa, or kPa (kiloPascal), which is 1000 Pa. As a bonus the BMP085 also provides a temperature measurement, anywhere from 0 to 65 °C.
The BMP085 has a digital interface, I2C to be specific. This means there may is a bit more overhead to get it talking to your microcontroller, but in return you get data that is much less susceptible to noise and other factors that may hamper an analog signal. I2C is a synchronous two-wire interface, the first wire, SDA, transmits data, while a second wire, SCL, transmits a clock, which is used to keep track of the data. If you're using an Arduino to talk to the BMP085, the Wire library will conveniently take care of most of the work in communicating with the sensor.
As with most SparkFun products, before you start toying around with something, it's always best to RTFM...err...read the fancy datasheet. The BMP085 datasheet covers everything you'd ever need to know about that little sensor on your breakout board.
Why do we care about barometric pressure?
There are a lot of projects that can make use of barometric pressure data. Here's at least a couple reasons to care about pressure. You've probably heard weather reporters going on about low pressure systems and high pressure systems, that's because atmospheric pressure can be directly related to changes in weather. Low pressure typically means cloudier, rainier, snowier, and just generally uglier weather. While high pressure generally means clearer, sunnier weather. This means the BMP085 would be a perfect component for your Weather Prophet robot.
A second, widely applied use for pressure sensors is in altimetry. Barometric pressure has a measurable relationship with altitude, meaning you can use the BMP085 to deduce how high you (or maybe one of your robots) have climbed. At sea level air pressure is on average 1013 hPa, while here in Boulder, CO, at 5184 ft above sea level, average air pressure is about 831.4 hPa. The measuring limits of the BMP085 should allow you to measure pressure at elevations anywhere between -1640 to about 29,000 ft above sea level.
Pressure and Altitude Relationship
Hardware Explanation and Assembly
When you receive the BMP085 Breakout board, you're presented with a tiny 0.65 x 0.65” board with just 4 components, and six un-soldered holes. Your first task will be to solder something into those holes to get a good, solid electrical connection. What you solder into those holes is up to you. If I'm using a breadboard I like to stick some male headers in there, but simple wires are also a good option. However, if you do choose to go with wire, make sure to keep it short, I2C is really only reliable at short distances (a few meters max).
Now, if you take a gander at the bottom side of the BMP085 Breakout you'll notice the labels of all six pins: 'SDA', 'SCL', 'XCLR', 'EOC', 'GND, and 'VCC.' VCC and GND are obviously the power pins. SDA and SCL are the I2C communication lines. SDA being where the data is transmitted, and SCL is the clock that keeps track of that data. The last two pins, XCLR and EOC, are a couple extra functions of the BMP085. XCLR acts as a master reset. It's active-low, so if it's pulled to GND it will reset the BMP085 and set its registers to their default state. EOC, standing for "end of conversion", is a signal generated by the BMP085 that's triggered whenever a pressure or temperature conversion has finished. These last two pins are optional, and if you don't need to use them you can just leave them unconnected.
Pay special attention to how you power the BMP085. Its maximum supply voltage is 3.6V. So don't go feeding it 5V! If you've got a regular Arduino Uno, use the 3.3V header to supply power to the chip.
Getting pressure and temperature readings
Now that you've got the gist of the sensor, let's play! In this example we'll use an Arduino to initialize the BMP085, collect its pressure and temperature data, and display it on the serial output. First off, you'll need to connect it to the Arduino like so:
For now, we're going to ignore XCLR and EOC, it's safe to just leave them unconnected.
Now let's get to the nitty-gritty Arduino code. You can download the code here, or get ready to do some copy-pasta.
The first thing I like to do with digital sensors like this is make sure I can actually communicate with it. For many sensors this is half the battle. Let's first look at the functions used to read 8- and 16-bit values from the BMP085.
// Put this line at the top of your program
#define BMP085_ADDRESS 0x77 // I2C address of BMP085
// Read 1 byte from the BMP085 at 'address'
char bmp085Read(unsigned char address){
unsigned char 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
int bmp085ReadInt(unsigned char address){
unsigned char 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 (int) msb<<8 | lsb;
}
Now, if you've glanced through the BMP085 datasheet, you may have noticed some very cryptic variables, and a lot of ugly math involving them. The BMP085 is calibrated in the factory, and left with a series of eleven 16-bit calibration coefficients stored in the device's EEPROM. These variables all play a small role in calculating the absolute pressure. They need to be read just once, at the start of the program, and stored for later use. We can use the I2C functions above to read and store each of the calibration coefficients. We'll stick all of this in the setup() function, so it'll be called just once at the start of the program.
// This is the top of the program
#include <Wire.h>
#define BMP085_ADDRESS 0x77 // I2C address of BMP085
const unsigned char OSS = 0; // Oversampling Setting
// Calibration values
int ac1;
int ac2;
int ac3;
unsigned int ac4;
unsigned int ac5;
unsigned int ac6;
int b1;
int b2;
int mb;
int mc;
int md;
// b5 is calculated in bmp085GetTemperature(...), this variable is also used in bmp085GetPressure(...)
// so ...Temperature(...) must be called before ...Pressure(...).
long b5;
short temperature;
long pressure;
// Use these for altitude conversions
const float p0 = 101325; // Pressure at sea level (Pa)
float altitude;
void setup(){
Serial.begin(9600);
Wire.begin();
bmp085Calibration();
}
// 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);
}
Once we've read the calibration values, we just need two more variables in order to calculate temperature and pressure. 'ut' and 'up' are the uncompensated temperature and pressure values, they're our starting point for finding the actual temperature and pressure. Every time we want to measure temperature or pressure, we need to first find out what these values are. The uncompensated temperature value is an unsigned 16-bit (int) number while 'up' is an unsigned 32-bit number (long).
// Read the uncompensated temperature value
unsigned int bmp085ReadUT(){
unsigned int 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
unsigned long 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 = (((unsigned long) msb << 16) | ((unsigned long) lsb << 8) | (unsigned long) xlsb) >> (8-OSS);
return up;
}
In those two functions, we're delaying to give the BMP085 enough time to finish its readings. The time for the delay is specified as the maximum conversion time in the datasheet. If you wanted to make it a bit more robust, you could read the EOC pin. While the conversion is still running, EOC will read LOW. Once finished it will go HIGH.
Now that we have all the variables we need, we need one last function that takes care of the following for us:
Necessary calculations to find temperature and pressure
Ugh. Calculating the temperature isn't so bad, but, just...ugh, those pressure calculations. A couple quick notes: dividing by 2N is the same thing as shifting a byte N bits to the right. Likewise, multiplying by 2N, is the same thing as shifting it N bits to the left. We also have to be careful not to overflow any of the variables, which means paying close attention to the order of operations, and doing a little typecasting.
Here's the two functions we need to calculate temperature and pressure. Note that the variable b5, which is calculated in the bmp085GetTemperature() function, is also required to calculate pressure. This means you'll have to call bmp085GetTemperature(), before calling bmp085GetPressure().
// Calculate temperature given ut.
// Value returned will be in units of 0.1 deg C
short bmp085GetTemperature(unsigned int ut){
long x1, x2;
x1 = (((long)ut - (long)ac6)*(long)ac5) >> 15;
x2 = ((long)mc << 11)/(x1 + md);
b5 = x1 + x2;
return ((b5 + 8)>>4);
}
// Calculate pressure given 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.
long bmp085GetPressure(unsigned long up){
long x1, x2, x3, b3, b6, p;
unsigned long b4, b7;
b6 = b5 - 4000;
// Calculate B3
x1 = (b2 * (b6 * b6)>>12)>>11;
x2 = (ac2 * b6)>>11;
x3 = x1 + x2;
b3 = (((((long)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 * (unsigned long)(x3 + 32768))>>15;
b7 = ((unsigned long)(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;
}
And finally, we need to write something into the main loop() function so that our program actually does something. Let's just calculate the temperature and pressure, and spit it out over a serial line.
void loop(){
temperature = bmp085GetTemperature(bmp085ReadUT());
pressure = bmp085GetPressure(bmp085ReadUP());
Serial.print("Temperature: ");
Serial.print(temperature, DEC);
Serial.println(" *0.1 deg C");
Serial.print("Pressure: ");
Serial.print(pressure, DEC);
Serial.println(" Pa");
Serial.println();
delay(1000);
}
Send that over to your Arduino. Open up the serial monitor and check that the baud rate is set to 9600. Are you getting something reasonable? The data should be in units of 0.1°C for the temperature, and Pa for pressure. Here in my office I'm reading a comfy 258 (25.8°C) for temperature, and a pressure of 83523 Pa. If your elevation is lower than Boulder's ~5200ft, the measured pressure should read higher. Average pressure for those of you at sea level should be about 101325 Pa.
If the program slows to a halt before printing any readings, that usually means something's hooked up incorrectly. Make sure you didn't swap SDA and SCL!
You may have noticed a few of the calculations take into account an oversampling setting, OSS. OSS selects which mode the BMP085 operates in, and can be set to either 0, 1, 2, or 3. OSS determines how many samples the BMP085 will take before it sends over its uncompensated pressure reading. With OSS set to 0, the BMP085 will consume the least current. Setting OSS to 3 increases resolution, as it samples pressure eight times before producing a reading, this comes at a cost of more power usage. If you want to change OSS, just set it accordingly at the top of the program. Try changing OSS to 3, does the data become more stable?
Applying the data: Altimetry and Meteorology
Knowing the pressure is all good fun, but what really matters is what we do with this shiny knowledge. As I mentioned above, pressure is most often used to either calculate altitude, or as an indicator of what the weather might be planning on doing. Let's make a altitude tracking weather forecaster!
We're really just one step away from calculating the altitude, all we need is the non-linear equation that converts pressure to altitude. This equation is conveniently in the BMP085's datasheet:
p0 is the average pressure at sea level 101325 Pa, and p is the pressure that we measured. Note that this equation gives you altitude in units of meters. So, after we calculate pressure in our code, we just need to add these lines to calculate and print out the altitude:
// Add these to the top of your program
const float p0 = 101325; // Pressure at sea level (Pa)
float altitude;
// Add this into loop(), after you've calculated the pressure
altitude = (float)44330 * (1 - pow(((float) pressure/p0), 0.190295));
Serial.print("Altitude: ");
Serial.print(altitude, 2);
Serial.println(" m");
The altitude that you calculate might be off a tad, as the pressure will vary depending on the weather. Google Earth says the altitude here at SparkFun is about 1580 m (5183 ft), while at the pressure I'm measuring altitude calculates out to about 1600m (5249 ft). Close enough for government SparkFun work.
Now, let's add a little code to turn our Arduino into a simple weather forecaster. If our altitude is known, we can turn the above formula around to calculate expected pressure at a specific altitude.
You can use Google Earth, this website, or even a GPS module to find your altitude.
If your measured pressure is about equal to the expected pressure at your altitude, weather will probably be fair, if somewhat cloudy. Typically anything over about 250 Pa of the expected pressure will result in awesome, sunny weather, while anything 250 Pa below means rainy/snowy weather. Knowing all of that, here's some quick code to implement a simple weather forecaster:
// Add these to the top of your program
const float currentAltitude = 1580.08; // current altitude in METERS
const float ePressure = p0 * pow((1-currentAltitude/44330), 5.255); // expected pressure (in Pa) at altitude
float weatherDiff;
// Add this into loop(), after you've calculated the pressure
weatherDiff = pressure - ePressure;
if(weatherDiff > 250)
Serial.println("Sunny!");
else if ((weatherDiff <= 250) || (weatherDiff >= -250))
Serial.println("Partly Cloudy");
else if (weatherDiff > -250)
Serial.println("Rain :-(");
Serial.println();
I'll stress that this is very simple. To get a better idea of what the weather's going to do, you need to look at how pressure is changing over time. Measuring the rate of change of pressure, you can get a much better idea of what the weather is going to do. For example, pressure changes of more than about -250 Pa per hour (Pa/h), result in unstable weather patterns like thunderstorms, while lower rates, between -50 and -250 Pa/h produce stable, though still rainy weather. Now that you know how to use the BMP085, maybe you can program your Arduino to out-forecast your local weatherman!