Class: Denko::Sensor::BMP180

Inherits:
Object
  • Object
show all
Includes:
Behaviors::Lifecycle, Behaviors::Poller, I2C::Peripheral, PressureHelper, TemperatureHelper
Defined in:
lib/denko/sensor/bmp180.rb

Constant Summary collapse

I2C_ADDRESS =
0x77
SOFT_RESET =

Write this to register 0xE0 for soft reset

0xB6
OVERSAMPLE_FACTORS =

Pressure Oversample Setting Values

General formula:

2 ** n, where n is the decimal value of the bits, up to 8x pressure oversampling.
{
  1  =>  0b00,
  2  =>  0b01,
  4  =>  0b10,
  8  =>  0b11,
}

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

Attributes included from Behaviors::Threaded

#interrupts_enabled, #thread

Attributes included from I2C::Peripheral

#i2c_frequency, #i2c_repeated_start

Attributes included from Behaviors::Component

#board, #params

Instance Method Summary collapse

Methods included from PressureHelper

#pressure, #pressure_atm, #pressure_bar

Methods included from TemperatureHelper

#temperature, #temperature_f, #temperature_k

Methods included from Behaviors::Lifecycle

included

Methods included from Behaviors::Poller

#poll, #poll_using, #stop

Methods included from Behaviors::Threaded

#enable_interrupts, included, #mruby_thread_check, #stop, #stop_thread, #threaded, #threaded_loop

Methods included from Behaviors::Reader

#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

#address

Methods included from Behaviors::BusPeripheral

#atomically

Methods included from Behaviors::Component

#initialize, #micro_delay

Instance Attribute Details

#calibration_data_loadedObject (readonly)

Calibration Methods



194
195
196
# File 'lib/denko/sensor/bmp180.rb', line 194

def calibration_data_loaded
  @calibration_data_loaded
end

#measurement_timeObject (readonly)

Returns the value of attribute measurement_time.



55
56
57
# File 'lib/denko/sensor/bmp180.rb', line 55

def measurement_time
  @measurement_time
end

Instance Method Details

#_readObject



105
106
107
108
109
110
# File 'lib/denko/sensor/bmp180.rb', line 105

def _read
  get_calibration_data unless calibration_data_loaded

  _read_temperature
  _read_pressure
end

#_read_pressureObject



90
91
92
93
94
95
# File 'lib/denko/sensor/bmp180.rb', line 90

def _read_pressure
  @register = 0x34 | (@oss << 6)
  write_settings
  sleep(@measurement_time)
  i2c_read(3, register: 0xF6)
end

#_read_temperatureObject

Reading Methods



83
84
85
86
87
88
# File 'lib/denko/sensor/bmp180.rb', line 83

def _read_temperature
  @register = 0x2E
  write_settings
  sleep(@measurement_time)
  i2c_read(2, register: 0xF6)
end

#decode_pressure(bytes, b5) ⇒ Object



164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
# File 'lib/denko/sensor/bmp180.rb', line 164

def decode_pressure(bytes, b5)
  # Pressure is bytes [2..3], MSB first.
  up = ((bytes[2] << 16) | (bytes[3] << 8) | (bytes[4])) >> (8 - @oss)

  # Calibration compensation from datasheet
  b6 = b5 - 4000
  x1 = (@calibration[:b2] * (b6 * b6 / 4096)) / 2048
  x2 = @calibration[:ac2] * b6 / 2048
  x3 = x1 + x2
  b3 = (((@calibration[:ac1]*4 + x3) << @oss) + 2) / 4
  x1 = @calibration[:ac3] * b6 / 8192
  x2 = (@calibration[:b1] * (b6 * b6 / 4096)) / 65536
  x3 = (x1 + x2 + 2) / 4
  b4 = (@calibration[:ac4] * ((x3+32768) & 0xFFFF_FFFF)) / 32768
  b7 = ((up & 0xFFFF_FFFF) - b3) * (50000 >> @oss)
  if (b7 < 0x8000_0000)
    p = (b7 * 2) / b4
  else
    p = (b7 / b4) * 2
  end
  x1 = (p / 256) * (p / 256)
  x1 = (x1 * 3038) / 65536
  x2 = (-7357 * p) / 65536
  p = p + (x1 + x2 + 3791) / 16
  pressure = p.to_f
end

#decode_reading(bytes) ⇒ Object

Decoding Methods



140
141
142
143
144
145
# File 'lib/denko/sensor/bmp180.rb', line 140

def decode_reading(bytes)
  temperature, b5 = decode_temperature(bytes)
  reading[:temperature] = temperature
  reading[:pressure] = decode_pressure(bytes, b5)
  reading
end

#decode_temperature(bytes) ⇒ Object



147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
# File 'lib/denko/sensor/bmp180.rb', line 147

