mirror of
https://github.com/cmur2/python-bme680.git
synced 2024-12-22 02:54:28 +01:00
Test suites and code QA fixes
This commit is contained in:
parent
c97791d720
commit
6374bc4241
4
.gitignore
vendored
4
.gitignore
vendored
@ -19,3 +19,7 @@ library/debian/
|
|||||||
pip-log.txt
|
pip-log.txt
|
||||||
pip-delete-this-directory.txt
|
pip-delete-this-directory.txt
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
.vscode/
|
||||||
|
.coverage
|
||||||
|
.tox/
|
||||||
|
.pytest_cache/
|
||||||
|
22
.travis.yml
22
.travis.yml
@ -1,10 +1,24 @@
|
|||||||
language: python
|
language: python
|
||||||
sudo: false
|
sudo: false
|
||||||
|
cache: pip
|
||||||
|
|
||||||
|
git:
|
||||||
|
submodules: true
|
||||||
|
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
python: "2.7"
|
- 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:
|
script:
|
||||||
- pip install --ignore-installed --upgrade flake8
|
- cd library
|
||||||
- flake8 --ignore F403,F405,E501
|
- tox -vv
|
||||||
|
|
||||||
|
after_success: if [ "$TOXENV" == "py35" ]; then coveralls; fi
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
|
|
||||||
import bme680
|
import bme680
|
||||||
import time
|
import time
|
||||||
|
|
||||||
print("""Estimate indoor air quality
|
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
|
combination of relative humidity and gas resistance
|
||||||
to estimate indoor air quality as a percentage.
|
to estimate indoor air quality as a percentage.
|
||||||
|
|
||||||
@ -15,7 +14,7 @@ Press Ctrl+C to exit
|
|||||||
|
|
||||||
sensor = bme680.BME680()
|
sensor = bme680.BME680()
|
||||||
|
|
||||||
# These oversampling settings can be tweaked to
|
# These oversampling settings can be tweaked to
|
||||||
# change the balance between accuracy and noise in
|
# change the balance between accuracy and noise in
|
||||||
# the data.
|
# the data.
|
||||||
|
|
||||||
@ -29,7 +28,7 @@ sensor.set_gas_heater_temperature(320)
|
|||||||
sensor.set_gas_heater_duration(150)
|
sensor.set_gas_heater_duration(150)
|
||||||
sensor.select_gas_heater_profile(0)
|
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.
|
# burn_in_time (in seconds) is kept track of.
|
||||||
|
|
||||||
start_time = time.time()
|
start_time = time.time()
|
||||||
@ -42,13 +41,13 @@ try:
|
|||||||
# Collect gas resistance burn-in values, then use the average
|
# Collect gas resistance burn-in values, then use the average
|
||||||
# of the last 50 values to set the upper limit for calculating
|
# of the last 50 values to set the upper limit for calculating
|
||||||
# gas_baseline.
|
# 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:
|
while curr_time - start_time < burn_in_time:
|
||||||
curr_time = time.time()
|
curr_time = time.time()
|
||||||
if sensor.get_sensor_data() and sensor.data.heat_stable:
|
if sensor.get_sensor_data() and sensor.data.heat_stable:
|
||||||
gas = sensor.data.gas_resistance
|
gas = sensor.data.gas_resistance
|
||||||
burn_in_data.append(gas)
|
burn_in_data.append(gas)
|
||||||
print("Gas: {0} Ohms".format(gas))
|
print('Gas: {0} Ohms'.format(gas))
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
|
|
||||||
gas_baseline = sum(burn_in_data[-50:]) / 50.0
|
gas_baseline = sum(burn_in_data[-50:]) / 50.0
|
||||||
@ -56,11 +55,13 @@ try:
|
|||||||
# Set the humidity baseline to 40%, an optimal indoor humidity.
|
# Set the humidity baseline to 40%, an optimal indoor humidity.
|
||||||
hum_baseline = 40.0
|
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)
|
# calculation of air_quality_score (25:75, humidity:gas)
|
||||||
hum_weighting = 0.25
|
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:
|
while True:
|
||||||
if sensor.get_sensor_data() and sensor.data.heat_stable:
|
if sensor.get_sensor_data() and sensor.data.heat_stable:
|
||||||
@ -72,22 +73,31 @@ try:
|
|||||||
|
|
||||||
# Calculate hum_score as the distance from the hum_baseline.
|
# Calculate hum_score as the distance from the hum_baseline.
|
||||||
if hum_offset > 0:
|
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:
|
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.
|
# Calculate gas_score as the distance from the gas_baseline.
|
||||||
if gas_offset > 0:
|
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:
|
else:
|
||||||
gas_score = 100 - (hum_weighting * 100)
|
gas_score = 100 - (hum_weighting * 100)
|
||||||
|
|
||||||
# Calculate air_quality_score.
|
# Calculate air_quality_score.
|
||||||
air_quality_score = hum_score + gas_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)
|
time.sleep(1)
|
||||||
|
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
|
@ -1,23 +1,28 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
|
|
||||||
import bme680
|
import bme680
|
||||||
import time
|
import time
|
||||||
|
|
||||||
|
print("""Display Temperature, Pressure, Humidity and Gas
|
||||||
|
|
||||||
|
Press Ctrl+C to exit
|
||||||
|
|
||||||
|
""")
|
||||||
|
|
||||||
sensor = bme680.BME680()
|
sensor = bme680.BME680()
|
||||||
|
|
||||||
# These calibration data can safely be commented
|
# These calibration data can safely be commented
|
||||||
# out, if desired.
|
# out, if desired.
|
||||||
|
|
||||||
print("Calibration data:")
|
print('Calibration data:')
|
||||||
for name in dir(sensor.calibration_data):
|
for name in dir(sensor.calibration_data):
|
||||||
|
|
||||||
if not name.startswith('_'):
|
if not name.startswith('_'):
|
||||||
value = getattr(sensor.calibration_data, name)
|
value = getattr(sensor.calibration_data, name)
|
||||||
|
|
||||||
if isinstance(value, int):
|
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
|
# change the balance between accuracy and noise in
|
||||||
# the data.
|
# the data.
|
||||||
|
|
||||||
@ -27,12 +32,12 @@ sensor.set_temperature_oversample(bme680.OS_8X)
|
|||||||
sensor.set_filter(bme680.FILTER_SIZE_3)
|
sensor.set_filter(bme680.FILTER_SIZE_3)
|
||||||
sensor.set_gas_status(bme680.ENABLE_GAS_MEAS)
|
sensor.set_gas_status(bme680.ENABLE_GAS_MEAS)
|
||||||
|
|
||||||
print("\n\nInitial reading:")
|
print('\n\nInitial reading:')
|
||||||
for name in dir(sensor.data):
|
for name in dir(sensor.data):
|
||||||
value = getattr(sensor.data, name)
|
value = getattr(sensor.data, name)
|
||||||
|
|
||||||
if not name.startswith('_'):
|
if not name.startswith('_'):
|
||||||
print("{}: {}".format(name, value))
|
print('{}: {}'.format(name, value))
|
||||||
|
|
||||||
sensor.set_gas_heater_temperature(320)
|
sensor.set_gas_heater_temperature(320)
|
||||||
sensor.set_gas_heater_duration(150)
|
sensor.set_gas_heater_duration(150)
|
||||||
@ -43,14 +48,19 @@ sensor.select_gas_heater_profile(0)
|
|||||||
# sensor.set_gas_heater_profile(200, 150, nb_profile=1)
|
# sensor.set_gas_heater_profile(200, 150, nb_profile=1)
|
||||||
# sensor.select_gas_heater_profile(1)
|
# sensor.select_gas_heater_profile(1)
|
||||||
|
|
||||||
print("\n\nPolling:")
|
print('\n\nPolling:')
|
||||||
try:
|
try:
|
||||||
while True:
|
while True:
|
||||||
if sensor.get_sensor_data():
|
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:
|
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:
|
else:
|
||||||
print(output)
|
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
|
@ -15,25 +15,29 @@ sensor.set_pressure_oversample(bme680.OS_4X)
|
|||||||
sensor.set_temperature_oversample(bme680.OS_8X)
|
sensor.set_temperature_oversample(bme680.OS_8X)
|
||||||
sensor.set_filter(bme680.FILTER_SIZE_3)
|
sensor.set_filter(bme680.FILTER_SIZE_3)
|
||||||
|
|
||||||
|
|
||||||
def display_data(offset=0):
|
def display_data(offset=0):
|
||||||
sensor.set_temp_offset(offset)
|
sensor.set_temp_offset(offset)
|
||||||
sensor.get_sensor_data()
|
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(output)
|
||||||
print("")
|
print('')
|
||||||
|
|
||||||
print("Initial readings")
|
|
||||||
|
print('Initial readings')
|
||||||
display_data()
|
display_data()
|
||||||
|
|
||||||
print("SET offset 4 degrees celsius")
|
print('SET offset 4 degrees celsius')
|
||||||
display_data(4)
|
display_data(4)
|
||||||
|
|
||||||
print("SET offset -1.87 degrees celsius")
|
print('SET offset -1.87 degrees celsius')
|
||||||
display_data(-1.87)
|
display_data(-1.87)
|
||||||
|
|
||||||
print("SET offset -100 degrees celsius")
|
print('SET offset -100 degrees celsius')
|
||||||
display_data(-100)
|
display_data(-100)
|
||||||
|
|
||||||
print("SET offset 0 degrees celsius")
|
print('SET offset 0 degrees celsius')
|
||||||
display_data(0)
|
display_data(0)
|
||||||
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
import bme680
|
import bme680
|
||||||
import time
|
|
||||||
|
|
||||||
print("""Display Temperature, Pressure and Humidity
|
print("""Display Temperature, Pressure and Humidity
|
||||||
|
|
||||||
@ -22,15 +21,17 @@ sensor.set_pressure_oversample(bme680.OS_4X)
|
|||||||
sensor.set_temperature_oversample(bme680.OS_8X)
|
sensor.set_temperature_oversample(bme680.OS_8X)
|
||||||
sensor.set_filter(bme680.FILTER_SIZE_3)
|
sensor.set_filter(bme680.FILTER_SIZE_3)
|
||||||
|
|
||||||
print("Polling:")
|
print('Polling:')
|
||||||
try:
|
try:
|
||||||
while True:
|
while True:
|
||||||
if sensor.get_sensor_data():
|
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)
|
print(output)
|
||||||
|
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
4
library/.coveragerc
Normal file
4
library/.coveragerc
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
[run]
|
||||||
|
source = bme680
|
||||||
|
omit =
|
||||||
|
.tox/*
|
@ -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 math
|
||||||
import time
|
import time
|
||||||
|
|
||||||
__version__ = '1.0.5'
|
__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):
|
class BME680(BME680Data):
|
||||||
"""BOSCH BME680
|
"""BOSCH BME680.
|
||||||
|
|
||||||
Gas, pressure, temperature and humidity sensor.
|
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.
|
: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)
|
BME680Data.__init__(self)
|
||||||
|
|
||||||
self.i2c_addr = i2c_addr
|
self.i2c_addr = i2c_addr
|
||||||
@ -22,45 +41,46 @@ class BME680(BME680Data):
|
|||||||
import smbus
|
import smbus
|
||||||
self._i2c = smbus.SMBus(1)
|
self._i2c = smbus.SMBus(1)
|
||||||
|
|
||||||
self.chip_id = self._get_regs(CHIP_ID_ADDR, 1)
|
self.chip_id = self._get_regs(constants.CHIP_ID_ADDR, 1)
|
||||||
if self.chip_id != CHIP_ID:
|
if self.chip_id != constants.CHIP_ID:
|
||||||
raise RuntimeError("BME680 Not Found. Invalid CHIP ID: 0x{0:02x}".format(self.chip_id))
|
raise RuntimeError('BME680 Not Found. Invalid CHIP ID: 0x{0:02x}'.format(self.chip_id))
|
||||||
|
|
||||||
self.soft_reset()
|
self.soft_reset()
|
||||||
self.set_power_mode(SLEEP_MODE)
|
self.set_power_mode(constants.SLEEP_MODE)
|
||||||
|
|
||||||
self._get_calibration_data()
|
self._get_calibration_data()
|
||||||
|
|
||||||
self.set_humidity_oversample(OS_2X)
|
self.set_humidity_oversample(constants.OS_2X)
|
||||||
self.set_pressure_oversample(OS_4X)
|
self.set_pressure_oversample(constants.OS_4X)
|
||||||
self.set_temperature_oversample(OS_8X)
|
self.set_temperature_oversample(constants.OS_8X)
|
||||||
self.set_filter(FILTER_SIZE_3)
|
self.set_filter(constants.FILTER_SIZE_3)
|
||||||
self.set_gas_status(ENABLE_GAS_MEAS)
|
self.set_gas_status(constants.ENABLE_GAS_MEAS)
|
||||||
self.set_temp_offset(0)
|
self.set_temp_offset(0)
|
||||||
self.get_sensor_data()
|
self.get_sensor_data()
|
||||||
|
|
||||||
def _get_calibration_data(self):
|
def _get_calibration_data(self):
|
||||||
"""Retrieves the sensor calibration data and stores it in .calibration_data"""
|
"""Retrieve the sensor calibration data and store it in .calibration_data."""
|
||||||
calibration = self._get_regs(COEFF_ADDR1, COEFF_ADDR1_LEN)
|
calibration = self._get_regs(constants.COEFF_ADDR1, constants.COEFF_ADDR1_LEN)
|
||||||
calibration += self._get_regs(COEFF_ADDR2, COEFF_ADDR2_LEN)
|
calibration += self._get_regs(constants.COEFF_ADDR2, constants.COEFF_ADDR2_LEN)
|
||||||
|
|
||||||
heat_range = self._get_regs(ADDR_RES_HEAT_RANGE_ADDR, 1)
|
heat_range = self._get_regs(constants.ADDR_RES_HEAT_RANGE_ADDR, 1)
|
||||||
heat_value = twos_comp(self._get_regs(ADDR_RES_HEAT_VAL_ADDR, 1), bits=8)
|
heat_value = constants.twos_comp(self._get_regs(constants.ADDR_RES_HEAT_VAL_ADDR, 1), bits=8)
|
||||||
sw_error = twos_comp(self._get_regs(ADDR_RANGE_SW_ERR_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_from_array(calibration)
|
||||||
self.calibration_data.set_other(heat_range, heat_value, sw_error)
|
self.calibration_data.set_other(heat_range, heat_value, sw_error)
|
||||||
|
|
||||||
def soft_reset(self):
|
def soft_reset(self):
|
||||||
"""Initiate a soft reset"""
|
"""Trigger a soft reset."""
|
||||||
self._set_regs(SOFT_RESET_ADDR, SOFT_RESET_CMD)
|
self._set_regs(constants.SOFT_RESET_ADDR, constants.SOFT_RESET_CMD)
|
||||||
time.sleep(RESET_PERIOD / 1000.0)
|
time.sleep(constants.RESET_PERIOD / 1000.0)
|
||||||
|
|
||||||
def set_temp_offset(self, value):
|
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.
|
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
|
:param value: Temperature offset in Celsius, eg. 4, -8, 1.25
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if value == 0:
|
if value == 0:
|
||||||
self.offset_temp_in_t_fine = 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))
|
self.offset_temp_in_t_fine = int(math.copysign((((int(abs(value) * 100)) << 8) - 128) / 5, value))
|
||||||
|
|
||||||
def set_humidity_oversample(self, value):
|
def set_humidity_oversample(self, value):
|
||||||
"""Set humidity oversampling
|
"""Set humidity oversampling.
|
||||||
|
|
||||||
A higher oversampling value means more stable sensor readings,
|
A higher oversampling value means more stable sensor readings,
|
||||||
with less noise and jitter.
|
with less noise and jitter.
|
||||||
|
|
||||||
@ -80,15 +100,15 @@ class BME680(BME680Data):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
self.tph_settings.os_hum = value
|
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):
|
def get_humidity_oversample(self):
|
||||||
"""Get humidity oversampling"""
|
"""Get humidity oversampling."""
|
||||||
return (self._get_regs(CONF_OS_H_ADDR, 1) & OSH_MSK) >> OSH_POS
|
return (self._get_regs(constants.CONF_OS_H_ADDR, 1) & constants.OSH_MSK) >> constants.OSH_POS
|
||||||
|
|
||||||
def set_pressure_oversample(self, value):
|
def set_pressure_oversample(self, value):
|
||||||
"""Set temperature oversampling
|
"""Set temperature oversampling.
|
||||||
|
|
||||||
A higher oversampling value means more stable sensor readings,
|
A higher oversampling value means more stable sensor readings,
|
||||||
with less noise and jitter.
|
with less noise and jitter.
|
||||||
|
|
||||||
@ -96,18 +116,18 @@ class BME680(BME680Data):
|
|||||||
causing a slower response time to fast transients.
|
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
|
: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.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):
|
def get_pressure_oversample(self):
|
||||||
"""Get pressure oversampling"""
|
"""Get pressure oversampling."""
|
||||||
return (self._get_regs(CONF_T_P_MODE_ADDR, 1) & OSP_MSK) >> OSP_POS
|
return (self._get_regs(constants.CONF_T_P_MODE_ADDR, 1) & constants.OSP_MSK) >> constants.OSP_POS
|
||||||
|
|
||||||
def set_temperature_oversample(self, value):
|
def set_temperature_oversample(self, value):
|
||||||
"""Set pressure oversampling
|
"""Set pressure oversampling.
|
||||||
|
|
||||||
A higher oversampling value means more stable sensor readings,
|
A higher oversampling value means more stable sensor readings,
|
||||||
with less noise and jitter.
|
with less noise and jitter.
|
||||||
|
|
||||||
@ -115,18 +135,18 @@ class BME680(BME680Data):
|
|||||||
causing a slower response time to fast transients.
|
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
|
: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.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):
|
def get_temperature_oversample(self):
|
||||||
"""Get temperature oversampling"""
|
"""Get temperature oversampling."""
|
||||||
return (self._get_regs(CONF_T_P_MODE_ADDR, 1) & OST_MSK) >> OST_POS
|
return (self._get_regs(constants.CONF_T_P_MODE_ADDR, 1) & constants.OST_MSK) >> constants.OST_POS
|
||||||
|
|
||||||
def set_filter(self, value):
|
def set_filter(self, value):
|
||||||
"""Set IIR filter size
|
"""Set IIR filter size.
|
||||||
|
|
||||||
Optionally remove short term fluctuations from the temperature and pressure readings,
|
Optionally remove short term fluctuations from the temperature and pressure readings,
|
||||||
increasing their resolution but reducing their bandwidth.
|
increasing their resolution but reducing their bandwidth.
|
||||||
|
|
||||||
@ -138,40 +158,42 @@ class BME680(BME680Data):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
self.tph_settings.filter = value
|
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):
|
def get_filter(self):
|
||||||
"""Get filter size"""
|
"""Get filter size."""
|
||||||
return (self._get_regs(CONF_ODR_FILT_ADDR, 1) & FILTER_MSK) >> FILTER_POS
|
return (self._get_regs(constants.CONF_ODR_FILT_ADDR, 1) & constants.FILTER_MSK) >> constants.FILTER_POS
|
||||||
|
|
||||||
def select_gas_heater_profile(self, value):
|
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.
|
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:
|
if value > constants.NBCONV_MAX or value < constants.NBCONV_MIN:
|
||||||
raise ValueError("Profile '{}' should be between {} and {}".format(value, NBCONV_MIN, NBCONV_MAX))
|
raise ValueError("Profile '{}' should be between {} and {}".format(value, constants.NBCONV_MIN, constants.NBCONV_MAX))
|
||||||
|
|
||||||
self.gas_settings.nb_conv = value
|
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):
|
def get_gas_heater_profile(self):
|
||||||
"""Get gas sensor conversion profile: 0 to 9"""
|
"""Get gas sensor conversion profile: 0 to 9."""
|
||||||
return self._get_regs(CONF_ODR_RUN_GAS_NBC_ADDR, 1) & NBCONV_MSK
|
return self._get_regs(constants.CONF_ODR_RUN_GAS_NBC_ADDR, 1) & constants.NBCONV_MSK
|
||||||
|
|
||||||
def set_gas_status(self, value):
|
def set_gas_status(self, value):
|
||||||
"""Enable/disable gas sensor"""
|
"""Enable/disable gas sensor."""
|
||||||
self.gas_settings.run_gas = value
|
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):
|
def get_gas_status(self):
|
||||||
"""Get the current gas status"""
|
"""Get the current gas status."""
|
||||||
return (self._get_regs(CONF_ODR_RUN_GAS_NBC_ADDR, 1) & RUN_GAS_MSK) >> RUN_GAS_POS
|
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):
|
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 temperature: Target temperature in degrees celsius, between 200 and 400
|
||||||
:param durarion: Target duration in milliseconds, between 1 and 4032
|
:param durarion: Target duration in milliseconds, between 1 and 4032
|
||||||
:param nb_profile: Target profile, between 0 and 9
|
: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)
|
self.set_gas_heater_duration(duration, nb_profile=nb_profile)
|
||||||
|
|
||||||
def set_gas_heater_temperature(self, value, nb_profile=0):
|
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
|
:param value: Target temperature in degrees celsius, between 200 and 400
|
||||||
|
|
||||||
When setting an nb_profile other than 0,
|
When setting an nb_profile other than 0,
|
||||||
make sure to select it with select_gas_heater_profile.
|
make sure to select it with select_gas_heater_profile.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if nb_profile > NBCONV_MAX or value < NBCONV_MIN:
|
if nb_profile > constants.NBCONV_MAX or value < constants.NBCONV_MIN:
|
||||||
raise ValueError("Profile '{}' should be between {} and {}".format(nb_profile, NBCONV_MIN, NBCONV_MAX))
|
raise ValueError('Profile "{}" should be between {} and {}'.format(nb_profile, constants.NBCONV_MIN, constants.NBCONV_MAX))
|
||||||
|
|
||||||
self.gas_settings.heatr_temp = value
|
self.gas_settings.heatr_temp = value
|
||||||
temp = int(self._calc_heater_resistance(self.gas_settings.heatr_temp))
|
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):
|
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.
|
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.
|
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,
|
When setting an nb_profile other than 0,
|
||||||
make sure to select it with select_gas_heater_profile.
|
make sure to select it with select_gas_heater_profile.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if nb_profile > NBCONV_MAX or value < NBCONV_MIN:
|
if nb_profile > constants.NBCONV_MAX or value < constants.NBCONV_MIN:
|
||||||
raise ValueError("Profile '{}' should be between {} and {}".format(nb_profile, NBCONV_MIN, NBCONV_MAX))
|
raise ValueError('Profile "{}" should be between {} and {}'.format(nb_profile, constants.NBCONV_MIN, constants.NBCONV_MAX))
|
||||||
|
|
||||||
self.gas_settings.heatr_dur = value
|
self.gas_settings.heatr_dur = value
|
||||||
temp = self._calc_heater_duration(self.gas_settings.heatr_dur)
|
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):
|
def set_power_mode(self, value, blocking=True):
|
||||||
"""Set power mode"""
|
"""Set power mode."""
|
||||||
if value not in (SLEEP_MODE, FORCED_MODE):
|
if value not in (constants.SLEEP_MODE, constants.FORCED_MODE):
|
||||||
print("Power mode should be one of SLEEP_MODE or FORCED_MODE")
|
raise ValueError('Power mode should be one of SLEEP_MODE or FORCED_MODE')
|
||||||
|
|
||||||
self.power_mode = value
|
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:
|
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):
|
def get_power_mode(self):
|
||||||
"""Get power mode"""
|
"""Get power mode."""
|
||||||
self.power_mode = self._get_regs(CONF_T_P_MODE_ADDR, 1)
|
self.power_mode = self._get_regs(constants.CONF_T_P_MODE_ADDR, 1)
|
||||||
return self.power_mode
|
return self.power_mode
|
||||||
|
|
||||||
def get_sensor_data(self):
|
def get_sensor_data(self):
|
||||||
"""Get sensor data.
|
"""Get sensor data.
|
||||||
|
|
||||||
Stores data in .data and returns True upon success.
|
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):
|
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:
|
if (status & constants.NEW_DATA_MSK) == 0:
|
||||||
time.sleep(POLL_PERIOD_MS / 1000.0)
|
time.sleep(constants.POLL_PERIOD_MS / 1000.0)
|
||||||
continue
|
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
|
# 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]
|
self.data.meas_index = regs[1]
|
||||||
|
|
||||||
adc_pres = (regs[2] << 12) | (regs[3] << 4) | (regs[4] >> 4)
|
adc_pres = (regs[2] << 12) | (regs[3] << 4) | (regs[4] >> 4)
|
||||||
adc_temp = (regs[5] << 12) | (regs[6] << 4) | (regs[7] >> 4)
|
adc_temp = (regs[5] << 12) | (regs[6] << 4) | (regs[7] >> 4)
|
||||||
adc_hum = (regs[8] << 8) | regs[9]
|
adc_hum = (regs[8] << 8) | regs[9]
|
||||||
adc_gas_res = (regs[13] << 2) | (regs[14] >> 6)
|
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] & constants.GASM_VALID_MSK
|
||||||
self.data.status |= regs[14] & HEAT_STAB_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)
|
temperature = self._calc_temperature(adc_temp)
|
||||||
self.data.temperature = temperature / 100.0
|
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.pressure = self._calc_pressure(adc_pres) / 100.0
|
||||||
self.data.humidity = self._calc_humidity(adc_hum) / 1000.0
|
self.data.humidity = self._calc_humidity(adc_hum) / 1000.0
|
||||||
@ -277,27 +299,28 @@ class BME680(BME680Data):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def _set_bits(self, register, mask, position, value):
|
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 = self._get_regs(register, 1)
|
||||||
temp &= ~mask
|
temp &= ~mask
|
||||||
temp |= value << position
|
temp |= value << position
|
||||||
self._set_regs(register, temp)
|
self._set_regs(register, temp)
|
||||||
|
|
||||||
def _set_regs(self, register, value):
|
def _set_regs(self, register, value):
|
||||||
"""Set one or more registers"""
|
"""Set one or more registers."""
|
||||||
if isinstance(value, int):
|
if isinstance(value, int):
|
||||||
self._i2c.write_byte_data(self.i2c_addr, register, value)
|
self._i2c.write_byte_data(self.i2c_addr, register, value)
|
||||||
else:
|
else:
|
||||||
self._i2c.write_i2c_block_data(self.i2c_addr, register, value)
|
self._i2c.write_i2c_block_data(self.i2c_addr, register, value)
|
||||||
|
|
||||||
def _get_regs(self, register, length):
|
def _get_regs(self, register, length):
|
||||||
"""Get one or more registers"""
|
"""Get one or more registers."""
|
||||||
if length == 1:
|
if length == 1:
|
||||||
return self._i2c.read_byte_data(self.i2c_addr, register)
|
return self._i2c.read_byte_data(self.i2c_addr, register)
|
||||||
else:
|
else:
|
||||||
return self._i2c.read_i2c_block_data(self.i2c_addr, register, length)
|
return self._i2c.read_i2c_block_data(self.i2c_addr, register, length)
|
||||||
|
|
||||||
def _calc_temperature(self, temperature_adc):
|
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)
|
var1 = (temperature_adc >> 3) - (self.calibration_data.par_t1 << 1)
|
||||||
var2 = (var1 * self.calibration_data.par_t2) >> 11
|
var2 = (var1 * self.calibration_data.par_t2) >> 11
|
||||||
var3 = ((var1 >> 1) * (var1 >> 1)) >> 12
|
var3 = ((var1 >> 1) * (var1 >> 1)) >> 12
|
||||||
@ -310,12 +333,13 @@ class BME680(BME680Data):
|
|||||||
return calc_temp
|
return calc_temp
|
||||||
|
|
||||||
def _calc_pressure(self, pressure_adc):
|
def _calc_pressure(self, pressure_adc):
|
||||||
|
"""Convert the raw pressure using calibration data."""
|
||||||
var1 = ((self.calibration_data.t_fine) >> 1) - 64000
|
var1 = ((self.calibration_data.t_fine) >> 1) - 64000
|
||||||
var2 = ((((var1 >> 2) * (var1 >> 2)) >> 11) *
|
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 + ((var1 * self.calibration_data.par_p5) << 1)
|
||||||
var2 = (var2 >> 2) + (self.calibration_data.par_p4 << 16)
|
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_p3 << 5)) >> 3) +
|
||||||
((self.calibration_data.par_p2 * var1) >> 1))
|
((self.calibration_data.par_p2 * var1) >> 1))
|
||||||
var1 = var1 >> 18
|
var1 = var1 >> 18
|
||||||
@ -330,26 +354,27 @@ class BME680(BME680Data):
|
|||||||
calc_pressure = ((calc_pressure << 1) // var1)
|
calc_pressure = ((calc_pressure << 1) // var1)
|
||||||
|
|
||||||
var1 = (self.calibration_data.par_p9 * (((calc_pressure >> 3) *
|
var1 = (self.calibration_data.par_p9 * (((calc_pressure >> 3) *
|
||||||
(calc_pressure >> 3)) >> 13)) >> 12
|
(calc_pressure >> 3)) >> 13)) >> 12
|
||||||
var2 = ((calc_pressure >> 2) *
|
var2 = ((calc_pressure >> 2) *
|
||||||
self.calibration_data.par_p8) >> 13
|
self.calibration_data.par_p8) >> 13
|
||||||
var3 = ((calc_pressure >> 8) * (calc_pressure >> 8) *
|
var3 = ((calc_pressure >> 8) * (calc_pressure >> 8) *
|
||||||
(calc_pressure >> 8) *
|
(calc_pressure >> 8) *
|
||||||
self.calibration_data.par_p10) >> 17
|
self.calibration_data.par_p10) >> 17
|
||||||
|
|
||||||
calc_pressure = (calc_pressure) + ((var1 + var2 + var3 +
|
calc_pressure = (calc_pressure) + ((var1 + var2 + var3 +
|
||||||
(self.calibration_data.par_p7 << 7)) >> 4)
|
(self.calibration_data.par_p7 << 7)) >> 4)
|
||||||
|
|
||||||
return calc_pressure
|
return calc_pressure
|
||||||
|
|
||||||
def _calc_humidity(self, humidity_adc):
|
def _calc_humidity(self, humidity_adc):
|
||||||
|
"""Convert the raw humidity using calibration data."""
|
||||||
temp_scaled = ((self.calibration_data.t_fine * 5) + 128) >> 8
|
temp_scaled = ((self.calibration_data.t_fine * 5) + 128) >> 8
|
||||||
var1 = (humidity_adc - ((self.calibration_data.par_h1 * 16))) \
|
var1 = (humidity_adc - ((self.calibration_data.par_h1 * 16))) -\
|
||||||
- (((temp_scaled * self.calibration_data.par_h3) // (100)) >> 1)
|
(((temp_scaled * self.calibration_data.par_h3) // (100)) >> 1)
|
||||||
var2 = (self.calibration_data.par_h2
|
var2 = (self.calibration_data.par_h2 *
|
||||||
* (((temp_scaled * self.calibration_data.par_h4) // (100))
|
(((temp_scaled * self.calibration_data.par_h4) // (100)) +
|
||||||
+ (((temp_scaled * ((temp_scaled * self.calibration_data.par_h5) // (100))) >> 6)
|
(((temp_scaled * ((temp_scaled * self.calibration_data.par_h5) // (100))) >> 6) //
|
||||||
// (100)) + (1 * 16384))) >> 10
|
(100)) + (1 * 16384))) >> 10
|
||||||
var3 = var1 * var2
|
var3 = var1 * var2
|
||||||
var4 = self.calibration_data.par_h6 << 7
|
var4 = self.calibration_data.par_h6 << 7
|
||||||
var4 = ((var4) + ((temp_scaled * self.calibration_data.par_h7) // (100))) >> 4
|
var4 = ((var4) + ((temp_scaled * self.calibration_data.par_h7) // (100))) >> 4
|
||||||
@ -357,21 +382,23 @@ class BME680(BME680Data):
|
|||||||
var6 = (var4 * var5) >> 1
|
var6 = (var4 * var5) >> 1
|
||||||
calc_hum = (((var3 + var6) >> 10) * (1000)) >> 12
|
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):
|
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
|
var1 = ((1340 + (5 * self.calibration_data.range_sw_err)) * (lookupTable1[gas_range])) >> 16
|
||||||
var2 = (((gas_res_adc << 15) - (16777216)) + var1)
|
var2 = (((gas_res_adc << 15) - (16777216)) + var1)
|
||||||
var3 = ((lookupTable2[gas_range] * var1) >> 9)
|
var3 = ((lookupTable2[gas_range] * var1) >> 9)
|
||||||
calc_gas_res = ((var3 + (var2 >> 1)) / var2)
|
calc_gas_res = ((var3 + (var2 >> 1)) / var2)
|
||||||
|
|
||||||
if calc_gas_res < 0:
|
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
|
return calc_gas_res
|
||||||
|
|
||||||
def _calc_heater_resistance(self, temperature):
|
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
|
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)
|
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
|
return heatr_res
|
||||||
|
|
||||||
def _calc_heater_duration(self, duration):
|
def _calc_heater_duration(self, duration):
|
||||||
|
"""Calculate correct value for heater duration setting from milliseconds."""
|
||||||
if duration < 0xfc0:
|
if duration < 0xfc0:
|
||||||
factor = 0
|
factor = 0
|
||||||
|
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
"""BME680 constants, structures and utilities."""
|
||||||
|
|
||||||
# BME680 General config
|
# BME680 General config
|
||||||
POLL_PERIOD_MS = 10
|
POLL_PERIOD_MS = 10
|
||||||
|
|
||||||
@ -118,7 +120,7 @@ TMP_BUFFER_LENGTH = 40
|
|||||||
REG_BUFFER_LENGTH = 6
|
REG_BUFFER_LENGTH = 6
|
||||||
FIELD_DATA_LENGTH = 3
|
FIELD_DATA_LENGTH = 3
|
||||||
GAS_REG_BUF_LENGTH = 20
|
GAS_REG_BUF_LENGTH = 20
|
||||||
GAS_HEATER_PROF_LEN_MAX = 10
|
GAS_HEATER_PROF_LEN_MAX = 10
|
||||||
|
|
||||||
# Settings selector
|
# Settings selector
|
||||||
OST_SEL = 1
|
OST_SEL = 1
|
||||||
@ -133,7 +135,7 @@ GAS_SENSOR_SEL = GAS_MEAS_SEL | RUN_GAS_SEL | NBCONV_SEL
|
|||||||
|
|
||||||
# Number of conversion settings
|
# Number of conversion settings
|
||||||
NBCONV_MIN = 0
|
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
|
# Mask definitions
|
||||||
GAS_MEAS_MSK = 0x30
|
GAS_MEAS_MSK = 0x30
|
||||||
@ -214,30 +216,37 @@ REG_HCTRL_INDEX = 0
|
|||||||
|
|
||||||
# Look up tables for the possible gas range values
|
# Look up tables for the possible gas range values
|
||||||
lookupTable1 = [2147483647, 2147483647, 2147483647, 2147483647,
|
lookupTable1 = [2147483647, 2147483647, 2147483647, 2147483647,
|
||||||
2147483647, 2126008810, 2147483647, 2130303777, 2147483647,
|
2147483647, 2126008810, 2147483647, 2130303777, 2147483647,
|
||||||
2147483647, 2143188679, 2136746228, 2147483647, 2126008810,
|
2147483647, 2143188679, 2136746228, 2147483647, 2126008810,
|
||||||
2147483647, 2147483647]
|
2147483647, 2147483647]
|
||||||
|
|
||||||
lookupTable2 = [4096000000, 2048000000, 1024000000, 512000000,
|
lookupTable2 = [4096000000, 2048000000, 1024000000, 512000000,
|
||||||
255744255, 127110228, 64000000, 32258064,
|
255744255, 127110228, 64000000, 32258064,
|
||||||
16016016, 8000000, 4000000, 2000000,
|
16016016, 8000000, 4000000, 2000000,
|
||||||
1000000, 500000, 250000, 125000]
|
1000000, 500000, 250000, 125000]
|
||||||
|
|
||||||
|
|
||||||
def bytes_to_word(msb, lsb, bits=16, signed=False):
|
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
|
word = (msb << 8) | lsb
|
||||||
if signed:
|
if signed:
|
||||||
word = twos_comp(word, bits)
|
word = twos_comp(word, bits)
|
||||||
return word
|
return word
|
||||||
|
|
||||||
|
|
||||||
def twos_comp(val, bits=16):
|
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:
|
if val & (1 << (bits - 1)) != 0:
|
||||||
val = val - (1 << bits)
|
val = val - (1 << bits)
|
||||||
return val
|
return val
|
||||||
|
|
||||||
# Sensor field data structure
|
|
||||||
|
|
||||||
class FieldData:
|
class FieldData:
|
||||||
def __init__(self):
|
"""Structure for storing BME680 sensor data."""
|
||||||
|
|
||||||
|
def __init__(self): # noqa D107
|
||||||
# Contains new_data, gasm_valid & heat_stab
|
# Contains new_data, gasm_valid & heat_stab
|
||||||
self.status = None
|
self.status = None
|
||||||
self.heat_stable = False
|
self.heat_stable = False
|
||||||
@ -254,10 +263,11 @@ class FieldData:
|
|||||||
# Gas resistance in Ohms
|
# Gas resistance in Ohms
|
||||||
self.gas_resistance = None
|
self.gas_resistance = None
|
||||||
|
|
||||||
# Structure to hold the Calibration data
|
|
||||||
|
|
||||||
class CalibrationData:
|
class CalibrationData:
|
||||||
def __init__(self):
|
"""Structure for storing BME680 calibration data."""
|
||||||
|
|
||||||
|
def __init__(self): # noqa D107
|
||||||
self.par_h1 = None
|
self.par_h1 = None
|
||||||
self.par_h2 = None
|
self.par_h2 = None
|
||||||
self.par_h3 = None
|
self.par_h3 = None
|
||||||
@ -291,6 +301,7 @@ class CalibrationData:
|
|||||||
self.range_sw_err = None
|
self.range_sw_err = None
|
||||||
|
|
||||||
def set_from_array(self, calibration):
|
def set_from_array(self, calibration):
|
||||||
|
"""Set paramaters from an array of bytes."""
|
||||||
# Temperature related coefficients
|
# Temperature related coefficients
|
||||||
self.par_t1 = bytes_to_word(calibration[T1_MSB_REG], calibration[T1_LSB_REG])
|
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)
|
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)
|
self.par_gh3 = twos_comp(calibration[GH3_REG], bits=8)
|
||||||
|
|
||||||
def set_other(self, heat_range, heat_value, sw_error):
|
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_range = (heat_range & RHRANGE_MSK) // 16
|
||||||
self.res_heat_val = heat_value
|
self.res_heat_val = heat_value
|
||||||
self.range_sw_err = (sw_error & RSERROR_MSK) // 16
|
self.range_sw_err = (sw_error & RSERROR_MSK) // 16
|
||||||
|
|
||||||
# BME680 sensor settings structure which comprises of ODR,
|
|
||||||
# over-sampling and filter settings.
|
|
||||||
|
|
||||||
class TPHSettings:
|
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
|
# Humidity oversampling
|
||||||
self.os_hum = None
|
self.os_hum = None
|
||||||
# Temperature oversampling
|
# Temperature oversampling
|
||||||
@ -341,11 +357,11 @@ class TPHSettings:
|
|||||||
# Filter coefficient
|
# Filter coefficient
|
||||||
self.filter = None
|
self.filter = None
|
||||||
|
|
||||||
# BME680 gas sensor which comprises of gas settings
|
|
||||||
## and status parameters
|
|
||||||
|
|
||||||
class GasSettings:
|
class GasSettings:
|
||||||
def __init__(self):
|
"""Structure for storing BME680 gas settings and status."""
|
||||||
|
|
||||||
|
def __init__(self): # noqa D107
|
||||||
# Variable to store nb conversion
|
# Variable to store nb conversion
|
||||||
self.nb_conv = None
|
self.nb_conv = None
|
||||||
# Variable to store heater control
|
# Variable to store heater control
|
||||||
@ -357,10 +373,11 @@ class GasSettings:
|
|||||||
# Pointer to store duration profile
|
# Pointer to store duration profile
|
||||||
self.heatr_dur = None
|
self.heatr_dur = None
|
||||||
|
|
||||||
# BME680 device structure
|
|
||||||
|
|
||||||
class BME680Data:
|
class BME680Data:
|
||||||
def __init__(self):
|
"""Structure to represent BME680 device."""
|
||||||
|
|
||||||
|
def __init__(self): # noqa D107
|
||||||
# Chip Id
|
# Chip Id
|
||||||
self.chip_id = None
|
self.chip_id = None
|
||||||
# Device Id
|
# 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
|
#!/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
|
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
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
@ -38,17 +38,17 @@ classifiers = ['Development Status :: 5 - Production/Stable',
|
|||||||
'Topic :: System :: Hardware']
|
'Topic :: System :: Hardware']
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
name = 'bme680',
|
name='bme680',
|
||||||
version = '1.0.5',
|
version='1.0.5',
|
||||||
author = 'Philip Howard',
|
author='Philip Howard',
|
||||||
author_email = 'phil@pimoroni.com',
|
author_email='phil@pimoroni.com',
|
||||||
description = """Python library for driving the Pimoroni BME680 Breakout""",
|
description="""Python library for driving the Pimoroni BME680 Breakout""",
|
||||||
long_description= open('README.rst').read() + "\n" + open('CHANGELOG.txt').read(),
|
long_description=open('README.rst').read() + '\n' + open('CHANGELOG.txt').read(),
|
||||||
license = 'MIT',
|
license='MIT',
|
||||||
keywords = 'Raspberry Pi',
|
keywords='Raspberry Pi',
|
||||||
url = 'http://www.pimoroni.com',
|
url='http://www.pimoroni.com',
|
||||||
classifiers = classifiers,
|
classifiers=classifiers,
|
||||||
packages = ['bme680'],
|
packages=['bme680'],
|
||||||
py_modules = [],
|
py_modules=[],
|
||||||
install_requires= []
|
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