Skip to content

Adopt changes from the BME280 library #13

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
May 3, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
303 changes: 252 additions & 51 deletions adafruit_bmp280.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,19 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
"""
`adafruit_bmp280`
====================================================
`adafruit_bmp280` - Adafruit BMP280 - Temperature & Barometic Pressure Sensor
===============================================================================

CircuitPython driver from BMP280 Temperature and Barometic Pressure sensor

* Author(s): ladyada
"""
import math
import time
from time import sleep
try:
import struct
except ImportError:
import ustruct as struct

from micropython import const

__version__ = "0.0.0-auto.0"
Expand All @@ -45,24 +44,61 @@

_REGISTER_CHIPID = const(0xD0)
_REGISTER_DIG_T1 = const(0x88)
# _REGISTER_DIG_T2 = const(0x8A)
# _REGISTER_DIG_T3 = const(0x8C)
# _REGISTER_DIG_P1 = const(0x8E)
# _REGISTER_DIG_P2 = const(0x90)
# _REGISTER_DIG_P3 = const(0x92)
# _REGISTER_DIG_P4 = const(0x94)
# _REGISTER_DIG_P5 = const(0x96)
# _REGISTER_DIG_P6 = const(0x98)
# _REGISTER_DIG_P7 = const(0x9A)
# _REGISTER_DIG_P8 = const(0x9C)
# _REGISTER_DIG_P9 = const(0x9E)
_REGISTER_SOFTRESET = const(0xE0)
_REGISTER_STATUS = const(0xF3)
_REGISTER_CONTROL = const(0xF4)
_REGISTER_CTRL_MEAS = const(0xF4)
_REGISTER_CONFIG = const(0xF5)
_REGISTER_PRESSUREDATA = const(0xF7)
_REGISTER_TEMPDATA = const(0xFA)

_BMP280_PRESSURE_MIN_HPA = const(300)
_BMP280_PRESSURE_MAX_HPA = const(1100)


"""iir_filter values"""
IIR_FILTER_DISABLE = const(0)
IIR_FILTER_X2 = const(0x01)
IIR_FILTER_X4 = const(0x02)
IIR_FILTER_X8 = const(0x03)
IIR_FILTER_X16 = const(0x04)

_BMP280_IIR_FILTERS = frozenset((IIR_FILTER_DISABLE, IIR_FILTER_X2,
IIR_FILTER_X4, IIR_FILTER_X8, IIR_FILTER_X16))

"""overscan values for temperature, pressure, and humidity"""
OVERSCAN_DISABLE = const(0x00)
OVERSCAN_X1 = const(0x01)
OVERSCAN_X2 = const(0x02)
OVERSCAN_X4 = const(0x03)
OVERSCAN_X8 = const(0x04)
OVERSCAN_X16 = const(0x05)

_BMP280_OVERSCANS = {OVERSCAN_DISABLE:0, OVERSCAN_X1:1, OVERSCAN_X2:2,
OVERSCAN_X4:4, OVERSCAN_X8:8, OVERSCAN_X16:16}

"""mode values"""
MODE_SLEEP = const(0x00)
MODE_FORCE = const(0x01)
MODE_NORMAL = const(0x03)

_BMP280_MODES = frozenset((MODE_SLEEP, MODE_FORCE, MODE_NORMAL))
"""
standby timeconstant values
TC_X[_Y] where X=milliseconds and Y=tenths of a millisecond
"""
STANDBY_TC_0_5 = const(0x00) #0.5ms
STANDBY_TC_10 = const(0x06) #10ms
STANDBY_TC_20 = const(0x07) #20ms
STANDBY_TC_62_5 = const(0x01) #62.5ms
STANDBY_TC_125 = const(0x02) #125ms
STANDBY_TC_250 = const(0x03) #250ms
STANDBY_TC_500 = const(0x04) #500ms
STANDBY_TC_1000 = const(0x05) #1000ms

_BMP280_STANDBY_TCS = frozenset((STANDBY_TC_0_5, STANDBY_TC_10, STANDBY_TC_20,
STANDBY_TC_62_5, STANDBY_TC_125, STANDBY_TC_250,
STANDBY_TC_500, STANDBY_TC_1000))

