denko 0.13.3 → 0.13.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|