This commit is contained in:
cn 2016-05-30 22:13:05 +02:00
parent 1750cff8b3
commit d6d215d1f9
6 changed files with 305 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
*.pyc

20
README.md Normal file
View File

@ -0,0 +1,20 @@
# python-bme280
A Python library for accessing the [BME280 combined humidity and pressure](https://ae-bst.resource.bosch.com/media/_tech/media/datasheets/BST-BME280_DS001-11.pdf) from Bosch via `python-smbus` using the I2C interface.
Default settings are suitable for Raspberry Pi 2 and 3 and was successfully tested using a [breakout](https://github.com/watterott/BME280-Breakout).
I created this Python library in style of e.g. [python-veml6070](https://github.com/cmur2/python-veml6070) or [python-tsl2591](https://github.com/maxlklaxl/python-tsl2591) since I found only [python scripts](https://github.com/SWITCHSCIENCE/BME280) with limited functionality and minor bugs and the official driver from Bosch is only in C(++).
Although, it is heavily based on existing code from Bosch translated to Python and from [SWITCHSCIENCE](https://github.com/SWITCHSCIENCE/BME280).
## Usage
Consult the data sheet and see [demo.py](demo.py) for clues how to use this library.
Not all functions of the chip are supported since I focused on the *forced mode* but data readout and parameter setting should work in *normal mode*, too. Please send pull requests for improvements and bug fixes!
## License
Python files in this repository are released under the [MIT license](LICENSE) except those parts from other sources which are indicated where appropriate in the files.

4
bme280/__init__.py Normal file
View File

@ -0,0 +1,4 @@
from bme280 import Bme280
#from bme280 import HO_SKIPPED, HO_1, HO_2, HO_4, HO_8, HO_16, PO_SKIPPED, PO_1, PO_2, PO_4, PO_8, PO_16, TO_SKIPPED, TO_1, TO_2, TO_4, TO_8, TO_16
#from bme280 import MODE_SLEEP, MODE_FORCED, MODE_NORMAL

259
bme280/bme280.py Normal file
View File

@ -0,0 +1,259 @@
import smbus
import time
ADDR=0x76 # 7bit address of the BME280 for SDO=0, else 0x77
REGISTER_ID=0xD0
REGISTER_RESET=0xE0
REGISTER_CTRL_HUM=0xF2
REGISTER_STATUS=0xF3
REGISTER_CTRL_MEAS=0xF4
REGISTER_CONFIG=0xF5
HO_SKIPPED=0x00
HO_1=0x01
HO_2=0x02
HO_4=0x03
HO_8=0x04
HO_16=0x05 # and all higher
PO_SKIPPED=0x00
PO_1=0x01
PO_2=0x02
PO_4=0x03
PO_8=0x04
PO_16=0x05 # and all higher
TO_SKIPPED=0x00
TO_1=0x01
TO_2=0x02
TO_4=0x03
TO_8=0x04
TO_16=0x05 # and all higher
MODE_SLEEP=0x00
MODE_FORCED=0x01 # and 0x02
MODE_NORMAL=0x03
TSTANDBY_0_5=0x00
TSTANDBY_62_5=0x01
TSTANDBY_125=0x02
TSTANDBY_250=0x03
TSTANDBY_500=0x04
TSTANDBY_1000=0x05
TSTANDBY_10=0x06
TSTANDBY_20=0x07
FILTER_OFF=0x00
FILTER_2=0x01
FILTER_4=0x02
FILTER_8=0x03
FILTER_16=0x04 # and all higher
class Bme280:
def __init__(self, i2c_bus=1, sensor_address=ADDR):
self.bus = smbus.SMBus(i2c_bus)
self.sensor_address = sensor_address
self.ho = HO_1
self.po = PO_1
self.to = TO_1
self.mode = MODE_SLEEP
self.tstandy = TSTANDBY_1000
self.filter = FILTER_OFF
self.read_calibration_parameters()
# initialize once
self.bus.write_byte_data(self.sensor_address, REGISTER_CTRL_HUM, self.get_reg_ctrl_hum())
self.bus.write_byte_data(self.sensor_address, REGISTER_CTRL_MEAS, self.get_reg_ctrl_meas())
self.bus.write_byte_data(self.sensor_address, REGISTER_CONFIG, self.get_reg_config())
def get_chip_id(self):
return self.bus.read_byte_data(self.sensor_address, REGISTER_ID)
def reset(self):
self.bus.write_byte_data(self.sensor_address, REGISTER_RESET, 0xB6)
def is_status_measuring(self):
return (self.bus.read_byte_data(self.sensor_address, REGISTER_STATUS) & 0x08) != 0x00
def is_status_image_register_updating(self):
return (self.bus.read_byte_data(self.sensor_address, REGISTER_STATUS) & 0x01) != 0x00
def set_humidity_oversampling(self, ho):
self.ho = ho
self.bus.write_byte_data(self.sensor_address, REGISTER_CTRL_HUM, self.get_reg_ctrl_hum())
# flush (unchanged) CTRL_MEAS to make CTRL_HUM effective!
self.bus.write_byte_data(self.sensor_address, REGISTER_CTRL_MEAS, self.get_reg_ctrl_meas())
def get_humidity_oversampling(self):
return self.ho
def set_temperature_oversampling(self, to):
self.to = to
self.bus.write_byte_data(self.sensor_address, REGISTER_CTRL_MEAS, self.get_reg_ctrl_meas())
def get_temperature_oversampling(self):
return self.to
def set_pressure_oversampling(self, po):
self.po = po
self.bus.write_byte_data(self.sensor_address, REGISTER_CTRL_MEAS, self.get_reg_ctrl_meas())
def get_pressure_oversampling(self):
return self.po
def set_mode(self, mode):
self.mode = mode
self.bus.write_byte_data(self.sensor_address, REGISTER_CTRL_MEAS, self.get_reg_ctrl_meas())
def get_mode(self):
return self.mode
def set_tstandy(self, tstandy):
self.tstandy = tstandy
self.bus.write_byte_data(self.sensor_address, REGISTER_CONFIG, self.get_reg_config())
def get_tstandy(self):
return self.tstandy
def set_filter(self, fil):
self.filter = fil
self.bus.write_byte_data(self.sensor_address, REGISTER_CONFIG, self.get_reg_config())
def get_filter(self):
return self.filter
def get_data(self):
if self.get_mode() == MODE_FORCED:
t_measure_max = 1.25 + (2.3 * self.to) + (2.3 * self.po + 0.575) + (2.3 * self.ho + 0.575)
time.sleep(t_measure_max/1000.0)
data = []
for i in range(0xF7, 0xF7+8):
data.append(self.bus.read_byte_data(self.sensor_address, i))
pressure_raw = (data[0] << 12) | (data[1] << 4) | (data[2] >> 4)
temperature_raw = (data[3] << 12) | (data[4] << 4) | (data[5] >> 4)
humidity_raw = (data[6] << 8) | data[7]
t_fine = self.calc_t_fine(temperature_raw)
t = self.calc_compensated_temperature(t_fine)
p = self.calc_compensated_pressure(t_fine, pressure_raw)
h = self.calc_compensated_humidity(t_fine, humidity_raw)
if self.get_mode() == MODE_FORCED:
# chip returns to sleep after data readout automatically, mirror it
self.mode = MODE_SLEEP
return (t, p, h)
def get_reg_ctrl_hum(self):
"""
returns the bit pattern for CTRL_HUM corresponding to the desired state of this class
"""
return (self.ho & 0x07)
def get_reg_ctrl_meas(self):
"""
returns the bit pattern for CTRL_MEAS corresponding to the desired state of this class
"""
return ((self.to & 0x07) << 5) | ((self.po & 0x07) << 2) | self.mode
def get_reg_config(self):
"""
returns the bit pattern for CONFIG corresponding to the desired state of this class
"""
# SPI permanently disabled
return ((self.tstandy & 0x07) << 5) | ((self.filter & 0x07) << 2) | 0x00
# Bug-fixed code, originally from https://github.com/SWITCHSCIENCE/BME280
def read_calibration_parameters(self):
# read all calibration registers from chip NVM
calibration_regs = []
for i in range(0x88, 0x88+24):
calibration_regs.append(self.bus.read_byte_data(self.sensor_address, i))
calibration_regs.append(self.bus.read_byte_data(self.sensor_address, 0xA1))
for i in range(0xE1, 0xE1+7):
calibration_regs.append(self.bus.read_byte_data(self.sensor_address, i))
# reorganize 8-bit words into compensation words (without correct sign)
self.digT = []
self.digT.append((calibration_regs[1] << 8) | calibration_regs[0])
self.digT.append((calibration_regs[3] << 8) | calibration_regs[2])
self.digT.append((calibration_regs[5] << 8) | calibration_regs[4])
self.digP = []
self.digP.append((calibration_regs[7] << 8) | calibration_regs[6])
self.digP.append((calibration_regs[9] << 8) | calibration_regs[8])
self.digP.append((calibration_regs[11]<< 8) | calibration_regs[10])
self.digP.append((calibration_regs[13]<< 8) | calibration_regs[12])
self.digP.append((calibration_regs[15]<< 8) | calibration_regs[14])
self.digP.append((calibration_regs[17]<< 8) | calibration_regs[16])
self.digP.append((calibration_regs[19]<< 8) | calibration_regs[18])
self.digP.append((calibration_regs[21]<< 8) | calibration_regs[20])
self.digP.append((calibration_regs[23]<< 8) | calibration_regs[22])
self.digH = []
self.digH.append( calibration_regs[24] )
self.digH.append((calibration_regs[26]<< 8) | calibration_regs[25])
self.digH.append( calibration_regs[27] )
self.digH.append((calibration_regs[28]<< 4) | (0x0F & calibration_regs[29]))
self.digH.append((calibration_regs[30]<< 4) | ((calibration_regs[29] >> 4) & 0x0F))
self.digH.append( calibration_regs[31] )
# fix sign for integers in two's complement
for i in [1,2]:
if self.digT[i] & 0x8000:
self.digT[i] = (-self.digT[i] ^ 0xFFFF) + 1
for i in [1,2,3,4,5,6,7,8]:
if self.digP[i] & 0x8000:
self.digP[i] = (-self.digP[i] ^ 0xFFFF) + 1
for i in [1]:
if self.digH[i] & 0x8000:
self.digH[i] = (-self.digH[i] ^ 0xFFFF) + 1
for i in [3,4]:
if self.digH[i] & 0x0800:
self.digH[i] = (-self.digH[i] ^ 0x0FFF) + 1
for i in [5]:
if self.digH[i] & 0x0080:
self.digH[i] = (-self.digH[i] ^ 0x00FF) + 1
# Code from Bosch datasheet translated to Python
def calc_t_fine(self, adc_T):
var1 = (adc_T / 16384.0 - self.digT[0] / 1024.0) * self.digT[1]
var2 = (adc_T / 131072.0 - self.digT[0] / 8192.0) * (adc_T / 131072.0 - self.digT[0] / 8192.0) * self.digT[2]
return var1 + var2
def calc_compensated_temperature(self, t_fine):
return t_fine / 5120.0
def calc_compensated_pressure(self, t_fine, adc_P):
var1 = (t_fine/2.0) - 64000.0
var2 = var1 * var1 * (self.digP[5]) / 32768.0
var2 = var2 + var1 * (self.digP[4]) * 2.0
var2 = (var2/4.0)+(self.digP[3] * 65536.0)
var1 = (self.digP[2] * var1 * var1 / 524288.0 + self.digP[1] * var1) / 524288.0
var1 = (1.0 + var1 / 32768.0)*self.digP[0]
if var1 == 0.0:
return 0 # avoid exception caused by division by zero
p = 1048576.0 - adc_P
p = (p - (var2 / 4096.0)) * 6250.0 / var1
var1 = self.digP[8] * p * p / 2147483648.0
var2 = p * self.digP[7] / 32768.0
return p + (var1 + var2 + self.digP[6]) / 16.0
def calc_compensated_humidity(self, t_fine, adc_H):
var_H = t_fine - 76800.0
var_H = (adc_H - (self.digH[3] * 64.0 + self.digH[4] / 16384.0 * var_H)) * (self.digH[1] / 65536.0 * (1.0 + self.digH[5] / 67108864.0 * var_H * (1.0 + self.digH[2] / 67108864.0 * var_H)))
var_H = var_H * (1.0 - self.digH[0] * var_H / 524288.0)
if var_H > 100.0:
var_H = 100.0
elif var_H < 0.0:
var_H = 0.0
return var_H

10
demo.py Normal file
View File

@ -0,0 +1,10 @@
import bme280
if __name__ == '__main__':
bme = bme280.Bme280()
bme.set_mode(bme280.MODE_FORCED)
t, p, h = bme.get_data()
print "Temperature: %f °C" % t
print "Pressure: %f P" % p
print "Humidity: %f %" % h

11
setup.py Normal file
View File

@ -0,0 +1,11 @@
from setuptools import setup
setup(name='bme280',
version='1.0',
url='http://github.com/cmur2/python-bme280',
author='Christian Nicolai',
description='A python library for accessing the BME280 combined humidity and pressure sensor from Bosch.',
packages=['bme280'],
long_description=open('README.md').read(),
requires=['python (>= 2.7)', 'smbus (>= 0.4.1)'],
install_requires=['smbus-cffi'])