Skip to content

Commit bccab54

Browse files
authored
Merge pull request #13 from jraber/master
Adopt changes from the BME280 library
2 parents 82792ef + b9e41db commit bccab54

File tree

2 files changed

+287
-51
lines changed

2 files changed

+287
-51
lines changed

adafruit_bmp280.py

Lines changed: 252 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -20,20 +20,19 @@
2020
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2121
# THE SOFTWARE.
2222
"""
23-
`adafruit_bmp280`
24-
====================================================
23+
`adafruit_bmp280` - Adafruit BMP280 - Temperature & Barometic Pressure Sensor
24+
===============================================================================
2525
2626
CircuitPython driver from BMP280 Temperature and Barometic Pressure sensor
2727
2828
* Author(s): ladyada
2929
"""
3030
import math
31-
import time
31+
from time import sleep
3232
try:
3333
import struct
3434
except ImportError:
3535
import ustruct as struct
36-
3736
from micropython import const
3837

3938
__version__ = "0.0.0-auto.0"
@@ -45,24 +44,61 @@
4544

4645
_REGISTER_CHIPID = const(0xD0)
4746
_REGISTER_DIG_T1 = const(0x88)
48-
# _REGISTER_DIG_T2 = const(0x8A)
49-
# _REGISTER_DIG_T3 = const(0x8C)
50-
# _REGISTER_DIG_P1 = const(0x8E)
51-
# _REGISTER_DIG_P2 = const(0x90)
52-
# _REGISTER_DIG_P3 = const(0x92)
53-
# _REGISTER_DIG_P4 = const(0x94)
54-
# _REGISTER_DIG_P5 = const(0x96)
55-
# _REGISTER_DIG_P6 = const(0x98)
56-
# _REGISTER_DIG_P7 = const(0x9A)
57-
# _REGISTER_DIG_P8 = const(0x9C)
58-
# _REGISTER_DIG_P9 = const(0x9E)
5947
_REGISTER_SOFTRESET = const(0xE0)
6048
_REGISTER_STATUS = const(0xF3)
61-
_REGISTER_CONTROL = const(0xF4)
49+
_REGISTER_CTRL_MEAS = const(0xF4)
6250
_REGISTER_CONFIG = const(0xF5)
6351
_REGISTER_PRESSUREDATA = const(0xF7)
6452
_REGISTER_TEMPDATA = const(0xFA)
6553

