@@ -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
130159end
0 commit comments