denko 0.13.3 → 0.13.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +39 -0
- data/HARDWARE.md +50 -23
- data/README.md +3 -3
- data/examples/advanced/m5_env.rb +48 -0
- data/examples/digital_io/button.rb +13 -0
- data/examples/sensor/aht10.rb +7 -3
- data/examples/sensor/aht20.rb +7 -3
- data/examples/sensor/bme280.rb +6 -43
- data/examples/sensor/bmp180.rb +22 -0
- data/examples/sensor/generic_pir.rb +25 -0
- data/examples/sensor/htu21d.rb +1 -1
- data/examples/sensor/htu31d.rb +15 -11
- data/examples/sensor/neat_tph_readings.rb +26 -0
- data/examples/sensor/qmp6988.rb +53 -0
- data/examples/sensor/rcwl9620.rb +15 -0
- data/examples/sensor/sht3x.rb +34 -0
- data/lib/denko/sensor/bme280.rb +1 -1
- data/lib/denko/sensor/bmp180.rb +223 -0
- data/lib/denko/sensor/generic_pir.rb +8 -0
- data/lib/denko/sensor/htu21d.rb +6 -2
- data/lib/denko/sensor/qmp6988.rb +308 -0
- data/lib/denko/sensor/rcwl9620.rb +34 -0
- data/lib/denko/sensor/sht3x.rb +128 -0
- data/lib/denko/sensor.rb +6 -0
- data/lib/denko/version.rb +1 -1
- metadata +15 -2
@@ -0,0 +1,223 @@
|
|
1
|
+
module Denko
|
2
|
+
module Sensor
|
3
|
+
class BMP180
|
4
|
+
include I2C::Peripheral
|
5
|
+
include Behaviors::Poller
|
6
|
+
|
7
|
+
# Write this to register 0xE0 for soft reset
|
8
|
+
SOFT_RESET = 0xB6
|
9
|
+
|
10
|
+
#
|
11
|
+
# Pressure Oversample Setting Values
|
12
|
+
#
|
13
|
+
# General formula:
|
14
|
+
# 2 ** n, where n is the decimal value of the bits, up to 8x pressure oversampling.
|
15
|
+
#
|
16
|
+
OVERSAMPLE_FACTORS = {
|
17
|
+
1 => 0b00,
|
18
|
+
2 => 0b01,
|
19
|
+
4 => 0b10,
|
20
|
+
8 => 0b11,
|
21
|
+
}
|
22
|
+
|
23
|
+
def before_initialize(options={})
|
24
|
+
@i2c_address = 0x77
|
25
|
+
super(options)
|
26
|
+
end
|
27
|
+
|
28
|
+
def after_initialize(options={})
|
29
|
+
super(options)
|
30
|
+
|
31
|
+
# Avoid repeated memory allocation for callback data and state.
|
32
|
+
@reading = { temperature: nil, pressure: nil }
|
33
|
+
self.state = { temperature: nil, pressure: nil }
|
34
|
+
|
35
|
+
# Default to start conversion off, reading temperature, no pressure oversampling.
|
36
|
+
@register = 0b00001110
|
37
|
+
@calibration_data_loaded = false
|
38
|
+
@oss = 0b00
|
39
|
+
|
40
|
+
# Temporary storage for raw bytes, since two I2C reads are needed for temperature and pressure.
|
41
|
+
@raw_bytes = [0, 0, 0, 0, 0]
|
42
|
+
|
43
|
+
soft_reset
|
44
|
+
end
|
45
|
+
|
46
|
+
#
|
47
|
+
# Configuration Methods
|
48
|
+
#
|
49
|
+
def soft_reset
|
50
|
+
i2c_write(SOFT_RESET)
|
51
|
+
end
|
52
|
+
|
53
|
+
attr_reader :measurement_time
|
54
|
+
|
55
|
+
def update_measurement_time
|
56
|
+
# Get oversample bits from current register setting.
|
57
|
+
oversample_exponent = (@register & 0b11000000) >> 6
|
58
|
+
|
59
|
+
# Calculate time in milliseconds.
|
60
|
+
extra_samples = (2 ** oversample_exponent) - 1
|
61
|
+
extra_time = extra_samples * 3
|
62
|
+
total_time = 4.5 + extra_time
|
63
|
+
|
64
|
+
# Sleep 1ms extra for safety and convert it to seconds.
|
65
|
+
@measurement_time = (total_time + 1) / 1000.0
|
66
|
+
end
|
67
|
+
|
68
|
+
def pressure_samples=(factor)
|
69
|
+
raise ArgumentError, "invalid oversampling factor: #{factor}" unless OVERSAMPLE_FACTORS.keys.include? factor
|
70
|
+
@oss = OVERSAMPLE_FACTORS[factor]
|
71
|
+
end
|
72
|
+
|
73
|
+
def write_settings
|
74
|
+
update_measurement_time
|
75
|
+
i2c_write [0xF4, @register]
|
76
|
+
end
|
77
|
+
|
78
|
+
#
|
79
|
+
# Reading Methods
|
80
|
+
#
|
81
|
+
def _start_read_temperature
|
82
|
+
@register = 0x2E
|
83
|
+
write_settings
|
84
|
+
end
|
85
|
+
|
86
|
+
def _start_read_pressure
|
87
|
+
@register = 0x34 | (@oss << 6)
|
88
|
+
write_settings
|
89
|
+
end
|
90
|
+
|
91
|
+
def _read
|
92
|
+
get_calibration_data unless calibration_data_loaded
|
93
|
+
|
94
|
+
_start_read_temperature
|
95
|
+
sleep(@measurement_time)
|
96
|
+
i2c_read 0xF6, 2
|
97
|
+
|
98
|
+
_start_read_pressure
|
99
|
+
sleep(@measurement_time)
|
100
|
+
i2c_read 0xF6, 3
|
101
|
+
end
|
102
|
+
|
103
|
+
def pre_callback_filter(data)
|
104
|
+
# Temperature is 2 bytes.
|
105
|
+
if data.length == 2
|
106
|
+
@raw_bytes[0] = data[0]
|
107
|
+
@raw_bytes[1] = data[1]
|
108
|
+
# Pressure is 3 bytes and triggers callbacks.
|
109
|
+
elsif data.length == 3
|
110
|
+
@raw_bytes[2] = data[0]
|
111
|
+
@raw_bytes[3] = data[1]
|
112
|
+
@raw_bytes[4] = data[2]
|
113
|
+
return decode_reading(@raw_bytes)
|
114
|
+
# Calibration data is 22 bytes.
|
115
|
+
elsif data.length == 22
|
116
|
+
process_calibration(data)
|
117
|
+
end
|
118
|
+
|
119
|
+
# Anything other than pressure avoids callbacks.
|
120
|
+
return nil
|
121
|
+
end
|
122
|
+
|
123
|
+
def update_state(reading)
|
124
|
+
# Checking for Hash ignores calibration data and nil.
|
125
|
+
if reading.class == Hash
|
126
|
+
@state_mutex.synchronize do
|
127
|
+
@state[:temperature] = reading[:temperature]
|
128
|
+
@state[:pressure] = reading[:pressure]
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def [](key)
|
134
|
+
@state_mutex.synchronize do
|
135
|
+
return @state[key]
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
#
|
140
|
+
# Decoding Methods
|
141
|
+
#
|
142
|
+
def decode_reading(bytes)
|
143
|
+
temperature, b5 = decode_temperature(bytes)
|
144
|
+
@reading[:temperature] = temperature
|
145
|
+
@reading[:pressure] = decode_pressure(bytes, b5)
|
146
|
+
@reading
|
147
|
+
end
|
148
|
+
|
149
|
+
def decode_temperature(bytes)
|
150
|
+
# Temperature is bytes [0..2], MSB first.
|
151
|
+
ut = bytes[0] << 8 | bytes[1]
|
152
|
+
|
153
|
+
# Calibration compensation from datasheet
|
154
|
+
x1 = (ut - @calibration[:ac6]) * @calibration[:ac5] / 32768
|
155
|
+
x2 = (@calibration[:mc] * 2048) / (x1 + @calibration[:md])
|
156
|
+
b5 = x1 + x2
|
157
|
+
|
158
|
+
# 160 instead of 16 since datasheet calculates to 0.1 C units.
|
159
|
+
# Float to force the final value into float, but keep b5 integer for pressure.
|
160
|
+
temperature = (b5 + 8) / 160.0
|
161
|
+
|
162
|
+
# Return temperature and b5 for pressure calculation.
|
163
|
+
[temperature, b5]
|
164
|
+
end
|
165
|
+
|
166
|
+
def decode_pressure(bytes, b5)
|
167
|
+
# Pressure is bytes [2..3], MSB first.
|
168
|
+
up = ((bytes[2] << 16) | (bytes[3] << 8) | (bytes[4])) >> (8 - @oss)
|
169
|
+
|
170
|
+
# Calibration compensation from datasheet
|
171
|
+
b6 = b5 - 4000
|
172
|
+
x1 = (@calibration[:b2] * (b6 * b6 / 4096)) / 2048
|
173
|
+
x2 = @calibration[:ac2] * b6 / 2048
|
174
|
+
x3 = x1 + x2
|
175
|
+
b3 = (((@calibration[:ac1]*4 + x3) << @oss) + 2) / 4
|
176
|
+
x1 = @calibration[:ac3] * b6 / 8192
|
177
|
+
x2 = (@calibration[:b1] * (b6 * b6 / 4096)) / 65536
|
178
|
+
x3 = (x1 + x2 + 2) / 4
|
179
|
+
b4 = (@calibration[:ac4] * ((x3+32768) & 0xFFFF_FFFF)) / 32768
|
180
|
+
b7 = ((up & 0xFFFF_FFFF) - b3) * (50000 >> @oss)
|
181
|
+
if (b7 < 0x8000_0000)
|
182
|
+
p = (b7 * 2) / b4
|
183
|
+
else
|
184
|
+
p = (b7 / b4) * 2
|
185
|
+
end
|
186
|
+
x1 = (p / 256) * (p / 256)
|
187
|
+
x1 = (x1 * 3038) / 65536
|
188
|
+
x2 = (-7357 * p) / 65536
|
189
|
+
p = p + (x1 + x2 + 3791) / 16
|
190
|
+
pressure = p.to_f
|
191
|
+
end
|
192
|
+
|
193
|
+
#
|
194
|
+
# Calibration Methods
|
195
|
+
#
|
196
|
+
attr_reader :calibration_data_loaded
|
197
|
+
|
198
|
+
def get_calibration_data
|
199
|
+
# Calibration data is 22 bytes starting at address 0xAA.
|
200
|
+
read_using -> { i2c_read(0xAA, 22) }
|
201
|
+
end
|
202
|
+
|
203
|
+
def process_calibration(bytes)
|
204
|
+
if bytes
|
205
|
+
@calibration = {
|
206
|
+
ac1: bytes[0..1].pack('C*').unpack('s>')[0],
|
207
|
+
ac2: bytes[2..3].pack('C*').unpack('s>')[0],
|
208
|
+
ac3: bytes[4..5].pack('C*').unpack('s>')[0],
|
209
|
+
ac4: bytes[6..7].pack('C*').unpack('S>')[0],
|
210
|
+
ac5: bytes[8..9].pack('C*').unpack('S>')[0],
|
211
|
+
ac6: bytes[10..11].pack('C*').unpack('S>')[0],
|
212
|
+
b1: bytes[12..13].pack('C*').unpack('s>')[0],
|
213
|
+
b2: bytes[14..15].pack('C*').unpack('s>')[0],
|
214
|
+
mb: bytes[16..17].pack('C*').unpack('s>')[0],
|
215
|
+
mc: bytes[18..19].pack('C*').unpack('s>')[0],
|
216
|
+
md: bytes[20..21].pack('C*').unpack('s>')[0],
|
217
|
+
}
|
218
|
+
@calibration_data_loaded = true
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
data/lib/denko/sensor/htu21d.rb
CHANGED
@@ -128,8 +128,12 @@ module Denko
|
|
128
128
|
|
129
129
|
# Bit 1 of LSB determines type of reading; 0 for temperature, 1 for humidity.
|
130
130
|
if (bytes[1] & 0b00000010) > 0
|
131
|
+
# Calculate humidity and limit within 0-100 range.
|
132
|
+
humidity = (raw_value.to_f / 524.288) - 6
|
133
|
+
humidity = 0.0 if humidity < 0.0
|
134
|
+
humidity = 100.0 if humidity > 100.0
|
131
135
|
@reading[0] = :humidity
|
132
|
-
@reading[1] =
|
136
|
+
@reading[1] = humidity
|
133
137
|
@humidity.update(@reading[1])
|
134
138
|
else
|
135
139
|
@reading[0] = :temperature
|
@@ -138,7 +142,7 @@ module Denko
|
|
138
142
|
end
|
139
143
|
@reading
|
140
144
|
end
|
141
|
-
|
145
|
+
|
142
146
|
def update_state(reading)
|
143
147
|
@state_mutex.synchronize do
|
144
148
|
@state[reading[0]] = reading[1]
|
@@ -0,0 +1,308 @@
|
|
1
|
+
module Denko
|
2
|
+
module Sensor
|
3
|
+
class QMP6988
|
4
|
+
include I2C::Peripheral
|
5
|
+
include Behaviors::Poller
|
6
|
+
|
7
|
+
UPDATE_TIME = 0.020
|
8
|
+
RESET_REGISTER = 0xE0
|
9
|
+
RESET_COMMAND = 0xE6
|
10
|
+
CTRL_MEAS_REGISTER = 0xF4
|
11
|
+
STANDBY_TIME_REGISTER = 0xF5
|
12
|
+
IIR_REGISTER = 0xF1
|
13
|
+
CONFIG_LENGTH = 5
|
14
|
+
DATA_REGISTER = 0xF7
|
15
|
+
DATA_LENGTH = 6
|
16
|
+
CALIBRATION_REGISTER = 0xA0
|
17
|
+
CALIBRATION_LENGTH = 25
|
18
|
+
CHIP_ID_REGISTER = 0xD1
|
19
|
+
CHIP_ID_LENGTH = 1
|
20
|
+
FORCED_MODE = 0b01
|
21
|
+
NORMAL_MODE = 0b11
|
22
|
+
|
23
|
+
# Standby Times for Normal (Continuous) Mode in milliseconds
|
24
|
+
STANDBY_TIMES = {
|
25
|
+
1 => 0b000,
|
26
|
+
5 => 0b001,
|
27
|
+
50 => 0b010,
|
28
|
+
250 => 0b011,
|
29
|
+
500 => 0b100,
|
30
|
+
1000 => 0b101,
|
31
|
+
2000 => 0b110,
|
32
|
+
4000 => 0b111,
|
33
|
+
}
|
34
|
+
|
35
|
+
#
|
36
|
+
# Oversample Setting Values
|
37
|
+
# Note: Each sensor has a separate oversample setting.
|
38
|
+
#
|
39
|
+
# General formula:
|
40
|
+
# 2 ** (n-1), where n is the decimal value of the bits, up to 16x max oversampling.
|
41
|
+
#
|
42
|
+
OVERSAMPLE_FACTORS = {
|
43
|
+
# 0 => 0b000, # Sensor skipped. Value will be 0x800000.
|
44
|
+
1 => 0b001,
|
45
|
+
2 => 0b010,
|
46
|
+
4 => 0b011,
|
47
|
+
8 => 0b100,
|
48
|
+
16 => 0b101,
|
49
|
+
32 => 0b110,
|
50
|
+
64 => 0b111,
|
51
|
+
}
|
52
|
+
#
|
53
|
+
# Single sample times (in milliseconds) for each sensor, derived from datasheet examples.
|
54
|
+
TEMPERATURE_SAMPLE_TIME = 0.9
|
55
|
+
PRESSURE_SAMPLE_TIME = 0.85
|
56
|
+
|
57
|
+
# IIR Filter Coefficients
|
58
|
+
IIR_COEFFICIENTS = {
|
59
|
+
0 => 0b000,
|
60
|
+
2 => 0b001,
|
61
|
+
4 => 0b010,
|
62
|
+
8 => 0b011,
|
63
|
+
16 => 0b100,
|
64
|
+
32 => 0b101, # 0b110 and 0b111 are also valid for 16.
|
65
|
+
}
|
66
|
+
|
67
|
+
def before_initialize(options={})
|
68
|
+
@i2c_address = 0x70
|
69
|
+
super(options)
|
70
|
+
end
|
71
|
+
|
72
|
+
def after_initialize(options={})
|
73
|
+
super(options)
|
74
|
+
|
75
|
+
# Avoid repeated memory allocation for callback data and state.
|
76
|
+
@reading = { temperature: nil, pressure: nil }
|
77
|
+
self.state = { temperature: nil, pressure: nil }
|
78
|
+
|
79
|
+
reset
|
80
|
+
|
81
|
+
# Get 5 config registers. Copy 0xF4 to modify it for control.
|
82
|
+
get_config_registers
|
83
|
+
@ctrl_meas_register = @registers[:f4].dup
|
84
|
+
|
85
|
+
# Default settings
|
86
|
+
self.iir_coefficient = 0
|
87
|
+
self.temperature_samples = 1
|
88
|
+
self.pressure_samples = 1
|
89
|
+
self.forced_mode
|
90
|
+
|
91
|
+
# self.forced mode triggered an initial measurement so IIR works properly if enabled.
|
92
|
+
# Wait for those values to enter the data registers, but don't read them back.
|
93
|
+
sleep @measurement_time
|
94
|
+
|
95
|
+
get_calibration_data
|
96
|
+
end
|
97
|
+
|
98
|
+
#
|
99
|
+
# Configuration Methods
|
100
|
+
#
|
101
|
+
def reset
|
102
|
+
i2c_write [RESET_REGISTER, RESET_COMMAND]
|
103
|
+
sleep UPDATE_TIME
|
104
|
+
end
|
105
|
+
|
106
|
+
def iir_coefficient=(coeff)
|
107
|
+
raise ArgumentError, "invalid IIR coefficient: #{coeff}" unless IIR_COEFFICIENTS.keys.include? coeff
|
108
|
+
i2c_write [IIR_REGISTER, IIR_COEFFICIENTS[coeff]]
|
109
|
+
@iir_coefficient = coeff
|
110
|
+
end
|
111
|
+
attr_reader :iir_coefficient
|
112
|
+
|
113
|
+
def standby_time=(ms)
|
114
|
+
raise ArgumentError, "invalid standby time: #{ms}" unless self.class::STANDBY_TIMES.keys.include? ms
|
115
|
+
byte = STANDBY_TIMES[ms] << 5
|
116
|
+
@standby_time = ms
|
117
|
+
i2c_write [STANDBY_TIME_REGISTER, byte]
|
118
|
+
sleep UPDATE_TIME
|
119
|
+
end
|
120
|
+
attr_reader :standby_time
|
121
|
+
|
122
|
+
def temperature_samples=(factor)
|
123
|
+
raise ArgumentError, "invalid oversampling factor: #{factor}" unless OVERSAMPLE_FACTORS.keys.include? factor
|
124
|
+
@ctrl_meas_register = (@ctrl_meas_register & 0b00011111) | (OVERSAMPLE_FACTORS[factor] << 5)
|
125
|
+
@temperature_samples = factor
|
126
|
+
calculate_measurement_time
|
127
|
+
i2c_write [CTRL_MEAS_REGISTER, @ctrl_meas_register]
|
128
|
+
sleep UPDATE_TIME
|
129
|
+
end
|
130
|
+
attr_reader :temperature_samples
|
131
|
+
|
132
|
+
def pressure_samples=(factor)
|
133
|
+
raise ArgumentError, "invalid oversampling factor: #{factor}" unless OVERSAMPLE_FACTORS.keys.include? factor
|
134
|
+
@ctrl_meas_register = (@ctrl_meas_register & 0b11100011) | (OVERSAMPLE_FACTORS[factor] << 2)
|
135
|
+
@pressure_samples = factor
|
136
|
+
calculate_measurement_time
|
137
|
+
i2c_write [CTRL_MEAS_REGISTER, @ctrl_meas_register]
|
138
|
+
sleep UPDATE_TIME
|
139
|
+
end
|
140
|
+
attr_reader :pressure_samples
|
141
|
+
|
142
|
+
def calculate_measurement_time
|
143
|
+
@measurement_time = (@temperature_samples.to_i * TEMPERATURE_SAMPLE_TIME) +
|
144
|
+
(@pressure_samples.to_i * PRESSURE_SAMPLE_TIME)
|
145
|
+
# Add 5ms for safety and convert to seconds.
|
146
|
+
@measurement_time = (@measurement_time + 5) * 0.001
|
147
|
+
end
|
148
|
+
|
149
|
+
def forced_mode
|
150
|
+
@ctrl_meas_register = (@ctrl_meas_register & 0b11111100) | FORCED_MODE
|
151
|
+
i2c_write [CTRL_MEAS_REGISTER, @ctrl_meas_register]
|
152
|
+
@forced_mode = true
|
153
|
+
sleep UPDATE_TIME
|
154
|
+
end
|
155
|
+
|
156
|
+
def continuous_mode
|
157
|
+
@ctrl_meas_register = (@ctrl_meas_register & 0b11111100) | NORMAL_MODE
|
158
|
+
i2c_write [CTRL_MEAS_REGISTER, @ctrl_meas_register]
|
159
|
+
@forced_mode = false
|
160
|
+
sleep UPDATE_TIME
|
161
|
+
end
|
162
|
+
|
163
|
+
def chip_id
|
164
|
+
return @chip_id if @chip_id
|
165
|
+
i2c_read(CHIP_ID_REGISTER, 1)
|
166
|
+
sleep 0.001 while !@chip_id
|
167
|
+
@chip_id
|
168
|
+
end
|
169
|
+
|
170
|
+
#
|
171
|
+
# Reading & Processing
|
172
|
+
#
|
173
|
+
def _read
|
174
|
+
if @forced_mode
|
175
|
+
# Write CTRL_MEAS register to trigger reading, then wait for measurement.
|
176
|
+
i2c_write [CTRL_MEAS_REGISTER, @ctrl_meas_register]
|
177
|
+
sleep @measurement_time
|
178
|
+
end
|
179
|
+
|
180
|
+
# Read the data bytes.
|
181
|
+
i2c_read(DATA_REGISTER, DATA_LENGTH)
|
182
|
+
end
|
183
|
+
|
184
|
+
def pre_callback_filter(bytes)
|
185
|
+
if bytes.length == DATA_LENGTH
|
186
|
+
return process_reading(bytes)
|
187
|
+
elsif bytes.length == CONFIG_LENGTH
|
188
|
+
process_config(bytes)
|
189
|
+
elsif bytes.length == CALIBRATION_LENGTH
|
190
|
+
process_calibration(bytes)
|
191
|
+
elsif bytes.length == CHIP_ID_LENGTH
|
192
|
+
@chip_id = bytes[0]
|
193
|
+
end
|
194
|
+
return nil
|
195
|
+
end
|
196
|
+
|
197
|
+
def process_reading(bytes)
|
198
|
+
# Temperature and pressure are 24-bits long each, and need 2^23 subtracted.
|
199
|
+
dt = ((bytes[3] << 16) + (bytes[4] << 8) + bytes[5]) - (0b1 << 23)
|
200
|
+
dp = ((bytes[0] << 16) + (bytes[1] << 8) + bytes[2]) - (0b1 << 23)
|
201
|
+
|
202
|
+
# Compensated temperature calculated in 1/256 of a degree Celsius.
|
203
|
+
tr = @calibration[:a0] +
|
204
|
+
@calibration[:a1] * dt +
|
205
|
+
@calibration[:a2] * (dt ** 2)
|
206
|
+
@reading[:temperature] = tr / 256.0
|
207
|
+
|
208
|
+
# Compensated pressure calculated in Pascals.
|
209
|
+
@reading[:pressure] = @calibration[:b00] +
|
210
|
+
@calibration[:bt1] * tr +
|
211
|
+
@calibration[:bp1] * dp +
|
212
|
+
@calibration[:b11] * (tr * dp) +
|
213
|
+
@calibration[:bt2] * (tr ** 2) +
|
214
|
+
@calibration[:bp2] * (dp ** 2) +
|
215
|
+
@calibration[:b12] * (dp * (tr ** 2)) +
|
216
|
+
@calibration[:b21] * ((dp ** 2) * tr) +
|
217
|
+
@calibration[:bp3] * (dp ** 3)
|
218
|
+
|
219
|
+
# Return reading for callbacks.
|
220
|
+
@reading
|
221
|
+
end
|
222
|
+
|
223
|
+
def update_state(reading)
|
224
|
+
@state_mutex.synchronize do
|
225
|
+
@state[:temperature] = reading[:temperature]
|
226
|
+
@state[:pressure] = reading[:pressure]
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
def get_config_registers
|
231
|
+
@registers = {}
|
232
|
+
i2c_read(IIR_REGISTER, CONFIG_LENGTH)
|
233
|
+
sleep 0.001 while @registers.empty?
|
234
|
+
@registers
|
235
|
+
end
|
236
|
+
|
237
|
+
def process_config(bytes)
|
238
|
+
@registers = { f1: bytes[0], f2: bytes[1], f3: bytes[2], f4: bytes[3], f5: bytes[4] }
|
239
|
+
end
|
240
|
+
attr_reader :registers
|
241
|
+
|
242
|
+
#
|
243
|
+
# Calibration
|
244
|
+
#
|
245
|
+
attr_reader :calibration_data_loaded
|
246
|
+
|
247
|
+
CONVERSION_FACTORS = {
|
248
|
+
a1: { A: -6.3e-03, S: 4.3e-04 },
|
249
|
+
a2: { A: -1.9e-11, S: 1.2e-10 },
|
250
|
+
bt1: { A: 1.0e-01, S: 9.1e-02 },
|
251
|
+
bt2: { A: 1.2e-08, S: 1.2e-06 },
|
252
|
+
bp1: { A: 3.3e-02, S: 1.9e-02 },
|
253
|
+
b11: { A: 2.1e-07, S: 1.4e-07 },
|
254
|
+
bp2: { A: -6.3e-10, S: 3.5e-10 },
|
255
|
+
b12: { A: 2.9e-13, S: 7.6e-13 },
|
256
|
+
b21: { A: 2.1e-15, S: 1.2e-14 },
|
257
|
+
bp3: { A: 1.3e-16, S: 7.9e-17 },
|
258
|
+
a0: 16.0,
|
259
|
+
b00: 16.0,
|
260
|
+
}
|
261
|
+
|
262
|
+
def get_calibration_data
|
263
|
+
i2c_read(CALIBRATION_REGISTER, CALIBRATION_LENGTH)
|
264
|
+
end
|
265
|
+
|
266
|
+
def process_calibration(bytes)
|
267
|
+
if bytes
|
268
|
+
# These 2 values are 20-bit instead of 16-bit, so can't combine them with #pack.
|
269
|
+
a0_unsigned = (bytes[18] << 12) + (bytes[19] << 4) + (bytes[24] & 0b00001111)
|
270
|
+
b00_unsigned = (bytes[0] << 12) + (bytes[1] << 4) + ((bytes[24] & 0b11110000) >> 4)
|
271
|
+
|
272
|
+
# Cast the raw bytes as big-endian signed.
|
273
|
+
@calibration_raw = {
|
274
|
+
# Shift these to 32-bit before converting to signed, then reverse the shift after.
|
275
|
+
a0: [(a0_unsigned << 12)].pack('L>').unpack('l>')[0] >> 12,
|
276
|
+
b00: [(b00_unsigned << 12)].pack('L>').unpack('l>')[0] >> 12,
|
277
|
+
|
278
|
+
a1: bytes[20..21].pack('C*').unpack('s>')[0],
|
279
|
+
a2: bytes[22..23].pack('C*').unpack('s>')[0],
|
280
|
+
|
281
|
+
b11: bytes[8..9].pack('C*').unpack('s>')[0],
|
282
|
+
b12: bytes[12..13].pack('C*').unpack('s>')[0],
|
283
|
+
b21: bytes[14..15].pack('C*').unpack('s>')[0],
|
284
|
+
|
285
|
+
bp1: bytes[6..7].pack('C*').unpack('s>')[0],
|
286
|
+
bp2: bytes[10..11].pack('C*').unpack('s>')[0],
|
287
|
+
bp3: bytes[16..17].pack('C*').unpack('s>')[0],
|
288
|
+
|
289
|
+
bt1: bytes[2..3].pack('C*').unpack('s>')[0],
|
290
|
+
bt2: bytes[4..5].pack('C*').unpack('s>')[0],
|
291
|
+
}
|
292
|
+
|
293
|
+
# Use conversion formulae to calculate compensation coefficients, all as floats.
|
294
|
+
@calibration = {}
|
295
|
+
@calibration_raw.keys.each do |key|
|
296
|
+
if CONVERSION_FACTORS[key].class == Float
|
297
|
+
@calibration[key] = @calibration_raw[key] / CONVERSION_FACTORS[key]
|
298
|
+
else
|
299
|
+
@calibration[key] = CONVERSION_FACTORS[key][:A] + (CONVERSION_FACTORS[key][:S] * @calibration_raw[key] / 32767.0)
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
@calibration_data_loaded = true
|
304
|
+
end
|
305
|
+
end
|
306
|
+
end
|
307
|
+
end
|
308
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Denko
|
2
|
+
module Sensor
|
3
|
+
class RCWL9620
|
4
|
+
include I2C::Peripheral
|
5
|
+
include Behaviors::Poller
|
6
|
+
|
7
|
+
def before_initialize(options={})
|
8
|
+
@i2c_address = 0x57
|
9
|
+
super(options)
|
10
|
+
end
|
11
|
+
|
12
|
+
def _read
|
13
|
+
i2c_write(0x01)
|
14
|
+
sleep(0.120)
|
15
|
+
i2c_read(nil, 3)
|
16
|
+
end
|
17
|
+
|
18
|
+
def pre_callback_filter(bytes)
|
19
|
+
# Data is in micrometers, 3 bytes, big-endian.
|
20
|
+
um = (bytes[0] << 16) + (bytes[1] << 8) + bytes[2]
|
21
|
+
mm = um / 1000.0
|
22
|
+
|
23
|
+
# Limit output between 20 and 4500mm.
|
24
|
+
if mm > 4500.0
|
25
|
+
return 4500.0
|
26
|
+
elsif mm < 20.0
|
27
|
+
return 20.0
|
28
|
+
else
|
29
|
+
return mm
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|