def decode_temperature(bytes)
  # Temperature is bytes [0..2], MSB first.
  ut = bytes[0] << 8 | bytes[1]

  # Calibration compensation from datasheet
  x1 = (ut - @calibration[:ac6]) * @calibration[:ac5] / 32768
  x2 = (@calibration[:mc] * 2048) / (x1 + @calibration[:md])
  b5 = x1 + x2

  # 160 instead of 16 since datasheet calculates to 0.1 C units.
  # Float to force the final value into float, but keep b5 integer for pressure.
  temperature = (b5 + 8) / 160.0

  # Return temperature and b5 for pressure calculation.
  [temperature, b5]
end

#get_calibration_dataObject



196
197
198
199
# File 'lib/denko/sensor/bmp180.rb', line 196

def get_calibration_data
  bytes = i2c_read_raw(22, register: 0xAA)
  process_calibration(bytes) if bytes
end

#pre_callback_filter(data) ⇒ Object



112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/denko/sensor/bmp180.rb', line 112

def pre_callback_filter(data)
  # Temperature is 2 bytes.
  if data.length == 2
    @raw_bytes[0] = data[0]
    @raw_bytes[1] = data[1]
  # Pressure is 3 bytes and triggers callbacks.
  elsif data.length == 3
    @raw_bytes[2] = data[0]
    @raw_bytes[3] = data[1]
    @raw_bytes[4] = data[2]
    return decode_reading(@raw_bytes)
  end

  # Anything other than pressure avoids callbacks.
  return nil
end

#pressure_samples=(factor) ⇒ Object

Raises:

  • (ArgumentError)


70
71
72
73
# File 'lib/denko/sensor/bmp180.rb', line 70

def pressure_samples=(factor)
  raise ArgumentError, "invalid oversampling factor: #{factor}" unless OVERSAMPLE_FACTORS.keys.include? factor
  @oss = OVERSAMPLE_FACTORS[factor]
end

#process_calibration(bytes) ⇒ Object



201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
# File 'lib/denko/sensor/bmp180.rb', line 201

def process_calibration(bytes)
  if bytes
    @calibration = {
      ac1: bytes[0..1].pack('C*').unpack('s>')[0],
      ac2: bytes[2..3].pack('C*').unpack('s>')[0],
      ac3: bytes[4..5].pack('C*').unpack('s>')[0],
      ac4: bytes[6..7].pack('C*').unpack('S>')[0],
      ac5: bytes[8..9].pack('C*').unpack('S>')[0],
      ac6: bytes[10..11].pack('C*').unpack('S>')[0],
      b1: bytes[12..13].pack('C*').unpack('s>')[0],
      b2: bytes[14..15].pack('C*').unpack('s>')[0],
      mb: bytes[16..17].pack('C*').unpack('s>')[0],
      mc: bytes[18..19].pack('C*').unpack('s>')[0],
      md: bytes[20..21].pack('C*').unpack('s>')[0],
    }
    @calibration_data_loaded = true
  end
end

#read(*args, **kwargs, &block) ⇒ Object

Workaround for :read callbacks getting automatically removed on first reading.



98
99
100
101
102
103
# File 'lib/denko/sensor/bmp180.rb', line 98

def read(*args, **kwargs, &block)
  get_calibration_data unless calibration_data_loaded

  read_using(self.method(:_read_temperature), *args, **kwargs)
  read_using(self.method(:_read_pressure), *args, **kwargs, &block)
end

#readingObject



44
45
46
# File 'lib/denko/sensor/bmp180.rb', line 44

def reading
  @reading ||= { temperature: nil, pressure: nil }
end

#soft_resetObject

Configuration Methods



51
52
53
# File 'lib/denko/sensor/bmp180.rb', line 51

def soft_reset
  i2c_write(SOFT_RESET)
end

#stateObject



40
41
42
# File 'lib/denko/sensor/bmp180.rb', line 40

def state
  @state ||= { temperature: nil, pressure: nil }
end

#update_measurement_timeObject



57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/denko/sensor/bmp180.rb', line 57

def update_measurement_time
  # Get oversample bits from current register setting.
  oversample_exponent = (@register & 0b11000000) >> 6

  # Calculate time in milliseconds.
  extra_samples = (2 ** oversample_exponent) - 1
  extra_time = extra_samples * 3
  total_time = 4.5 + extra_time

  # Sleep 1ms extra for safety and convert it to seconds.
  @measurement_time = (total_time + 1) / 1000.0
end

#update_state(reading) ⇒ Object



129
130
131
132
133
134
135
# File 'lib/denko/sensor/bmp180.rb', line 129

def update_state(reading)
  @state_mutex.lock
  @state[:temperature] = reading[:temperature]
  @state[:pressure]    = reading[:pressure]
  @state_mutex.unlock
  @state
end

#write_settingsObject



75
76
77
78
# File 'lib/denko/sensor/bmp180.rb', line 75

def write_settings
  update_measurement_time
  i2c_write [0xF4, @register]
end