defmodule Circuits.GPIO do alias Circuits.GPIO.Nif @type pin_number :: non_neg_integer() @type pin_direction :: :input | :output @type value :: 0 | 1 @type trigger :: :rising | :falling | :both | :none @type pull_mode :: :not_set | :none | :pullup | :pulldown # Public API @doc """ Open a GPIO for use. `pin` should be a valid GPIO pin number on the system and `pin_direction` should be `:input` or `:output`. """ @spec open(pin_number(), pin_direction()) :: {:ok, reference()} | {:error, atom()} def open(pin_number, pin_direction) do Nif.open(pin_number, pin_direction) end @doc """ Release the resources associated with the GPIO. This is optional. The garbage collector will free GPIO resources that aren't in use, but this will free them sooner. """ @spec close(reference()) :: :ok def close(gpio) do Nif.close(gpio) end @doc """ Read the current value on a pin. """ @spec read(reference()) :: value() def read(gpio) do Nif.read(gpio) end @doc """ Set the value of a pin. The pin should be configured to an output for this to work. """ @spec write(reference(), value()) :: :ok def write(gpio, value) do Nif.write(gpio, value) end @doc """ Enable or disable pin value change notifications. The notifications are sent based on the trigger parameter: * :none - No notifications are sent * :rising - Send a notification when the pin changes from 0 to 1 * :falling - Send a notification when the pin changes from 1 to 0 * :both - Send a notification on all changes Available Options: * `suppress_glitches` - It is possible that the pin transitions to a value and back by the time that Circuits GPIO gets to process it. This controls whether a notification is sent. Set this to `false` to receive notifications. * `receiver` - Process which should receive the notifications. Defaults to the calling process (`self()`) Notifications look like: ``` {:gpio, pin_number, timestamp, value} ``` Where `pin_number` is the pin that changed values, `timestamp` is roughly when the transition occurred in nanoseconds, and `value` is the new value. """ @spec set_interrupts(reference(), trigger(), list()) :: :ok | {:error, atom()} def set_interrupts(gpio, trigger, opts \\ []) do suppress_glitches = Keyword.get(opts, :suppress_glitches, true) receiver = case Keyword.get(opts, :receiver) do pid when is_pid(pid) -> pid name when is_atom(name) -> Process.whereis(name) || self() _ -> self() end Nif.set_interrupts(gpio, trigger, suppress_glitches, receiver) end @doc """ Change the direction of the pin. """ @spec set_direction(reference(), pin_direction()) :: :ok | {:error, atom()} def set_direction(gpio, pin_direction) do Nif.set_direction(gpio, pin_direction) end @doc """ Enable or disable internal pull-up or pull-down resistor to GPIO pin """ @spec set_pull_mode(reference(), pull_mode()) :: :ok | {:error, atom()} def set_pull_mode(gpio, pull_mode) do Nif.set_pull_mode(gpio, pull_mode) end @doc """ Get the GPIO pin number """ @spec pin(reference) :: pin_number def pin(gpio) do Nif.pin(gpio) end @doc """ Return info about the low level GPIO interface This may be helpful when debugging issues. """ @spec info() :: map() defdelegate info(), to: Nif defmodule :circuits_gpio do @moduledoc """ Provide an Erlang friendly interface to Circuits Example Erlang code: circuits_gpio:open(5, output) """ defdelegate open(pin_number, pin_direction), to: Circuits.GPIO defdelegate read(gpio), to: Circuits.GPIO defdelegate write(gpio, value), to: Circuits.GPIO defdelegate set_interrupts(gpio, trigger), to: Circuits.GPIO defdelegate set_interrupts(gpio, trigger, opts), to: Circuits.GPIO defdelegate set_direction(gpio, pin_direction), to: Circuits.GPIO defdelegate set_pull_mode(gpio, pull_mode), to: Circuits.GPIO defdelegate pin(gpio), to: Circuits.GPIO end end