BMP180 with Raspberry PI 2b

BMP180

Pressure and Altitude, Temperature

What is I2C?

I2C (eye-squared-cee) is a communication protocol that the Raspberry Pi can use to speak to other embedded devices (temperature sensors, displays, accelerometers, etc). In this example, we’ll be connecting a BMP180 Temperature/Pressure Sensor to our Raspberry Pi.

I2C is a two wire bus, the connections are called SDA (Serial Data) and SCL (Serial Clock). Each I2C bus has one or more masters (the Raspberry Pi) and one or more slave devices, like the I/O Expander. As the same data and clock lines are shared between multiple slaves, we need some way to choose which device to communicate with. With I2C, every device has an address that each communication must be prefaced with. The temperature sensor defaults to an address of 0x77. Remember this number as we’ll see it shortly when we try to detect the chip using i2cdetect.

The Rasperry Pi has two I2C buses. One is available on the GPIO (P1) header, the other is only available from the P5 header. To access the P5 header, you’ll need to solder on your own header pins. This is generally unnecessary as you typically only need a single I2C bus.

Connecting the BMP180

Example

Preparing RPi for I2C

Activate I2C Drivers

On your Raspberry Pi, you must first enable the I2C drivers.

  1. Run sudo raspi-config
  2. At the menu, choose option 5. Interfacing Options
  3. Select P5 I2C and then say “Yes” to enable the I2C driver and “Yes” again to load the driver by default
  4. Reboot your Raspberry Pi by running sudo rebootback at the command line

Add i2c-dev to /etc/modules

1
2
pi@raspberrypi:~$ sudo nano /etc/modules
i2c-dev

Then from the prompt run:

1
sudo modprobe i2c-dev

Confirm that the i2c modules are loaded and active:

1
2
3
pi@raspberrypi ~ $ lsmod  | grep i2c
i2c_bcm2835 16384 0
i2c_dev 16384 0

Install some i2c utilities:

1
2
pi@raspberrypi:~$ sudo apt-get update
pi@raspberrypi:~$ sudo apt-get install i2c-tools

i2c-tools includes some cool utilities, like i2cdetect, which will enumerate the addresses of all slave devices on a single bus. Try it out by running sudo i2cdetect -y 1 with the sensor connected. Another utility, i2cdump lets you query the state of individual settings (registers) on a specific I2C device. This is the output you should see with the sensor connected as below, and configured for the default address of 0x77:

1
2
3
4
5
6
7
8
9
10
pi@pi-friedrich ~ $ sudo i2cdetect -y 1
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- 77

Reading values from the BMP180

First we’ll need to install some utilities for the Raspberry Pi to communicate over I2C inside of Python.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
pi@raspberrypi:~$ sudo apt-get install build-essential python-dev python-smbus

# Method 1, install Adafruit_Python_BMP lib
pi@raspberrypi:~$ git clone https://github.com/adafruit/Adafruit_Python_BMP.git
pi@raspberrypi:~$ cd Adafruit_Python_BMP
pi@raspberrypi:~$ sudo python setup.py install

# Method 2, install Adafruit_Python_BMP_lib
pi@raspberrypi:~$ echo '
#!/bin/bash

lib_27="/usr/local/lib/python2.7/dist-packages"
lib_35="/usr/local/lib/python3.5/dist-packages"

cd /tmp

git clone https://github.com/adafruit/Adafruit_Python_BMP.git
git clone https://github.com/adafruit/Adafruit_Python_GPIO.git
git clone https://github.com/adafruit/Adafruit_Python_PureIO.git

if [ ! -d "$lib_27" ]; then mkdir -p "$lib_27"; fi
cp -r "/tmp/Adafruit_Python_BMP/Adafruit_BMP" "$lib_27"
cp -r "/tmp/Adafruit_Python_GPIO/Adafruit_GPIO" "$lib_27"
cp -r "/tmp/Adafruit_Python_PureIO/Adafruit_PureIO" "$lib_27"

if [ ! -d "$lib_35" ]; then mkdir -p "$lib_35"; fi
cp -r "/tmp/Adafruit_Python_BMP/Adafruit_BMP" "$lib_35"
cp -r "/tmp/Adafruit_Python_GPIO/Adafruit_GPIO" "$lib_35"
cp -r "/tmp/Adafruit_Python_PureIO/Adafruit_PureIO" "$lib_35"

