Skip to content

Commit 7ad07ae

Browse files
authored
Add code to detect bad values from sensor, and bad converted values outside operating range (#30)
1 parent 94058e6 commit 7ad07ae

File tree

3 files changed

+86
-6
lines changed

3 files changed

+86
-6
lines changed

lib/sht4x.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ defmodule SHT4X do
2121
The possible values can be:
2222
- `:fresh` - This is a recent sample. See the `:stale_threshold`.
2323
- `:stale` - This is an old sample that should be used with caution.
24-
- `:unusable` - This is a default sample when no measurements are available.
24+
- `:unusable` - This is a default sample when no measurements are available, or, the sensor is giving know bad values (see: https://github.com/elixir-sensors/sht4x/issues/29)
2525
- `:converging` - This is optionally set by the temperature compensation algorithm to indicate that it was recently restarted without historic state information and needs more time to give accurate values
2626
"""
2727
@type quality :: :fresh | :stale | :unusable | :converging

lib/sht4x/measurement.ex

Lines changed: 57 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,19 @@ defmodule SHT4X.Measurement do
22
@moduledoc """
33
One sensor measurement
44
"""
5+
require Logger
56

67
use TypedStruct
78

9+
# Raw readings that, when converted, equate to the min and max operating
10+
# ranges of Rh and temp [0 - 100] for Rh and [-40 - 125] for temperature.
11+
# Expand range by 1 to avoid ceiling/floor issues.
12+
@min_raw_rh 0x0C4A - 1
13+
@max_raw_rh 0xD915 + 1
14+
15+
@min_raw_t 0x0750 - 1
16+
@max_raw_t 0xF8AE + 1
17+
818
typedstruct do
919
field(:dew_point_c, float)
1020
field(:humidity_rh, float, enforce: true)
@@ -22,11 +32,30 @@ defmodule SHT4X.Measurement do
2232
interpreted temperature and humidity. It does not apply any compensation so
2333
this is real temperature and humidity detected.
2434
"""
25-
@spec from_raw(<<_::48>>) :: t()
2635
def from_raw(<<raw_t::16, _crc1, raw_rh::16, _crc2>>) do
2736
timestamp_ms = System.monotonic_time(:millisecond)
28-
temperature_c = temperature_c_from_raw(raw_t)
29-
humidity_rh = humidity_rh_from_raw(raw_rh)
37+
38+
if raw_reading_valid?(raw_t, raw_rh) do
39+
make_measurement(raw_t, raw_rh, timestamp_ms)
40+
else
41+
# Raw readings invalid, don't even attempt to convert them
42+
Logger.warning("Your SHT4X is returning values that could indicate it is damaged!")
43+
44+
__struct__(
45+
temperature_c: 0.0,
46+
humidity_rh: 0.0,
47+
dew_point_c: 0.0,
48+
raw_reading_temperature: raw_t,
49+
raw_reading_humidity: raw_rh,
50+
timestamp_ms: timestamp_ms,
51+
quality: :unusable
52+
)
53+
end
54+
end
55+
56+
defp make_measurement(raw_t, raw_rh, timestamp_ms) do
57+
temperature_c = raw_to_temperature_c(raw_t)
58+
humidity_rh = raw_to_humidity_rh(raw_rh)
3059

3160
__struct__(
3261
temperature_c: temperature_c,
@@ -39,11 +68,34 @@ defmodule SHT4X.Measurement do
3968
)
4069
end
4170

42-
defp humidity_rh_from_raw(raw_rh) do
71+
@spec raw_to_humidity_rh(0..0xFFFF) :: float()
72+
def raw_to_humidity_rh(raw_rh) do
4373
-6 + 125 * raw_rh / (0xFFFF - 1)
4474
end
4575

46-
defp temperature_c_from_raw(raw_t) do
76+
@spec humidity_rh_to_raw(float()) :: integer()
77+
def humidity_rh_to_raw(rh) do
78+
round((rh + 6) / 125 * (0xFFFF - 1))
79+
end
80+
81+
@spec raw_to_temperature_c(0..0xFFFF) :: float()
82+
def raw_to_temperature_c(raw_t) do
4783
-45 + 175 * raw_t / (0xFFFF - 1)
4884
end
85+
86+
@spec temperature_c_to_raw(float()) :: integer()
87+
def temperature_c_to_raw(t) do
88+
round((t + 45) / 175 * (0xFFFF - 1))
89+
end
90+
91+
# Function to check the raw values read from the sensor
92+
# A few bad values are known: 0x8000 and 0x8001 (according to Sensirion)
93+
defp raw_reading_valid?(0x8000, 0x8000), do: false
94+
defp raw_reading_valid?(0x8001, 0x8000), do: false
95+
96+
# Ensure raw values would be within min/max operating ranges
97+
defp raw_reading_valid?(t, _rh) when t not in @min_raw_t..@max_raw_t, do: false
98+
defp raw_reading_valid?(_t, rh) when rh not in @min_raw_rh..@max_raw_rh, do: false
99+
100+
defp raw_reading_valid?(_t, _rh), do: true
49101
end

test/sht4x/measurement_test.exs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,39 @@ defmodule SHT4X.MeasurementTest do
77
test "converts raw measurement" do
88
result = Measurement.from_raw(<<101, 233, 234, 109, 229, 160>>)
99

10+
assert result.quality == :fresh
1011
assert result.raw_reading_temperature == 26_089
1112
assert result.raw_reading_humidity == 28_133
1213

1314
assert_in_delta result.temperature_c, 24.67, 0.01
1415
assert_in_delta result.humidity_rh, 47.66, 0.01
1516
assert_in_delta result.dew_point_c, 12.82, 0.01
1617
end
18+
19+
test "detects possible damaged sensor by looking for 0x8000 in both RH and Temp values" do
20+
result = Measurement.from_raw(<<128, 0, 162, 128, 0, 162>>)
21+
assert result.quality == :unusable
22+
end
23+
24+
test "detects possible damaged sensor by looking for 0x8001 in raw Temp value, and 0x8000 in raw Rh value" do
25+
result = Measurement.from_raw(<<128, 1, 162, 128, 0, 162>>)
26+
assert result.quality == :unusable
27+
end
28+
29+
test "detects possible damaged sensor by looking for values outside of operating range (Rh)" do
30+
result = Measurement.from_raw(<<101, 233, 234, 0xFF, 0xFF, 172>>)
31+
assert result.quality == :unusable
32+
end
33+
34+
test "detects possible damaged sensor by looking for values outside of operating range (C)" do
35+
result = Measurement.from_raw(<<0xFF, 0xFF, 172, 109, 229, 160>>)
36+
assert result.quality == :unusable
37+
end
38+
39+
test "converts boundary values to expected raw values" do
40+
assert Measurement.humidity_rh_to_raw(0) == 0xC4A
41+
assert Measurement.humidity_rh_to_raw(100) == 0xD915
42+
assert Measurement.temperature_c_to_raw(-40) == 0x750
43+
assert Measurement.temperature_c_to_raw(125) == 0xF8AE
44+
end
1745
end

0 commit comments

Comments
 (0)