Packages

Reads data from RaspberryPi GPIO pins

Current section

Files

Jump to
exred_node_gpio_in lib exred_node_gpio_in.ex
Raw

lib/exred_node_gpio_in.ex

defmodule Exred.Node.GPIOIn do
@moduledoc """
Receives data from a GPIO pin.
Uses [Elixir ALE](https://github.com/fhunleth/elixir_ale) to interface with GPIO.
It can work in two modes:
- 'monitor': monitors the pin and sends a message on pin interrupt
- 'read\\_on\\_message': reads the pin state and sends the data when triggered by an incoming message
**Incoming message format**
Anything
**Outgoing message format**
_read on message_ mode:
```elixir
msg = %{
payload :: 0 | 1
}
```
_monitor_ mode:
```elixir
msg = %{
payload :: %{
pin_number :: integer,
condition :: :rising | :falling
}
}
```
"""
@name "GPIO In"
@category "input"
@info @moduledoc
@config %{
name: %{
info: "Visible node name",
value: @name,
type: "string",
attrs: %{max: 20}
},
pin_number: %{
info: "GPIO pin number that the node will read",
value: 0,
type: "number",
attrs: %{min: 0}
},
mode: %{
info: "read_on_message or monitor",
type: "list-singleselect",
value: nil,
attrs: %{items: ["read_on_message", "monitor"]}
},
monitored_transition: %{
info: "send message in rising and/or falling edge",
type: "list-multiselect",
value: [],
attrs: %{items: ["rising", "falling"]}
}
}
@ui_attributes %{
fire_button: true,
right_icon: "settings-input-component",
config_order: [:name,:pin_number,:mode,:monitored_transition]
}
use Exred.Library.NodePrototype
alias ElixirALE.GPIO
require Logger
@impl true
def node_init(state) do
# return immediately and do inititalization in handle_cast
GenServer.cast self(), :do_init
Map.put(state, :init, :starting)
end
@impl true
def handle_cast(:do_init, %{init: :starting} = state) do
Logger.debug "node: #{state.node_id} #{get_in(state.config, [:name, :value])} GOT CAST: :do_init"
# start GPIO process
{:ok, pid} = GPIO.start_link(state.config.pin_number.value, :input)
Logger.debug "node: #{state.node_id} #{get_in(state.config, [:name, :value])} STARTED GPIO server: #{inspect pid}"
# set interrupt monitoring if in 'monitor' mode
if state.config.mode.value == "monitor" do
interrupt = case state.config.monitored_transition.value do
[] -> :none
["rising"] -> :rising
["falling"] -> :falling
_ -> :both
end
GPIO.set_int(pid, interrupt)
end
IO.puts "GPIO STARTED!!"
new_state = state
|> Map.put(:pid, pid)
|> Map.put(:init, :done)
{:noreply, new_state}
end
@impl true
def handle_cast(cast, state) do
Logger.debug "node: #{state.node_id} #{get_in(state.config, [:name, :value])} UNHANDLED CAST: #{inspect cast}"
{:noreply, state}
end
def handle_msg(msg, %{init: :done} = state) do
case [state.config.mode.value, msg] do
["read_on_message", _] ->
# pin state is 0 or 1
pin_state = GPIO.read(state.pid)
{ Map.put(msg, :payload, pin_state), state}
["monitor", {:gpio_interrupt, _pin, condition}] ->
# condition can be :rising or :falling
payload = %{pin: state.config.pin_number.value, condition: condition}
{ %{payload: payload}, state}
# catch all
[mode, _] ->
Logger.warn "unhandled message in #{mode} mode"
{msg, state}
end
end
def handle_msg(msg, %{init: :starting} = state) do
Logger.warn "UNHANDLED MSG DURING INIT node: #{state.node_id} #{get_in(state.config, [:name, :value])} msg: #{inspect msg}"
{nil, state}
end
@impl true
def fire(state) do
# send a message to self to trigger reading the pin
send self(), "fire"
state
end
end