lib/hts221/transport.ex

defmodule HTS221.Transport do
  @moduledoc """
  A behaviour to communicate to the HTS221
  """

  alias HTS221.{IORead, IOWrite}

  import Bitwise

  @typedoc """
  Arguments for the implementation `init/1` callback

  See the implementation's docs to see what the arguments are.
  """
  @type arg() :: any()

  @typedoc """
  A module that implements the `HTS221.Transport` behaviour
  """
  @type impl() :: module()

  @typedoc """
  The underline communication data structure for the transport implementation
  to use when sending data to the HTS221.

  For example in `Circuits.I2C` the data structure is a reference.
  """
  @type comm() :: any()

  @type t() :: {impl(), comm()}

  @doc """
  Initialize the transport implementation
  """
  @callback init([arg()]) :: {:ok, t()} | {:error, any()}

  @doc """
  Read the register
  """
  @callback read_register(comm(), binary(), non_neg_integer()) ::
              {:ok, binary()} | {:error, any()}

  @doc """
  Write the register
  """
  @callback write_register(comm(), binary()) :: :ok | {:error, any()}

  @doc """
  Helper function initializing the transport implementation
  """
  @spec init(impl(), [arg()]) :: {:ok, t()} | {:error, any()}
  def init(impl, args \\ []) do
    impl.init(args)
  end

  @doc """
  Send the IO request to the HTS221
  """
  @spec send(t(), IORead.t() | IOWrite.t()) :: :ok | {:ok, binary()} | {:error, any()}
  def send({impl, comm}, %IORead{} = io_request) do
    binary = make_binary(io_request)

    impl.read_register(comm, binary, io_request.length)
  end

  def send({impl, comm}, %IOWrite{} = io_request) do
    impl.write_register(comm, io_request.payload)
  end

  defp make_binary(%IORead{length: n} = io_request) when n > 1 do
    # When we want to read many registers at once the MSB bit needs to be set to
    # 1.
    <<io_request.register ||| 0x80>>
  end

  defp make_binary(io_request) do
    <<io_request.register>>
  end
end