概要
NervesにPhoenix LiveViewを入れて2つのLEDを操作しました。
操作したLEDの状態はループを使わずオン・オフを取得しています。
- NervesにPhoenix LiveViewをインストール
- LEDのオン・オフをLiveView経由で操作
- LEDの状態をループを使わず取得しLiveViewの画面に反映
❯ mix nerves.new -v
Nerves Bootstrap v1.8.1
❯ mix phx.new -v
Phoenix v1.5.4
構成
準備
Ponchoプロジェクトストラクチャーとしてプロジェクトを作成します。
細かい設定は以下の「NervesにPhoenixを入れてHTTPのGETでLEDをウェブチカ」シリーズを参照ください。
led_live
ディレクトリの中にNervesプロジェクトのexineris
、Phoenixプロジェクトのledlive
という構成としました。
❯ tree -L 1 led_live/
led_live/
├── exineris ←Nervesのプロジェクトディレクトリ
└── ledlive ←Phoenixのプロジェクトディレクトリ
Nerves 1.5になったあたりから--nerves-pack
オプションが不要になったようだったので今回からオプションを付けないことにしました。
❯ mix nerves.new exineris
❯ mix phx.new ledlive --no-ecto --live
Phoenixプロジェクトを作成する際に--live
オプションをつけてプロジェクト作成をして、NervesでPhoenixが稼働するところまで確認できているとします。
関連ファイル
mix.exs
config/
config.exs ← `mix phx.gen.secret 32`の結果を:ledliveのlive_view:に記載
prod.secret.exs ← `mix phx.gen.secret`の結果をsecret_key_baseに文字列として代入
lib/
ledlive/
gpioinout.ex
ledlive_web/
live/
led_live.ex
led_live.html.leex
router.ex
mix.exs
mix.exsファイルのdepsに以下の行を追記して、ElixirからGPIOの操作をできるようにするのと、ログを出すようにします。
defp deps do
[
{:circuits_gpio, "~> 0.4"},
{:ring_logger, "~> 0.6"},
]
end
追記した後、mix deps.getコマンドを実行します。
❯ mix deps.get
config/config.exs
mix phx.gen.secret 32
の実行結果をconfig/config.exs
内のsigning_salt
に記載します。
❯ mix phx.gen.secret 32
+g9KvVB5oVUgFONNEn+GJjxLumepvXYH
config :ledlive, LedliveWeb.Endpoint,
url: [host: "localhost"],
secret_key_base: "va8GycF6U0iNU41MOWMmNL88weq4LR97IV6VTk+geG4Xud5ga05PCzL5cdvAV8N1",
render_errors: [view: LedliveWeb.ErrorView, accepts: ~w(html json), layout: false],
pubsub_server: Ledlive.PubSub,
live_view: [signing_salt: "+g9KvVB5oVUgFONNEn+GJjxLumepvXYH"]
config/prod.secret.exs
mix phx.gen.secret
の実行結果をconfig/prod.secret.exs
内のsecret_key_base
に記載します。
❯ mix phx.gen.secret
xSmfKWUn3kmXou7tNV5lge4AeCoaJDJpALjB7/xZvRsxP7IPHs0Ojn37U6yJCLAn
secret_key_base = "xSmfKWUn3kmXou7tNV5lge4AeCoaJDJpALjB7/xZvRsxP7IPHs0Ojn37U6yJCLAn"
lib/ledlive/gpioinout.ex
kochi.exな @kikuyuta さん作ライブラリのgpioinout.ex
を設置します。
defmodule GpioInOut do
@behaviour GenServer
require Circuits.GPIO
require Logger
def start_link(pname, gpio_no, in_out, ppid \\ []) do
Logger.debug("#{__MODULE__} start_link: #{inspect(pname)}, #{gpio_no}, #{in_out} #{inspect(ppid)}")
GenServer.start_link(__MODULE__, {gpio_no, in_out, ppid}, name: pname)
end
def write(pname, :true), do: GenServer.cast(pname, {:write, 1})
def write(pname, :false), do: GenServer.cast(pname, {:write, 0})
def write(pname, val), do: GenServer.cast(pname, {:write, val})
def read(pname), do: GenServer.call(pname, :read)
def stop(pname), do: GenServer.stop(pname)
@impl GenServer
def init({gpio_no, in_out = :input, ppid}) do
Logger.debug("#{__MODULE__} init_open: #{gpio_no}, #{in_out} ")
{:ok, gpioref} = Circuits.GPIO.open(gpio_no, in_out)
Circuits.GPIO.set_interrupts(gpioref, :both, receiver: ppid)
{:ok, gpioref}
end
@impl GenServer
def init({gpio_no, in_out = :output, _ppid}) do
Logger.debug("#{__MODULE__} init_open: #{gpio_no}, #{in_out} ")
Circuits.GPIO.open(gpio_no, in_out)
end
@impl GenServer
def handle_cast({:write, val}, gpioref) do
Logger.debug("#{__MODULE__} :write #{val} ")
Circuits.GPIO.write(gpioref, val)
{:noreply, gpioref}
end
@impl GenServer
def handle_call(:read, _from, gpioref) do
{:reply, {:ok, Circuits.GPIO.read(gpioref)}, gpioref}
end
@impl GenServer
def handle_info(msg, gpioref) do
Logger.warn("#{__MODULE__} get_message: #{inspect(msg)}")
Circuits.GPIO.set_interrupts(gpioref, :both)
{:noreply, gpioref}
end
@impl GenServer
def terminate(reason, gpioref) do
Logger.debug("#{__MODULE__} terminate: #{inspect(reason)}")
Circuits.GPIO.close(gpioref)
reason
end
end
ledlive_web/live/led_live.ex
defmodule LedliveWeb.LedLive do
use Phoenix.LiveView
# import Calendar.Strftime
require Logger
@gpio24 24
@gpio25 25
def mount(_param, _session, socket) do
if connected?(socket) do
GpioInOut.start_link(:gpio24in, @gpio24, :input, self())
GpioInOut.start_link(:gpio25in, @gpio25, :input, self())
GpioInOut.start_link(:gpio24out, @gpio24, :output)
GpioInOut.start_link(:gpio25out, @gpio25, :output)
end
{:ok, assign(socket, led24: "...", led25: "...", ledon: "...", led24_click: "", led25_click: "")}
end
def handle_info({:circuits_gpio, @gpio24, _time, on_off}, socket) do
{:noreply, assign(socket, led24: on_off)}
end
def handle_info({:circuits_gpio, @gpio25, _time, on_off}, socket) do
{:noreply, assign(socket, led25: on_off)}
end
def handle_event("led24_click_on", _, socket) do
GpioInOut.write(:gpio24out, 1)
{:noreply, assign(socket, led24_click: "on")}
end
def handle_event("led24_click_off", _, socket) do
GpioInOut.write(:gpio24out, 0)
{:noreply, assign(socket, led24_click: "off")}
end
def handle_event("led25_click_on", _, socket) do
GpioInOut.write(:gpio25out, 1)
{:noreply, assign(socket, led25_click: "on")}
end
def handle_event("led25_click_off", _, socket) do
GpioInOut.write(:gpio25out, 0)
{:noreply, assign(socket, led25_click: "off")}
end
end
ledlive_web/live/led_live.html.leex
<h2>LEDの操作と状態</h2>
<div>
LED24: <%= @led24 %>
<button phx-click="led24_click_on">on</button>
<button phx-click="led24_click_off">off</button>
<%= @led24_click %>
</div>
<div>
LED25: <%= @led25 %>
<button phx-click="led25_click_on">on</button>
<button phx-click="led25_click_off">off</button>
<%= @led25_click %>
</div>
ledlive_web/router.ex
scope "/", LedliveWeb do
pipe_through :browser
live "/", PageLive, :index
live "/led", LedLive ←追記
end
ファームウェアをアップロード
❯ ./upload.sh 192.168.5.55
確認
http://192.168.5.55/led にアクセスすると次のような画面が表示されるので、ボタンをクリックしてLEDがオン・オフするかどうかを確認します。