defmodule SHT4X do
@moduledoc """
Use Sensirion SHT4X humidity and temperature sensors in Elixir
"""
use GenServer
require Logger
@typedoc """
SHT4X GenServer start_link options
* `:name` - a name for the GenServer
* `:bus_name` - which I2C bus to use (e.g., `"i2c-1"`)
* `:retries` - the number of retries before failing (defaults to no retries)
"""
@type options() :: [GenServer.option() | {:bus_name, binary}]
@default_bus_name "i2c-1"
@bus_address 0x44
## Public API
@doc """
Start a new GenServer for interacting with a SHT4X.
"""
@spec start_link(options()) :: GenServer.on_start()
def start_link(opts \\ []) do
gen_server_opts = Keyword.take(opts, [:name, :debug, :timeout, :spawn_opt, :hibernate_after])
init_arg = Keyword.take(opts, [:bus_name, :retries])
GenServer.start_link(__MODULE__, init_arg, gen_server_opts)
end
@doc """
Measure the current temperature and pressure.
An error is returned if the I2C transactions fail.
"""
@spec measure(GenServer.server(), Keyword.t()) :: {:ok, SHT4X.Measurement.t()} | {:error, any}
def measure(server, opts \\ []) do
GenServer.call(server, {:measure, opts})
end
## Callbacks
@impl GenServer
def init(init_arg) do
bus_name = init_arg[:bus_name] || @default_bus_name
bus_address = @bus_address
retries = init_arg[:retries] || 0
Logger.info(
"[SHT4X] Starting on bus #{bus_name} at address #{inspect(bus_address, base: :hex)}"
)
with {:ok, transport} <- SHT4X.Transport.new(bus_name, bus_address, retries),
{:ok, serial_number} <- SHT4X.Comm.serial_number(transport) do
Logger.info("[SHT4X] Initializing sensor #{serial_number}")
state = %{
serial_number: serial_number,
transport: transport
}
{:ok, state}
else
{:error, reason} ->
Logger.error("[SHT4X] Error connecting to sensor: #{reason}")
{:stop, :normal}
:error ->
Logger.error("[SHT4X] Error connecting to sensor")
{:stop, :normal}
end
end
@impl GenServer
def handle_call({:measure, opts}, _from, state) do
{:ok, data} = SHT4X.Comm.measure(state.transport, opts)
{:reply, {:ok, SHT4X.Measurement.from_raw(data)}, state}
end
end