Class: Denko::Sensor::BME280
- Inherits:
-
Object
- Object
- Denko::Sensor::BME280
- Includes:
- Behaviors::Lifecycle, Behaviors::Poller, I2C::Peripheral, HumidityHelper, PressureHelper, TemperatureHelper
- Defined in:
- lib/denko/sensor/bme280.rb
Direct Known Subclasses
Constant Summary collapse
- I2C_ADDRESS =
0x76- SLEEP_MODE =
Reading Mode Settings
0b00- ONESHOT_MODE =
0b10 is also valid. Called “forced mode” in datasheet.
0b01- CONTINUOUS_MODE =
Called “normal mode” in datashseet.
0b11- STANDBY_TIMES =
Standby Times for Normal (Continuous) Mode in milliseconds
{ 0.5 => 0b000, 62.5 => 0b001, 125 => 0b010, 250 => 0b011, 500 => 0b100, 1000 => 0b101, 10 => 0b110, 20 => 0b111, }
- OVERSAMPLE_FACTORS =
Oversample Setting Values
Note: Each of the 3 sensors has a separate oversample setting, but temperature cannot be skipped, since the other 2 calculations depend on it.
General formula:
2 ** (n-1), where n is the decimal value of the bits, up to 16x max oversampling. { 0 => 0b000, # Sensor skipped. Value will be 0x800000. 1 => 0b001, 2 => 0b010, 4 => 0b011, 8 => 0b100, 16 => 0b101, # 0b110 and 0b111 are also valid for 16x. }
- IIR_COEFFICIENTS =
IIR Filter Coefficients
{ 0 => 0b000, 2 => 0b001, 4 => 0b010, 8 => 0b011, 16 => 0b100, # 0b101, 0b110 and 0b111 are also valid for 16. }
Constants included from Behaviors::Lifecycle
Behaviors::Lifecycle::CALLBACK_METHODS
Constants included from Behaviors::Reader
Behaviors::Reader::READ_WAIT_TIME
Constants included from I2C::Peripheral
I2C::Peripheral::I2C_FREQUENCY, I2C::Peripheral::I2C_REPEATED_START
Instance Attribute Summary collapse
-
#calibration_data_loaded ⇒ Object
readonly
Calibration Methods.
-
#measurement_time ⇒ Object
readonly
Returns the value of attribute measurement_time.
Attributes included from Behaviors::Threaded
Attributes included from I2C::Peripheral
#i2c_frequency, #i2c_repeated_start
Attributes included from Behaviors::Component
Instance Method Summary collapse
-
#_read ⇒ Object
Reading Methods.
- #config_register_bits ⇒ Object
- #continuous_mode ⇒ Object
- #decode_humidity(bytes, t_fine) ⇒ Object
- #decode_pressure(bytes, t_fine) ⇒ Object
-
#decode_temperature(bytes) ⇒ Object
Decoding Methods.
- #get_calibration_data ⇒ Object
-
#humidity_available? ⇒ Boolean
No humidity on the BMP280.
- #humidity_samples=(factor) ⇒ Object
- #iir_coefficient=(coeff) ⇒ Object
-
#oneshot_mode ⇒ Object
Configuration Methods.
- #pre_callback_filter(bytes) ⇒ Object
- #pressure_samples=(factor) ⇒ Object
- #process_calibration_a(cal_a) ⇒ Object
- #process_calibration_b(cal_b) ⇒ Object
- #reading ⇒ Object
- #reading_humidity? ⇒ Boolean
- #reading_pressure? ⇒ Boolean
- #standby_time=(ms) ⇒ Object
- #state ⇒ Object
- #temperature_samples=(factor) ⇒ Object
- #update_measurement_time ⇒ Object
- #update_state(reading) ⇒ Object
- #write_settings ⇒ Object
Methods included from HumidityHelper
Methods included from PressureHelper
#pressure, #pressure_atm, #pressure_bar
Methods included from TemperatureHelper
#temperature, #temperature_f, #temperature_k
Methods included from Behaviors::Lifecycle
Methods included from Behaviors::Poller
Methods included from Behaviors::Threaded
#enable_interrupts, included, #mruby_thread_check, #stop, #stop_thread, #threaded, #threaded_loop
Methods included from Behaviors::Reader
#read, #read_busy?, #read_nb, #read_raw, #read_using, #update
Methods included from Behaviors::Callbacks
#add_callback, #callbacks, #remove_callback, #update
Methods included from I2C::Peripheral
#address, #i2c_default, #i2c_read, #i2c_read_raw, #i2c_write
Methods included from Behaviors::BusPeripheralAddressed
Methods included from Behaviors::BusPeripheral
Methods included from Behaviors::Component
Instance Attribute Details
#calibration_data_loaded ⇒ Object (readonly)
Calibration Methods
278 279 280 |
# File 'lib/denko/sensor/bme280.rb', line 278 def calibration_data_loaded @calibration_data_loaded end |
#measurement_time ⇒ Object (readonly)
Returns the value of attribute measurement_time.
99 100 101 |
# File 'lib/denko/sensor/bme280.rb', line 99 def measurement_time @measurement_time end |
Instance Method Details
#_read ⇒ Object
Reading Methods
178 179 180 181 182 |
# File 'lib/denko/sensor/bme280.rb', line 178 def _read get_calibration_data unless calibration_data_loaded write_settings i2c_read 8, register: 0xF7 end |
#config_register_bits ⇒ Object
167 168 169 170 171 172 173 |
# File 'lib/denko/sensor/bme280.rb', line 167 def config_register_bits str = String.new @registers.each_key do |key| str << "0x#{key.upcase}: #{@registers[key].to_s(2).rjust(8, '0')}\n" end str end |
#continuous_mode ⇒ Object
117 118 119 120 |
# File 'lib/denko/sensor/bme280.rb', line 117 def continuous_mode @registers[:f4] = (@registers[:f4] & 0b11111100) | CONTINUOUS_MODE write_settings end |
#decode_humidity(bytes, t_fine) ⇒ Object
245 246 247 248 249 250 251 252 253 254 255 256 257 |
# File 'lib/denko/sensor/bme280.rb', line 245 def decode_humidity(bytes, t_fine) # Raw data for humidity is big-endian uint16. adc_h = (bytes[6] << 8) | bytes[7] # Floating point humidity calculation from datasheet. Result in % RH. humidity = t_fine - 76800.0 humidity = (adc_h - (@calibration[:h4] * 64.0 + @calibration[:h5] / 16384.0 * humidity)) * (@calibration[:h2] / 65536.0 * (1.0 + @calibration[:h6] / 67108864.0 * humidity * (1.0 + @calibration[:h3] / 67108864.0 * humidity))) humidity = humidity * (1.0 - @calibration[:h1] * humidity / 524288.0) humidity = 100.0 if humidity > 100 humidity = 0.0 if humidity < 0 humidity end |
#decode_pressure(bytes, t_fine) ⇒ Object
222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 |
# File 'lib/denko/sensor/bme280.rb', line 222 def decode_pressure(bytes, t_fine) # Reformat raw pressure bytes (20-bits in 24) to uint32. adc_p = ((bytes[0] << 16) | (bytes[1] << 8) | (bytes[2])) >> 4 # Floating point pressure calculation from datasheet. Result in Pascals. var1 = (t_fine / 2.0) - 64000.0 var2 = var1 * var1 * @calibration[:p6] / 32768.0 var2 = var2 + var1 * @calibration[:p5] * 2.0 var2 = (var2 / 4.0) + (@calibration[:p4] * 65536.0) var1 = (@calibration[:p3] * var1 * var1 / 524288.0 + @calibration[:p2] * var1) / 524288.0 var1 = (1.0 + var1 / 32768.0) * @calibration[:p1] if var1 == 0 pressure = nil else pressure = 1048576.0 - adc_p pressure = (pressure - (var2 / 4096.0)) * 6250.0 / var1 var1 = @calibration[:p9] * pressure * pressure / 2147483648.0 var2 = pressure * @calibration[:p8] / 32768.0 pressure = pressure + (var1 + var2 + @calibration[:p7]) / 16.0 end pressure end |
#decode_temperature(bytes) ⇒ Object
Decoding Methods
210 211 212 213 214 215 216 217 218 219 220 |
# File 'lib/denko/sensor/bme280.rb', line 210 def decode_temperature(bytes) # Reformat raw temeprature bytes (20-bits in 24) to uint32. adc_t = ((bytes[3] << 16) | (bytes[4] << 8) | (bytes[5])) >> 4 # Floating point temperature calculation from datasheet. Result in degrees Celsius. var1 = (adc_t / 16384.0 - @calibration[:t1] / 1024.0) * @calibration[:t2] var2 = (adc_t / 131072.0 - @calibration[:t1] / 8192.0) ** 2 * @calibration[:t3] t_fine = var1 + var2 temperature = (var1 + var2) / 5120.0 [temperature, t_fine] end |
#get_calibration_data ⇒ Object
280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 |
# File 'lib/denko/sensor/bme280.rb', line 280 def get_calibration_data # Calibration A cal_a_bytes = i2c_read_raw(26, register: 0x88) process_calibration_a(cal_a_bytes) if cal_a_bytes # Calibration B, only on BME280. if humidity_available? cal_b_bytes = i2c_read_raw(7, register: 0xE1) process_calibration_b(cal_b_bytes) if cal_b_bytes end if (@calibration[:cal_a] && @calibration[:cal_b]) || (@calibration[:cal_a] && !humidity_available?) @calibration.delete(:cal_a).delete(:cal_b) @calibration_data_loaded = true end end |
#humidity_available? ⇒ Boolean
No humidity on the BMP280.
271 272 273 |
# File 'lib/denko/sensor/bme280.rb', line 271 def humidity_available? self.class.name.split('::').last.downcase.start_with? "bme" end |
#humidity_samples=(factor) ⇒ Object
144 145 146 147 148 149 |
# File 'lib/denko/sensor/bme280.rb', line 144 def humidity_samples=(factor) raise ArgumentError, "invalid oversampling factor: #{factor}" unless OVERSAMPLE_FACTORS.keys.include? factor @registers[:f2] = (@registers[:f2] & 0b11111000) | OVERSAMPLE_FACTORS[factor] write_settings end |
#iir_coefficient=(coeff) ⇒ Object
151 152 153 154 155 156 |
# File 'lib/denko/sensor/bme280.rb', line 151 def iir_coefficient=(coeff) raise ArgumentError, "invalid IIR coefficient: #{coeff}" unless IIR_COEFFICIENTS.keys.include? coeff @registers[:f5] = (@registers[:f5] & 0b11100011) | (IIR_COEFFICIENTS[coeff] << 2) write_settings end |
#oneshot_mode ⇒ Object
Configuration Methods
94 95 96 97 |
# File 'lib/denko/sensor/bme280.rb', line 94 def oneshot_mode @registers[:f4] = (@registers[:f4] & 0b11111100) | ONESHOT_MODE write_settings end |
#pre_callback_filter(bytes) ⇒ Object
184 185 186 187 188 189 190 191 192 193 194 195 196 |
# File 'lib/denko/sensor/bme280.rb', line 184 def pre_callback_filter(bytes) return nil unless bytes.length == 8 # Always read temperature since t_fine is needed to calibrate other values. temperature, t_fine = decode_temperature(bytes) reading[:temperature] = temperature # Pressure and humidity are optional. Humidity is not available on the BMP280. reading[:pressure] = decode_pressure(bytes, t_fine) if reading_pressure? reading[:humidity] = decode_humidity(bytes, t_fine) if reading_humidity? reading end |
#pressure_samples=(factor) ⇒ Object
137 138 139 140 141 142 |
# File 'lib/denko/sensor/bme280.rb', line 137 def pressure_samples=(factor) raise ArgumentError, "invalid oversampling factor: #{factor}" unless OVERSAMPLE_FACTORS.keys.include? factor @registers[:f4] = (@registers[:f4] & 0b11100011) | (OVERSAMPLE_FACTORS[factor] << 2) write_settings end |
#process_calibration_a(cal_a) ⇒ Object
297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 |
# File 'lib/denko/sensor/bme280.rb', line 297 def process_calibration_a(cal_a) if cal_a @calibration = { t1: cal_a[0..1].pack('C*').unpack('S<')[0], t2: cal_a[2..3].pack('C*').unpack('s<')[0], t3: cal_a[4..5].pack('C*').unpack('s<')[0], p1: cal_a[6..7].pack('C*').unpack('S<')[0], p2: cal_a[8..9].pack('C*').unpack('s<')[0], p3: cal_a[10..11].pack('C*').unpack('s<')[0], p4: cal_a[12..13].pack('C*').unpack('s<')[0], p5: cal_a[14..15].pack('C*').unpack('s<')[0], p6: cal_a[16..17].pack('C*').unpack('s<')[0], p7: cal_a[18..19].pack('C*').unpack('s<')[0], p8: cal_a[20..21].pack('C*').unpack('s<')[0], p9: cal_a[22..23].pack('C*').unpack('s<')[0], } @calibration[:cal_a] = cal_a end end |
#process_calibration_b(cal_b) ⇒ Object
318 319 320 321 322 323 324 325 326 327 328 329 330 |
# File 'lib/denko/sensor/bme280.rb', line 318 def process_calibration_b(cal_b) if cal_b && @calibration[:cal_a] @calibration.merge!( h1: @calibration[:cal_a][25], h2: cal_b[0..1].pack('C*').unpack('s<')[0], h3: cal_b[2], h4: [(cal_b[3] << 4) | (cal_b[4] & 0b00001111)].pack('S').unpack('s')[0], h5: [(cal_b[5] << 4) | (cal_b[4] >> 4) ].pack('S').unpack('s')[0], h6: [cal_b[6]].pack('C').unpack('c')[0] ) @calibration[:cal_b] = cal_b end end |
#reading ⇒ Object
87 88 89 |
# File 'lib/denko/sensor/bme280.rb', line 87 def reading @reading ||= { temperature: nil, humidity: nil, pressure: nil } end |
#reading_humidity? ⇒ Boolean
264 265 266 267 268 |
# File 'lib/denko/sensor/bme280.rb', line 264 def reading_humidity? return false unless humidity_available? # Lowest 3 bits of 0xF2 register must not be 0. (@registers[:f2] & 0b111) != OVERSAMPLE_FACTORS[0] end |
#reading_pressure? ⇒ Boolean
259 260 261 262 |
# File 'lib/denko/sensor/bme280.rb', line 259 def reading_pressure? # Bits 2..4 of 0xF4 register must not be 0. (@registers[:f4] >> 2 & 0b111) != OVERSAMPLE_FACTORS[0] end |
#standby_time=(ms) ⇒ Object
122 123 124 125 126 127 |
# File 'lib/denko/sensor/bme280.rb', line 122 def standby_time=(ms) raise ArgumentError, "invalid standby time: #{ms}" unless self.class::STANDBY_TIMES.keys.include? ms @registers[:f5] = (@registers[:f5] & 0b00011111) | (self.class::STANDBY_TIMES[ms] << 5) write_settings end |
#state ⇒ Object
83 84 85 |
# File 'lib/denko/sensor/bme280.rb', line 83 def state @state ||= { temperature: nil, humidity: nil, pressure: nil } end |
#temperature_samples=(factor) ⇒ Object
129 130 131 132 133 134 135 |
# File 'lib/denko/sensor/bme280.rb', line 129 def temperature_samples=(factor) raise ArgumentError, "invalid oversampling factor: #{factor}" unless OVERSAMPLE_FACTORS.keys.include? factor raise ArgumentError, "temperature must be read. Invalid oversampling factor: #{factor}" if factor == 0 @registers[:f4] = (@registers[:f4] & 0b00011111) | (OVERSAMPLE_FACTORS[factor] << 5) write_settings end |
#update_measurement_time ⇒ Object
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 |
# File 'lib/denko/sensor/bme280.rb', line 101 def update_measurement_time t_oversampling = 2 ** (((@registers[:f4] & 0b11100000) >> 5) - 1) p_oversampling = 2 ** (((@registers[:f4] & 0b00011100) >> 2) - 1) # Use the maximum measurement time from the datasheet. @measurement_time = 1.25 + (2.3 * t_oversampling) + (2.3 * p_oversampling) + 0.575 if humidity_available? h_oversampling = 2 ** ((@registers[:f2] & 0b00000111) - 1) if humidity_available? @measurement_time = @measurement_time + (2.3 * h_oversampling) + 0.575 end # Milliseconds to seconds @measurement_time = @measurement_time / 1000 end |
#update_state(reading) ⇒ Object
198 199 200 201 202 203 204 205 |
# File 'lib/denko/sensor/bme280.rb', line 198 def update_state(reading) @state_mutex.lock @state[:temperature] = reading[:temperature] @state[:pressure] = reading[:pressure] @state[:humidity] = reading[:humidity] @state_mutex.unlock @state end |
#write_settings ⇒ Object
158 159 160 161 162 163 164 165 |
# File 'lib/denko/sensor/bme280.rb', line 158 def write_settings if humidity_available? i2c_write [0xF2, @registers[:f2], 0xF4, @registers[:f4], 0xF5, @registers[:f5]] else i2c_write [0xF4, @registers[:f4], 0xF5, @registers[:f5]] end update_measurement_time end |