Class: Denko::Sensor::BMP180

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

Constant Summary collapse

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,
}

Instance Attribute Summary collapse

Attributes included from Behaviors::Threaded

#interrupts_enabled, #thread

Attributes included from Behaviors::Callbacks

#callback_mutex

Attributes included from I2C::Peripheral

#i2c_frequency, #i2c_repeated_start

Attributes included from Behaviors::BusPeripheral

#address

Attributes included from Behaviors::Component

#board

Instance Method Summary collapse

Methods included from Behaviors::Poller

#poll, #poll_using, #stop

Methods included from Behaviors::Threaded

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

Methods included from Behaviors::Reader

#read, #read_using, #wait_for_read

Methods included from Behaviors::Callbacks

#add_callback, #callbacks, #initialize, #remove_callback, #update

Methods included from Behaviors::State

#initialize, #state

Methods included from I2C::Peripheral

#i2c_read, #i2c_write

Methods included from Behaviors::BusPeripheral

#atomically

Methods included from Behaviors::Component

#initialize, #micro_delay

Instance Attribute Details

#calibration_data_loadedObject (readonly)

Calibration Methods



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

def calibration_data_loaded
  @calibration_data_loaded
end

#measurement_timeObject (readonly)

Returns the value of attribute measurement_time.



53
54
55
# File 'lib/denko/sensor/bmp180.rb', line 53

def measurement_time
  @measurement_time
end

Instance Method Details

#[](key) ⇒ Object



133
134
135
136
137
# File 'lib/denko/sensor/bmp180.rb', line 133

def [](key)
  @state_mutex.synchronize do
    return @state[key]
  end
end

#_readObject



91
92
93
94
95
96
97
98
99
100
101
# File 'lib/denko/sensor/bmp180.rb', line 91

def _read
  get_calibration_data unless calibration_data_loaded

  _start_read_temperature
  sleep(@measurement_time)
  i2c_read 0xF6, 2

  _start_read_pressure
  sleep(@measurement_time)
  i2c_read 0xF6, 3
end

#_start_read_pressureObject



86
87
88
89
# File 'lib/denko/sensor/bmp180.rb', line 86

def _start_read_pressure
  @register = 0x34 | (@oss << 6)
  write_settings
end

#_start_read_temperatureObject

Reading Methods



81
82
83
84
# File 'lib/denko/sensor/bmp180.rb', line 81

def _start_read_temperature
  @register = 0x2E
  write_settings
end

#after_initialize(options = {}) ⇒ Object



28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/denko/sensor/bmp180.rb', line 28

def after_initialize(options={})
  super(options)
  
  # Avoid repeated memory allocation for callback data and state.
  @reading   = { temperature: nil, pressure: nil }
  self.state = { temperature: nil, pressure: nil }

  # Default to start conversion off, reading temperature, no pressure oversampling.
  @register = 0b00001110
  @calibration_data_loaded = false
  @oss = 0b00

  # Temporary storage for raw bytes, since two I2C reads are needed for temperature and pressure.
  @raw_bytes = [0, 0, 0, 0, 0]

  soft_reset
end

#before_initialize(options = {}) ⇒ Object



23
24
25
26
# File 'lib/denko/sensor/bmp180.rb', line 23

def before_initialize(options={})
  @i2c_address = 0x77
  super(options)
end

#decode_pressure(bytes, b5) ⇒ Object



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

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



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

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

#decode_temperature(bytes) ⇒ Object



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

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



198
199
200
201
# File 'lib/denko/sensor/bmp180.rb', line 198

def get_calibration_data        
  #  Calibration data is 22 bytes starting at address 0xAA.
  read_using -> { i2c_read(0xAA, 22) }
end

#pre_callback_filter(data) ⇒ Object



103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/denko/sensor/bmp180.rb', line 103

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)
  # Calibration data is 22 bytes.
  elsif data.length == 22
    process_calibration(data)
  end

  # Anything other than pressure avoids callbacks.
  return nil
end

#pressure_samples=(factor) ⇒ Object

Raises:

  • (ArgumentError)


68
69
70
71
# File 'lib/denko/sensor/bmp180.rb', line 68

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



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

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

#soft_resetObject

Configuration Methods



49
50
51
# File 'lib/denko/sensor/bmp180.rb', line 49

def soft_reset
  i2c_write(SOFT_RESET)
end

#update_measurement_timeObject



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

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



123
124
125
126
127
128
129
130
131
# File 'lib/denko/sensor/bmp180.rb', line 123

def update_state(reading)
  # Checking for Hash ignores calibration data and nil.
  if reading.class == Hash
    @state_mutex.synchronize do
      @state[:temperature] = reading[:temperature]
      @state[:pressure]    = reading[:pressure]
    end
  end
end

#write_settingsObject



73
74
75
76
# File 'lib/denko/sensor/bmp180.rb', line 73

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