exit 0
' > /tmp/install.sh
pi@raspberrypi:~$ sudo sh /tmp/install.sh

Next, place the following into a Python script and run it.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#!/usr/bin/python

# Can enable debug output by uncommenting:
#import logging
#logging.basicConfig(level=logging.DEBUG)

import Adafruit_BMP.BMP085 as BMP085

# Default constructor will pick a default I2C bus.
#
# For the Raspberry Pi this means you should hook up to the only exposed I2C bus
# from the main GPIO header and the library will figure out the bus number based
# on the Pi's revision.
#
# For the Beaglebone Black the library will assume bus 1 by default, which is
# exposed with SCL = P9_19 and SDA = P9_20.
sensor = BMP085.BMP085()

# Optionally you can override the bus number:
#sensor = BMP085.BMP085(busnum=2)

# You can also optionally change the BMP085 mode to one of BMP085_ULTRALOWPOWER,
# BMP085_STANDARD, BMP085_HIGHRES, or BMP085_ULTRAHIGHRES. See the BMP085
# datasheet for more details on the meanings of each mode (accuracy and power
# consumption are primarily the differences). The default mode is STANDARD.
#sensor = BMP085.BMP085(mode=BMP085.BMP085_ULTRAHIGHRES)

print('Temp = {0:0.2f} *C'.format(sensor.read_temperature()-4))
print('Pressure = {0:0.2f} Pa'.format(sensor.read_pressure()))
print('Altitude = {0:0.2f} m'.format(sensor.read_altitude()))
print('Sealevel Pressure = {0:0.2f} Pa'.format(sensor.read_sealevel_pressure()))