54+
_BMP280_PRESSURE_MIN_HPA = const(300)
55+
_BMP280_PRESSURE_MAX_HPA = const(1100)
56+
57+
58+
"""iir_filter values"""
59+
IIR_FILTER_DISABLE = const(0)
60+
IIR_FILTER_X2 = const(0x01)
61+
IIR_FILTER_X4 = const(0x02)
62+
IIR_FILTER_X8 = const(0x03)
63+
IIR_FILTER_X16 = const(0x04)
64+
65+
_BMP280_IIR_FILTERS = frozenset((IIR_FILTER_DISABLE, IIR_FILTER_X2,
66+
IIR_FILTER_X4, IIR_FILTER_X8, IIR_FILTER_X16))
67+
68+
"""overscan values for temperature, pressure, and humidity"""
69+
OVERSCAN_DISABLE = const(0x00)
70+
OVERSCAN_X1 = const(0x01)
71+
OVERSCAN_X2 = const(0x02)
72+
OVERSCAN_X4 = const(0x03)
73+
OVERSCAN_X8 = const(0x04)
74+
OVERSCAN_X16 = const(0x05)
75+
76+
_BMP280_OVERSCANS = {OVERSCAN_DISABLE:0, OVERSCAN_X1:1, OVERSCAN_X2:2,
77+
OVERSCAN_X4:4, OVERSCAN_X8:8, OVERSCAN_X16:16}
78+
79+
"""mode values"""
80+
MODE_SLEEP = const(0x00)
81+
MODE_FORCE = const(0x01)
82+
MODE_NORMAL = const(0x03)
83+
84+
_BMP280_MODES = frozenset((MODE_SLEEP, MODE_FORCE, MODE_NORMAL))
85+
"""
86+
standby timeconstant values
87+
TC_X[_Y] where X=milliseconds and Y=tenths of a millisecond
88+
"""
89+
STANDBY_TC_0_5 = const(0x00) #0.5ms
90+
STANDBY_TC_10 = const(0x06) #10ms
91+
STANDBY_TC_20 = const(0x07) #20ms
92+
STANDBY_TC_62_5 = const(0x01) #62.5ms
93+
STANDBY_TC_125 = const(0x02) #125ms
94+
STANDBY_TC_250 = const(0x03) #250ms
95+
STANDBY_TC_500 = const(0x04) #500ms
96+
STANDBY_TC_1000 = const(0x05) #1000ms
97+
98+
_BMP280_STANDBY_TCS = frozenset((STANDBY_TC_0_5, STANDBY_TC_10, STANDBY_TC_20,
99+
STANDBY_TC_62_5, STANDBY_TC_125, STANDBY_TC_250,
100+
STANDBY_TC_500, STANDBY_TC_1000))
101+
66102
class Adafruit_BMP280: # pylint: disable=invalid-name
67103
"""Base BMP280 object. Use `Adafruit_BMP280_I2C` or `Adafruit_BMP280_SPI` instead of this. This
68104
checks the BMP280 was found, reads the coefficients and enables the sensor for continuous
@@ -72,56 +108,221 @@ def __init__(self):
72108
chip_id = self._read_byte(_REGISTER_CHIPID)
73109
if _CHIP_ID != chip_id:
74110
raise RuntimeError('Failed to find BMP280! Chip ID 0x%x' % chip_id)
111+
#Set some reasonable defaults.
112+
self._iir_filter = IIR_FILTER_DISABLE
113+
self._overscan_temperature = OVERSCAN_X2
114+
self._overscan_pressure = OVERSCAN_X16
115+
self._t_standby = STANDBY_TC_0_5
116+
self._mode = MODE_SLEEP
117+
self._reset()
75118
self._read_coefficients()
119+
self._write_ctrl_meas()
120+
self._write_config()
76121
self.sea_level_pressure = 1013.25
77122
"""Pressure in hectoPascals at sea level. Used to calibrate `altitude`."""
123+
self._t_fine = None
78124

79-
def _read(self):
80-
"""Returns a tuple for temperature and pressure."""
81-
# perform one measurement in high res, forced mode
82-
self._write_register_byte(_REGISTER_CONTROL, 0xFE)
83-
84-
# Wait for conversion to complete
85-
while self._read_byte(_REGISTER_STATUS) & 0x08:
86-
time.sleep(0.002)
87-
# lowest 4 bits get dropped
88-
UT = self._read24(_REGISTER_TEMPDATA) / 16
125+
def _read_temperature(self):
126+
# perform one measurement
127+
if self.mode != MODE_NORMAL:
128+
self.mode = MODE_FORCE
129+
# Wait for conversion to complete
130+
while self._get_status() & 0x08:
131+
sleep(0.002)
132+
raw_temperature = self._read24(_REGISTER_TEMPDATA) / 16 # lowest 4 bits get dropped
89133
#print("raw temp: ", UT)
134+
var1 = (raw_temperature / 16384.0 - self._temp_calib[0] / 1024.0) * self._temp_calib[1]
135+
#print(var1)
136+
var2 = ((raw_temperature / 131072.0 - self._temp_calib[0] / 8192.0) * (
137+
raw_temperature / 131072.0 - self._temp_calib[0] / 8192.0)) * self._temp_calib[2]
138+
#print(var2)
90139

91-
var1 = (UT / 16384.0 - self._temp_calib[0] / 1024.0) * self._temp_calib[1]
92-
var2 = ((UT / 131072.0 - self._temp_calib[0] / 8192.0) * (
93-
UT / 131072.0 - self._temp_calib[0] / 8192.0)) * self._temp_calib[2]
140+
self._t_fine = int(var1 + var2)
94141
#print("t_fine: ", self.t_fine)
95-
t_fine = int(var1 + var2)
96-
temperature = t_fine / 5120.0
97142

98-
adc = self._read24(_REGISTER_PRESSUREDATA) / 16
99-
var1 = float(t_fine) / 2.0 - 64000.0
100-
var2 = var1 * var1 * self._pressure_calib[5] / 32768.0
101-
var2 = var2 + var1 * self._pressure_calib[4] * 2.0
102-
var2 = var2 / 4.0 + self._pressure_calib[3] * 65536.0
103-
var1 = (self._pressure_calib[2] * var1 * var1 / 524288.0 +
104-
self._pressure_calib[1] * var1) / 524288.0
105-
var1 = (1.0 + var1 / 32768.0) * self._pressure_calib[0]
106-
if var1 == 0:
107-
return 0
108-
p = 1048576.0 - adc
109-
p = ((p - var2 / 4096.0) * 6250.0) / var1
110-
var1 = self._pressure_calib[8] * p * p / 2147483648.0
111-
var2 = p * self._pressure_calib[7] / 32768.0
112-
p = p + (var1 + var2 + self._pressure_calib[6]) / 16.0
113-
pressure = p / 100
114-
return (temperature, pressure)
143+
def _reset(self):
144+
"""Soft reset the sensor"""
145+
self._write_register_byte(_REGISTER_SOFTRESET, 0xB6)
146+
sleep(0.004) #Datasheet says 2ms. Using 4ms just to be safe
147+
148+
def _write_ctrl_meas(self):
149+
"""
150+
Write the values to the ctrl_meas register in the device
151+
ctrl_meas sets the pressure and temperature data acquistion options
152+
"""
153+
self._write_register_byte(_REGISTER_CTRL_MEAS, self._ctrl_meas)
154+
155+
def _get_status(self):
156+
"""Get the value from the status register in the device """
157+
return self._read_byte(_REGISTER_STATUS)
158+
159+
def _read_config(self):
160+
"""Read the value from the config register in the device """
161+
return self._read_byte(_REGISTER_CONFIG)
162+
163+
def _write_config(self):
164+
"""Write the value to the config register in the device """
165+
normal_flag = False
166+
if self._mode == MODE_NORMAL:
167+
#Writes to the config register may be ignored while in Normal mode
168+
normal_flag = True
169+
self.mode = MODE_SLEEP #So we switch to Sleep mode first
170+
self._write_register_byte(_REGISTER_CONFIG, self._config)
171+
if normal_flag:
172+
self.mode = MODE_NORMAL
173+
174+
@property
175+
def mode(self):
176+
"""
177+
Operation mode
178+
Allowed values are set in the MODE enum class
179+
"""
180+
return self._mode
181+
182+
@mode.setter
183+
def mode(self, value):
184+
if not value in _BMP280_MODES:
185+
raise ValueError('Mode \'%s\' not supported' % (value))
186+
self._mode = value
187+
self._write_ctrl_meas()
188+
189+
@property
190+
def standby_period(self):
191+
"""
192+
Control the inactive period when in Normal mode
193+
Allowed standby periods are set the STANDBY enum class
194+
"""
195+
return self._t_standby
196+
197+
@standby_period.setter
198+
def standby_period(self, value):
199+
if not value in _BMP280_STANDBY_TCS:
200+
raise ValueError('Standby Period \'%s\' not supported' % (value))
201+
if self._t_standby == value:
202+
return
203+
self._t_standby = value
204+
self._write_config()
205+
206+
@property
207+
def overscan_temperature(self):
208+
"""
209+
Temperature Oversampling
210+
Allowed values are set in the OVERSCAN enum class
211+
"""
212+
return self._overscan_temperature
213+
214+
@overscan_temperature.setter
215+
def overscan_temperature(self, value):
216+
if not value in _BMP280_OVERSCANS:
217+
raise ValueError('Overscan value \'%s\' not supported' % (value))
218+
self._overscan_temperature = value
219+
self._write_ctrl_meas()
220+
221+
@property
222+
def overscan_pressure(self):
223+
"""
224+
Pressure Oversampling
225+
Allowed values are set in the OVERSCAN enum class
226+
"""
227+
return self._overscan_pressure
228+
229+
@overscan_pressure.setter
230+
def overscan_pressure(self, value):
231+
if not value in _BMP280_OVERSCANS:
232+
raise ValueError('Overscan value \'%s\' not supported' % (value))
233+
self._overscan_pressure = value
234+
self._write_ctrl_meas()
235+
236+
@property
237+
def iir_filter(self):
238+
"""
239+
Controls the time constant of the IIR filter
240+
Allowed values are set in the IIR_FILTER enum class
241+
"""
242+
return self._iir_filter
243+
244+
@iir_filter.setter
245+
def iir_filter(self, value):
246+
if not value in _BMP280_IIR_FILTERS:
247+
raise ValueError('IIR Filter \'%s\' not supported' % (value))
248+
self._iir_filter = value
249+
self._write_config()
250+
251+
@property
252+
def _config(self):
253+
"""Value to be written to the device's config register """
254+
config = 0
255+
if self.mode == MODE_NORMAL:
256+
config += (self._t_standby << 5)
257+
if self._iir_filter:
258+
config += (self._iir_filter << 2)
259+
return config
260+
261+
@property
262+
def _ctrl_meas(self):
263+
"""Value to be written to the device's ctrl_meas register """
264+
ctrl_meas = (self.overscan_temperature << 5)
265+
ctrl_meas += (self.overscan_pressure << 2)
266+
ctrl_meas += self.mode
267+
return ctrl_meas
268+
269+
@property
270+
def measurement_time_typical(self):
271+
"""Typical time in milliseconds required to complete a measurement in normal mode"""
272+
meas_time_ms = 1
273+
if self.overscan_temperature != OVERSCAN_DISABLE:
274+
meas_time_ms += (2 * _BMP280_OVERSCANS.get(self.overscan_temperature))
275+
if self.overscan_pressure != OVERSCAN_DISABLE:
276+
meas_time_ms += (2 * _BMP280_OVERSCANS.get(self.overscan_pressure) + 0.5)
277+
return meas_time_ms
278+
279+
@property
280+
def measurement_time_max(self):
281+
"""Maximum time in milliseconds required to complete a measurement in normal mode"""
282+
meas_time_ms = 1.25
283+
if self.overscan_temperature != OVERSCAN_DISABLE:
284+
meas_time_ms += (2.3 * _BMP280_OVERSCANS.get(self.overscan_temperature))
285+
if self.overscan_pressure != OVERSCAN_DISABLE:
286+
meas_time_ms += (2.3 * _BMP280_OVERSCANS.get(self.overscan_pressure) + 0.575)
287+
return meas_time_ms
115288

