Czujnik ciśnienia BMP085 z Arduino

Od już dłuższego czasu w ofercie Nettigo dostępny jest adapter do BMP085 – czujnika ciśnienia atmosferycznego z wbudowanym czujnikiem temperatury. Jest on jak wszystkie adaptery ze SparkFun łatwy w użyciu dzięki wyprowadzeniom w rozstawie 0.1″.

Kilka razy byłem już pytany jak wykorzystać ten czujnik, więc krótka instrukcja. Najpierw podłączenie – czujnik ma interfejs I2C, więc wykorzystamy na ten cel wejścia analogowe A4 i i A5. Po włączeniu I2C te dwa wejścia zmieniają się w SDA i SCL odpowiednio – czyli szynę I2C, którą w Arduino IDE obsługuje biblioteka Wire.

Czujnik może być zasilany napięciem od 1.8 do 3.6V, więc nie podłączajcie 5V bo może skończyć się to zniszczeniem czujnika. Czyli poza A4 i A5 do SDA i SCL na adapterze podłączamy 3.3 V do VCC na adapterze i masę do GND. Wygląda to na przykład tak:

Podłączenie BMP085 do Arduino
Podłączenie BMP085 do Arduino

Teraz szkic… Czujnik nie podaje wprost wartości ciśnienia ani temperatury. Wg karty katalogowej trzeba to wyliczyć, tyle że algorytm jest dokładnie opisany. Ja jednak z wrodzonego lenistwa wolałem skorzystać z gotowych, dostępnych rozwiązań :) I tak na tym blogu znalazłem gotowy szkic. Po minimalnych modyfikacjach, zweryfikowaniu z kartą katalogową oraz kilku testach przybrał taką postać:

// BMP08 with Arduino

// DANGER: The BMP08 accepts 1.8 to 3.6 Volts – so no chance to connect it directly to 5 Volts.

// Connect VCC to VCC and GND to GND, SCL goes to analogue pin 5, SDA to analogue pin4.
// Notice! Sparkfun breakoutboard contains already 4.7K pull ups,
// If not using pre-built pull-ups:
// --> Add some pull up resistors (1K to 20K, most often something like 4.7K) between SDA, SCL and VCC finishes the setup.

// References: http://interactive-matter.org/2009/12/arduino-barometric-pressure-sensor-bmp085/ and http://news.jeelabs.org/2009/02/19/hooking-up-a-bmp085-sensor/
// Specification: http://www.bosch-sensortec.com/content/language1/downloads/BST-BMP085-DS000-05.pdf
// SparkFun breakout board: http://www.sparkfun.com/commerce/product_info.php?products_id=9694

#include "Wire.h"

#define I2C_ADDRESS 0x77

const unsigned char oversampling_setting = 3; //oversamplig for measurement
const unsigned char pressure_waittime[4] = { 5, 8, 14, 26 };

//just taken from the BMP085 datasheet
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;

void setup()
{
Serial.begin(9600); // start serial for output
Serial.println("Setting up BMP085");
Wire.begin();
bmp085_get_cal_data();
}
void bmp085_read_temperature_and_pressure(int& temperature, long& pressure);
void loop()
{
int temperature = 0;
long pressure = 0;

bmp085_read_temperature_and_pressure(&temperature,&pressure);
Serial.print(temperature/10.0);
Serial.print(" ");
Serial.print(pressure/100.0);
Serial.println();
delay(100);
}

void bmp085_read_temperature_and_pressure(int* temperature, long* pressure) {
int ut= bmp085_read_ut();
long up = bmp085_read_up();
long x1, x2, x3, b3, b5, b6, p;
unsigned long b4, b7;

//calculate the temperature
x1 = ((long)ut - ac6) * ac5 >> 15;
x2 = ((long) mc << 11) / (x1 + md);
b5 = x1 + x2;
*temperature = (b5 + 8) >> 4;

//calculate the pressure
b6 = b5 - 4000;
x1 = (b2 * (b6 * b6 >> 12)) >> 11;
x2 = ac2 * b6 >> 11;
x3 = x1 + x2;

//b3 = (((int32_t) ac1 * 4 + x3)<> 2;

if (oversampling_setting == 3) b3 = ((int32_t) ac1 * 4 + x3 + 2) << 1;
if (oversampling_setting == 2) b3 = ((int32_t) ac1 * 4 + x3 + 2);
if (oversampling_setting == 1) b3 = ((int32_t) ac1 * 4 + x3 + 2) >> 1;
if (oversampling_setting == 0) b3 = ((int32_t) ac1 * 4 + x3 + 2) >> 2;

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 >> oversampling_setting);
p = b7 < 0x80000000 ? (b7 * 2) / b4 : (b7 / b4) * 2;

