defmodule AHT20 do
@moduledoc """
Read temperature and humidity from AHT20 sensor in Elixir.
"""
use GenServer
require Logger
@default_bus_name "i2c-1"
@aht20_address 0x38
@typedoc """
AHT20 GenServer start_link options
* `:name` - a name for the `GenServer`
* `:bus_name` - which I2C bus to use (defaults to `"i2c-1"`)
"""
@type option() :: {:name, GenServer.name()} | {:bus_name, String.t()}
@doc """
Starts a new GenServer for interacting with a AHT20.
"""
@spec start_link([option()]) :: GenServer.on_start()
def start_link(init_arg \\ []) do
GenServer.start_link(__MODULE__, init_arg, name: init_arg[:name])
end
@doc """
Measures the current temperature and humidity.
"""
@spec measure(GenServer.server()) :: {:ok, AHT20.Measurement.t()} | {:error, any()}
def measure(server), do: GenServer.call(server, :measure)
@impl GenServer
def init(config) do
bus_name = config[:bus_name] || @default_bus_name
Logger.info("[AHT20] Starting on bus #{bus_name} at address #{inspect(@aht20_address, base: :hex)}")
case transport_mod().open(bus_name: bus_name, bus_address: @aht20_address) do
{:ok, transport} ->
{:ok, %{transport: transport}, {:continue, :init_sensor}}
error ->
raise("Error opening i2c: #{inspect(error)}")
end
end
@impl GenServer
def handle_continue(:init_sensor, state) do
Logger.info("[AHT20] Initializing sensor")
case AHT20.Sensor.init(state.transport) do
:ok ->
{:noreply, state}
error ->
raise("Error initializing sensor: #{inspect(error)}")
end
end
@impl GenServer
def handle_call(:measure, _from, state) do
{:reply, AHT20.Sensor.measure(state.transport), state}
end
defp transport_mod() do
Application.get_env(:aht20, :transport_mod, AHT20.Transport.I2C)
end
end