Skip to content

Commit 8bc7742

Browse files
committed
Support injecting CRC errors in SHT4X messages
1 parent 5dfe664 commit 8bc7742

1 file changed

Lines changed: 54 additions & 25 deletions

File tree

lib/circuits_sim/device/sht4x.ex

Lines changed: 54 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,20 @@ defmodule CircuitsSim.Device.SHT4X do
1010
alias CircuitsSim.I2C.I2CDevice
1111
alias CircuitsSim.I2C.I2CServer
1212

13-
defstruct current: nil, serial_number: 0x12345678, humidity_rh: 30.0, temperature_c: 22.2
13+
defstruct current: nil,
14+
serial_number: 0x12345678,
15+
humidity_rh: 30.0,
16+
temperature_c: 22.2,
17+
crc_injection_count: 0,
18+
acc: <<>>
1419

1520
@type t() :: %__MODULE__{
1621
current: atom(),
1722
serial_number: integer(),
1823
humidity_rh: float(),
19-
temperature_c: float()
24+
temperature_c: float(),
25+
crc_injection_count: non_neg_integer(),
26+
acc: binary()
2027
}
2128

2229
@spec child_spec(keyword()) :: Supervisor.child_spec()
@@ -32,7 +39,9 @@ defmodule CircuitsSim.Device.SHT4X do
3239
current: nil,
3340
serial_number: integer(),
3441
humidity_rh: float(),
35-
temperature_c: float()
42+
temperature_c: float(),
43+
crc_injection_count: non_neg_integer(),
44+
acc: binary()
3645
}
3746
def new(options \\ []) do
3847
serial_number = options[:serial_number] || 0
@@ -49,30 +58,36 @@ defmodule CircuitsSim.Device.SHT4X do
4958
I2CServer.send_message(bus_name, address, {:set_temperature_c, value})
5059
end
5160

61+
@doc """
62+
Inject CRC errors into the next n CRC fields
63+
64+
Currently all messages have 2 CRC fields, so this will cause CRC mismatch
65+
errors on the next n/2 messages.
66+
"""
67+
@spec inject_crc_errors(String.t(), Circuits.I2C.address(), non_neg_integer()) :: :ok
68+
def inject_crc_errors(bus_name, address, count) when is_integer(count) do
69+
I2CServer.send_message(bus_name, address, {:inject_crc_errors, count})
70+
end
71+
5272
## protocol implementation
5373

5474
defimpl I2CDevice do
5575
@crc_alg :cerlc.init(:crc8_sensirion)
5676

5777
@impl I2CDevice
5878
def read(%{current: :serial_number} = state, count) do
59-
result = binary_for_serial_number(state) |> trim_pad(count)
60-
{result, %{state | current: nil}}
61-
end
62-
63-
def read(%{current: :measure_high_repeatability} = state, count) do
64-
result = raw_sample(state) |> trim_pad(count)
65-
{result, %{state | current: nil}}
66-
end
67-
68-
def read(%{current: :measure_medium_repeatability} = state, count) do
69-
result = raw_sample(state) |> trim_pad(count)
70-
{result, %{state | current: nil}}
79+
new_state = binary_for_serial_number(state)
80+
{trim_pad(new_state.acc, count), %{new_state | current: nil, acc: <<>>}}
7181
end
7282

73-
def read(%{current: :measure_low_repeatability} = state, count) do
74-
result = raw_sample(state) |> trim_pad(count)
75-
{result, %{state | current: nil}}
83+
def read(%{current: op} = state, count)
84+
when op in [
85+
:measure_high_repeatability,
86+
:measure_medium_repeatability,
87+
:measure_low_repeatability
88+
] do
89+
new_state = raw_sample(state)
90+
{trim_pad(new_state.acc, count), %{new_state | current: nil, acc: <<>>}}
7691
end
7792

7893
def read(state, count) do
@@ -110,21 +125,35 @@ defmodule CircuitsSim.Device.SHT4X do
110125
{:ok, %{state | temperature_c: value}}
111126
end
112127

128+
def handle_message(state, {:inject_crc_errors, count}) do
129+
{:ok, %{state | crc_injection_count: count}}
130+
end
131+
113132
defp binary_for_serial_number(state) do
114-
<<state.serial_number::32>> |> add_crcs()
133+
state |> add_crcs(<<state.serial_number::32>>)
115134
end
116135

117136
defp raw_sample(state) do
118137
raw_rh = round((state.humidity_rh + 6) * (0xFFFF - 1) / 125)
119138
raw_t = round((state.temperature_c + 45) * (0xFFFF - 1) / 175)
120-
<<raw_t::16, raw_rh::16>> |> add_crcs()
139+
140+
state |> add_crcs(<<raw_t::16, raw_rh::16>>)
141+
end
142+
143+
defp add_crcs(state, <<>>), do: state
144+
145+
defp add_crcs(state, <<val::2-bytes, rest::binary>>) do
146+
{next_state, crc} = crc(state, val)
147+
this_part = <<val::2-bytes, crc>>
148+
add_crcs(%{next_state | acc: state.acc <> this_part}, rest)
149+
end
150+
151+
defp crc(%{crc_injection_count: 0} = state, v) do
152+
{state, :cerlc.calc_crc(v, @crc_alg)}
121153
end
122154

123-
defp add_crcs(data) do
124-
for <<uint16::16 <- data>>, into: <<>> do
125-
crc = :cerlc.calc_crc(<<uint16::16>>, @crc_alg)
126-
<<uint16::16, crc>>
127-
end
155+
defp crc(%{crc_injection_count: n} = state, v) do
156+
{%{state | crc_injection_count: n - 1}, :cerlc.calc_crc(v, @crc_alg) |> Bitwise.bxor(1)}
128157
end
129158
end
130159
end

0 commit comments

Comments
 (0)