116289
@property
117290
def temperature(self):
118291
"""The compensated temperature in degrees celsius."""
119-
return self._read()[0]
292+
self._read_temperature()
293+
return self._t_fine / 5120.0
120294

121295
@property
122296
def pressure(self):
123-
"""The compensated pressure in hectoPascals."""
124-
return self._read()[1]
297+
"""
298+
The compensated pressure in hectoPascals.
299+
returns None if pressure measurement is disabled
300+
"""
301+
self._read_temperature()
302+
303+
# Algorithm from the BMP280 driver
304+
# https://github.com/BoschSensortec/BMP280_driver/blob/master/bmp280.c
305+
adc = self._read24(_REGISTER_PRESSUREDATA) / 16 # lowest 4 bits get dropped
306+
var1 = float(self._t_fine) / 2.0 - 64000.0
307+
var2 = var1 * var1 * self._pressure_calib[5] / 32768.0
308+
var2 = var2 + var1 * self._pressure_calib[4] * 2.0
309+
var2 = var2 / 4.0 + self._pressure_calib[3] * 65536.0
310+
var3 = self._pressure_calib[2] * var1 * var1 / 524288.0
311+
var1 = (var3 + self._pressure_calib[1] * var1) / 524288.0
312+
var1 = (1.0 + var1 / 32768.0) * self._pressure_calib[0]
313+
if not var1:
314+
return _BMP280_PRESSURE_MIN_HPA
315+
pressure = 1048576.0 - adc
316+
pressure = ((pressure - var2 / 4096.0) * 6250.0) / var1
317+
var1 = self._pressure_calib[8] * pressure * pressure / 2147483648.0
318+
var2 = pressure * self._pressure_calib[7] / 32768.0
319+
pressure = pressure + (var1 + var2 + self._pressure_calib[6]) / 16.0
320+
pressure /= 100
321+
if pressure < _BMP280_PRESSURE_MIN_HPA:
322+
return _BMP280_PRESSURE_MIN_HPA
323+
if pressure > _BMP280_PRESSURE_MAX_HPA:
324+
return _BMP280_PRESSURE_MAX_HPA
325+
return pressure
125326