class Adafruit_BMP280: # pylint: disable=invalid-name
"""Base BMP280 object. Use `Adafruit_BMP280_I2C` or `Adafruit_BMP280_SPI` instead of this. This
checks the BMP280 was found, reads the coefficients and enables the sensor for continuous
Expand All @@ -72,56 +108,221 @@ def __init__(self):
chip_id = self._read_byte(_REGISTER_CHIPID)
if _CHIP_ID != chip_id:
raise RuntimeError('Failed to find BMP280! Chip ID 0x%x' % chip_id)
#Set some reasonable defaults.
self._iir_filter = IIR_FILTER_DISABLE
self._overscan_temperature = OVERSCAN_X2
self._overscan_pressure = OVERSCAN_X16
self._t_standby = STANDBY_TC_0_5
self._mode = MODE_SLEEP
self._reset()
self._read_coefficients()
self._write_ctrl_meas()
self._write_config()
self.sea_level_pressure = 1013.25
"""Pressure in hectoPascals at sea level. Used to calibrate `altitude`."""
self._t_fine = None

def _read(self):
"""Returns a tuple for temperature and pressure."""
# perform one measurement in high res, forced mode
self._write_register_byte(_REGISTER_CONTROL, 0xFE)

# Wait for conversion to complete
while self._read_byte(_REGISTER_STATUS) & 0x08:
time.sleep(0.002)
# lowest 4 bits get dropped
UT = self._read24(_REGISTER_TEMPDATA) / 16
def _read_temperature(self):
# perform one measurement
if self.mode != MODE_NORMAL:
self.mode = MODE_FORCE
# Wait for conversion to complete
while self._get_status() & 0x08:
sleep(0.002)
raw_temperature = self._read24(_REGISTER_TEMPDATA) / 16 # lowest 4 bits get dropped
#print("raw temp: ", UT)
var1 = (raw_temperature / 16384.0 - self._temp_calib[0] / 1024.0) * self._temp_calib[1]
#print(var1)
var2 = ((raw_temperature / 131072.0 - self._temp_calib[0] / 8192.0) * (
raw_temperature / 131072.0 - self._temp_calib[0] / 8192.0)) * self._temp_calib[2]
#print(var2)

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

adc = self._read24(_REGISTER_PRESSUREDATA) / 16
var1 = float(t_fine) / 2.0 - 64000.0
var2 = var1 * var1 * self._pressure_calib[5] / 32768.0
var2 = var2 + var1 * self._pressure_calib[4] * 2.0
var2 = var2 / 4.0 + self._pressure_calib[3] * 65536.0
var1 = (self._pressure_calib[2] * var1 * var1 / 524288.0 +
self._pressure_calib[1] * var1) / 524288.0
var1 = (1.0 + var1 / 32768.0) * self._pressure_calib[0]
if var1 == 0:
return 0
p = 1048576.0 - adc
p = ((p - var2 / 4096.0) * 6250.0) / var1
var1 = self._pressure_calib[8] * p * p / 2147483648.0
var2 = p * self._pressure_calib[7] / 32768.0
p = p + (var1 + var2 + self._pressure_calib[6]) / 16.0
pressure = p / 100
return (temperature, pressure)
def _reset(self):
"""Soft reset the sensor"""
self._write_register_byte(_REGISTER_SOFTRESET, 0xB6)
sleep(0.004) #Datasheet says 2ms. Using 4ms just to be safe

def _write_ctrl_meas(self):
"""
Write the values to the ctrl_meas register in the device
ctrl_meas sets the pressure and temperature data acquistion options
"""
self._write_register_byte(_REGISTER_CTRL_MEAS, self._ctrl_meas)

def _get_status(self):
"""Get the value from the status register in the device """
return self._read_byte(_REGISTER_STATUS)

def _read_config(self):
"""Read the value from the config register in the device """
return self._read_byte(_REGISTER_CONFIG)

def _write_config(self):
"""Write the value to the config register in the device """
normal_flag = False
if self._mode == MODE_NORMAL:
#Writes to the config register may be ignored while in Normal mode
normal_flag = True
self.mode = MODE_SLEEP #So we switch to Sleep mode first
self._write_register_byte(_REGISTER_CONFIG, self._config)
if normal_flag:
self.mode = MODE_NORMAL

@property
def mode(self):
"""
Operation mode
Allowed values are set in the MODE enum class
"""
return self._mode

@mode.setter
def mode(self, value):
if not value in _BMP280_MODES:
raise ValueError('Mode \'%s\' not supported' % (value))
self._mode = value
self._write_ctrl_meas()

@property
def standby_period(self):
"""
Control the inactive period when in Normal mode
Allowed standby periods are set the STANDBY enum class
"""
return self._t_standby

@standby_period.setter
def standby_period(self, value):
if not value in _BMP280_STANDBY_TCS:
raise ValueError('Standby Period \'%s\' not supported' % (value))
if self._t_standby == value:
return
self._t_standby = value
self._write_config()

@property
def overscan_temperature(self):
"""
Temperature Oversampling
Allowed values are set in the OVERSCAN enum class
"""
return self._overscan_temperature

@overscan_temperature.setter
def overscan_temperature(self, value):
if not value in _BMP280_OVERSCANS:
raise ValueError('Overscan value \'%s\' not supported' % (value))
self._overscan_temperature = value
self._write_ctrl_meas()

@property
def overscan_pressure(self):
"""
Pressure Oversampling
Allowed values are set in the OVERSCAN enum class
"""
return self._overscan_pressure

@overscan_pressure.setter
def overscan_pressure(self, value):
if not value in _BMP280_OVERSCANS:
raise ValueError('Overscan value \'%s\' not supported' % (value))
self._overscan_pressure = value
self._write_ctrl_meas()

@property
def iir_filter(self):
"""
Controls the time constant of the IIR filter
Allowed values are set in the IIR_FILTER enum class
"""
return self._iir_filter

@iir_filter.setter
def iir_filter(self, value):
if not value in _BMP280_IIR_FILTERS:
raise ValueError('IIR Filter \'%s\' not supported' % (value))
self._iir_filter = value
self._write_config()

@property
def _config(self):
"""Value to be written to the device's config register """
config = 0
if self.mode == MODE_NORMAL:
config += (self._t_standby << 5)
if self._iir_filter:
config += (self._iir_filter << 2)
return config