x1 = (p >> 8) * (p >> 8);
x1 = (x1 * 3038) >> 16;
x2 = (-7357 * p) >> 16;
*pressure = p + ((x1 + x2 + 3791) >> 4);

}

unsigned int bmp085_read_ut() {
write_register(0xf4,0x2e);
delay(5); //longer than 4.5 ms
return read_int_register(0xf6);
}

void bmp085_get_cal_data() {
Serial.println("Reading Calibration Data");
ac1 = read_int_register(0xAA);
Serial.print("AC1: ");
Serial.println(ac1,DEC);
ac2 = read_int_register(0xAC);
Serial.print("AC2: ");
Serial.println(ac2,DEC);
ac3 = read_int_register(0xAE);
Serial.print("AC3: ");
Serial.println(ac3,DEC);
ac4 = read_int_register(0xB0);
Serial.print("AC4: ");
Serial.println(ac4,DEC);
ac5 = read_int_register(0xB2);
Serial.print("AC5: ");
Serial.println(ac5,DEC);
ac6 = read_int_register(0xB4);
Serial.print("AC6: ");
Serial.println(ac6,DEC);
b1 = read_int_register(0xB6);
Serial.print("B1: ");
Serial.println(b1,DEC);
b2 = read_int_register(0xB8);
Serial.print("B2: ");
Serial.println(b1,DEC);
mb = read_int_register(0xBA);
Serial.print("MB: ");
Serial.println(mb,DEC);
mc = read_int_register(0xBC);
Serial.print("MC: ");
Serial.println(mc,DEC);
md = read_int_register(0xBE);
Serial.print("MD: ");
Serial.println(md,DEC);
}

long bmp085_read_up() {
write_register(0xf4,0x34+(oversampling_setting<<6));
delay(pressure_waittime[oversampling_setting]);

unsigned char msb, lsb, xlsb;
Wire.beginTransmission(I2C_ADDRESS);
Wire.send(0xf6); // register to read
Wire.endTransmission();

Wire.requestFrom(I2C_ADDRESS, 3); // read a byte
while(!Wire.available()) {
// waiting
}
msb = Wire.receive();
while(!Wire.available()) {
// waiting
}
lsb |= Wire.receive();
while(!Wire.available()) {
// waiting
}
xlsb |= Wire.receive();
return (((long)msb<<16) | ((long)lsb<<8) | ((long)xlsb)) >>(8-oversampling_setting);
}

void write_register(unsigned char r, unsigned char v)
{
Wire.beginTransmission(I2C_ADDRESS);
Wire.send(r);
Wire.send(v);
Wire.endTransmission();
}

char read_register(unsigned char r)
{
unsigned char v;
Wire.beginTransmission(I2C_ADDRESS);
Wire.send(r); // register to read
Wire.endTransmission();

Wire.requestFrom(I2C_ADDRESS, 1); // read a byte
while(!Wire.available()) {
// waiting
}
v = Wire.receive();
return v;
}

int read_int_register(unsigned char r)
{
unsigned char msb, lsb;
Wire.beginTransmission(I2C_ADDRESS);
Wire.send(r); // register to read
Wire.endTransmission();

Wire.requestFrom(I2C_ADDRESS, 2); // read a byte
while(!Wire.available()) {
// waiting
}
msb = Wire.receive();
while(!Wire.available()) {
// waiting
}
lsb = Wire.receive();
return (((int)msb<<8) | ((int)lsb));
}

Jako rezultat na porcie szeregowym powinniśmy dostać ciąg wartości – temperatura i ciśnienie w hektoPascalach.

Jest wersja alternatywna (z której nie korzystałem jeszcze ale wygląda bardzo dobrze, zresztą jest intensywnie przetestowana) – biblioteka do ArduCoptera od DIYDrones ma w sobie obsługę tego czujnika. Kod jest na Google Code.

A jeżeli ktoś chciałby podłączyć ten czujnik do OpenWrt to tutaj może znaleźć informację jak podłączyć urządzenie po I2C.