FAQ

  • Question: When I run the script, I get the following error:

    1
    2
    3
    4
    5
    Traceback (most recent call last): 
    File "<stdin>", line 1, in <module>
    File "/usr/local/lib/python2.7/dist-packages/Adafruit_BMP-1.0.0-py2.7.egg/Adafruit_BMP/BMP085.py", line 66, in __init__ self._device = I2C.Device(address, busnum)
    File "/usr/local/lib/python2.7/dist-packages/Adafruit_GPIO-0.5.5-py2.7.egg/Adafruit_GPIO/I2C.py", line 68, in __init__ self._bus = smbus.SMBus(busnum)
    IOError: [Errno 2] No such file or directory
  • Answer: Ensure you have loaded the i2c_bcm2708 and i2c-dev modules using lsmod. If they are not present, follow the instructions at the top of the page to modify the files in /etc/modules

  • Question: When I run the script, I get the flollowing error:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    Traceback (most recent call last):
    File "simpletest.py", line 37, in <module>
    sensor = BMP085.BMP085()
    File "build/bdist.linux-armv7l/egg/Adafruit_BMP/BMP085.py", line 69, in __init__
    File "build/bdist.linux-armv7l/egg/Adafruit_BMP/BMP085.py", line 72, in _load_calibration
    File "build/bdist.linux-armv7l/egg/Adafruit_GPIO/I2C.py", line 202, in readS16BE
    File "build/bdist.linux-armv7l/egg/Adafruit_GPIO/I2C.py", line 179, in readS16
    File "build/bdist.linux-armv7l/egg/Adafruit_GPIO/I2C.py", line 166, in readU16
    File "build/bdist.linux-armv7l/egg/Adafruit_PureIO/smbus.py", line 187, in read_word_data
    IOError: [Errno 121] Remote I/O error`
  • Answer:sudo i2cdetect -y 1 should report a device on address 0x77 (the bmp180). If not check the connections between the PI and bmp180.

References

  • Adafruit Tutorial
  • Streaming Data with Plotly

I2C总线控制BMP180

点击查看原文

BMP180压强传感器操作原理比较简单,开机先通过I2C读取出AC1,AC2,AC3,AC4,AC5,AC6,B1,B2,MB,MC,MD等寄存器的值,这些寄存器的值作为校准时使用。如何读取温度寄存器,压强寄存器的值,根据下图公式算出测得的当前温度和压强。

bmp180

驱动文件bmp180.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
import time
import smbus

# BMP085 default address.
BMP180_I2CADDR = 0x77

# Operating Modes
BMP180_ULTRALOWPOWER = 0
BMP180_STANDARD = 1
BMP180_HIGHRES = 2
BMP180_ULTRAHIGHRES = 3

# BMP085 Registers
BMP180_CAL_AC1 = 0xAA # R Calibration data (16 bits)
BMP180_CAL_AC2 = 0xAC # R Calibration data (16 bits)
BMP180_CAL_AC3 = 0xAE # R Calibration data (16 bits)
BMP180_CAL_AC4 = 0xB0 # R Calibration data (16 bits)
BMP180_CAL_AC5 = 0xB2 # R Calibration data (16 bits)
BMP180_CAL_AC6 = 0xB4 # R Calibration data (16 bits)
BMP180_CAL_B1 = 0xB6 # R Calibration data (16 bits)
BMP180_CAL_B2 = 0xB8 # R Calibration data (16 bits)
BMP180_CAL_MB = 0xBA # R Calibration data (16 bits)
BMP180_CAL_MC = 0xBC # R Calibration data (16 bits)
BMP180_CAL_MD = 0xBE # R Calibration data (16 bits)
BMP180_CONTROL = 0xF4
BMP180_TEMPDATA = 0xF6
BMP180_PRESSUREDATA = 0xF6

# Commands
BMP180_READTEMPCMD = 0x2E
BMP180_READPRESSURECMD = 0x34


class BMP180(object):
def __init__(self, address=BMP180_I2CADDR, mode=BMP180_STANDARD):
self._mode = mode
self._address = address
self._bus = smbus.SMBus(1)
# Load calibration values.
self._load_calibration()

def _read_byte(self,cmd):
return self._bus.read_byte_data(self._address,cmd)

def _read_u16(self,cmd):
MSB = self._bus.read_byte_data(self._address,cmd)
LSB = self._bus.read_byte_data(self._address,cmd+1)
return (MSB << 8) + LSB

def _read_s16(self,cmd):
result = self._read_u16(cmd)
if result > 32767:result -= 65536
return result

def _write_byte(self,cmd,val):
self._bus.write_byte_data(self._address,cmd,val)

def _load_calibration(self):
"load calibration"
self.cal_AC1 = self._read_s16(BMP180_CAL_AC1) # INT16
self.cal_AC2 = self._read_s16(BMP180_CAL_AC2) # INT16
self.cal_AC3 = self._read_s16(BMP180_CAL_AC3) # INT16
self.cal_AC4 = self._read_u16(BMP180_CAL_AC4) # UINT16
self.cal_AC5 = self._read_u16(BMP180_CAL_AC5) # UINT16
self.cal_AC6 = self._read_u16(BMP180_CAL_AC6) # UINT16
self.cal_B1 = self._read_s16(BMP180_CAL_B1) # INT16
self.cal_B2 = self._read_s16(BMP180_CAL_B2) # INT16
self.cal_MB = self._read_s16(BMP180_CAL_MB) # INT16
self.cal_MC = self._read_s16(BMP180_CAL_MC) # INT16
self.cal_MD = self._read_s16(BMP180_CAL_MD) # INT16

def read_raw_temp(self):
"""Reads the raw (uncompensated) temperature from the sensor."""
self._write_byte(BMP180_CONTROL, BMP180_READTEMPCMD)
time.sleep(0.005) # Wait 5ms
MSB = self._read_byte(BMP180_TEMPDATA)
LSB = self._read_byte(BMP180_TEMPDATA+1)
raw = (MSB << 8) + LSB
return raw

def read_raw_pressure(self):
"""Reads the raw (uncompensated) pressure level from the sensor."""
self._write_byte(BMP180_CONTROL, BMP180_READPRESSURECMD + (self._mode << 6))
if self._mode == BMP180_ULTRALOWPOWER:
time.sleep(0.005)
elif self._mode == BMP180_HIGHRES:
time.sleep(0.014)
elif self._mode == BMP180_ULTRAHIGHRES:
time.sleep(0.026)
else:
time.sleep(0.008)
MSB = self._read_byte(BMP180_PRESSUREDATA)
LSB = self._read_byte(BMP180_PRESSUREDATA+1)
XLSB = self._read_byte(BMP180_PRESSUREDATA+2)
raw = ((MSB << 16) + (LSB << 8) + XLSB) >> (8 - self._mode)
return raw

def read_temperature(self):
"""Gets the compensated temperature in degrees celsius."""
UT = self.read_raw_temp()

X1 = ((UT - self.cal_AC6) * self.cal_AC5) >> 15
X2 = (self.cal_MC << 11) / (X1 + self.cal_MD)
B5 = X1 + X2
temp = ((B5 + 8) >> 4) / 10.0
return temp

def read_pressure(self):
"""Gets the compensated pressure in Pascals."""
UT = self.read_raw_temp()
UP = self.read_raw_pressure()

X1 = ((UT - self.cal_AC6) * self.cal_AC5) >> 15
X2 = (self.cal_MC << 11) / (X1 + self.cal_MD)
B5 = X1 + X2

# Pressure Calculations
B6 = B5 - 4000
X1 = (self.cal_B2 * (B6 * B6) >> 12) >> 11
X2 = (self.cal_AC2 * B6) >> 11
X3 = X1 + X2
B3 = (((self.cal_AC1 * 4 + X3) << self._mode) + 2) / 4
X1 = (self.cal_AC3 * B6) >> 13
X2 = (self.cal_B1 * ((B6 * B6) >> 12)) >> 16
X3 = ((X1 + X2) + 2) >> 2
B4 = (self.cal_AC4 * (X3 + 32768)) >> 15
B7 = (UP - B3) * (50000 >> self._mode)

if B7 < 0x80000000:
p = (B7 * 2) / B4
else:
p = (B7 / B4) * 2

X1 = (p >> 8) * (p >> 8)
X1 = (X1 * 3038) >> 16
X2 = (-7357 * p) >> 16

p = p + ((X1 + X2 + 3791) >> 4)
return p

def read_altitude(self, sealevel_pa=101325.0):
"""Calculates the altitude in meters."""
# Calculation taken straight from section 3.6 of the datasheet.
pressure = float(self.read_pressure())
altitude = 44330.0 * (1.0 - pow(pressure / sealevel_pa, (1.0/5.255)))
return altitude

def read_sealevel_pressure(self, altitude_m=0.0):
"""Calculates the pressure at sealevel when given a known altitude in
meters. Returns a value in Pascals."""
pressure = float(self.read_pressure())
p0 = pressure / pow(1.0 - altitude_m/44330.0, 5.255)
return p0

主文件bmp180_example.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#!/usr/bin/python

import time
from BMP180 import BMP180

# Initialise the BMP085 and use STANDARD mode (default value)
# bmp = BMP085(0x77, debug=True)
bmp = BMP180()

# To specify a different operating mode, uncomment one of the following:
# bmp = BMP085(0x77, 0) # ULTRALOWPOWER Mode
# bmp = BMP085(0x77, 1) # STANDARD Mode
# bmp = BMP085(0x77, 2) # HIRES Mode
# bmp = BMP085(0x77, 3) # ULTRAHIRES Mode
while True:
temp = bmp.read_temperature()

# Read the current barometric pressure level
pressure = bmp.read_pressure()

# To calculate altitude based on an estimated mean sea level pressure
# (1013.25 hPa) call the function as follows, but this won't be very accurate
altitude = bmp.read_altitude()

# To specify a more accurate altitude, enter the correct mean sea level
# pressure level. For example, if the current pressure level is 1023.50 hPa
# enter 102350 since we include two decimal places in the integer value
# altitude = bmp.readAltitude(102350)

print "Temperature: %.2f C" % temp
print "Pressure: %.2f hPa" % (pressure / 100.0)
print "Altitude: %.2f\n" % altitude
time.sleep(1)

海拔和气压的转换公式

一般来说海拔越高,气压越低,他们之间存在某种关系。有不少学者对海拔与气压做了研究,回归了不少海拔与气压的数据,他们的变化关系大概服从下面的表达式。
其中P为当前的大气压,P0为标准大气压(0℃,101.325kPa),H为海拔。
P=P0(1&#x2212;H44300)5.255
因为我们要计算海拔(H),所以略作变化,可以知道A的计算公式如下:
H=44300&times;[1&#x2212;(PP0)15.255]