@property
def _ctrl_meas(self):
"""Value to be written to the device's ctrl_meas register """
ctrl_meas = (self.overscan_temperature << 5)
ctrl_meas += (self.overscan_pressure << 2)
ctrl_meas += self.mode
return ctrl_meas

@property
def measurement_time_typical(self):
"""Typical time in milliseconds required to complete a measurement in normal mode"""
meas_time_ms = 1
if self.overscan_temperature != OVERSCAN_DISABLE:
meas_time_ms += (2 * _BMP280_OVERSCANS.get(self.overscan_temperature))
if self.overscan_pressure != OVERSCAN_DISABLE:
meas_time_ms += (2 * _BMP280_OVERSCANS.get(self.overscan_pressure) + 0.5)
return meas_time_ms

@property
def measurement_time_max(self):
"""Maximum time in milliseconds required to complete a measurement in normal mode"""
meas_time_ms = 1.25
if self.overscan_temperature != OVERSCAN_DISABLE:
meas_time_ms += (2.3 * _BMP280_OVERSCANS.get(self.overscan_temperature))
if self.overscan_pressure != OVERSCAN_DISABLE:
meas_time_ms += (2.3 * _BMP280_OVERSCANS.get(self.overscan_pressure) + 0.575)
return meas_time_ms

@property
def temperature(self):
"""The compensated temperature in degrees celsius."""
return self._read()[0]
self._read_temperature()
return self._t_fine / 5120.0

@property
def pressure(self):
"""The compensated pressure in hectoPascals."""
return self._read()[1]
"""
The compensated pressure in hectoPascals.
returns None if pressure measurement is disabled
"""
self._read_temperature()

# Algorithm from the BMP280 driver
# https://github.com/BoschSensortec/BMP280_driver/blob/master/bmp280.c
adc = self._read24(_REGISTER_PRESSUREDATA) / 16 # lowest 4 bits get dropped
var1 = float(self._t_fine) / 2.0 - 64000.0
var2 = var1 * var1 * self._pressure_calib[5] / 32768.0
var2 = var2 + var1 * self._pressure_calib[4] * 2.0
var2 = var2 / 4.0 + self._pressure_calib[3] * 65536.0
var3 = self._pressure_calib[2] * var1 * var1 / 524288.0
var1 = (var3 + self._pressure_calib[1] * var1) / 524288.0
var1 = (1.0 + var1 / 32768.0) * self._pressure_calib[0]
if not var1:
return _BMP280_PRESSURE_MIN_HPA
pressure = 1048576.0 - adc
pressure = ((pressure - var2 / 4096.0) * 6250.0) / var1
var1 = self._pressure_calib[8] * pressure * pressure / 2147483648.0
var2 = pressure * self._pressure_calib[7] / 32768.0
pressure = pressure + (var1 + var2 + self._pressure_calib[6]) / 16.0
pressure /= 100
if pressure < _BMP280_PRESSURE_MIN_HPA:
return _BMP280_PRESSURE_MIN_HPA
if pressure > _BMP280_PRESSURE_MAX_HPA:
return _BMP280_PRESSURE_MAX_HPA
return pressure

@property
def altitude(self):
Expand Down
35 changes: 35 additions & 0 deletions examples/bmp280_normal_mode.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
"""
Example showing how the BMP280 library can be used to set the various
parameters supported by the sensor.
Refer to the BMP280 datasheet to understand what these parameters do
"""
import time

import board
import busio
import adafruit_bmp280

# Create library object using our Bus I2C port
i2c = busio.I2C(board.SCL, board.SDA)
bmp280 = adafruit_bmp280.Adafruit_BMP280_I2C(i2c)

# OR create library object using our Bus SPI port
#spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
#bmp_cs = digitalio.DigitalInOut(board.D10)
#bmp280 = adafruit_bmp280.Adafruit_BMP280_SPI(spi, bmp_cs)

# change this to match the location's pressure (hPa) at sea level
bmp280.sea_level_pressure = 1013.25
bmp280.mode = adafruit_bmp280.MODE_NORMAL
bmp280.standby_period = adafruit_bmp280.STANDBY_TC_500
bmp280.iir_filter = adafruit_bmp280.IIR_FILTER_X16
bmp280.overscan_pressure = adafruit_bmp280.OVERSCAN_X16
bmp280.overscan_temperature = adafruit_bmp280.OVERSCAN_X2
#The sensor will need a moment to gather inital readings
time.sleep(1)

while True:
print("\nTemperature: %0.1f C" % bmp280.temperature)
print("Pressure: %0.1f hPa" % bmp280.pressure)
print("Altitude = %0.2f meters" % bmp280.altitude)
time.sleep(2)