mirror of
https://github.com/cmur2/python-bme680.git
synced 2024-09-28 11:02:21 +02:00
Compare commits
59 Commits
Author | SHA1 | Date | |
---|---|---|---|
76630cf903 | |||
d93b500b71 | |||
2089e809e8 | |||
294281658e | |||
9f3be44151 | |||
01d590d826 | |||
0ce1b1a83c | |||
|
da98fcacb4 | ||
|
191dc9e115 | ||
|
9855b10969 | ||
|
6b5c136823 | ||
|
d7f712247a | ||
|
adfd58db6b | ||
|
8ecf91fdd8 | ||
|
02ca7a4353 | ||
7106a373e5 | |||
|
056049ee03 | ||
28ebcb63a5 | |||
b36d259ae1 | |||
efce546001 | |||
cd32ec320d | |||
|
91bb131713 | ||
|
7e6bdd0dc4 | ||
|
5806466739 | ||
|
45eeba9bb4 | ||
|
90fabf53fa | ||
f61ae9a86a | |||
|
3a48112445 | ||
|
91434caf0b | ||
|
5a5dd139c3 | ||
db4626f370 | |||
|
e827e5d622 | ||
5a4709992e | |||
6442510ab0 | |||
db02dd0d65 | |||
c85e3de250 | |||
405867a4aa | |||
|
e97c5f9241 | ||
|
e43b362a33 | ||
|
15171e57f6 | ||
|
8dce708fef | ||
|
df382a50aa | ||
|
6374bc4241 | ||
|
c97791d720 | ||
|
4b61558566 | ||
|
a4a0cfcd2a | ||
|
b2641c48df | ||
|
2dc799a7a4 | ||
|
50902ac08e | ||
|
05f80690e0 | ||
|
0f0be7f2c2 | ||
|
4ae50e2f2c | ||
|
7e08c2d749 | ||
|
bc2f729013 | ||
|
fed191e40e | ||
|
8cb0dead6b | ||
|
ae004aa62b | ||
|
1c9ba41306 | ||
|
40b4b63b93 |
36
.github/workflows/ci.yml
vendored
Normal file
36
.github/workflows/ci.yml
vendored
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
---
|
||||||
|
name: ci
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [master]
|
||||||
|
pull_request:
|
||||||
|
branches: [master]
|
||||||
|
workflow_dispatch:
|
||||||
|
schedule:
|
||||||
|
- cron: '47 4 * * 4' # weekly on thursday morning
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
python-version:
|
||||||
|
- '3.7'
|
||||||
|
- '3.9'
|
||||||
|
- '3.10'
|
||||||
|
- '3.11'
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Set up Python ${{ matrix.python-version }}
|
||||||
|
uses: actions/setup-python@v4
|
||||||
|
with:
|
||||||
|
python-version: ${{ matrix.python-version }}
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
python -m pip install --upgrade setuptools tox
|
||||||
|
- name: Test
|
||||||
|
working-directory: library
|
||||||
|
run: |
|
||||||
|
tox -e py
|
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/
|
||||||
|
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2018 Pimoroni Ltd
|
||||||
|
|
||||||
|
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 the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
44
Makefile
Normal file
44
Makefile
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
.PHONY: usage install uninstall
|
||||||
|
usage:
|
||||||
|
@echo "Usage: make <target>, where target is one of:\n"
|
||||||
|
@echo "install: install the library locally from source"
|
||||||
|
@echo "uninstall: uninstall the local library"
|
||||||
|
@echo "python-readme: generate library/README.rst from README.md"
|
||||||
|
@echo "python-wheels: build python .whl files for distribution"
|
||||||
|
@echo "python-sdist: build python source distribution"
|
||||||
|
@echo "python-clean: clean python build and dist directories"
|
||||||
|
@echo "python-dist: build all python distribution files"
|
||||||
|
|
||||||
|
install:
|
||||||
|
./install.sh
|
||||||
|
|
||||||
|
uninstall:
|
||||||
|
./uninstall.sh
|
||||||
|
|
||||||
|
python-readme: library/README.rst
|
||||||
|
|
||||||
|
python-license: library/LICENSE.txt
|
||||||
|
|
||||||
|
library/README.rst: README.md
|
||||||
|
pandoc --from=markdown --to=rst -o library/README.rst README.md
|
||||||
|
|
||||||
|
library/LICENSE.txt: LICENSE
|
||||||
|
cp LICENSE library/LICENSE.txt
|
||||||
|
|
||||||
|
python-wheels: python-readme python-license
|
||||||
|
cd library; python3 setup.py bdist_wheel
|
||||||
|
cd library; python setup.py bdist_wheel
|
||||||
|
|
||||||
|
python-sdist: python-readme python-license
|
||||||
|
cd library; python setup.py sdist
|
||||||
|
|
||||||
|
python-clean:
|
||||||
|
-rm -r library/dist
|
||||||
|
-rm -r library/build
|
||||||
|
-rm -r library/*.egg-info
|
||||||
|
|
||||||
|
python-dist: python-clean python-wheels python-sdist
|
||||||
|
ls library/dist
|
||||||
|
|
||||||
|
python-deploy: python-dist
|
||||||
|
twine upload library/dist/*
|
38
README.md
38
README.md
@ -1,40 +1,20 @@
|
|||||||
# BME680
|
# BME680
|
||||||
|
|
||||||
|
![ci](https://github.com/cmur2/python-bme680/workflows/ci/badge.svg?branch=master)
|
||||||
|
|
||||||
https://shop.pimoroni.com/products/bme680
|
https://shop.pimoroni.com/products/bme680
|
||||||
|
|
||||||
The state-of-the-art BME680 breakout lets you measure temperature, pressure, humidity, and indoor air quality.
|
The state-of-the-art BME680 breakout lets you measure temperature, pressure, humidity, and indoor air quality.
|
||||||
|
|
||||||
## Installing
|
## About this Fork
|
||||||
|
|
||||||
### Full install (recommended):
|
In general this fork tries to improve some details of the [original pimoroni/bme680-python repo](https://github.com/pimoroni/bme680-python):
|
||||||
|
|
||||||
We've created an easy installation script that will install all pre-requisites and get your BME680
|
- use standard unit for pressure measurement (Pascal instead Hectopascal, user can convert if he likes)
|
||||||
up and running with minimal efforts. To run it, fire up Terminal which you'll find in Menu -> Accessories -> Terminal
|
- floating point precision for measurement compensations (user has all precision available to choose *based on measurement settings and datasheet* how much is trustable)
|
||||||
on your Raspberry Pi desktop, as illustrated below:
|
- no redundant tweaking of measurement settings after soft reset
|
||||||
|
|
||||||
![Finding the terminal](http://get.pimoroni.com/resources/github-repo-terminal.png)
|
## Development
|
||||||
|
|
||||||
In the new terminal window type the command exactly as it appears below (check for typos) and follow the on-screen instructions:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl https://get.pimoroni.com/bme680 | bash
|
|
||||||
```
|
|
||||||
|
|
||||||
### Manual install:
|
|
||||||
|
|
||||||
#### Library install for Python 3:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo pip3 install bme680
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Library install for Python 2:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo pip2 install bme680
|
|
||||||
```
|
|
||||||
|
|
||||||
### Development:
|
|
||||||
|
|
||||||
If you want to contribute, or like living on the edge of your seat by having the latest code, you should clone this repository, `cd` to the library directory, and run:
|
If you want to contribute, or like living on the edge of your seat by having the latest code, you should clone this repository, `cd` to the library directory, and run:
|
||||||
|
|
||||||
@ -47,5 +27,5 @@ In all cases you will have to enable the i2c bus.
|
|||||||
|
|
||||||
## Documentation & Support
|
## Documentation & Support
|
||||||
|
|
||||||
* Guides and tutorials - https://learn.pimoroni.com/bme680
|
* Guides and tutorials - https://learn.pimoroni.com/bme680-breakout
|
||||||
* Get help - http://forums.pimoroni.com/c/support
|
* Get help - http://forums.pimoroni.com/c/support
|
||||||
|
57
examples/compensated-temperature.py
Executable file
57
examples/compensated-temperature.py
Executable file
@ -0,0 +1,57 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
import time
|
||||||
|
import bme680
|
||||||
|
from subprocess import PIPE, Popen
|
||||||
|
|
||||||
|
|
||||||
|
print("""compensated-temperature.py - Use the CPU temperature to compensate temperature
|
||||||
|
readings from the BME680 sensor. Method adapted from Initial State's Enviro pHAT
|
||||||
|
review: https://medium.com/@InitialState/tutorial-review-enviro-phat-for-raspberry-pi-4cd6d8c63441
|
||||||
|
|
||||||
|
Press Ctrl+C to exit!
|
||||||
|
|
||||||
|
""")
|
||||||
|
|
||||||
|
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
|
||||||
|
# the data.
|
||||||
|
|
||||||
|
sensor.set_humidity_oversample(bme680.OS_2X)
|
||||||
|
sensor.set_pressure_oversample(bme680.OS_4X)
|
||||||
|
sensor.set_temperature_oversample(bme680.OS_8X)
|
||||||
|
sensor.set_filter(bme680.FILTER_SIZE_3)
|
||||||
|
|
||||||
|
|
||||||
|
# Gets the CPU temperature in degrees C
|
||||||
|
def get_cpu_temperature():
|
||||||
|
process = Popen(['vcgencmd', 'measure_temp'], stdout=PIPE)
|
||||||
|
output, _error = process.communicate()
|
||||||
|
return float(output[output.index(b'=') + 1:output.rindex(b"'")])
|
||||||
|
|
||||||
|
|
||||||
|
factor = 1.0 # Smaller numbers adjust temp down, vice versa
|
||||||
|
smooth_size = 10 # Dampens jitter due to rapid CPU temp changes
|
||||||
|
|
||||||
|
cpu_temps = []
|
||||||
|
|
||||||
|
while True:
|
||||||
|
if sensor.get_sensor_data():
|
||||||
|
cpu_temp = get_cpu_temperature()
|
||||||
|
cpu_temps.append(cpu_temp)
|
||||||
|
|
||||||
|
if len(cpu_temps) > smooth_size:
|
||||||
|
cpu_temps = cpu_temps[1:]
|
||||||
|
|
||||||
|
smoothed_cpu_temp = sum(cpu_temps) / float(len(cpu_temps))
|
||||||
|
raw_temp = sensor.data.temperature
|
||||||
|
comp_temp = raw_temp - ((smoothed_cpu_temp - raw_temp) / factor)
|
||||||
|
|
||||||
|
print("Compensated temperature: {:05.2f} *C".format(comp_temp))
|
||||||
|
|
||||||
|
time.sleep(1.0)
|
@ -3,19 +3,22 @@
|
|||||||
import bme680
|
import bme680
|
||||||
import time
|
import time
|
||||||
|
|
||||||
print("""Estimate indoor air quality
|
print("""indoor-air-quality.py - Estimates 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.
|
||||||
|
|
||||||
Press Ctrl+C to exit
|
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
|
# change the balance between accuracy and noise in
|
||||||
# the data.
|
# the data.
|
||||||
|
|
||||||
@ -29,7 +32,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 +45,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 +59,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 +77,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:
|
||||||
|
@ -3,21 +3,30 @@
|
|||||||
import bme680
|
import bme680
|
||||||
import time
|
import time
|
||||||
|
|
||||||
sensor = bme680.BME680()
|
print("""read-all.py - Displays 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
|
# 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 +36,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 +52,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 / 100.0,
|
||||||
|
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
|
50
examples/temperature-offset.py
Executable file
50
examples/temperature-offset.py
Executable file
@ -0,0 +1,50 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
import bme680
|
||||||
|
|
||||||
|
print("""temperature-offset.py - Displays temperature, pressure, and humidity with different offsets.
|
||||||
|
|
||||||
|
Press Ctrl+C to exit!
|
||||||
|
|
||||||
|
""")
|
||||||
|
|
||||||
|
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
|
||||||
|
# the data.
|
||||||
|
|
||||||
|
sensor.set_humidity_oversample(bme680.OS_2X)
|
||||||
|
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 / 100.0,
|
||||||
|
sensor.data.humidity)
|
||||||
|
print(output)
|
||||||
|
print('')
|
||||||
|
|
||||||
|
|
||||||
|
print('Initial readings')
|
||||||
|
display_data()
|
||||||
|
|
||||||
|
print('SET offset 4 degrees celsius')
|
||||||
|
display_data(4)
|
||||||
|
|
||||||
|
print('SET offset -1.87 degrees celsius')
|
||||||
|
display_data(-1.87)
|
||||||
|
|
||||||
|
print('SET offset -100 degrees celsius')
|
||||||
|
display_data(-100)
|
||||||
|
|
||||||
|
print('SET offset 0 degrees celsius')
|
||||||
|
display_data(0)
|
@ -1,8 +1,8 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
import bme680
|
|
||||||
import time
|
|
||||||
|
|
||||||
print("""Display Temperature, Pressure and Humidity
|
import bme680
|
||||||
|
|
||||||
|
print("""temperature-pressure-humidity.py - Displays temperature, pressure, and humidity.
|
||||||
|
|
||||||
If you don't need gas readings, then you can read temperature,
|
If you don't need gas readings, then you can read temperature,
|
||||||
pressure and humidity quickly.
|
pressure and humidity quickly.
|
||||||
@ -11,7 +11,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
|
# These oversampling settings can be tweaked to
|
||||||
# change the balance between accuracy and noise in
|
# change the balance between accuracy and noise in
|
||||||
@ -22,15 +25,15 @@ 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(
|
||||||
output = "{0:.2f} C,{1:.2f} hPa,{2:.3f} %RH".format(sensor.data.temperature, sensor.data.pressure, sensor.data.humidity)
|
sensor.data.temperature,
|
||||||
|
sensor.data.pressure / 100.0,
|
||||||
|
sensor.data.humidity)
|
||||||
print(output)
|
print(output)
|
||||||
|
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
pass
|
pass
|
||||||
|
|
22
install.sh
Executable file
22
install.sh
Executable file
@ -0,0 +1,22 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
printf "BME680 Python Library: Installer\n\n"
|
||||||
|
|
||||||
|
if [ $(id -u) -ne 0 ]; then
|
||||||
|
printf "Script must be run as root. Try 'sudo ./install.sh'\n"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd library
|
||||||
|
|
||||||
|
printf "Installing for Python 2..\n"
|
||||||
|
python setup.py install
|
||||||
|
|
||||||
|
if [ -f "/usr/bin/python3" ]; then
|
||||||
|
printf "Installing for Python 3..\n"
|
||||||
|
python3 setup.py install
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd ..
|
||||||
|
|
||||||
|
printf "Done!\n"
|
4
library/.coveragerc
Normal file
4
library/.coveragerc
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
[run]
|
||||||
|
source = bme680
|
||||||
|
omit =
|
||||||
|
.tox/*
|
@ -1,3 +1,8 @@
|
|||||||
|
1.0.5
|
||||||
|
-----
|
||||||
|
|
||||||
|
* New: set_temp_offset to calibrate temperature offset in degrees C
|
||||||
|
|
||||||
1.0.4
|
1.0.4
|
||||||
-----
|
-----
|
||||||
|
|
||||||
|
21
library/LICENSE.txt
Normal file
21
library/LICENSE.txt
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2018 Pimoroni Ltd
|
||||||
|
|
||||||
|
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 the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
@ -1,5 +1,5 @@
|
|||||||
include CHANGELOG.txt
|
include CHANGELOG.txt
|
||||||
include LICENSE.txt
|
include LICENSE.txt
|
||||||
include README.txt
|
include README.rst
|
||||||
include setup.py
|
include setup.py
|
||||||
include bme680.py
|
recursive-include bme680 *.py
|
||||||
|
80
library/README.rst
Normal file
80
library/README.rst
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
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,
|
||||||
|
pressure, humidity, and indoor air quality.
|
||||||
|
|
||||||
|
Installing
|
||||||
|
----------
|
||||||
|
|
||||||
|
Full install (recommended):
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
We've created an easy installation script that will install all
|
||||||
|
pre-requisites and get your BME680 up and running with minimal efforts.
|
||||||
|
To run it, fire up Terminal which you'll find in Menu -> Accessories ->
|
||||||
|
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:
|
||||||
|
|
||||||
|
.. code:: bash
|
||||||
|
|
||||||
|
curl https://get.pimoroni.com/bme680 | bash
|
||||||
|
|
||||||
|
Manual install:
|
||||||
|
~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Library install for Python 3:
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. code:: bash
|
||||||
|
|
||||||
|
sudo pip3 install bme680
|
||||||
|
|
||||||
|
Library install for Python 2:
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. code:: bash
|
||||||
|
|
||||||
|
sudo pip2 install bme680
|
||||||
|
|
||||||
|
Development:
|
||||||
|
~~~~~~~~~~~~
|
||||||
|
|
||||||
|
If you want to contribute, or like living on the edge of your seat by
|
||||||
|
having the latest code, you should clone this repository, ``cd`` to the
|
||||||
|
library directory, and run:
|
||||||
|
|
||||||
|
.. code:: bash
|
||||||
|
|
||||||
|
sudo python3 setup.py install
|
||||||
|
|
||||||
|
(or ``sudo python setup.py install`` whichever your primary Python
|
||||||
|
environment may be)
|
||||||
|
|
||||||
|
In all cases you will have to enable the i2c bus.
|
||||||
|
|
||||||
|
Documentation & Support
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
- Guides and tutorials - https://learn.pimoroni.com/bme680
|
||||||
|
- Get help - http://forums.pimoroni.com/c/support
|
||||||
|
|
||||||
|
.. |Build Status| image:: https://travis-ci.org/pimoroni/bme680-python.svg?branch=master
|
||||||
|
:target: https://travis-ci.org/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
|
After Width: | Height: | Size: 2.2 KiB |
@ -1,11 +1,24 @@
|
|||||||
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.4'
|
__version__ = '1.0.5'
|
||||||
|
|
||||||
|
|
||||||
|
# Export constants to global namespace
|
||||||
|
# so end-users can "from BME680 import NAME"
|
||||||
|
if hasattr(constants, '__dict__'):
|
||||||
|
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 +26,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,43 +42,50 @@ 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_temp_offset(0)
|
||||||
self.set_pressure_oversample(OS_4X)
|
self.ambient_temperature = 25 # only for gas heater temperature, nearly irrelevant
|
||||||
self.set_temperature_oversample(OS_8X)
|
|
||||||
self.set_filter(FILTER_SIZE_3)
|
|
||||||
self.set_gas_status(ENABLE_GAS_MEAS)
|
|
||||||
|
|
||||||
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):
|
||||||
|
"""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
|
||||||
|
else:
|
||||||
|
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.
|
||||||
|
|
||||||
@ -69,15 +96,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.
|
||||||
|
|
||||||
@ -85,18 +112,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.
|
||||||
|
|
||||||
@ -104,18 +131,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.
|
||||||
|
|
||||||
@ -127,40 +154,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
|
||||||
@ -170,23 +199,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.
|
||||||
@ -195,70 +224,70 @@ 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)
|
||||||
self.data.humidity = self._calc_humidity(adc_hum) / 1000.0
|
self.data.humidity = self._calc_humidity(adc_hum) / 1000.0
|
||||||
self.data.gas_resistance = self._calc_gas_resistance(adc_gas_res, gas_range)
|
self.data.gas_resistance = self._calc_gas_resistance(adc_gas_res, gas_range)
|
||||||
return True
|
return True
|
||||||
@ -266,101 +295,106 @@ 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):
|
||||||
var1 = (temperature_adc >> 3) - (self.calibration_data.par_t1 << 1)
|
"""Convert the raw temperature to degrees C using calibration_data."""
|
||||||
var2 = (var1 * self.calibration_data.par_t2) >> 11
|
var1 = (temperature_adc / 8.0) - (self.calibration_data.par_t1 * 2.0)
|
||||||
var3 = ((var1 >> 1) * (var1 >> 1)) >> 12
|
var2 = (var1 * self.calibration_data.par_t2) / 2048.0
|
||||||
var3 = ((var3) * (self.calibration_data.par_t3 << 4)) >> 14
|
var3 = ((var1 / 2.0) * (var1 / 2.0)) / 4096.0
|
||||||
|
var3 = ((var3) * (self.calibration_data.par_t3 * 16.0)) / 16384.0
|
||||||
|
|
||||||
# Save teperature data for pressure calculations
|
# Save teperature data for pressure calculations
|
||||||
self.calibration_data.t_fine = (var2 + var3)
|
self.calibration_data.t_fine = (var2 + var3) + self.offset_temp_in_t_fine
|
||||||
calc_temp = (((self.calibration_data.t_fine * 5) + 128) >> 8)
|
calc_temp = (((self.calibration_data.t_fine * 5.0) + 128.0) / 256.0)
|
||||||
|
|
||||||
return calc_temp
|
return calc_temp
|
||||||
|
|
||||||
def _calc_pressure(self, pressure_adc):
|
def _calc_pressure(self, pressure_adc):
|
||||||
var1 = ((self.calibration_data.t_fine) >> 1) - 64000
|
"""Convert the raw pressure using calibration data."""
|
||||||
var2 = ((((var1 >> 2) * (var1 >> 2)) >> 11) *
|
var1 = (self.calibration_data.t_fine / 2.0) - 64000.0
|
||||||
self.calibration_data.par_p6) >> 2
|
var2 = ((((var1 / 4.0) * (var1 / 4.0)) / 2048.0) *
|
||||||
var2 = var2 + ((var1 * self.calibration_data.par_p5) << 1)
|
self.calibration_data.par_p6) / 4.0
|
||||||
var2 = (var2 >> 2) + (self.calibration_data.par_p4 << 16)
|
var2 = var2 + ((var1 * self.calibration_data.par_p5) * 2.0)
|
||||||
var1 = (((((var1 >> 2) * (var1 >> 2)) >> 13 ) *
|
var2 = (var2 / 4.0) + (self.calibration_data.par_p4 * 65536.0)
|
||||||
((self.calibration_data.par_p3 << 5)) >> 3) +
|
var1 = (((((var1 / 4.0) * (var1 / 4.0)) / 8192.0) *
|
||||||
((self.calibration_data.par_p2 * var1) >> 1))
|
((self.calibration_data.par_p3 * 32.0)) / 8.0) +
|
||||||
var1 = var1 >> 18
|
((self.calibration_data.par_p2 * var1) / 2.0))
|
||||||
|
var1 = var1 / 262144.0
|
||||||
|
|
||||||
var1 = ((32768 + var1) * self.calibration_data.par_p1) >> 15
|
var1 = ((32768.0 + var1) * self.calibration_data.par_p1) / 32768.0
|
||||||
calc_pressure = 1048576 - pressure_adc
|
calc_pressure = 1048576.0 - pressure_adc
|
||||||
calc_pressure = ((calc_pressure - (var2 >> 12)) * (3125))
|
calc_pressure = ((calc_pressure - (var2 / 4096.0)) * (3125.0))
|
||||||
|
|
||||||
if calc_pressure >= (1 << 31):
|
if calc_pressure >= (1 << 31):
|
||||||
calc_pressure = ((calc_pressure // var1) << 1)
|
calc_pressure = ((calc_pressure / var1) * 2.0)
|
||||||
else:
|
else:
|
||||||
calc_pressure = ((calc_pressure << 1) // var1)
|
calc_pressure = ((calc_pressure * 2.0) / var1)
|
||||||
|
|
||||||
var1 = (self.calibration_data.par_p9 * (((calc_pressure >> 3) *
|
var1 = (self.calibration_data.par_p9 * (((calc_pressure / 8.0) *
|
||||||
(calc_pressure >> 3)) >> 13)) >> 12
|
(calc_pressure / 8.0)) / 8192.0)) / 4096.0
|
||||||
var2 = ((calc_pressure >> 2) *
|
var2 = ((calc_pressure / 4.0) *
|
||||||
self.calibration_data.par_p8) >> 13
|
self.calibration_data.par_p8) / 8192.0
|
||||||
var3 = ((calc_pressure >> 8) * (calc_pressure >> 8) *
|
var3 = ((calc_pressure / 256.0) * (calc_pressure / 256.0) *
|
||||||
(calc_pressure >> 8) *
|
(calc_pressure / 256.0) *
|
||||||
self.calibration_data.par_p10) >> 17
|
self.calibration_data.par_p10) / 131072.0
|
||||||
|
|
||||||
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 * 128.0)) / 16.0)
|
||||||
|
|
||||||
return calc_pressure
|
return calc_pressure
|
||||||
|
|
||||||
def _calc_humidity(self, humidity_adc):
|
def _calc_humidity(self, humidity_adc):
|
||||||
temp_scaled = ((self.calibration_data.t_fine * 5) + 128) >> 8
|
"""Convert the raw humidity using calibration data."""
|
||||||
var1 = (humidity_adc - ((self.calibration_data.par_h1 * 16))) \
|
temp_scaled = ((self.calibration_data.t_fine * 5.0) + 128.0) / 256.0
|
||||||
- (((temp_scaled * self.calibration_data.par_h3) // (100)) >> 1)
|
var1 = (humidity_adc - ((self.calibration_data.par_h1 * 16.0))) -\
|
||||||
var2 = (self.calibration_data.par_h2
|
(((temp_scaled * self.calibration_data.par_h3) / (100.0)) / 2.0)
|
||||||
* (((temp_scaled * self.calibration_data.par_h4) // (100))
|
var2 = (self.calibration_data.par_h2 *
|
||||||
+ (((temp_scaled * ((temp_scaled * self.calibration_data.par_h5) // (100))) >> 6)
|
(((temp_scaled * self.calibration_data.par_h4) / (100.0)) +
|
||||||
// (100)) + (1 * 16384))) >> 10
|
(((temp_scaled * ((temp_scaled * self.calibration_data.par_h5) / (100.0))) / 64.0) /
|
||||||
|
(100.0)) + (1.0 * 16384.0))) / 1024.0
|
||||||
var3 = var1 * var2
|
var3 = var1 * var2
|
||||||
var4 = self.calibration_data.par_h6 << 7
|
var4 = self.calibration_data.par_h6 * 128.0
|
||||||
var4 = ((var4) + ((temp_scaled * self.calibration_data.par_h7) // (100))) >> 4
|
var4 = ((var4) + ((temp_scaled * self.calibration_data.par_h7) / (100.0))) / 16.0
|
||||||
var5 = ((var3 >> 14) * (var3 >> 14)) >> 10
|
var5 = ((var3 / 16384.0) * (var3 / 16384.0)) / 1024.0
|
||||||
var6 = (var4 * var5) >> 1
|
var6 = (var4 * var5) / 2.0
|
||||||
calc_hum = (((var3 + var6) >> 10) * (1000)) >> 12
|
calc_hum = (((var3 + var6) / 1024.0) * (1000.0)) / 4096.0
|
||||||
|
|
||||||
return min(max(calc_hum,0),100000)
|
return min(max(calc_hum, 0.0), 100000.0)
|
||||||
|
|
||||||
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)
|
||||||
@ -373,6 +407,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,5 +1,7 @@
|
|||||||
|
"""BME680 constants, structures and utilities."""
|
||||||
|
|
||||||
# BME680 General config
|
# BME680 General config
|
||||||
POLL_PERIOD_MS = 10
|
POLL_PERIOD_MS = 50
|
||||||
|
|
||||||
# BME680 I2C addresses
|
# BME680 I2C addresses
|
||||||
I2C_ADDR_PRIMARY = 0x76
|
I2C_ADDR_PRIMARY = 0x76
|
||||||
@ -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
|
||||||
|
15
library/setup.cfg
Normal file
15
library/setup.cfg
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
[flake8]
|
||||||
|
exclude =
|
||||||
|
test.py
|
||||||
|
.tox
|
||||||
|
.eggs
|
||||||
|
.git
|
||||||
|
__pycache__
|
||||||
|
build
|
||||||
|
dist
|
||||||
|
tests
|
||||||
|
ignore =
|
||||||
|
E501
|
||||||
|
F403
|
||||||
|
F405
|
||||||
|
W504
|
@ -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.4',
|
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.txt').read() + 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=['smbus'] # preferably: install `python3-smbus` instead of relying on this
|
||||||
)
|
)
|
||||||
|
101
library/tests/conftest.py
Normal file
101
library/tests/conftest.py
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
import sys
|
||||||
|
import mock
|
||||||
|
import pytest
|
||||||
|
import bme680
|
||||||
|
from bme680.constants import CalibrationData
|
||||||
|
|
||||||
|
|
||||||
|
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]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope='function', autouse=False)
|
||||||
|
def smbus_notpresent():
|
||||||
|
"""Mock smbus module."""
|
||||||
|
smbus = mock.MagicMock()
|
||||||
|
smbus.SMBus = MockSMBus
|
||||||
|
sys.modules['smbus'] = smbus
|
||||||
|
yield smbus
|
||||||
|
del sys.modules['smbus']
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope='function', autouse=False)
|
||||||
|
def smbus():
|
||||||
|
"""Mock smbus module."""
|
||||||
|
smbus = mock.MagicMock()
|
||||||
|
smbus.SMBus = MockSMBusPresent
|
||||||
|
sys.modules['smbus'] = smbus
|
||||||
|
yield smbus
|
||||||
|
del sys.modules['smbus']
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope='function', autouse=False)
|
||||||
|
def calibration():
|
||||||
|
"""Mock bme680 calibration."""
|
||||||
|
calibration = CalibrationData()
|
||||||
|
# Dump of calibration data borrowed from:
|
||||||
|
# https://github.com/pimoroni/bme680-python/issues/11
|
||||||
|
data = {
|
||||||
|
'par_gh1': -30,
|
||||||
|
'par_gh2': -24754,
|
||||||
|
'par_gh3': 18,
|
||||||
|
'par_h1': 676,
|
||||||
|
'par_h2': 1029,
|
||||||
|
'par_h3': 0,
|
||||||
|
'par_h4': 45,
|
||||||
|
'par_h5': 20,
|
||||||
|
'par_h6': 120,
|
||||||
|
'par_h7': -100,
|
||||||
|
'par_p1': 36673,
|
||||||
|
'par_p10': 30,
|
||||||
|
'par_p2': -10515,
|
||||||
|
'par_p3': 88,
|
||||||
|
'par_p4': 7310,
|
||||||
|
'par_p5': -129,
|
||||||
|
'par_p6': 30,
|
||||||
|
'par_p7': 46,
|
||||||
|
'par_p8': -3177,
|
||||||
|
'par_p9': -2379,
|
||||||
|
'par_t1': 26041,
|
||||||
|
'par_t2': 26469,
|
||||||
|
'par_t3': 3,
|
||||||
|
'range_sw_err': 0,
|
||||||
|
'res_heat_range': 1,
|
||||||
|
'res_heat_val': 48,
|
||||||
|
't_fine': 136667
|
||||||
|
}
|
||||||
|
for k, v in data.items():
|
||||||
|
setattr(calibration, k, v)
|
||||||
|
return calibration
|
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
|
42
library/tests/test_compensation.py
Normal file
42
library/tests/test_compensation.py
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import pytest
|
||||||
|
import bme680
|
||||||
|
|
||||||
|
|
||||||
|
def test_calc_temperature(smbus, calibration):
|
||||||
|
"""Validate temperature calculation against mock calibration data."""
|
||||||
|
sensor = bme680.BME680()
|
||||||
|
sensor.calibration_data = calibration
|
||||||
|
assert sensor._calc_temperature(501240) == pytest.approx(2669.81, 0.01)
|
||||||
|
assert sensor.calibration_data.t_fine == pytest.approx(136668.78, 0.01)
|
||||||
|
|
||||||
|
|
||||||
|
def test_calc_pressure(smbus, calibration):
|
||||||
|
"""Validate pressure calculation against mock calibration data."""
|
||||||
|
sensor = bme680.BME680()
|
||||||
|
sensor.calibration_data = calibration
|
||||||
|
sensor._calc_temperature(501240)
|
||||||
|
assert sensor._calc_pressure(353485) == pytest.approx(98716.92, 0.01)
|
||||||
|
|
||||||
|
|
||||||
|
def test_calc_humidity(smbus, calibration):
|
||||||
|
"""Validate humidity calculation against mock calibration data."""
|
||||||
|
sensor = bme680.BME680()
|
||||||
|
sensor.calibration_data = calibration
|
||||||
|
sensor._calc_temperature(501240)
|
||||||
|
assert sensor._calc_humidity(19019) == pytest.approx(42410.28, 0.01)
|
||||||
|
|
||||||
|
|
||||||
|
def test_calc_gas_resistance(smbus, calibration):
|
||||||
|
"""Validate gas calculation against mock calibration data."""
|
||||||
|
sensor = bme680.BME680()
|
||||||
|
sensor.calibration_data = calibration
|
||||||
|
assert int(sensor._calc_gas_resistance(0, 0)) == 12946860
|
||||||
|
|
||||||
|
|
||||||
|
def test_temp_offset(smbus, calibration):
|
||||||
|
"""Validate temperature calculation with offset against mock calibration data."""
|
||||||
|
sensor = bme680.BME680()
|
||||||
|
sensor.calibration_data = calibration
|
||||||
|
sensor.set_temp_offset(1.99)
|
||||||
|
assert sensor._calc_temperature(501240) == pytest.approx(2868.30, 0.01)
|
||||||
|
assert sensor.calibration_data.t_fine == pytest.approx(146831.78, 0.01)
|
13
library/tests/test_setup.py
Normal file
13
library/tests/test_setup.py
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import pytest
|
||||||
|
import bme680
|
||||||
|
|
||||||
|
|
||||||
|
def test_setup_not_present(smbus_notpresent):
|
||||||
|
"""Mock the adbsence of a BME680 and test initialisation."""
|
||||||
|
with pytest.raises(RuntimeError):
|
||||||
|
sensor = bme680.BME680() # noqa F841
|
||||||
|
|
||||||
|
|
||||||
|
def test_setup_mock_present(smbus):
|
||||||
|
"""Mock the presence of a BME680 and test initialisation."""
|
||||||
|
sensor = bme680.BME680() # noqa F841
|
25
library/tox.ini
Normal file
25
library/tox.ini
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
[tox]
|
||||||
|
envlist = py{37,38,39},qa
|
||||||
|
skip_missing_interpreters = True
|
||||||
|
|
||||||
|
[testenv]
|
||||||
|
commands =
|
||||||
|
python setup.py install
|
||||||
|
coverage run -m pytest -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
|
@ -1,3 +1,9 @@
|
|||||||
|
bme680 (1.0.5) stable; urgency=low
|
||||||
|
|
||||||
|
* New: set_temp_offset to calibrate temperature offset in degrees C
|
||||||
|
|
||||||
|
-- Phil Howard <phil@pimoroni.com> Fri, 01 Jun 2018 00:00:00 +0000
|
||||||
|
|
||||||
bme680 (1.0.4) stable; urgency=low
|
bme680 (1.0.4) stable; urgency=low
|
||||||
|
|
||||||
* Fix to range_sw_err for extremely high gas readings
|
* Fix to range_sw_err for extremely high gas readings
|
||||||
|
@ -1,3 +1,9 @@
|
|||||||
|
bme680 (1.0.5) stable; urgency=low
|
||||||
|
|
||||||
|
* New: set_temp_offset to calibrate temperature offset in degrees C
|
||||||
|
|
||||||
|
-- Phil Howard <phil@pimoroni.com> Fri, 01 Jun 2018 00:00:00 +0000
|
||||||
|
|
||||||
bme680 (1.0.4) stable; urgency=low
|
bme680 (1.0.4) stable; urgency=low
|
||||||
|
|
||||||
* Fix to range_sw_err for extremely high gas readings
|
* Fix to range_sw_err for extremely high gas readings
|
||||||
|
24
uninstall.sh
Executable file
24
uninstall.sh
Executable file
@ -0,0 +1,24 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
PACKAGE="bme680"
|
||||||
|
|
||||||
|
printf "BME680 Python Library: Uninstaller\n\n"
|
||||||
|
|
||||||
|
if [ $(id -u) -ne 0 ]; then
|
||||||
|
printf "Script must be run as root. Try 'sudo ./uninstall.sh'\n"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd library
|
||||||
|
|
||||||
|
printf "Unnstalling for Python 2..\n"
|
||||||
|
pip uninstall $PACKAGE
|
||||||
|
|
||||||
|
if [ -f "/usr/bin/pip3" ]; then
|
||||||
|
printf "Uninstalling for Python 3..\n"
|
||||||
|
pip3 uninstall $PACKAGE
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd ..
|
||||||
|
|
||||||
|
printf "Done!\n"
|
Loading…
Reference in New Issue
Block a user