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:
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.
W sklepowym opisie przetwornika znajdujemy informację:
„Czujnik BMP085 to czujnik ciśnienia atmosferycznego. Interfejs I2C, dokładność 0.03 hPa, zakres pracy 300 – 1100 hPa.”
Wydaje mi się, że jest tu błąd. Liczba 0.03 hPa to nie dokładność a max szum w trybie ultra res (noise). Ze specyfikacji wynika, że dokładność tego czujnika to (w zależności od warunków zewnętrznych i badania) pi razy drzwi 1hPa (absolute accurency pressure) i 0.2hPa względnego. Przykładowa seria danych ze stolika i kilkunastu sekund:
97406 246,22
97405 246,56
97407 246,47
97398 246,73
97405 246,65
97400 247,34
97409 245,87
97403 245,87
97407 245,7
97403 246,13
97418 245,96
97413 245,87
97411 245,44
97413 245,96
97398 246,13
97410 245,96
97412 245,7
97410 245,61
97406 245,87
97414 245,87
97398 246,99
97404 246,39
97399 245,36
97403 245,44
97404 246,47
97397 245,96
97401 246,47
97412 246,47
97406 246,73
97411 245,87
97408 246,56
97414 245,96
97405 246,47
97412 245,79
97406 246,22
97406 246,04
Czyli max różnica 20Pa i jakieś 2 metry pływania góra/dół.
…a tak w ogóle warto podciągnąć tu bibliotekę z Adafruit:
http://learn.adafruit.com/bmp085/using-the-bmp085