Packages

Wafer is an Elixir library to make writing drivers for i2c and SPI connected peripherals and interacting with GPIO pins easier.

Current section

Files

Jump to
wafer lib wafer drivers elixir_ale i2c.ex
Raw

lib/wafer/drivers/elixir_ale/i2c.ex

defmodule Wafer.Driver.ElixirALE.I2C do
defstruct ~w[address bus pid]a
alias Wafer.Driver.ElixirALE.I2C.Wrapper
alias Wafer.I2C
import Wafer.Guards
@moduledoc """
A connection to a chip via ElixirALE's I2C driver.
Implements the `Wafer.Conn` behaviour as well as the `Wafer.Chip` and `Wafer.I2C` protocols.
"""
@type t :: %__MODULE__{address: I2C.address(), bus: binary, pid: pid}
@type options :: [option]
@type option :: {:bus_name, binary} | {:address, I2C.address()}
@doc """
Acquire a connection to a peripheral using the ElixirALE I2C driver on the
specified bus and address.
"""
@spec acquire(options) :: {:ok, t} | {:error, reason :: any}
def acquire(opts) when is_list(opts) do
with {:ok, bus} when is_binary(bus) <- Keyword.fetch(opts, :bus_name),
{:ok, address} when is_i2c_address(address) <- Keyword.fetch(opts, :address),
{:ok, pid} <- Wrapper.start_link(bus, address),
devices when is_list(devices) <- Wrapper.detect_devices(pid),
true <- Keyword.get(opts, :force, false) || Enum.member?(devices, address) do
{:ok, %__MODULE__{bus: bus, address: address, pid: pid}}
else
false ->
{:error, "No device detected at address. Pass `force: true` to override."}
:error ->
{:error, "ElixirALE.I2C requires both `bus_name` and `address` options."}
{:error, reason} ->
{:error, reason}
end
end
end
defimpl Wafer.Release, for: Wafer.Driver.ElixirALE.I2C do
alias Wafer.Driver.ElixirALE.I2C.Wrapper
alias Wafer.Driver.ElixirALE.I2C
@doc """
Release all resources associated with this I2C device.
"""
@spec release(I2C.t()) :: :ok | {:error, reason :: any}
def release(%I2C{pid: pid} = _conn) when is_pid(pid), do: Wrapper.release(pid)
end
defimpl Wafer.Chip, for: Wafer.Driver.ElixirALE.I2C do
alias Wafer.Driver.ElixirALE.I2C.Wrapper
import Wafer.Guards
def read_register(%{pid: pid}, register_address, bytes)
when is_pid(pid) and is_register_address(register_address) and is_byte_size(bytes) do
case Wrapper.write_read(pid, <<register_address>>, bytes) do
data when is_binary(data) and byte_size(data) == bytes -> {:ok, data}
{:error, reason} -> {:error, reason}
other -> {:error, "Invalid response from driver: #{inspect(other)}"}
end
end
def read_register(_conn, _register_address, _bytes), do: {:error, "Invalid argument"}
def write_register(%{pid: pid} = conn, register_address, data)
when is_pid(pid) and is_register_address(register_address) and is_binary(data) do
with :ok <- Wrapper.write(pid, <<register_address, data::binary>>), do: {:ok, conn}
end
def write_register(_conn, _register_address, _data), do: {:error, "Invalid argument"}
def swap_register(conn, register_address, data)
when is_register_address(register_address) and is_binary(data) do
with {:ok, old_data} <- read_register(conn, register_address, byte_size(data)),
{:ok, conn} <- write_register(conn, register_address, data) do
{:ok, old_data, conn}
end
end
def swap_register(_conn, _register_address, _data), do: {:error, "Invalid argument"}
end
defimpl Wafer.I2C, for: Wafer.Driver.ElixirALE.I2C do
import Wafer.Guards
alias Wafer.Driver.ElixirALE.I2C.Wrapper
def read(%{pid: pid}, bytes, options \\ [])
when is_pid(pid) and is_byte_size(bytes) and is_list(options) do
case Wrapper.read(pid, bytes) do
data when is_binary(data) and byte_size(data) == bytes -> {:ok, data}
{:error, reason} -> {:error, reason}
other -> {:error, "Invalid response from driver: #{inspect(other)}"}
end
end
def write(%{pid: pid} = conn, data, options \\ [])
when is_pid(pid) and is_binary(data) and is_list(options) do
with :ok <- Wrapper.write(pid, data), do: {:ok, conn}
end
def write_read(%{pid: pid} = conn, data, bytes, options \\ [])
when is_pid(pid) and is_binary(data) and is_byte_size(bytes) and is_list(options) do
case Wrapper.write_read(pid, data, bytes) do
data when is_binary(data) and byte_size(data) == bytes -> {:ok, data, conn}
{:error, reason} -> {:error, reason}
other -> {:error, "Invalid response from driver: #{inspect(other)}"}
end
end
def detect_devices(%{pid: pid}) do
case Wrapper.detect_devices(pid) do
devices when is_list(devices) -> {:ok, devices}
{:error, reason} -> {:error, reason}
other -> {:error, "Invalid response from driver: #{inspect(other)}"}
end
end
end
defimpl Wafer.DeviceID, for: Wafer.Driver.ElixirALE.I2C do
def id(%{address: address, bus: bus}), do: {Wafer.Driver.ElixirALE.I2C, bus, address}
end