Skip to content

Commit df3774e

Browse files
authored
BMP280: support all sensor types (#12)
1 parent f67c8f7 commit df3774e

3 files changed

Lines changed: 271 additions & 97 deletions

File tree

lib/circuits_sim/device/bmp280.ex

Lines changed: 90 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -6,121 +6,121 @@ defmodule CircuitsSim.Device.BMP280 do
66
See the [datasheet](https://www.mouser.com/datasheet/2/783/BST-BME280-DS002-1509607.pdf) for details.
77
Many features aren't implemented.
88
"""
9+
alias CircuitsSim.Device.BMP280.Register
910
alias CircuitsSim.I2C.I2CServer
1011
alias CircuitsSim.I2C.SimpleI2CDevice
11-
alias CircuitsSim.Tools
1212

1313
@spec child_spec(keyword()) :: Supervisor.child_spec()
1414
def child_spec(args) do
15-
device = __MODULE__.new()
15+
device_options = Keyword.take(args, [:sensor_type])
16+
device = __MODULE__.new(device_options)
1617
I2CServer.child_spec_helper(device, args)
1718
end
1819

19-
defstruct registers: %{}
20+
defstruct registers: %{}, sensor_type: nil
2021

21-
@type t() :: %__MODULE__{registers: map()}
22+
@type sensor_type() :: :bmp180 | :bmp280 | :bme280 | :bme680
23+
@type options() :: [sensor_type: sensor_type()]
24+
@type t() :: %__MODULE__{registers: map(), sensor_type: sensor_type()}
2225

23-
@spec new() :: t()
24-
def new() do
25-
%__MODULE__{registers: default_registers()}
26+
@spec new(options()) :: t()
27+
def new(options \\ []) do
28+
sensor_type = options[:sensor_type] || :bme280
29+
registers = Register.default_registers(sensor_type)
30+
%__MODULE__{registers: registers, sensor_type: sensor_type}
2631
end
2732

28-
defp default_registers() do
29-
for(r <- 0x00..0xFF, into: %{}, do: {r, <<0>>})
30-
|> put_sensor_type_register()
31-
|> put_calibration_registers()
32-
|> put_raw_sample_registers()
33-
end
33+
## protocol implementation
3434

35-
defp put_sensor_type_register(%{} = registers) do
36-
registers |> put_in([reg(:sensor_type)], sensor_type_value(:bme280))
37-
end
35+
defimpl SimpleI2CDevice do
36+
@bmp180_reg_control_measurement 0xF4
37+
@bmp180_set_pressure_reading 0x34
38+
@bmp180_set_temperature_reading 0x2E
39+
@bmp280_reg_control_measurement 0xF4
40+
@bme280_reg_control_measurement 0xF4
41+
@bme680_reg_status0 0x1D
3842

39-
defp reg(:sensor_type), do: 0xD0
40-
# defp sensor_type_value(:bmp180), do: <<0x55>>
41-
# defp sensor_type_value(:bmp280), do: <<0x58>>
42-
defp sensor_type_value(:bme280), do: <<0x60>>
43-
# defp sensor_type_value(:bme680), do: <<0x61>>
44-
45-
defp put_calibration_registers(%{} = registers) do
46-
[
47-
# calib00: 26 bytes from 0x88
48-
{0x88, <<29>>},
49-
{0x89, <<110>>},
50-
{0x8A, <<173>>},
51-
{0x8B, <<102>>},
52-
{0x8C, <<50>>},
53-
{0x8D, <<0>>},
54-
{0x8E, <<27>>},
55-
{0x8F, <<143>>},
56-
{0x90, <<56>>},
57-
{0x91, <<214>>},
58-
{0x92, <<208>>},
59-
{0x93, <<11>>},
60-
{0x94, <<84>>},
61-
{0x95, <<43>>},
62-
{0x96, <<15>>},
63-
{0x97, <<255>>},
64-
{0x98, <<249>>},
65-
{0x99, <<255>>},
66-
{0x9A, <<12>>},
67-
{0x9B, <<48>>},
68-
{0x9C, <<32>>},
69-
{0x9D, <<209>>},
70-
{0x9E, <<136>>},
71-
{0x9F, <<19>>},
72-
{0xA0, <<0>>},
73-
{0xA1, <<75>>},
74-
# calib26: 7 bytes from 0xE1
75-
{0xE1, <<82>>},
76-
{0xE2, <<1>>},
77-
{0xE3, <<0>>},
78-
{0xE4, <<23>>},
79-
{0xE5, <<44>>},
80-
{0xE6, <<3>>},
81-
{0xE7, <<30>>}
82-
]
83-
|> Enum.into(registers)
84-
end
43+
@impl SimpleI2CDevice
44+
def write_register(
45+
%{sensor_type: :bmp180} = state,
46+
@bmp180_reg_control_measurement = reg,
47+
@bmp180_set_pressure_reading = value
48+
) do
49+
registers =
50+
state.registers
51+
|> Map.put(reg, value)
52+
|> Map.merge(Register.measurement_registers(:bmp180, :pressure))
8553

86-
defp put_raw_sample_registers(%{} = registers) do
87-
[
88-
# press_msb: 8 bytes from 0xF7
89-
{0xF7, <<69>>},
90-
{0xF8, <<89>>},
91-
{0xF9, <<64>>},
92-
{0xFA, <<130>>},
93-
{0xFB, <<243>>},
94-
{0xFC, <<0>>},
95-
{0xFD, <<137>>},
96-
{0xFE, <<109>>}
97-
]
98-
|> Enum.into(registers)
99-
end
54+
%{state | registers: registers}
55+
end
10056

101-
## protocol implementation
57+
def write_register(
58+
%{sensor_type: :bmp180} = state,
59+
@bmp180_reg_control_measurement = reg,
60+
@bmp180_set_temperature_reading = value
61+
) do
62+
registers =
63+
state.registers
64+
|> Map.put(reg, value)
65+
|> Map.merge(Register.measurement_registers(:bmp180, :temperature))
10266

103-
defimpl SimpleI2CDevice do
104-
@impl SimpleI2CDevice
105-
def write_register(state, reg, value), do: put_in(state.registers[reg], <<value>>)
67+
%{state | registers: registers}
68+
end
69+
70+
def write_register(
71+
%{sensor_type: :bmp280} = state,
72+
@bmp280_reg_control_measurement = reg,
73+
value
74+
) do
75+
registers =
76+
state.registers
77+
|> Map.put(reg, value)
78+
|> Map.merge(Register.measurement_registers(:bmp280))
79+
80+
%{state | registers: registers}
81+
end
82+
83+
def write_register(
84+
%{sensor_type: :bme280} = state,
85+
@bme280_reg_control_measurement = reg,
86+
value
87+
) do
88+
registers =
89+
state.registers
90+
|> Map.put(reg, value)
91+
|> Map.merge(Register.measurement_registers(:bme280))
92+
93+
%{state | registers: registers}
94+
end
95+
96+
def write_register(state, reg, value) do
97+
put_in(state.registers[reg], value)
98+
end
10699

107100
@impl SimpleI2CDevice
101+
def read_register(
102+
%{sensor_type: :bme680} = state,
103+
@bme680_reg_status0
104+
) do
105+
registers =
106+
state.registers
107+
|> Map.merge(Register.measurement_registers(:bme680))
108+
109+
new_data = 1
110+
result = <<new_data::1, 0::7>>
111+
{result, %{state | registers: registers}}
112+
end
113+
108114
def read_register(state, reg), do: {state.registers[reg], state}
109115

110116
@impl SimpleI2CDevice
111117
def render(state) do
112-
for {reg, data} <- state.registers do
113-
[
114-
" ",
115-
Tools.hex_byte(reg),
116-
": ",
117-
for(<<b::1 <- data>>, do: to_string(b)),
118-
"\n"
119-
]
120-
end
118+
"Sensor type: #{state.sensor_type}\n"
121119
end
122120

123121
@impl SimpleI2CDevice
124-
def handle_message(state, _message), do: state
122+
def handle_message(state, _message) do
123+
{:not_implemented, state}
124+
end
125125
end
126126
end
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
defmodule CircuitsSim.Device.BMP280.Register do
2+
@moduledoc false
3+
4+
alias CircuitsSim.Device.BMP280
5+
6+
@spec default_registers(BMP280.sensor_type()) :: %{byte() => byte()}
7+
def default_registers(sensor_type) do
8+
for(r <- 0x00..0xFF, into: %{}, do: {r, 0})
9+
|> Map.merge(sensor_type_register(sensor_type))
10+
|> Map.merge(calibration_registers(sensor_type))
11+
end
12+
13+
defp sensor_type_register(:bmp180), do: %{0xD0 => 0x55}
14+
defp sensor_type_register(:bmp280), do: %{0xD0 => 0x58}
15+
defp sensor_type_register(:bme280), do: %{0xD0 => 0x60}
16+
defp sensor_type_register(:bme680), do: %{0xD0 => 0x61}
17+
18+
defp calibration_registers(:bmp180) do
19+
# calib00: 22 bytes from 0xAA
20+
<<25, 38, 251, 185, 200, 200, 133, 213, 100, 76, 63, 129, 25, 115, 0, 40, 128, 0, 209, 246, 9,
21+
104>>
22+
|> binary_to_address_byte_map({0xAA, 22})
23+
end
24+
25+
defp calibration_registers(:bmp280) do
26+
# calib00: 24 bytes from 0x88
27+
<<29, 110, 173, 102, 50, 0, 27, 143, 56, 214, 208, 11, 84, 43, 15, 255, 249, 255, 12, 48, 32,
28+
209, 136, 19>>
29+
|> binary_to_address_byte_map({0x88, 24})
30+
end
31+
32+
defp calibration_registers(:bme280) do
33+
# calib00: 26 bytes from 0x88
34+
calib00 =
35+
<<29, 110, 173, 102, 50, 0, 27, 143, 56, 214, 208, 11, 84, 43, 15, 255, 249, 255, 12, 48,
36+
32, 209, 136, 19, 0, 75>>
37+
|> binary_to_address_byte_map({0x88, 26})
38+
39+
# calib26: 7 bytes from 0xE1
40+
calib26 =
41+
<<82, 1, 0, 23, 44, 3, 30>>
42+
|> binary_to_address_byte_map({0xE1, 7})
43+
44+
Map.merge(calib00, calib26)
45+
end
46+
47+
defp calibration_registers(:bme680) do
48+
# coeff1: 23 bytes from 0x8A
49+
coeff1 =
50+
<<178, 102, 3, 16, 67, 138, 91, 215, 88, 0, 228, 18, 138, 255, 26, 30, 0, 0, 3, 253, 217,
51+
242, 30>>
52+
|> binary_to_address_byte_map({0x8A, 23})
53+
54+
# coeff2: 14 bytes from 0xE1
55+
coeff2 =
56+
<<63, 221, 44, 0, 45, 20, 120, 156, 83, 102, 175, 232, 226, 18>>
57+
|> binary_to_address_byte_map({0xE1, 14})
58+
59+
# coeff3: 5 bytes from 0x00
60+
coeff3 =
61+
<<50, 170, 22, 74, 19>>
62+
|> binary_to_address_byte_map({0x00, 5})
63+
64+
coeff1 |> Map.merge(coeff2) |> Map.merge(coeff3)
65+
end
66+
67+
@spec measurement_registers(BMP280.sensor_type(), any()) :: %{byte() => byte()}
68+
def measurement_registers(_sensor_type, _options \\ nil)
69+
70+
def measurement_registers(:bmp180, :temperature) do
71+
<<95, 16, 0>>
72+
|> binary_to_address_byte_map({0xF6, 3})
73+
end
74+
75+
def measurement_registers(:bmp180, :pressure) do
76+
<<161, 135, 0>>
77+
|> binary_to_address_byte_map({0xF6, 3})
78+
end
79+
80+
def measurement_registers(:bmp280, _) do
81+
# press_msb: 6 bytes from 0xF7
82+
<<69, 89, 64, 130, 243, 0>>
83+
|> binary_to_address_byte_map({0xF7, 6})
84+
end
85+
86+
def measurement_registers(:bme280, _) do
87+
# press_msb: 8 bytes from 0xF7
88+
<<69, 89, 64, 130, 243, 0, 137, 109>>
89+
|> binary_to_address_byte_map({0xF7, 8})
90+
end
91+
92+
def measurement_registers(:bme680, _) do
93+
# press_msb: 8 bytes from 0x1F
94+
pres_msb =
95+
<<96, 30, 144, 117, 93, 192, 65, 180>>
96+
|> binary_to_address_byte_map({0x1F, 8})
97+
98+
# gas_r_msb: 2bytes from 0x2A
99+
gas_r_msb =
100+
<<166, 139>>
101+
|> binary_to_address_byte_map({0x2A, 2})
102+
103+
Map.merge(pres_msb, gas_r_msb)
104+
end
105+
106+
defp binary_to_address_byte_map(data, {address, how_many}) do
107+
addresses = address..(address + how_many - 1)
108+
bytes = for(<<r::8 <- data>>, do: r)
109+
Enum.zip(addresses, bytes) |> Map.new()
110+
end
111+
end

0 commit comments

Comments
 (0)