Skip to content

Commit db17f7d

Browse files
committed
Add libraries and test program
1 parent a0de5c0 commit db17f7d

9 files changed

Lines changed: 992 additions & 1 deletion

File tree

README.md

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,21 @@
11
# dusty-python
22

3-
Python program for the luftdaten.info sensor network.
3+
Python 3 program for the luftdaten.info sensor network.
4+
5+
## Supported Hardware
6+
7+
* [Nova Fitness SDS011](http://aqicn.org/sensor/sds011/) or compatible connected via `ttyUSB` for dust
8+
* [Bosch BME 2280](https://www.bosch-sensortec.com/bst/products/all_products/bme280) connected via I²C for temperature, humidity and pressure
9+
10+
## Used libraries
11+
12+
* https://gitlab.com/frankrich/sds011_particle_sensor
13+
* https://github.com/adafruit/Adafruit_Python_BME280
14+
15+
## Dependencies
16+
17+
apt install python3-numpy python3-requests
18+
19+
## To-Do
20+
21+
* submit data to luftdaten.info

bme280/Adafruit_BME280.py

Lines changed: 249 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,249 @@
1+
# Copyright (c) 2014 Adafruit Industries
2+
# Author: Tony DiCola
3+
#
4+
# Based on the BMP280 driver with BME280 changes provided by
5+
# David J Taylor, Edinburgh (www.satsignal.eu)
6+
#
7+
# Permission is hereby granted, free of charge, to any person obtaining a copy
8+
# of this software and associated documentation files (the "Software"), to deal
9+
# in the Software without restriction, including without limitation the rights
10+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
# copies of the Software, and to permit persons to whom the Software is
12+
# furnished to do so, subject to the following conditions:
13+
#
14+
# The above copyright notice and this permission notice shall be included in
15+
# all copies or substantial portions of the Software.
16+
#
17+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23+
# THE SOFTWARE.
24+
import logging
25+
import time
26+
27+
28+
# BME280 default address.
29+
BME280_I2CADDR = 0x77
30+
31+
# Operating Modes
32+
BME280_OSAMPLE_1 = 1
33+
BME280_OSAMPLE_2 = 2
34+
BME280_OSAMPLE_4 = 3
35+
BME280_OSAMPLE_8 = 4
36+
BME280_OSAMPLE_16 = 5
37+
38+
# Standby Settings
39+
BME280_STANDBY_0p5 = 0
40+
BME280_STANDBY_62p5 = 1
41+
BME280_STANDBY_125 = 2
42+
BME280_STANDBY_250 = 3
43+
BME280_STANDBY_500 = 4
44+
BME280_STANDBY_1000 = 5
45+
BME280_STANDBY_10 = 6
46+
BME280_STANDBY_20 = 7
47+
48+
# Filter Settings
49+
BME280_FILTER_off = 0
50+
BME280_FILTER_2 = 1
51+
BME280_FILTER_4 = 2
52+
BME280_FILTER_8 = 3
53+
BME280_FILTER_16 = 4
54+
55+
# BME280 Registers
56+
57+
BME280_REGISTER_DIG_T1 = 0x88 # Trimming parameter registers
58+
BME280_REGISTER_DIG_T2 = 0x8A
59+
BME280_REGISTER_DIG_T3 = 0x8C
60+
61+
BME280_REGISTER_DIG_P1 = 0x8E
62+
BME280_REGISTER_DIG_P2 = 0x90
63+
BME280_REGISTER_DIG_P3 = 0x92
64+
BME280_REGISTER_DIG_P4 = 0x94
65+
BME280_REGISTER_DIG_P5 = 0x96
66+
BME280_REGISTER_DIG_P6 = 0x98
67+
BME280_REGISTER_DIG_P7 = 0x9A
68+
BME280_REGISTER_DIG_P8 = 0x9C
69+
BME280_REGISTER_DIG_P9 = 0x9E
70+
71+
BME280_REGISTER_DIG_H1 = 0xA1
72+
BME280_REGISTER_DIG_H2 = 0xE1
73+
BME280_REGISTER_DIG_H3 = 0xE3
74+
BME280_REGISTER_DIG_H4 = 0xE4
75+
BME280_REGISTER_DIG_H5 = 0xE5
76+
BME280_REGISTER_DIG_H6 = 0xE6
77+
BME280_REGISTER_DIG_H7 = 0xE7
78+
79+
BME280_REGISTER_CHIPID = 0xD0
80+
BME280_REGISTER_VERSION = 0xD1
81+
BME280_REGISTER_SOFTRESET = 0xE0
82+
83+
BME280_REGISTER_STATUS = 0xF3
84+
BME280_REGISTER_CONTROL_HUM = 0xF2
85+
BME280_REGISTER_CONTROL = 0xF4
86+
BME280_REGISTER_CONFIG = 0xF5
87+
BME280_REGISTER_DATA = 0xF7
88+
89+
90+
class BME280(object):
91+
def __init__(self, t_mode=BME280_OSAMPLE_1, p_mode=BME280_OSAMPLE_1, h_mode=BME280_OSAMPLE_1,
92+
standby=BME280_STANDBY_250, filter=BME280_FILTER_off, address=BME280_I2CADDR, i2c=None,
93+
**kwargs):
94+
self._logger = logging.getLogger('Adafruit_BMP.BMP085')
95+
# Check that t_mode is valid.
96+
if t_mode not in [BME280_OSAMPLE_1, BME280_OSAMPLE_2, BME280_OSAMPLE_4,
97+
BME280_OSAMPLE_8, BME280_OSAMPLE_16]:
98+
raise ValueError(
99+
'Unexpected t_mode value {0}.'.format(t_mode))
100+
self._t_mode = t_mode
101+
# Check that p_mode is valid.
102+
if p_mode not in [BME280_OSAMPLE_1, BME280_OSAMPLE_2, BME280_OSAMPLE_4,
103+
BME280_OSAMPLE_8, BME280_OSAMPLE_16]:
104+
raise ValueError(
105+
'Unexpected p_mode value {0}.'.format(p_mode))
106+
self._p_mode = p_mode
107+
# Check that h_mode is valid.
108+
if h_mode not in [BME280_OSAMPLE_1, BME280_OSAMPLE_2, BME280_OSAMPLE_4,
109+
BME280_OSAMPLE_8, BME280_OSAMPLE_16]:
110+
raise ValueError(
111+
'Unexpected h_mode value {0}.'.format(h_mode))
112+
self._h_mode = h_mode
113+
# Check that standby is valid.
114+
if standby not in [BME280_STANDBY_0p5, BME280_STANDBY_62p5, BME280_STANDBY_125, BME280_STANDBY_250,
115+
BME280_STANDBY_500, BME280_STANDBY_1000, BME280_STANDBY_10, BME280_STANDBY_20]:
116+
raise ValueError(
117+
'Unexpected standby value {0}.'.format(standby))
118+
self._standby = standby
119+
# Check that filter is valid.
120+
if filter not in [BME280_FILTER_off, BME280_FILTER_2, BME280_FILTER_4, BME280_FILTER_8, BME280_FILTER_16]:
121+
raise ValueError(
122+
'Unexpected filter value {0}.'.format(filter))
123+
self._filter = filter
124+
# Create I2C device.
125+
if i2c is None:
126+
import Adafruit_GPIO.I2C as I2C
127+
i2c = I2C
128+
self._device = i2c.get_i2c_device(address, **kwargs)
129+
# Load calibration values.
130+
self._load_calibration()
131+
self._device.write8(BME280_REGISTER_CONTROL, 0x24) # Sleep mode
132+
time.sleep(0.002)
133+
self._device.write8(BME280_REGISTER_CONFIG, ((standby << 5) | (filter << 2)))
134+
time.sleep(0.002)
135+
self._device.write8(BME280_REGISTER_CONTROL_HUM, h_mode) # Set Humidity Oversample
136+
self._device.write8(BME280_REGISTER_CONTROL, ((t_mode << 5) | (p_mode << 2) | 3)) # Set Temp/Pressure Oversample and enter Normal mode
137+
self.t_fine = 0.0
138+
139+
def _load_calibration(self):
140+
141+
self.dig_T1 = self._device.readU16LE(BME280_REGISTER_DIG_T1)
142+
self.dig_T2 = self._device.readS16LE(BME280_REGISTER_DIG_T2)
143+
self.dig_T3 = self._device.readS16LE(BME280_REGISTER_DIG_T3)
144+
145+
self.dig_P1 = self._device.readU16LE(BME280_REGISTER_DIG_P1)
146+
self.dig_P2 = self._device.readS16LE(BME280_REGISTER_DIG_P2)
147+
self.dig_P3 = self._device.readS16LE(BME280_REGISTER_DIG_P3)
148+
self.dig_P4 = self._device.readS16LE(BME280_REGISTER_DIG_P4)
149+
self.dig_P5 = self._device.readS16LE(BME280_REGISTER_DIG_P5)
150+
self.dig_P6 = self._device.readS16LE(BME280_REGISTER_DIG_P6)
151+
self.dig_P7 = self._device.readS16LE(BME280_REGISTER_DIG_P7)
152+
self.dig_P8 = self._device.readS16LE(BME280_REGISTER_DIG_P8)
153+
self.dig_P9 = self._device.readS16LE(BME280_REGISTER_DIG_P9)
154+
155+
self.dig_H1 = self._device.readU8(BME280_REGISTER_DIG_H1)
156+
self.dig_H2 = self._device.readS16LE(BME280_REGISTER_DIG_H2)
157+
self.dig_H3 = self._device.readU8(BME280_REGISTER_DIG_H3)
158+
self.dig_H6 = self._device.readS8(BME280_REGISTER_DIG_H7)
159+
160+
h4 = self._device.readS8(BME280_REGISTER_DIG_H4)
161+
h4 = (h4 << 4)
162+
self.dig_H4 = h4 | (self._device.readU8(BME280_REGISTER_DIG_H5) & 0x0F)
163+
164+
h5 = self._device.readS8(BME280_REGISTER_DIG_H6)
165+
h5 = (h5 << 4)
166+
self.dig_H5 = h5 | (
167+
self._device.readU8(BME280_REGISTER_DIG_H5) >> 4 & 0x0F)
168+
169+
'''
170+
print '0xE4 = {0:2x}'.format (self._device.readU8 (BME280_REGISTER_DIG_H4))
171+
print '0xE5 = {0:2x}'.format (self._device.readU8 (BME280_REGISTER_DIG_H5))
172+
print '0xE6 = {0:2x}'.format (self._device.readU8 (BME280_REGISTER_DIG_H6))
173+
174+
print 'dig_H1 = {0:d}'.format (self.dig_H1)
175+
print 'dig_H2 = {0:d}'.format (self.dig_H2)
176+
print 'dig_H3 = {0:d}'.format (self.dig_H3)
177+
print 'dig_H4 = {0:d}'.format (self.dig_H4)
178+
print 'dig_H5 = {0:d}'.format (self.dig_H5)
179+
print 'dig_H6 = {0:d}'.format (self.dig_H6)
180+
'''
181+
182+
def read_raw_temp(self):
183+
"""Waits for reading to become available on device."""
184+
"""Does a single burst read of all data values from device."""
185+
"""Returns the raw (uncompensated) temperature from the sensor."""
186+
while (self._device.readU8(BME280_REGISTER_STATUS) & 0x08): # Wait for conversion to complete (TODO : add timeout)
187+
time.sleep(0.002)
188+
self.BME280Data = self._device.readList(BME280_REGISTER_DATA, 8)
189+
raw = ((self.BME280Data[3] << 16) | (self.BME280Data[4] << 8) | self.BME280Data[5]) >> 4
190+
return raw
191+
192+
def read_raw_pressure(self):
193+
"""Returns the raw (uncompensated) pressure level from the sensor."""
194+
"""Assumes that the temperature has already been read """
195+
"""i.e. that BME280Data[] has been populated."""
196+
raw = ((self.BME280Data[0] << 16) | (self.BME280Data[1] << 8) | self.BME280Data[2]) >> 4
197+
return raw
198+
199+
def read_raw_humidity(self):
200+
"""Returns the raw (uncompensated) humidity value from the sensor."""
201+
"""Assumes that the temperature has already been read """
202+
"""i.e. that BME280Data[] has been populated."""
203+
raw = (self.BME280Data[6] << 8) | self.BME280Data[7]
204+
return raw
205+
206+
def read_temperature(self):
207+
"""Gets the compensated temperature in degrees celsius."""
208+
# float in Python is double precision
209+
UT = float(self.read_raw_temp())
210+
var1 = (UT / 16384.0 - float(self.dig_T1) / 1024.0) * float(self.dig_T2)
211+
var2 = ((UT / 131072.0 - float(self.dig_T1) / 8192.0) * (
212+
UT / 131072.0 - float(self.dig_T1) / 8192.0)) * float(self.dig_T3)
213+
self.t_fine = int(var1 + var2)
214+
temp = (var1 + var2) / 5120.0
215+
return temp
216+
217+
def read_pressure(self):
218+
"""Gets the compensated pressure in Pascals."""
219+
adc = float(self.read_raw_pressure())
220+
var1 = float(self.t_fine) / 2.0 - 64000.0
221+
var2 = var1 * var1 * float(self.dig_P6) / 32768.0
222+
var2 = var2 + var1 * float(self.dig_P5) * 2.0
223+
var2 = var2 / 4.0 + float(self.dig_P4) * 65536.0
224+
var1 = (
225+
float(self.dig_P3) * var1 * var1 / 524288.0 + float(self.dig_P2) * var1) / 524288.0
226+
var1 = (1.0 + var1 / 32768.0) * float(self.dig_P1)
227+
if var1 == 0:
228+
return 0
229+
p = 1048576.0 - adc
230+
p = ((p - var2 / 4096.0) * 6250.0) / var1
231+
var1 = float(self.dig_P9) * p * p / 2147483648.0
232+
var2 = p * float(self.dig_P8) / 32768.0
233+
p = p + (var1 + var2 + float(self.dig_P7)) / 16.0
234+
return p
235+
236+
def read_humidity(self):
237+
adc = float(self.read_raw_humidity())
238+
# print 'Raw humidity = {0:d}'.format (adc)
239+
h = float(self.t_fine) - 76800.0
240+
h = (adc - (float(self.dig_H4) * 64.0 + float(self.dig_H5) / 16384.0 * h)) * (
241+
float(self.dig_H2) / 65536.0 * (1.0 + float(self.dig_H6) / 67108864.0 * h * (
242+
1.0 + float(self.dig_H3) / 67108864.0 * h)))
243+
h = h * (1.0 - float(self.dig_H1) * h / 524288.0)
244+
if h > 100:
245+
h = 100
246+
elif h < 0:
247+
h = 0
248+
return h
249+

bme280/Adafruit_BME280_Example.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
from Adafruit_BME280 import *
2+
3+
sensor = BME280(t_mode=BME280_OSAMPLE_8, p_mode=BME280_OSAMPLE_8, h_mode=BME280_OSAMPLE_8)
4+
5+
degrees = sensor.read_temperature()
6+
pascals = sensor.read_pressure()
7+
hectopascals = pascals / 100
8+
humidity = sensor.read_humidity()
9+
10+
print 'Temp = {0:0.3f} deg C'.format(degrees)
11+
print 'Pressure = {0:0.2f} hPa'.format(hectopascals)
12+
print 'Humidity = {0:0.2f} %'.format(humidity)

bme280/Adafruit_BME280_Example2.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
from Adafruit_BME280 import *
2+
import curses
3+
4+
def main(stdscr):
5+
sensor = BME280(p_mode=BME280_OSAMPLE_8, t_mode=BME280_OSAMPLE_2, h_mode=BME280_OSAMPLE_1, filter=BME280_FILTER_16)
6+
stdscr.nodelay(1)
7+
tstart = time.time()
8+
while (stdscr.getch() == -1) :
9+
degrees = sensor.read_temperature()
10+
pascals = sensor.read_pressure()
11+
hectopascals = pascals / 100
12+
humidity = sensor.read_humidity()
13+
14+
stdscr.addstr(0, 0, 'Timestamp = %0.3f sec' % (time.time() - tstart))
15+
stdscr.addstr(1, 0, 'Temp = %0.3f deg C (%0.3f deg F)' % (degrees, ((degrees*9/5)+32)))
16+
stdscr.addstr(2, 0, 'Pressure = %0.2f hPa' % hectopascals)
17+
stdscr.addstr(3, 0, 'Humidity = %0.2f %%' % humidity)
18+
stdscr.addstr(5, 0, 'Press any key to exit...')
19+
stdscr.refresh()
20+
21+
time.sleep(3)
22+
23+
stdscr.erase()
24+
25+
26+
curses.wrapper(main)
27+

bme280/LICENSE

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
The MIT License (MIT)
2+
3+
Copyright (c) 2015 Adafruit Industries
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.
22+

bme280/README.md

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# Adafruit_Python_BME280
2+
3+
This Python driver allows you to read data from the [Adafruit BME280 Breakout](https://www.adafruit.com/products/2652) on a Raspberry Pi, Pi2 or similar device.
4+
5+
## Requirements
6+
7+
This driver requires that you have previously installed the
8+
[Adafruit_Python_GPIO](https://github.com/adafruit/Adafruit_Python_GPIO) package.
9+
10+
On Raspbian, you can install this package with the following commands:
11+
12+
```
13+
sudo apt-get update
14+
sudo apt-get install build-essential python-pip python-dev python-smbus git
15+
git clone https://github.com/adafruit/Adafruit_Python_GPIO.git
16+
cd Adafruit_Python_GPIO
17+
sudo python setup.py install
18+
```
19+
20+
## Usage
21+
22+
To read a single set of data points from the BME, connect your Pi or Pi2
23+
to the BME280 breakout using I2C (connect SCL0/1 to the SCK pin and SCL0/1
24+
to the SDI pin), and run the following command from this folder:
25+
26+
```
27+
python Adafruit_BME280_Example.py
28+
```
29+
30+
## Credits
31+
32+
This driver is based on the [Adafruit_BMP](https://github.com/adafruit/Adafruit_Python_BMP)
33+
driver by Tony DiCola (Adafruit Industries), with BME280 additions kindly provided by
34+
David J. Taylor (www.satsignal.eu).
35+
36+
# MIT License
37+
38+
Permission is hereby granted, free of charge, to any person obtaining a copy
39+
of this software and associated documentation files (the "Software"), to deal
40+
in the Software without restriction, including without limitation the rights
41+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
42+
copies of the Software, and to permit persons to whom the Software is
43+
furnished to do so, subject to the following conditions:
44+
45+
The above copyright notice and this permission notice shall be included in
46+
all copies or substantial portions of the Software.
47+
48+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
49+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
50+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
51+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
52+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
53+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
54+
THE SOFTWARE.

0 commit comments

Comments
 (0)