diff --git a/adafruit_bme280.py b/adafruit_bme280.py index 24c7502..1e3e09c 100644 --- a/adafruit_bme280.py +++ b/adafruit_bme280.py @@ -20,15 +20,15 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. """ -`adafruit_bme280` - Adafruit BME680 - Temperature, Humidity, Pressure & Gas Sensor -==================================================================================== +`adafruit_bme280` - Adafruit BME280 - Temperature, Humidity & Barometic Pressure Sensor +========================================================================================= CircuitPython driver from BME280 Temperature, Humidity and Barometic Pressure sensor * Author(s): ladyada """ import math -import time +from time import sleep from micropython import const try: import struct @@ -67,34 +67,83 @@ _BME280_HUMIDITY_MIN = const(0) _BME280_HUMIDITY_MAX = const(100) +"""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) + +_BME280_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) + +_BME280_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) + +_BME280_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 + +_BME280_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_BME280: """Driver from BME280 Temperature, Humidity and Barometic Pressure sensor""" + # pylint: disable=too-many-instance-attributes def __init__(self): - """Check the BME280 was found, read the coefficients and enable the sensor for continuous - reads""" + """Check the BME280 was found, read the coefficients and enable the sensor""" # Check device ID. chip_id = self._read_byte(_BME280_REGISTER_CHIPID) if _BME280_CHIPID != chip_id: raise RuntimeError('Failed to find BME280! Chip ID 0x%x' % chip_id) - self._write_register_byte(_BME280_REGISTER_SOFTRESET, 0xB6) - time.sleep(0.5) + #Set some reasonable defaults. + self._iir_filter = IIR_FILTER_DISABLE + self._overscan_humidity = OVERSCAN_X1 + self._overscan_temperature = OVERSCAN_X1 + self._overscan_pressure = OVERSCAN_X16 + self._t_standby = STANDBY_TC_125 + 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`.""" - # turn on humidity oversample 16x - self._write_register_byte(_BME280_REGISTER_CTRL_HUM, 0x03) self._t_fine = None def _read_temperature(self): # perform one measurement - self._write_register_byte(_BME280_REGISTER_CTRL_MEAS, 0xFE) # high res, forced mode - - # Wait for conversion to complete - while self._read_byte(_BME280_REGISTER_STATUS) & 0x08: - time.sleep(0.002) - raw_temperature = self._read24(_BME280_REGISTER_TEMPDATA) / 16 # lowest 4 bits get dropped + 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(_BME280_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) * ( @@ -104,6 +153,173 @@ def _read_temperature(self): self._t_fine = int(var1 + var2) #print("t_fine: ", self.t_fine) + def _reset(self): + """Soft reset the sensor""" + self._write_register_byte(_BME280_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 and ctrl_hum registers in the device + ctrl_meas sets the pressure and temperature data acquistion options + ctrl_hum sets the humidty oversampling and must be written to first + """ + self._write_register_byte(_BME280_REGISTER_CTRL_HUM, self.overscan_humidity) + self._write_register_byte(_BME280_REGISTER_CTRL_MEAS, self._ctrl_meas) + + def _get_status(self): + """Get the value from the status register in the device """ + return self._read_byte(_BME280_REGISTER_STATUS) + + def _read_config(self): + """Read the value from the config register in the device """ + return self._read_byte(_BME280_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(_BME280_REGISTER_CONFIG, self._config) + if normal_flag: + self.mode = MODE_NORMAL + + @property + def mode(self): + """ + Operation mode + Allowed values are the constants MODE_* + """ + return self._mode + + @mode.setter + def mode(self, value): + if not value in _BME280_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 the constants STANDBY_TC_* + """ + return self._t_standby + + @standby_period.setter + def standby_period(self, value): + if not value in _BME280_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_humidity(self): + """ + Humidity Oversampling + Allowed values are the constants OVERSCAN_* + """ + return self._overscan_humidity + + @overscan_humidity.setter + def overscan_humidity(self, value): + if not value in _BME280_OVERSCANS: + raise ValueError('Overscan value \'%s\' not supported' % (value)) + self._overscan_humidity = value + self._write_ctrl_meas() + + @property + def overscan_temperature(self): + """ + Temperature Oversampling + Allowed values are the constants OVERSCAN_* + """ + return self._overscan_temperature + + @overscan_temperature.setter + def overscan_temperature(self, value): + if not value in _BME280_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 the constants OVERSCAN_* + """ + return self._overscan_pressure + + @overscan_pressure.setter + def overscan_pressure(self, value): + if not value in _BME280_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 the constants IIR_FILTER_* + """ + return self._iir_filter + + @iir_filter.setter + def iir_filter(self, value): + if not value in _BME280_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.0 + if self.overscan_temperature != OVERSCAN_DISABLE: + meas_time_ms += (2 * _BME280_OVERSCANS.get(self.overscan_temperature)) + if self.overscan_pressure != OVERSCAN_DISABLE: + meas_time_ms += (2 * _BME280_OVERSCANS.get(self.overscan_pressure) + 0.5) + if self.overscan_humidity != OVERSCAN_DISABLE: + meas_time_ms += (2 * _BME280_OVERSCANS.get(self.overscan_humidity) + 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 * _BME280_OVERSCANS.get(self.overscan_temperature)) + if self.overscan_pressure != OVERSCAN_DISABLE: + meas_time_ms += (2.3 * _BME280_OVERSCANS.get(self.overscan_pressure) + 0.575) + if self.overscan_humidity != OVERSCAN_DISABLE: + meas_time_ms += (2.3 * _BME280_OVERSCANS.get(self.overscan_humidity) + 0.575) + return meas_time_ms + @property def temperature(self): """The compensated temperature in degrees celsius.""" @@ -112,7 +328,10 @@ def temperature(self): @property def pressure(self): - """The compensated pressure in hectoPascals.""" + """ + The compensated pressure in hectoPascals. + returns None if pressure measurement is disabled + """ self._read_temperature() # Algorithm from the BME280 driver @@ -145,7 +364,10 @@ def pressure(self): @property def humidity(self): - """The relative humidity in RH %""" + """ + The relative humidity in RH % + returns None if humidity measurement is disabled + """ self._read_temperature() hum = self._read_register(_BME280_REGISTER_HUMIDDATA, 2) #print("Humidity data: ", hum) diff --git a/examples/bme280_normal_mode.py b/examples/bme280_normal_mode.py new file mode 100644 index 0000000..3167b49 --- /dev/null +++ b/examples/bme280_normal_mode.py @@ -0,0 +1,37 @@ +""" +Example showing how the BME280 library can be used to set the various +parameters supported by the sensor. +Refer to the BME280 datasheet to understand what these parameters do +""" +import time + +import board +import busio +import adafruit_bme280 + +# Create library object using our Bus I2C port +i2c = busio.I2C(board.SCL, board.SDA) +bme280 = adafruit_bme280.Adafruit_BME280_I2C(i2c) + +# OR create library object using our Bus SPI port +#spi = busio.SPI(board.SCK, board.MOSI, board.MISO) +#bme_cs = digitalio.DigitalInOut(board.D10) +#bme280 = adafruit_bme280.Adafruit_BME280_SPI(spi, bme_cs) + +# change this to match the location's pressure (hPa) at sea level +bme280.sea_level_pressure = 1013.25 +bme280.mode = adafruit_bme280.MODE_NORMAL +bme280.standby_period = adafruit_bme280.STANDBY_TC_500 +bme280.iir_filter = adafruit_bme280.IIR_FILTER_X16 +bme280.overscan_pressure = adafruit_bme280.OVERSCAN_X16 +bme280.overscan_humidity = adafruit_bme280.OVERSCAN_X1 +bme280.overscan_temperature = adafruit_bme280.OVERSCAN_X2 +#The sensor will need a moment to gather inital readings +time.sleep(1) + +while True: + print("\nTemperature: %0.1f C" % bme280.temperature) + print("Humidity: %0.1f %%" % bme280.humidity) + print("Pressure: %0.1f hPa" % bme280.pressure) + print("Altitude = %0.2f meters" % bme280.altitude) + time.sleep(2)