mirror of
https://github.com/cmur2/python-bme680.git
synced 2024-12-22 12:54:29 +01:00
Merge pull request #16 from pimoroni/tests
Test suites and code QA fixes
This commit is contained in:
commit
15171e57f6
4
.gitignore
vendored
4
.gitignore
vendored
@ -19,3 +19,7 @@ library/debian/
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
.DS_Store
|
||||
.vscode/
|
||||
.coverage
|
||||
.tox/
|
||||
.pytest_cache/
|
||||
|
22
.travis.yml
22
.travis.yml
@ -1,10 +1,24 @@
|
||||
language: python
|
||||
sudo: false
|
||||
cache: pip
|
||||
|
||||
git:
|
||||
submodules: true
|
||||
|
||||
matrix:
|
||||
include:
|
||||
python: "2.7"
|
||||
include:
|
||||
- python: "2.7"
|
||||
env: TOXENV=py27
|
||||
- python: "3.5"
|
||||
env: TOXENV=py35
|
||||
- python: "2.7"
|
||||
env: TOXENV=py27
|
||||
|
||||
install:
|
||||
- pip install --ignore-installed --upgrade setuptools pip tox coveralls
|
||||
|
||||
script:
|
||||
- pip install --ignore-installed --upgrade flake8
|
||||
- flake8 --ignore F403,F405,E501
|
||||
- cd library
|
||||
- tox -vv
|
||||
|
||||
after_success: if [ "$TOXENV" == "py35" ]; then coveralls; fi
|
||||
|
@ -1,6 +1,9 @@
|
||||
# BME680
|
||||
|
||||
[![Build Status](https://travis-ci.org/pimoroni/bme680-python.svg?branch=master)](https://travis-ci.org/pimoroni/bme680-python)
|
||||
[![Build Status](https://travis-ci.com/pimoroni/bme680-python.svg?branch=master)](https://travis-ci.com/pimoroni/bme680-python)
|
||||
[![Coverage Status](https://coveralls.io/repos/github/pimoroni/bme680-python/badge.svg?branch=master)](https://coveralls.io/github/pimoroni/bme680-python?branch=master)
|
||||
[![PyPi Package](https://img.shields.io/pypi/v/bme680.svg)](https://pypi.python.org/pypi/bme680)
|
||||
[![Python Versions](https://img.shields.io/pypi/pyversions/bme680.svg)](https://pypi.python.org/pypi/bme680)
|
||||
|
||||
https://shop.pimoroni.com/products/bme680
|
||||
|
||||
|
@ -1,11 +1,10 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import bme680
|
||||
import time
|
||||
|
||||
print("""Estimate indoor air quality
|
||||
|
||||
Runs the sensor for a burn-in period, then uses a
|
||||
Runs the sensor for a burn-in period, then uses a
|
||||
combination of relative humidity and gas resistance
|
||||
to estimate indoor air quality as a percentage.
|
||||
|
||||
@ -13,9 +12,12 @@ Press Ctrl+C to exit
|
||||
|
||||
""")
|
||||
|
||||
sensor = bme680.BME680()
|
||||
try:
|
||||
sensor = bme680.BME680(bme680.I2C_ADDR_PRIMARY)
|
||||
except IOError:
|
||||
sensor = bme680.BME680(bme680.I2C_ADDR_SECONDARY)
|
||||
|
||||
# These oversampling settings can be tweaked to
|
||||
# These oversampling settings can be tweaked to
|
||||
# change the balance between accuracy and noise in
|
||||
# the data.
|
||||
|
||||
@ -29,7 +31,7 @@ sensor.set_gas_heater_temperature(320)
|
||||
sensor.set_gas_heater_duration(150)
|
||||
sensor.select_gas_heater_profile(0)
|
||||
|
||||
# start_time and curr_time ensure that the
|
||||
# start_time and curr_time ensure that the
|
||||
# burn_in_time (in seconds) is kept track of.
|
||||
|
||||
start_time = time.time()
|
||||
@ -42,13 +44,13 @@ try:
|
||||
# Collect gas resistance burn-in values, then use the average
|
||||
# of the last 50 values to set the upper limit for calculating
|
||||
# gas_baseline.
|
||||
print("Collecting gas resistance burn-in data for 5 mins\n")
|
||||
print('Collecting gas resistance burn-in data for 5 mins\n')
|
||||
while curr_time - start_time < burn_in_time:
|
||||
curr_time = time.time()
|
||||
if sensor.get_sensor_data() and sensor.data.heat_stable:
|
||||
gas = sensor.data.gas_resistance
|
||||
burn_in_data.append(gas)
|
||||
print("Gas: {0} Ohms".format(gas))
|
||||
print('Gas: {0} Ohms'.format(gas))
|
||||
time.sleep(1)
|
||||
|
||||
gas_baseline = sum(burn_in_data[-50:]) / 50.0
|
||||
@ -56,11 +58,13 @@ try:
|
||||
# Set the humidity baseline to 40%, an optimal indoor humidity.
|
||||
hum_baseline = 40.0
|
||||
|
||||
# This sets the balance between humidity and gas reading in the
|
||||
# This sets the balance between humidity and gas reading in the
|
||||
# calculation of air_quality_score (25:75, humidity:gas)
|
||||
hum_weighting = 0.25
|
||||
|
||||
print("Gas baseline: {0} Ohms, humidity baseline: {1:.2f} %RH\n".format(gas_baseline, hum_baseline))
|
||||
print('Gas baseline: {0} Ohms, humidity baseline: {1:.2f} %RH\n'.format(
|
||||
gas_baseline,
|
||||
hum_baseline))
|
||||
|
||||
while True:
|
||||
if sensor.get_sensor_data() and sensor.data.heat_stable:
|
||||
@ -72,22 +76,31 @@ try:
|
||||
|
||||
# Calculate hum_score as the distance from the hum_baseline.
|
||||
if hum_offset > 0:
|
||||
hum_score = (100 - hum_baseline - hum_offset) / (100 - hum_baseline) * (hum_weighting * 100)
|
||||
hum_score = (100 - hum_baseline - hum_offset)
|
||||
hum_score /= (100 - hum_baseline)
|
||||
hum_score *= (hum_weighting * 100)
|
||||
|
||||
else:
|
||||
hum_score = (hum_baseline + hum_offset) / hum_baseline * (hum_weighting * 100)
|
||||
hum_score = (hum_baseline + hum_offset)
|
||||
hum_score /= hum_baseline
|
||||
hum_score *= (hum_weighting * 100)
|
||||
|
||||
# Calculate gas_score as the distance from the gas_baseline.
|
||||
if gas_offset > 0:
|
||||
gas_score = (gas / gas_baseline) * (100 - (hum_weighting * 100))
|
||||
gas_score = (gas / gas_baseline)
|
||||
gas_score *= (100 - (hum_weighting * 100))
|
||||
|
||||
else:
|
||||
gas_score = 100 - (hum_weighting * 100)
|
||||
|
||||
# Calculate air_quality_score.
|
||||
# Calculate air_quality_score.
|
||||
air_quality_score = hum_score + gas_score
|
||||
|
||||
print("Gas: {0:.2f} Ohms,humidity: {1:.2f} %RH,air quality: {2:.2f}".format(gas, hum, air_quality_score))
|
||||
print('Gas: {0:.2f} Ohms,humidity: {1:.2f} %RH,air quality: {2:.2f}'.format(
|
||||
gas,
|
||||
hum,
|
||||
air_quality_score))
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
except KeyboardInterrupt:
|
||||
|
@ -1,23 +1,31 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import bme680
|
||||
import time
|
||||
|
||||
sensor = bme680.BME680()
|
||||
print("""Display Temperature, Pressure, Humidity and Gas
|
||||
|
||||
Press Ctrl+C to exit
|
||||
|
||||
""")
|
||||
|
||||
try:
|
||||
sensor = bme680.BME680(bme680.I2C_ADDR_PRIMARY)
|
||||
except IOError:
|
||||
sensor = bme680.BME680(bme680.I2C_ADDR_SECONDARY)
|
||||
|
||||
# These calibration data can safely be commented
|
||||
# out, if desired.
|
||||
|
||||
print("Calibration data:")
|
||||
print('Calibration data:')
|
||||
for name in dir(sensor.calibration_data):
|
||||
|
||||
if not name.startswith('_'):
|
||||
value = getattr(sensor.calibration_data, name)
|
||||
|
||||
if isinstance(value, int):
|
||||
print("{}: {}".format(name, value))
|
||||
print('{}: {}'.format(name, value))
|
||||
|
||||
# These oversampling settings can be tweaked to
|
||||
# These oversampling settings can be tweaked to
|
||||
# change the balance between accuracy and noise in
|
||||
# the data.
|
||||
|
||||
@ -27,12 +35,12 @@ sensor.set_temperature_oversample(bme680.OS_8X)
|
||||
sensor.set_filter(bme680.FILTER_SIZE_3)
|
||||
sensor.set_gas_status(bme680.ENABLE_GAS_MEAS)
|
||||
|
||||
print("\n\nInitial reading:")
|
||||
print('\n\nInitial reading:')
|
||||
for name in dir(sensor.data):
|
||||
value = getattr(sensor.data, name)
|
||||
|
||||
if not name.startswith('_'):
|
||||
print("{}: {}".format(name, value))
|
||||
print('{}: {}'.format(name, value))
|
||||
|
||||
sensor.set_gas_heater_temperature(320)
|
||||
sensor.set_gas_heater_duration(150)
|
||||
@ -43,14 +51,19 @@ sensor.select_gas_heater_profile(0)
|
||||
# sensor.set_gas_heater_profile(200, 150, nb_profile=1)
|
||||
# sensor.select_gas_heater_profile(1)
|
||||
|
||||
print("\n\nPolling:")
|
||||
print('\n\nPolling:')
|
||||
try:
|
||||
while True:
|
||||
if sensor.get_sensor_data():
|
||||
output = "{0:.2f} C,{1:.2f} hPa,{2:.2f} %RH".format(sensor.data.temperature, sensor.data.pressure, sensor.data.humidity)
|
||||
output = '{0:.2f} C,{1:.2f} hPa,{2:.2f} %RH'.format(
|
||||
sensor.data.temperature,
|
||||
sensor.data.pressure,
|
||||
sensor.data.humidity)
|
||||
|
||||
if sensor.data.heat_stable:
|
||||
print("{0},{1} Ohms".format(output, sensor.data.gas_resistance))
|
||||
print('{0},{1} Ohms'.format(
|
||||
output,
|
||||
sensor.data.gas_resistance))
|
||||
|
||||
else:
|
||||
print(output)
|
||||
|
8
examples/setup.cfg
Normal file
8
examples/setup.cfg
Normal file
@ -0,0 +1,8 @@
|
||||
[flake8]
|
||||
ignore =
|
||||
E501 # line too long
|
||||
F403
|
||||
F405
|
||||
# Don't require docstrings in example code
|
||||
D100 # Missing docstring in public module
|
||||
D103 # Missing docstring in public function
|
@ -4,7 +4,10 @@ import bme680
|
||||
print("""Display Temperature, Pressure and Humidity with different offsets.
|
||||
""")
|
||||
|
||||
sensor = bme680.BME680()
|
||||
try:
|
||||
sensor = bme680.BME680(bme680.I2C_ADDR_PRIMARY)
|
||||
except IOError:
|
||||
sensor = bme680.BME680(bme680.I2C_ADDR_SECONDARY)
|
||||
|
||||
# These oversampling settings can be tweaked to
|
||||
# change the balance between accuracy and noise in
|
||||
@ -15,25 +18,29 @@ sensor.set_pressure_oversample(bme680.OS_4X)
|
||||
sensor.set_temperature_oversample(bme680.OS_8X)
|
||||
sensor.set_filter(bme680.FILTER_SIZE_3)
|
||||
|
||||
|
||||
def display_data(offset=0):
|
||||
sensor.set_temp_offset(offset)
|
||||
sensor.get_sensor_data()
|
||||
output = "{0:.2f} C, {1:.2f} hPa, {2:.3f} %RH".format(sensor.data.temperature, sensor.data.pressure, sensor.data.humidity)
|
||||
output = '{0:.2f} C, {1:.2f} hPa, {2:.3f} %RH'.format(
|
||||
sensor.data.temperature,
|
||||
sensor.data.pressure,
|
||||
sensor.data.humidity)
|
||||
print(output)
|
||||
print("")
|
||||
print('')
|
||||
|
||||
print("Initial readings")
|
||||
|
||||
print('Initial readings')
|
||||
display_data()
|
||||
|
||||
print("SET offset 4 degrees celsius")
|
||||
print('SET offset 4 degrees celsius')
|
||||
display_data(4)
|
||||
|
||||
print("SET offset -1.87 degrees celsius")
|
||||
print('SET offset -1.87 degrees celsius')
|
||||
display_data(-1.87)
|
||||
|
||||
print("SET offset -100 degrees celsius")
|
||||
print('SET offset -100 degrees celsius')
|
||||
display_data(-100)
|
||||
|
||||
print("SET offset 0 degrees celsius")
|
||||
print('SET offset 0 degrees celsius')
|
||||
display_data(0)
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
#!/usr/bin/env python
|
||||
import bme680
|
||||
import time
|
||||
|
||||
print("""Display Temperature, Pressure and Humidity
|
||||
|
||||
@ -11,7 +10,10 @@ Press Ctrl+C to exit
|
||||
|
||||
""")
|
||||
|
||||
sensor = bme680.BME680()
|
||||
try:
|
||||
sensor = bme680.BME680(bme680.I2C_ADDR_PRIMARY)
|
||||
except IOError:
|
||||
sensor = bme680.BME680(bme680.I2C_ADDR_SECONDARY)
|
||||
|
||||
# These oversampling settings can be tweaked to
|
||||
# change the balance between accuracy and noise in
|
||||
@ -22,15 +24,17 @@ sensor.set_pressure_oversample(bme680.OS_4X)
|
||||
sensor.set_temperature_oversample(bme680.OS_8X)
|
||||
sensor.set_filter(bme680.FILTER_SIZE_3)
|
||||
|
||||
print("Polling:")
|
||||
print('Polling:')
|
||||
try:
|
||||
while True:
|
||||
if sensor.get_sensor_data():
|
||||
|
||||
output = "{0:.2f} C,{1:.2f} hPa,{2:.3f} %RH".format(sensor.data.temperature, sensor.data.pressure, sensor.data.humidity)
|
||||
output = '{0:.2f} C,{1:.2f} hPa,{2:.3f} %RH'.format(
|
||||
sensor.data.temperature,
|
||||
sensor.data.pressure,
|
||||
sensor.data.humidity)
|
||||
|
||||
print(output)
|
||||
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
|
||||
|
4
library/.coveragerc
Normal file
4
library/.coveragerc
Normal file
@ -0,0 +1,4 @@
|
||||
[run]
|
||||
source = bme680
|
||||
omit =
|
||||
.tox/*
|
@ -1,6 +1,8 @@
|
||||
BME680
|
||||
======
|
||||
|
||||
|Build Status| |Coverage Status| |PyPi Package| |Python Versions|
|
||||
|
||||
https://shop.pimoroni.com/products/bme680
|
||||
|
||||
The state-of-the-art BME680 breakout lets you measure temperature,
|
||||
@ -20,6 +22,8 @@ Terminal on your Raspberry Pi desktop, as illustrated below:
|
||||
.. figure:: http://get.pimoroni.com/resources/github-repo-terminal.png
|
||||
:alt: Finding the terminal
|
||||
|
||||
Finding the terminal
|
||||
|
||||
In the new terminal window type the command exactly as it appears below
|
||||
(check for typos) and follow the on-screen instructions:
|
||||
|
||||
@ -66,3 +70,11 @@ Documentation & Support
|
||||
- Guides and tutorials - https://learn.pimoroni.com/bme680
|
||||
- Get help - http://forums.pimoroni.com/c/support
|
||||
|
||||
.. |Build Status| image:: https://travis-ci.com/pimoroni/bme680-python.svg?branch=master
|
||||
:target: https://travis-ci.com/pimoroni/bme680-python
|
||||
.. |Coverage Status| image:: https://coveralls.io/repos/github/pimoroni/bme680-python/badge.svg?branch=master
|
||||
:target: https://coveralls.io/github/pimoroni/bme680-python?branch=master
|
||||
.. |PyPi Package| image:: https://img.shields.io/pypi/v/bme680.svg
|
||||
:target: https://pypi.python.org/pypi/bme680
|
||||
.. |Python Versions| image:: https://img.shields.io/pypi/pyversions/bme680.svg
|
||||
:target: https://pypi.python.org/pypi/bme680
|
||||
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 2.2 KiB |
@ -1,11 +1,23 @@
|
||||
from .constants import *
|
||||
"""BME680 Temperature, Pressure, Humidity & Gas Sensor."""
|
||||
from .constants import lookupTable1, lookupTable2
|
||||
from .constants import BME680Data
|
||||
from . import constants
|
||||
import math
|
||||
import time
|
||||
|
||||
__version__ = '1.0.5'
|
||||
|
||||
|
||||
# Export constants to global namespace
|
||||
# so end-users can "from BME680 import NAME"
|
||||
for key in constants.__dict__:
|
||||
value = constants.__dict__[key]
|
||||
if key not in globals():
|
||||
globals()[key] = value
|
||||
|
||||
|
||||
class BME680(BME680Data):
|
||||
"""BOSCH BME680
|
||||
"""BOSCH BME680.
|
||||
|
||||
Gas, pressure, temperature and humidity sensor.
|
||||
|
||||
@ -13,7 +25,14 @@ class BME680(BME680Data):
|
||||
:param i2c_device: Optional smbus or compatible instance for facilitating i2c communications.
|
||||
|
||||
"""
|
||||
def __init__(self, i2c_addr=I2C_ADDR_PRIMARY, i2c_device=None):
|
||||
|
||||
def __init__(self, i2c_addr=constants.I2C_ADDR_PRIMARY, i2c_device=None):
|
||||
"""Initialise BME680 sensor instance and verify device presence.
|
||||
|
||||
:param i2c_addr: i2c address of BME680
|
||||
:param i2c_device: Optional SMBus-compatible instance for i2c transport
|
||||
|
||||
"""
|
||||
BME680Data.__init__(self)
|
||||
|
||||
self.i2c_addr = i2c_addr
|
||||
@ -22,45 +41,46 @@ class BME680(BME680Data):
|
||||
import smbus
|
||||
self._i2c = smbus.SMBus(1)
|
||||
|
||||
self.chip_id = self._get_regs(CHIP_ID_ADDR, 1)
|
||||
if self.chip_id != CHIP_ID:
|
||||
raise RuntimeError("BME680 Not Found. Invalid CHIP ID: 0x{0:02x}".format(self.chip_id))
|
||||
self.chip_id = self._get_regs(constants.CHIP_ID_ADDR, 1)
|
||||
if self.chip_id != constants.CHIP_ID:
|
||||
raise RuntimeError('BME680 Not Found. Invalid CHIP ID: 0x{0:02x}'.format(self.chip_id))
|
||||
|
||||
self.soft_reset()
|
||||
self.set_power_mode(SLEEP_MODE)
|
||||
self.set_power_mode(constants.SLEEP_MODE)
|
||||
|
||||
self._get_calibration_data()
|
||||
|
||||
self.set_humidity_oversample(OS_2X)
|
||||
self.set_pressure_oversample(OS_4X)
|
||||
self.set_temperature_oversample(OS_8X)
|
||||
self.set_filter(FILTER_SIZE_3)
|
||||
self.set_gas_status(ENABLE_GAS_MEAS)
|
||||
self.set_humidity_oversample(constants.OS_2X)
|
||||
self.set_pressure_oversample(constants.OS_4X)
|
||||
self.set_temperature_oversample(constants.OS_8X)
|
||||
self.set_filter(constants.FILTER_SIZE_3)
|
||||
self.set_gas_status(constants.ENABLE_GAS_MEAS)
|
||||
self.set_temp_offset(0)
|
||||
self.get_sensor_data()
|
||||
|
||||
def _get_calibration_data(self):
|
||||
"""Retrieves the sensor calibration data and stores it in .calibration_data"""
|
||||
calibration = self._get_regs(COEFF_ADDR1, COEFF_ADDR1_LEN)
|
||||
calibration += self._get_regs(COEFF_ADDR2, COEFF_ADDR2_LEN)
|
||||
"""Retrieve the sensor calibration data and store it in .calibration_data."""
|
||||
calibration = self._get_regs(constants.COEFF_ADDR1, constants.COEFF_ADDR1_LEN)
|
||||
calibration += self._get_regs(constants.COEFF_ADDR2, constants.COEFF_ADDR2_LEN)
|
||||
|
||||
heat_range = self._get_regs(ADDR_RES_HEAT_RANGE_ADDR, 1)
|
||||
heat_value = twos_comp(self._get_regs(ADDR_RES_HEAT_VAL_ADDR, 1), bits=8)
|
||||
sw_error = twos_comp(self._get_regs(ADDR_RANGE_SW_ERR_ADDR, 1), bits=8)
|
||||
heat_range = self._get_regs(constants.ADDR_RES_HEAT_RANGE_ADDR, 1)
|
||||
heat_value = constants.twos_comp(self._get_regs(constants.ADDR_RES_HEAT_VAL_ADDR, 1), bits=8)
|
||||
sw_error = constants.twos_comp(self._get_regs(constants.ADDR_RANGE_SW_ERR_ADDR, 1), bits=8)
|
||||
|
||||
self.calibration_data.set_from_array(calibration)
|
||||
self.calibration_data.set_other(heat_range, heat_value, sw_error)
|
||||
|
||||
def soft_reset(self):
|
||||
"""Initiate a soft reset"""
|
||||
self._set_regs(SOFT_RESET_ADDR, SOFT_RESET_CMD)
|
||||
time.sleep(RESET_PERIOD / 1000.0)
|
||||
"""Trigger a soft reset."""
|
||||
self._set_regs(constants.SOFT_RESET_ADDR, constants.SOFT_RESET_CMD)
|
||||
time.sleep(constants.RESET_PERIOD / 1000.0)
|
||||
|
||||
def set_temp_offset(self, value):
|
||||
"""Set temperature offset in celsius
|
||||
"""Set temperature offset in celsius.
|
||||
|
||||
If set, the temperature t_fine will be increased by given value in celsius.
|
||||
:param value: Temperature offset in Celsius, eg. 4, -8, 1.25
|
||||
|
||||
"""
|
||||
if value == 0:
|
||||
self.offset_temp_in_t_fine = 0
|
||||
@ -68,8 +88,8 @@ class BME680(BME680Data):
|
||||
self.offset_temp_in_t_fine = int(math.copysign((((int(abs(value) * 100)) << 8) - 128) / 5, value))
|
||||
|
||||
def set_humidity_oversample(self, value):
|
||||
"""Set humidity oversampling
|
||||
|
||||
"""Set humidity oversampling.
|
||||
|
||||
A higher oversampling value means more stable sensor readings,
|
||||
with less noise and jitter.
|
||||
|
||||
@ -80,15 +100,15 @@ class BME680(BME680Data):
|
||||
|
||||
"""
|
||||
self.tph_settings.os_hum = value
|
||||
self._set_bits(CONF_OS_H_ADDR, OSH_MSK, OSH_POS, value)
|
||||
self._set_bits(constants.CONF_OS_H_ADDR, constants.OSH_MSK, constants.OSH_POS, value)
|
||||
|
||||
def get_humidity_oversample(self):
|
||||
"""Get humidity oversampling"""
|
||||
return (self._get_regs(CONF_OS_H_ADDR, 1) & OSH_MSK) >> OSH_POS
|
||||
"""Get humidity oversampling."""
|
||||
return (self._get_regs(constants.CONF_OS_H_ADDR, 1) & constants.OSH_MSK) >> constants.OSH_POS
|
||||
|
||||
def set_pressure_oversample(self, value):
|
||||
"""Set temperature oversampling
|
||||
|
||||
"""Set temperature oversampling.
|
||||
|
||||
A higher oversampling value means more stable sensor readings,
|
||||
with less noise and jitter.
|
||||
|
||||
@ -96,18 +116,18 @@ class BME680(BME680Data):
|
||||
causing a slower response time to fast transients.
|
||||
|
||||
:param value: Oversampling value, one of: OS_NONE, OS_1X, OS_2X, OS_4X, OS_8X, OS_16X
|
||||
|
||||
|
||||
"""
|
||||
self.tph_settings.os_pres = value
|
||||
self._set_bits(CONF_T_P_MODE_ADDR, OSP_MSK, OSP_POS, value)
|
||||
self._set_bits(constants.CONF_T_P_MODE_ADDR, constants.OSP_MSK, constants.OSP_POS, value)
|
||||
|
||||
def get_pressure_oversample(self):
|
||||
"""Get pressure oversampling"""
|
||||
return (self._get_regs(CONF_T_P_MODE_ADDR, 1) & OSP_MSK) >> OSP_POS
|
||||
"""Get pressure oversampling."""
|
||||
return (self._get_regs(constants.CONF_T_P_MODE_ADDR, 1) & constants.OSP_MSK) >> constants.OSP_POS
|
||||
|
||||
def set_temperature_oversample(self, value):
|
||||
"""Set pressure oversampling
|
||||
|
||||
"""Set pressure oversampling.
|
||||
|
||||
A higher oversampling value means more stable sensor readings,
|
||||
with less noise and jitter.
|
||||
|
||||
@ -115,18 +135,18 @@ class BME680(BME680Data):
|
||||
causing a slower response time to fast transients.
|
||||
|
||||
:param value: Oversampling value, one of: OS_NONE, OS_1X, OS_2X, OS_4X, OS_8X, OS_16X
|
||||
|
||||
|
||||
"""
|
||||
self.tph_settings.os_temp = value
|
||||
self._set_bits(CONF_T_P_MODE_ADDR, OST_MSK, OST_POS, value)
|
||||
self._set_bits(constants.CONF_T_P_MODE_ADDR, constants.OST_MSK, constants.OST_POS, value)
|
||||
|
||||
def get_temperature_oversample(self):
|
||||
"""Get temperature oversampling"""
|
||||
return (self._get_regs(CONF_T_P_MODE_ADDR, 1) & OST_MSK) >> OST_POS
|
||||
"""Get temperature oversampling."""
|
||||
return (self._get_regs(constants.CONF_T_P_MODE_ADDR, 1) & constants.OST_MSK) >> constants.OST_POS
|
||||
|
||||
def set_filter(self, value):
|
||||
"""Set IIR filter size
|
||||
|
||||
"""Set IIR filter size.
|
||||
|
||||
Optionally remove short term fluctuations from the temperature and pressure readings,
|
||||
increasing their resolution but reducing their bandwidth.
|
||||
|
||||
@ -138,40 +158,42 @@ class BME680(BME680Data):
|
||||
|
||||
"""
|
||||
self.tph_settings.filter = value
|
||||
self._set_bits(CONF_ODR_FILT_ADDR, FILTER_MSK, FILTER_POS, value)
|
||||
self._set_bits(constants.CONF_ODR_FILT_ADDR, constants.FILTER_MSK, constants.FILTER_POS, value)
|
||||
|
||||
def get_filter(self):
|
||||
"""Get filter size"""
|
||||
return (self._get_regs(CONF_ODR_FILT_ADDR, 1) & FILTER_MSK) >> FILTER_POS
|
||||
"""Get filter size."""
|
||||
return (self._get_regs(constants.CONF_ODR_FILT_ADDR, 1) & constants.FILTER_MSK) >> constants.FILTER_POS
|
||||
|
||||
def select_gas_heater_profile(self, value):
|
||||
"""Set current gas sensor conversion profile: 0 to 9
|
||||
|
||||
"""Set current gas sensor conversion profile.
|
||||
|
||||
Select one of the 10 configured heating durations/set points.
|
||||
|
||||
|
||||
:param value: Profile index from 0 to 9
|
||||
|
||||
"""
|
||||
if value > NBCONV_MAX or value < NBCONV_MIN:
|
||||
raise ValueError("Profile '{}' should be between {} and {}".format(value, NBCONV_MIN, NBCONV_MAX))
|
||||
if value > constants.NBCONV_MAX or value < constants.NBCONV_MIN:
|
||||
raise ValueError("Profile '{}' should be between {} and {}".format(value, constants.NBCONV_MIN, constants.NBCONV_MAX))
|
||||
|
||||
self.gas_settings.nb_conv = value
|
||||
self._set_bits(CONF_ODR_RUN_GAS_NBC_ADDR, NBCONV_MSK, NBCONV_POS, value)
|
||||
self._set_bits(constants.CONF_ODR_RUN_GAS_NBC_ADDR, constants.NBCONV_MSK, constants.NBCONV_POS, value)
|
||||
|
||||
def get_gas_heater_profile(self):
|
||||
"""Get gas sensor conversion profile: 0 to 9"""
|
||||
return self._get_regs(CONF_ODR_RUN_GAS_NBC_ADDR, 1) & NBCONV_MSK
|
||||
"""Get gas sensor conversion profile: 0 to 9."""
|
||||
return self._get_regs(constants.CONF_ODR_RUN_GAS_NBC_ADDR, 1) & constants.NBCONV_MSK
|
||||
|
||||
def set_gas_status(self, value):
|
||||
"""Enable/disable gas sensor"""
|
||||
"""Enable/disable gas sensor."""
|
||||
self.gas_settings.run_gas = value
|
||||
self._set_bits(CONF_ODR_RUN_GAS_NBC_ADDR, RUN_GAS_MSK, RUN_GAS_POS, value)
|
||||
self._set_bits(constants.CONF_ODR_RUN_GAS_NBC_ADDR, constants.RUN_GAS_MSK, constants.RUN_GAS_POS, value)
|
||||
|
||||
def get_gas_status(self):
|
||||
"""Get the current gas status"""
|
||||
return (self._get_regs(CONF_ODR_RUN_GAS_NBC_ADDR, 1) & RUN_GAS_MSK) >> RUN_GAS_POS
|
||||
"""Get the current gas status."""
|
||||
return (self._get_regs(constants.CONF_ODR_RUN_GAS_NBC_ADDR, 1) & constants.RUN_GAS_MSK) >> constants.RUN_GAS_POS
|
||||
|
||||
def set_gas_heater_profile(self, temperature, duration, nb_profile=0):
|
||||
"""Set temperature and duration of gas sensor heater
|
||||
|
||||
"""Set temperature and duration of gas sensor heater.
|
||||
|
||||
:param temperature: Target temperature in degrees celsius, between 200 and 400
|
||||
:param durarion: Target duration in milliseconds, between 1 and 4032
|
||||
:param nb_profile: Target profile, between 0 and 9
|
||||
@ -181,23 +203,23 @@ class BME680(BME680Data):
|
||||
self.set_gas_heater_duration(duration, nb_profile=nb_profile)
|
||||
|
||||
def set_gas_heater_temperature(self, value, nb_profile=0):
|
||||
"""Set gas sensor heater temperature
|
||||
"""Set gas sensor heater temperature.
|
||||
|
||||
:param value: Target temperature in degrees celsius, between 200 and 400
|
||||
|
||||
|
||||
When setting an nb_profile other than 0,
|
||||
make sure to select it with select_gas_heater_profile.
|
||||
|
||||
"""
|
||||
if nb_profile > NBCONV_MAX or value < NBCONV_MIN:
|
||||
raise ValueError("Profile '{}' should be between {} and {}".format(nb_profile, NBCONV_MIN, NBCONV_MAX))
|
||||
if nb_profile > constants.NBCONV_MAX or value < constants.NBCONV_MIN:
|
||||
raise ValueError('Profile "{}" should be between {} and {}'.format(nb_profile, constants.NBCONV_MIN, constants.NBCONV_MAX))
|
||||
|
||||
self.gas_settings.heatr_temp = value
|
||||
temp = int(self._calc_heater_resistance(self.gas_settings.heatr_temp))
|
||||
self._set_regs(RES_HEAT0_ADDR + nb_profile, temp)
|
||||
self._set_regs(constants.RES_HEAT0_ADDR + nb_profile, temp)
|
||||
|
||||
def set_gas_heater_duration(self, value, nb_profile=0):
|
||||
"""Set gas sensor heater duration
|
||||
"""Set gas sensor heater duration.
|
||||
|
||||
Heating durations between 1 ms and 4032 ms can be configured.
|
||||
Approximately 20-30 ms are necessary for the heater to reach the intended target temperature.
|
||||
@ -206,68 +228,68 @@ class BME680(BME680Data):
|
||||
|
||||
When setting an nb_profile other than 0,
|
||||
make sure to select it with select_gas_heater_profile.
|
||||
|
||||
|
||||
"""
|
||||
if nb_profile > NBCONV_MAX or value < NBCONV_MIN:
|
||||
raise ValueError("Profile '{}' should be between {} and {}".format(nb_profile, NBCONV_MIN, NBCONV_MAX))
|
||||
if nb_profile > constants.NBCONV_MAX or value < constants.NBCONV_MIN:
|
||||
raise ValueError('Profile "{}" should be between {} and {}'.format(nb_profile, constants.NBCONV_MIN, constants.NBCONV_MAX))
|
||||
|
||||
self.gas_settings.heatr_dur = value
|
||||
temp = self._calc_heater_duration(self.gas_settings.heatr_dur)
|
||||
self._set_regs(GAS_WAIT0_ADDR + nb_profile, temp)
|
||||
self._set_regs(constants.GAS_WAIT0_ADDR + nb_profile, temp)
|
||||
|
||||
def set_power_mode(self, value, blocking=True):
|
||||
"""Set power mode"""
|
||||
if value not in (SLEEP_MODE, FORCED_MODE):
|
||||
print("Power mode should be one of SLEEP_MODE or FORCED_MODE")
|
||||
"""Set power mode."""
|
||||
if value not in (constants.SLEEP_MODE, constants.FORCED_MODE):
|
||||
raise ValueError('Power mode should be one of SLEEP_MODE or FORCED_MODE')
|
||||
|
||||
self.power_mode = value
|
||||
|
||||
self._set_bits(CONF_T_P_MODE_ADDR, MODE_MSK, MODE_POS, value)
|
||||
self._set_bits(constants.CONF_T_P_MODE_ADDR, constants.MODE_MSK, constants.MODE_POS, value)
|
||||
|
||||
while blocking and self.get_power_mode() != self.power_mode:
|
||||
time.sleep(POLL_PERIOD_MS / 1000.0)
|
||||
time.sleep(constants.POLL_PERIOD_MS / 1000.0)
|
||||
|
||||
def get_power_mode(self):
|
||||
"""Get power mode"""
|
||||
self.power_mode = self._get_regs(CONF_T_P_MODE_ADDR, 1)
|
||||
"""Get power mode."""
|
||||
self.power_mode = self._get_regs(constants.CONF_T_P_MODE_ADDR, 1)
|
||||
return self.power_mode
|
||||
|
||||
def get_sensor_data(self):
|
||||
"""Get sensor data.
|
||||
|
||||
|
||||
Stores data in .data and returns True upon success.
|
||||
|
||||
"""
|
||||
self.set_power_mode(FORCED_MODE)
|
||||
self.set_power_mode(constants.FORCED_MODE)
|
||||
|
||||
for attempt in range(10):
|
||||
status = self._get_regs(FIELD0_ADDR, 1)
|
||||
status = self._get_regs(constants.FIELD0_ADDR, 1)
|
||||
|
||||
if (status & NEW_DATA_MSK) == 0:
|
||||
time.sleep(POLL_PERIOD_MS / 1000.0)
|
||||
if (status & constants.NEW_DATA_MSK) == 0:
|
||||
time.sleep(constants.POLL_PERIOD_MS / 1000.0)
|
||||
continue
|
||||
|
||||
regs = self._get_regs(FIELD0_ADDR, FIELD_LENGTH)
|
||||
regs = self._get_regs(constants.FIELD0_ADDR, constants.FIELD_LENGTH)
|
||||
|
||||
self.data.status = regs[0] & NEW_DATA_MSK
|
||||
self.data.status = regs[0] & constants.NEW_DATA_MSK
|
||||
# Contains the nb_profile used to obtain the current measurement
|
||||
self.data.gas_index = regs[0] & GAS_INDEX_MSK
|
||||
self.data.gas_index = regs[0] & constants.GAS_INDEX_MSK
|
||||
self.data.meas_index = regs[1]
|
||||
|
||||
adc_pres = (regs[2] << 12) | (regs[3] << 4) | (regs[4] >> 4)
|
||||
adc_temp = (regs[5] << 12) | (regs[6] << 4) | (regs[7] >> 4)
|
||||
adc_hum = (regs[8] << 8) | regs[9]
|
||||
adc_gas_res = (regs[13] << 2) | (regs[14] >> 6)
|
||||
gas_range = regs[14] & GAS_RANGE_MSK
|
||||
gas_range = regs[14] & constants.GAS_RANGE_MSK
|
||||
|
||||
self.data.status |= regs[14] & GASM_VALID_MSK
|
||||
self.data.status |= regs[14] & HEAT_STAB_MSK
|
||||
self.data.status |= regs[14] & constants.GASM_VALID_MSK
|
||||
self.data.status |= regs[14] & constants.HEAT_STAB_MSK
|
||||
|
||||
self.data.heat_stable = (self.data.status & HEAT_STAB_MSK) > 0
|
||||
self.data.heat_stable = (self.data.status & constants.HEAT_STAB_MSK) > 0
|
||||
|
||||
temperature = self._calc_temperature(adc_temp)
|
||||
self.data.temperature = temperature / 100.0
|
||||
self.ambient_temperature = temperature # Saved for heater calc
|
||||
self.ambient_temperature = temperature # Saved for heater calc
|
||||
|
||||
self.data.pressure = self._calc_pressure(adc_pres) / 100.0
|
||||
self.data.humidity = self._calc_humidity(adc_hum) / 1000.0
|
||||
@ -277,27 +299,28 @@ class BME680(BME680Data):
|
||||
return False
|
||||
|
||||
def _set_bits(self, register, mask, position, value):
|
||||
"""Mask out and set one or more bits in a register"""
|
||||
"""Mask out and set one or more bits in a register."""
|
||||
temp = self._get_regs(register, 1)
|
||||
temp &= ~mask
|
||||
temp |= value << position
|
||||
self._set_regs(register, temp)
|
||||
|
||||
def _set_regs(self, register, value):
|
||||
"""Set one or more registers"""
|
||||
"""Set one or more registers."""
|
||||
if isinstance(value, int):
|
||||
self._i2c.write_byte_data(self.i2c_addr, register, value)
|
||||
else:
|
||||
self._i2c.write_i2c_block_data(self.i2c_addr, register, value)
|
||||
|
||||
def _get_regs(self, register, length):
|
||||
"""Get one or more registers"""
|
||||
"""Get one or more registers."""
|
||||
if length == 1:
|
||||
return self._i2c.read_byte_data(self.i2c_addr, register)
|
||||
else:
|
||||
return self._i2c.read_i2c_block_data(self.i2c_addr, register, length)
|
||||
|
||||
|
||||
def _calc_temperature(self, temperature_adc):
|
||||
"""Convert the raw temperature to degrees C using calibration_data."""
|
||||
var1 = (temperature_adc >> 3) - (self.calibration_data.par_t1 << 1)
|
||||
var2 = (var1 * self.calibration_data.par_t2) >> 11
|
||||
var3 = ((var1 >> 1) * (var1 >> 1)) >> 12
|
||||
@ -310,12 +333,13 @@ class BME680(BME680Data):
|
||||
return calc_temp
|
||||
|
||||
def _calc_pressure(self, pressure_adc):
|
||||
"""Convert the raw pressure using calibration data."""
|
||||
var1 = ((self.calibration_data.t_fine) >> 1) - 64000
|
||||
var2 = ((((var1 >> 2) * (var1 >> 2)) >> 11) *
|
||||
self.calibration_data.par_p6) >> 2
|
||||
self.calibration_data.par_p6) >> 2
|
||||
var2 = var2 + ((var1 * self.calibration_data.par_p5) << 1)
|
||||
var2 = (var2 >> 2) + (self.calibration_data.par_p4 << 16)
|
||||
var1 = (((((var1 >> 2) * (var1 >> 2)) >> 13 ) *
|
||||
var1 = (((((var1 >> 2) * (var1 >> 2)) >> 13) *
|
||||
((self.calibration_data.par_p3 << 5)) >> 3) +
|
||||
((self.calibration_data.par_p2 * var1) >> 1))
|
||||
var1 = var1 >> 18
|
||||
@ -330,26 +354,27 @@ class BME680(BME680Data):
|
||||
calc_pressure = ((calc_pressure << 1) // var1)
|
||||
|
||||
var1 = (self.calibration_data.par_p9 * (((calc_pressure >> 3) *
|
||||
(calc_pressure >> 3)) >> 13)) >> 12
|
||||
(calc_pressure >> 3)) >> 13)) >> 12
|
||||
var2 = ((calc_pressure >> 2) *
|
||||
self.calibration_data.par_p8) >> 13
|
||||
self.calibration_data.par_p8) >> 13
|
||||
var3 = ((calc_pressure >> 8) * (calc_pressure >> 8) *
|
||||
(calc_pressure >> 8) *
|
||||
self.calibration_data.par_p10) >> 17
|
||||
(calc_pressure >> 8) *
|
||||
self.calibration_data.par_p10) >> 17
|
||||
|
||||
calc_pressure = (calc_pressure) + ((var1 + var2 + var3 +
|
||||
(self.calibration_data.par_p7 << 7)) >> 4)
|
||||
(self.calibration_data.par_p7 << 7)) >> 4)
|
||||
|
||||
return calc_pressure
|
||||
|
||||
def _calc_humidity(self, humidity_adc):
|
||||
"""Convert the raw humidity using calibration data."""
|
||||
temp_scaled = ((self.calibration_data.t_fine * 5) + 128) >> 8
|
||||
var1 = (humidity_adc - ((self.calibration_data.par_h1 * 16))) \
|
||||
- (((temp_scaled * self.calibration_data.par_h3) // (100)) >> 1)
|
||||
var2 = (self.calibration_data.par_h2
|
||||
* (((temp_scaled * self.calibration_data.par_h4) // (100))
|
||||
+ (((temp_scaled * ((temp_scaled * self.calibration_data.par_h5) // (100))) >> 6)
|
||||
// (100)) + (1 * 16384))) >> 10
|
||||
var1 = (humidity_adc - ((self.calibration_data.par_h1 * 16))) -\
|
||||
(((temp_scaled * self.calibration_data.par_h3) // (100)) >> 1)
|
||||
var2 = (self.calibration_data.par_h2 *
|
||||
(((temp_scaled * self.calibration_data.par_h4) // (100)) +
|
||||
(((temp_scaled * ((temp_scaled * self.calibration_data.par_h5) // (100))) >> 6) //
|
||||
(100)) + (1 * 16384))) >> 10
|
||||
var3 = var1 * var2
|
||||
var4 = self.calibration_data.par_h6 << 7
|
||||
var4 = ((var4) + ((temp_scaled * self.calibration_data.par_h7) // (100))) >> 4
|
||||
@ -357,21 +382,23 @@ class BME680(BME680Data):
|
||||
var6 = (var4 * var5) >> 1
|
||||
calc_hum = (((var3 + var6) >> 10) * (1000)) >> 12
|
||||
|
||||
return min(max(calc_hum,0),100000)
|
||||
return min(max(calc_hum, 0), 100000)
|
||||
|
||||
def _calc_gas_resistance(self, gas_res_adc, gas_range):
|
||||
"""Convert the raw gas resistance using calibration data."""
|
||||
var1 = ((1340 + (5 * self.calibration_data.range_sw_err)) * (lookupTable1[gas_range])) >> 16
|
||||
var2 = (((gas_res_adc << 15) - (16777216)) + var1)
|
||||
var3 = ((lookupTable2[gas_range] * var1) >> 9)
|
||||
calc_gas_res = ((var3 + (var2 >> 1)) / var2)
|
||||
|
||||
if calc_gas_res < 0:
|
||||
calc_gas_res = (1<<32) + calc_gas_res
|
||||
calc_gas_res = (1 << 32) + calc_gas_res
|
||||
|
||||
return calc_gas_res
|
||||
|
||||
def _calc_heater_resistance(self, temperature):
|
||||
temperature = min(max(temperature,200),400)
|
||||
"""Convert raw heater resistance using calibration data."""
|
||||
temperature = min(max(temperature, 200), 400)
|
||||
|
||||
var1 = ((self.ambient_temperature * self.calibration_data.par_gh3) / 1000) * 256
|
||||
var2 = (self.calibration_data.par_gh1 + 784) * (((((self.calibration_data.par_gh2 + 154009) * temperature * 5) / 100) + 3276800) / 10)
|
||||
@ -384,6 +411,7 @@ class BME680(BME680Data):
|
||||
return heatr_res
|
||||
|
||||
def _calc_heater_duration(self, duration):
|
||||
"""Calculate correct value for heater duration setting from milliseconds."""
|
||||
if duration < 0xfc0:
|
||||
factor = 0
|
||||
|
||||
|
@ -1,3 +1,5 @@
|
||||
"""BME680 constants, structures and utilities."""
|
||||
|
||||
# BME680 General config
|
||||
POLL_PERIOD_MS = 10
|
||||
|
||||
@ -118,7 +120,7 @@ TMP_BUFFER_LENGTH = 40
|
||||
REG_BUFFER_LENGTH = 6
|
||||
FIELD_DATA_LENGTH = 3
|
||||
GAS_REG_BUF_LENGTH = 20
|
||||
GAS_HEATER_PROF_LEN_MAX = 10
|
||||
GAS_HEATER_PROF_LEN_MAX = 10
|
||||
|
||||
# Settings selector
|
||||
OST_SEL = 1
|
||||
@ -133,7 +135,7 @@ GAS_SENSOR_SEL = GAS_MEAS_SEL | RUN_GAS_SEL | NBCONV_SEL
|
||||
|
||||
# Number of conversion settings
|
||||
NBCONV_MIN = 0
|
||||
NBCONV_MAX = 9 # Was 10, but there are only 10 settings: 0 1 2 ... 8 9
|
||||
NBCONV_MAX = 9 # Was 10, but there are only 10 settings: 0 1 2 ... 8 9
|
||||
|
||||
# Mask definitions
|
||||
GAS_MEAS_MSK = 0x30
|
||||
@ -214,30 +216,37 @@ REG_HCTRL_INDEX = 0
|
||||
|
||||
# Look up tables for the possible gas range values
|
||||
lookupTable1 = [2147483647, 2147483647, 2147483647, 2147483647,
|
||||
2147483647, 2126008810, 2147483647, 2130303777, 2147483647,
|
||||
2147483647, 2143188679, 2136746228, 2147483647, 2126008810,
|
||||
2147483647, 2147483647]
|
||||
2147483647, 2126008810, 2147483647, 2130303777, 2147483647,
|
||||
2147483647, 2143188679, 2136746228, 2147483647, 2126008810,
|
||||
2147483647, 2147483647]
|
||||
|
||||
lookupTable2 = [4096000000, 2048000000, 1024000000, 512000000,
|
||||
255744255, 127110228, 64000000, 32258064,
|
||||
16016016, 8000000, 4000000, 2000000,
|
||||
1000000, 500000, 250000, 125000]
|
||||
255744255, 127110228, 64000000, 32258064,
|
||||
16016016, 8000000, 4000000, 2000000,
|
||||
1000000, 500000, 250000, 125000]
|
||||
|
||||
|
||||
def bytes_to_word(msb, lsb, bits=16, signed=False):
|
||||
"""Convert a most and least significant byte into a word."""
|
||||
# TODO: Reimpliment with struct
|
||||
word = (msb << 8) | lsb
|
||||
if signed:
|
||||
word = twos_comp(word, bits)
|
||||
return word
|
||||
|
||||
|
||||
def twos_comp(val, bits=16):
|
||||
"""Convert two bytes into a two's compliment signed word."""
|
||||
# TODO: Reimpliment with struct
|
||||
if val & (1 << (bits - 1)) != 0:
|
||||
val = val - (1 << bits)
|
||||
return val
|
||||
|
||||
# Sensor field data structure
|
||||
|
||||
class FieldData:
|
||||
def __init__(self):
|
||||
"""Structure for storing BME680 sensor data."""
|
||||
|
||||
def __init__(self): # noqa D107
|
||||
# Contains new_data, gasm_valid & heat_stab
|
||||
self.status = None
|
||||
self.heat_stable = False
|
||||
@ -254,10 +263,11 @@ class FieldData:
|
||||
# Gas resistance in Ohms
|
||||
self.gas_resistance = None
|
||||
|
||||
# Structure to hold the Calibration data
|
||||
|
||||
class CalibrationData:
|
||||
def __init__(self):
|
||||
"""Structure for storing BME680 calibration data."""
|
||||
|
||||
def __init__(self): # noqa D107
|
||||
self.par_h1 = None
|
||||
self.par_h2 = None
|
||||
self.par_h3 = None
|
||||
@ -291,6 +301,7 @@ class CalibrationData:
|
||||
self.range_sw_err = None
|
||||
|
||||
def set_from_array(self, calibration):
|
||||
"""Set paramaters from an array of bytes."""
|
||||
# Temperature related coefficients
|
||||
self.par_t1 = bytes_to_word(calibration[T1_MSB_REG], calibration[T1_LSB_REG])
|
||||
self.par_t2 = bytes_to_word(calibration[T2_MSB_REG], calibration[T2_LSB_REG], bits=16, signed=True)
|
||||
@ -323,15 +334,20 @@ class CalibrationData:
|
||||
self.par_gh3 = twos_comp(calibration[GH3_REG], bits=8)
|
||||
|
||||
def set_other(self, heat_range, heat_value, sw_error):
|
||||
"""Set other values."""
|
||||
self.res_heat_range = (heat_range & RHRANGE_MSK) // 16
|
||||
self.res_heat_val = heat_value
|
||||
self.range_sw_err = (sw_error & RSERROR_MSK) // 16
|
||||
|
||||
# BME680 sensor settings structure which comprises of ODR,
|
||||
# over-sampling and filter settings.
|
||||
|
||||
class TPHSettings:
|
||||
def __init__(self):
|
||||
"""Structure for storing BME680 sensor settings.
|
||||
|
||||
Comprises of output data rate, over-sampling and filter settings.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self): # noqa D107
|
||||
# Humidity oversampling
|
||||
self.os_hum = None
|
||||
# Temperature oversampling
|
||||
@ -341,11 +357,11 @@ class TPHSettings:
|
||||
# Filter coefficient
|
||||
self.filter = None
|
||||
|
||||
# BME680 gas sensor which comprises of gas settings
|
||||
## and status parameters
|
||||
|
||||
class GasSettings:
|
||||
def __init__(self):
|
||||
"""Structure for storing BME680 gas settings and status."""
|
||||
|
||||
def __init__(self): # noqa D107
|
||||
# Variable to store nb conversion
|
||||
self.nb_conv = None
|
||||
# Variable to store heater control
|
||||
@ -357,10 +373,11 @@ class GasSettings:
|
||||
# Pointer to store duration profile
|
||||
self.heatr_dur = None
|
||||
|
||||
# BME680 device structure
|
||||
|
||||
class BME680Data:
|
||||
def __init__(self):
|
||||
"""Structure to represent BME680 device."""
|
||||
|
||||
def __init__(self): # noqa D107
|
||||
# Chip Id
|
||||
self.chip_id = None
|
||||
# Device Id
|
||||
|
14
library/setup.cfg
Normal file
14
library/setup.cfg
Normal file
@ -0,0 +1,14 @@
|
||||
[flake8]
|
||||
exclude =
|
||||
test.py
|
||||
.tox
|
||||
.eggs
|
||||
.git
|
||||
__pycache__
|
||||
build
|
||||
dist
|
||||
tests
|
||||
ignore =
|
||||
E501
|
||||
F403
|
||||
F405
|
@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Copyright (c) 2016 Pimoroni
|
||||
Copyright (c) 2016 Pimoroni.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
@ -38,17 +38,17 @@ classifiers = ['Development Status :: 5 - Production/Stable',
|
||||
'Topic :: System :: Hardware']
|
||||
|
||||
setup(
|
||||
name = 'bme680',
|
||||
version = '1.0.5',
|
||||
author = 'Philip Howard',
|
||||
author_email = 'phil@pimoroni.com',
|
||||
description = """Python library for driving the Pimoroni BME680 Breakout""",
|
||||
long_description= open('README.rst').read() + "\n" + open('CHANGELOG.txt').read(),
|
||||
license = 'MIT',
|
||||
keywords = 'Raspberry Pi',
|
||||
url = 'http://www.pimoroni.com',
|
||||
classifiers = classifiers,
|
||||
packages = ['bme680'],
|
||||
py_modules = [],
|
||||
install_requires= []
|
||||
name='bme680',
|
||||
version='1.0.5',
|
||||
author='Philip Howard',
|
||||
author_email='phil@pimoroni.com',
|
||||
description="""Python library for driving the Pimoroni BME680 Breakout""",
|
||||
long_description=open('README.rst').read() + '\n' + open('CHANGELOG.txt').read(),
|
||||
license='MIT',
|
||||
keywords='Raspberry Pi',
|
||||
url='http://www.pimoroni.com',
|
||||
classifiers=classifiers,
|
||||
packages=['bme680'],
|
||||
py_modules=[],
|
||||
install_requires=[]
|
||||
)
|
||||
|
8
library/tests/setup.cfg
Normal file
8
library/tests/setup.cfg
Normal file
@ -0,0 +1,8 @@
|
||||
[flake8]
|
||||
exclude =
|
||||
__pycache__
|
||||
ignore =
|
||||
D100 # Do not require docstrings
|
||||
E501
|
||||
F403
|
||||
F405
|
57
library/tests/test_setup.py
Normal file
57
library/tests/test_setup.py
Normal file
@ -0,0 +1,57 @@
|
||||
import sys
|
||||
import mock
|
||||
import pytest
|
||||
import bme680
|
||||
|
||||
|
||||
class MockSMBus:
|
||||
"""Mock a basic non-presence SMBus device to cause BME680 to fail.
|
||||
|
||||
Returns 0 in all cases, so that CHIP_ID will never match.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, bus): # noqa D107
|
||||
pass
|
||||
|
||||
def read_byte_data(self, addr, register):
|
||||
"""Return 0 for all read attempts."""
|
||||
return 0
|
||||
|
||||
|
||||
class MockSMBusPresent:
|
||||
"""Mock enough of the BME680 for the library to initialise and test."""
|
||||
|
||||
def __init__(self, bus):
|
||||
"""Initialise with test data."""
|
||||
self.regs = [0 for _ in range(256)]
|
||||
self.regs[bme680.CHIP_ID_ADDR] = bme680.CHIP_ID
|
||||
|
||||
def read_byte_data(self, addr, register):
|
||||
"""Read a single byte from fake registers."""
|
||||
return self.regs[register]
|
||||
|
||||
def write_byte_data(self, addr, register, value):
|
||||
"""Write a single byte to fake registers."""
|
||||
self.regs[register] = value
|
||||
|
||||
def read_i2c_block_data(self, addr, register, length):
|
||||
"""Read up to length bytes from register."""
|
||||
return self.regs[register:register + length]
|
||||
|
||||
|
||||
def test_setup_not_present():
|
||||
"""Mock the adbsence of a BME680 and test initialisation."""
|
||||
sys.modules['smbus'] = mock.MagicMock()
|
||||
sys.modules['smbus'].SMBus = MockSMBus
|
||||
|
||||
with pytest.raises(RuntimeError):
|
||||
sensor = bme680.BME680() # noqa F841
|
||||
|
||||
|
||||
def test_setup_mock_present():
|
||||
"""Mock the presence of a BME680 and test initialisation."""
|
||||
sys.modules['smbus'] = mock.MagicMock()
|
||||
sys.modules['smbus'].SMBus = MockSMBusPresent
|
||||
|
||||
sensor = bme680.BME680() # noqa F841
|
25
library/tox.ini
Normal file
25
library/tox.ini
Normal file
@ -0,0 +1,25 @@
|
||||
[tox]
|
||||
envlist = py{27,35},qa
|
||||
skip_missing_interpreters = True
|
||||
|
||||
[testenv]
|
||||
commands =
|
||||
python setup.py install
|
||||
coverage run -m py.test -v -r wsx
|
||||
coverage report
|
||||
deps =
|
||||
mock
|
||||
pytest>=3.1
|
||||
pytest-cov
|
||||
|
||||
[testenv:qa]
|
||||
commands =
|
||||
flake8
|
||||
flake8 tests/
|
||||
flake8 ../examples/
|
||||
rstcheck README.rst
|
||||
deps =
|
||||
flake8
|
||||
flake8-docstrings
|
||||
flake8-quotes
|
||||
rstcheck
|
Loading…
Reference in New Issue
Block a user