LoginSignup

This article is a Private article. Only a writer and users who know the URL can access it.
Please change open range to public in publish setting if you want to share this article with other users.

More than 3 years have passed since last update.

NervesでPhoenix LiveViewを動かし2つのLEDを操作する

Posted at

概要

NervesにPhoenix LiveViewを入れて2つのLEDを操作しました。
操作したLEDの状態はループを使わずオン・オフを取得しています。

  • NervesにPhoenix LiveViewをインストール
  • LEDのオン・オフをLiveView経由で操作
  • LEDの状態をループを使わず取得しLiveViewの画面に反映
fish
❯ mix nerves.new -v
Nerves Bootstrap v1.8.1

❯ mix phx.new -v
Phoenix v1.5.4

構成

ネットワークはこのようないつもの構成にしました。
ネットワーク構成図.png

準備

Ponchoプロジェクトストラクチャーとしてプロジェクトを作成します。
細かい設定は以下の「NervesにPhoenixを入れてHTTPのGETでLEDをウェブチカ」シリーズを参照ください。

led_liveディレクトリの中にNervesプロジェクトのexineris、Phoenixプロジェクトのledliveという構成としました。

fish
❯ tree -L 1 led_live/
led_live/
├── exineris    ←Nervesのプロジェクトディレクトリ
└── ledlive     ←Phoenixのプロジェクトディレクトリ

Nerves 1.5になったあたりから--nerves-packオプションが不要になったようだったので今回からオプションを付けないことにしました。

fish
❯ 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の操作をできるようにするのと、ログを出すようにします。

mix.exs
defp deps do
  [
    {:circuits_gpio, "~> 0.4"},
    {:ring_logger, "~> 0.6"},
  ]
end

追記した後、mix deps.getコマンドを実行します。

fish
 mix deps.get

config/config.exs

mix phx.gen.secret 32の実行結果をconfig/config.exs内のsigning_saltに記載します。

fish
❯ mix phx.gen.secret 32
+g9KvVB5oVUgFONNEn+GJjxLumepvXYH
config/config.exs
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に記載します。

fish
❯ mix phx.gen.secret
xSmfKWUn3kmXou7tNV5lge4AeCoaJDJpALjB7/xZvRsxP7IPHs0Ojn37U6yJCLAn
config/prod.secret.exs
secret_key_base = "xSmfKWUn3kmXou7tNV5lge4AeCoaJDJpALjB7/xZvRsxP7IPHs0Ojn37U6yJCLAn"

lib/ledlive/gpioinout.ex

kochi.exな @kikuyuta さん作ライブラリのgpioinout.exを設置します。

lib/ledlive/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

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

ledlive_web/live/led_live.html.leex
<h2>LEDの操作と状態</h2>
<div>
    LED24: <%= @led24 %>&nbsp;&nbsp;
    <button phx-click="led24_click_on">on</button>
    <button phx-click="led24_click_off">off</button>
    <%= @led24_click %>
</div>
<div>
    LED25: <%= @led25 %>&nbsp;&nbsp;
    <button phx-click="led25_click_on">on</button>
    <button phx-click="led25_click_off">off</button>
    <%= @led25_click %>
</div>

ledlive_web/router.ex

ledlive_web/router.ex
scope "/", LedliveWeb do
  pipe_through :browser

  live "/", PageLive, :index
  live "/led", LedLive    ←追記
end

ファームウェアをアップロード

fish
❯ ./upload.sh 192.168.5.55

確認

http://192.168.5.55/led にアクセスすると次のような画面が表示されるので、ボタンをクリックしてLEDがオン・オフするかどうかを確認します。

スクリーンショット 2020-08-07 18.02.05.png

0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up