126327
@property
127328
def altitude(self):

examples/bmp280_normal_mode.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
"""
2+
Example showing how the BMP280 library can be used to set the various
3+
parameters supported by the sensor.
4+
Refer to the BMP280 datasheet to understand what these parameters do
5+
"""
6+
import time
7+
8+
import board
9+
import busio
10+
import adafruit_bmp280
11+
12+
# Create library object using our Bus I2C port
13+
i2c = busio.I2C(board.SCL, board.SDA)
14+
bmp280 = adafruit_bmp280.Adafruit_BMP280_I2C(i2c)
15+
16+
# OR create library object using our Bus SPI port
17+
#spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
18+
#bmp_cs = digitalio.DigitalInOut(board.D10)
19+
#bmp280 = adafruit_bmp280.Adafruit_BMP280_SPI(spi, bmp_cs)
20+
21+
# change this to match the location's pressure (hPa) at sea level
22+
bmp280.sea_level_pressure = 1013.25
23+
bmp280.mode = adafruit_bmp280.MODE_NORMAL
24+
bmp280.standby_period = adafruit_bmp280.STANDBY_TC_500
25+
bmp280.iir_filter = adafruit_bmp280.IIR_FILTER_X16
26+
bmp280.overscan_pressure = adafruit_bmp280.OVERSCAN_X16
27+
bmp280.overscan_temperature = adafruit_bmp280.OVERSCAN_X2
28+
#The sensor will need a moment to gather inital readings
29+
time.sleep(1)
30+
31+
while True:
32+
print("\nTemperature: %0.1f C" % bmp280.temperature)
33+
print("Pressure: %0.1f hPa" % bmp280.pressure)
34+
print("Altitude = %0.2f meters" % bmp280.altitude)
35+
time.sleep(2)

0 commit comments

Comments
 (0)