1
+ # Authors: Paul Cunnane 2016, Peter Dahlebrg 2016
2
+ #
3
+ # This module borrows from the Adafruit BME280 Python library. Original
4
+ # Copyright notices are reproduced below.
5
+ #
6
+ # Those libraries were written for the Raspberry Pi. This modification is
7
+ # intended for the MicroPython and esp8266 boards.
8
+ #
9
+ # Copyright (c) 2014 Adafruit Industries
10
+ # Author: Tony DiCola
11
+ #
12
+ # Based on the BMP280 driver with BME280 changes provided by
13
+ # David J Taylor, Edinburgh (www.satsignal.eu)
14
+ #
15
+ # Based on Adafruit_I2C.py created by Kevin Townsend.
16
+ #
17
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
18
+ # of this software and associated documentation files (the "Software"), to deal
19
+ # in the Software without restriction, including without limitation the rights
20
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
21
+ # copies of the Software, and to permit persons to whom the Software is
22
+ # furnished to do so, subject to the following conditions:
23
+ #
24
+ # The above copyright notice and this permission notice shall be included in
25
+ # all copies or substantial portions of the Software.
26
+ #
27
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
28
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
29
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
30
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
31
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
32
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
33
+ # THE SOFTWARE.
34
+
35
+ import time
36
+ from ustruct import unpack , unpack_from
37
+ from array import array
38
+
39
+ # BME280 default address.
40
+ BME280_I2CADDR = 0x76
41
+
42
+ # Operating Modes
43
+ BME280_OSAMPLE_1 = 1
44
+ BME280_OSAMPLE_2 = 2
45
+ BME280_OSAMPLE_4 = 3
46
+ BME280_OSAMPLE_8 = 4
47
+ BME280_OSAMPLE_16 = 5
48
+
49
+ BME280_REGISTER_CONTROL_HUM = 0xF2
50
+ BME280_REGISTER_CONTROL = 0xF4
51
+
52
+
53
+ class BME280 :
54
+
55
+ def __init__ (self ,
56
+ mode = BME280_OSAMPLE_1 ,
57
+ address = BME280_I2CADDR ,
58
+ i2c = None ,
59
+ ** kwargs ):
60
+ # Check that mode is valid.
61
+ if mode not in [BME280_OSAMPLE_1 , BME280_OSAMPLE_2 , BME280_OSAMPLE_4 ,
62
+ BME280_OSAMPLE_8 , BME280_OSAMPLE_16 ]:
63
+ raise ValueError (
64
+ 'Unexpected mode value {0}. Set mode to one of '
65
+ 'BME280_ULTRALOWPOWER, BME280_STANDARD, BME280_HIGHRES, or '
66
+ 'BME280_ULTRAHIGHRES' .format (mode ))
67
+ self ._mode = mode
68
+ self .address = address
69
+ if i2c is None :
70
+ raise ValueError ('An I2C object is required.' )
71
+ self .i2c = i2c
72
+
73
+ # load calibration data
74
+ dig_88_a1 = self .i2c .readfrom_mem (self .address , 0x88 , 26 )
75
+ dig_e1_e7 = self .i2c .readfrom_mem (self .address , 0xE1 , 7 )
76
+ self .dig_T1 , self .dig_T2 , self .dig_T3 , self .dig_P1 , \
77
+ self .dig_P2 , self .dig_P3 , self .dig_P4 , self .dig_P5 , \
78
+ self .dig_P6 , self .dig_P7 , self .dig_P8 , self .dig_P9 , \
79
+ _ , self .dig_H1 = unpack ("<HhhHhhhhhhhhBB" , dig_88_a1 )
80
+
81
+ self .dig_H2 , self .dig_H3 = unpack ("<hB" , dig_e1_e7 )
82
+ e4_sign = unpack_from ("<b" , dig_e1_e7 , 3 )[0 ]
83
+ self .dig_H4 = (e4_sign << 4 ) | (dig_e1_e7 [4 ] & 0xF )
84
+
85
+ e6_sign = unpack_from ("<b" , dig_e1_e7 , 5 )[0 ]
86
+ self .dig_H5 = (e6_sign << 4 ) | (dig_e1_e7 [4 ] >> 4 )
87
+
88
+ self .dig_H6 = unpack_from ("<b" , dig_e1_e7 , 6 )[0 ]
89
+
90
+ self .i2c .writeto_mem (self .address , BME280_REGISTER_CONTROL ,
91
+ bytearray ([0x3F ]))
92
+ self .t_fine = 0
93
+
94
+ # temporary data holders which stay allocated
95
+ self ._l1_barray = bytearray (1 )
96
+ self ._l8_barray = bytearray (8 )
97
+ self ._l3_resultarray = array ("i" , [0 , 0 , 0 ])
98
+
99
+ def read_raw_data (self , result ):
100
+ """ Reads the raw (uncompensated) data from the sensor.
101
+ Args:
102
+ result: array of length 3 or alike where the result will be
103
+ stored, in temperature, pressure, humidity order
104
+ Returns:
105
+ None
106
+ """
107
+
108
+ self ._l1_barray [0 ] = self ._mode
109
+ self .i2c .writeto_mem (self .address , BME280_REGISTER_CONTROL_HUM ,
110
+ self ._l1_barray )
111
+ self ._l1_barray [0 ] = self ._mode << 5 | self ._mode << 2 | 1
112
+ self .i2c .writeto_mem (self .address , BME280_REGISTER_CONTROL ,
113
+ self ._l1_barray )
114
+
115
+ sleep_time = 1250 + 2300 * (1 << self ._mode )
116
+ sleep_time = sleep_time + 2300 * (1 << self ._mode ) + 575
117
+ sleep_time = sleep_time + 2300 * (1 << self ._mode ) + 575
118
+ time .sleep_us (sleep_time ) # Wait the required time
119
+
120
+ # burst readout from 0xF7 to 0xFE, recommended by datasheet
121
+ self .i2c .readfrom_mem_into (self .address , 0xF7 , self ._l8_barray )
122
+ readout = self ._l8_barray
123
+ # pressure(0xF7): ((msb << 16) | (lsb << 8) | xlsb) >> 4
124
+ raw_press = ((readout [0 ] << 16 ) | (readout [1 ] << 8 ) | readout [2 ]) >> 4
125
+ # temperature(0xFA): ((msb << 16) | (lsb << 8) | xlsb) >> 4
126
+ raw_temp = ((readout [3 ] << 16 ) | (readout [4 ] << 8 ) | readout [5 ]) >> 4
127
+ # humidity(0xFD): (msb << 8) | lsb
128
+ raw_hum = (readout [6 ] << 8 ) | readout [7 ]
129
+
130
+ result [0 ] = raw_temp
131
+ result [1 ] = raw_press
132
+ result [2 ] = raw_hum
133
+
134
+ def read_compensated_data (self , result = None ):
135
+ """ Reads the data from the sensor and returns the compensated data.
136
+ Args:
137
+ result: array of length 3 or alike where the result will be
138
+ stored, in temperature, pressure, humidity order. You may use
139
+ this to read out the sensor without allocating heap memory
140
+ Returns:
141
+ array with temperature, pressure, humidity. Will be the one from
142
+ the result parameter if not None
143
+ """
144
+ self .read_raw_data (self ._l3_resultarray )
145
+ raw_temp , raw_press , raw_hum = self ._l3_resultarray
146
+ # temperature
147
+ var1 = ((raw_temp >> 3 ) - (self .dig_T1 << 1 )) * (self .dig_T2 >> 11 )
148
+ var2 = (((((raw_temp >> 4 ) - self .dig_T1 ) *
149
+ ((raw_temp >> 4 ) - self .dig_T1 )) >> 12 ) * self .dig_T3 ) >> 14
150
+ self .t_fine = var1 + var2
151
+ temp = (self .t_fine * 5 + 128 ) >> 8
152
+
153
+ # pressure
154
+ var1 = self .t_fine - 128000
155
+ var2 = var1 * var1 * self .dig_P6
156
+ var2 = var2 + ((var1 * self .dig_P5 ) << 17 )
157
+ var2 = var2 + (self .dig_P4 << 35 )
158
+ var1 = (((var1 * var1 * self .dig_P3 ) >> 8 ) +
159
+ ((var1 * self .dig_P2 ) << 12 ))
160
+ var1 = (((1 << 47 ) + var1 ) * self .dig_P1 ) >> 33
161
+ if var1 == 0 :
162
+ pressure = 0
163
+ else :
164
+ p = 1048576 - raw_press
165
+ p = (((p << 31 ) - var2 ) * 3125 ) // var1
166
+ var1 = (self .dig_P9 * (p >> 13 ) * (p >> 13 )) >> 25
167
+ var2 = (self .dig_P8 * p ) >> 19
168
+ pressure = ((p + var1 + var2 ) >> 8 ) + (self .dig_P7 << 4 )
169
+
170
+ # humidity
171
+ h = self .t_fine - 76800
172
+ h = (((((raw_hum << 14 ) - (self .dig_H4 << 20 ) -
173
+ (self .dig_H5 * h )) + 16384 )
174
+ >> 15 ) * (((((((h * self .dig_H6 ) >> 10 ) *
175
+ (((h * self .dig_H3 ) >> 11 ) + 32768 )) >> 10 ) +
176
+ 2097152 ) * self .dig_H2 + 8192 ) >> 14 ))
177
+ h = h - (((((h >> 15 ) * (h >> 15 )) >> 7 ) * self .dig_H1 ) >> 4 )
178
+ h = 0 if h < 0 else h
179
+ h = 419430400 if h > 419430400 else h
180
+ humidity = h >> 12
181
+
182
+ if result :
183
+ result [0 ] = temp
184
+ result [1 ] = pressure
185
+ result [2 ] = humidity
186
+ return result
187
+
188
+ return array ("i" , (temp , pressure , humidity ))
189
+
190
+ @property
191
+ def values (self ):
192
+ """ human readable values """
193
+
194
+ t , p , h = self .read_compensated_data ()
195
+
196
+ p = p // 256
197
+ pi = p // 100
198
+ pd = p - pi * 100
199
+
200
+ hi = h // 1024
201
+ hd = h * 100 // 1024 - hi * 100
202
+ return ("{}C" .format (t / 100 ), "{}.{:02d}hPa" .format (pi , pd ),
203
+ "{}.{:02d}%" .format (hi , hd ))
0 commit comments