5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Elixir/Nerves: GenServerでブレットボード上のLEDを点滅させる

Last updated at Posted at 2024-06-24

はじめに

Nerves を初めて学ぶとき、ブレッドボード上で LED を点滅させたくなることがあると思います。

やり方は色々考えられますが、今日は GenServer を使用した簡単な実装に挑戦してみたいと思います。

本記事は、先週末に参加したイベントでの成果です。Nerves 愛好家と東京の秋葉原で買い物を楽しみ、Nervesを使った IoT デバイス開発について学びました。主催者のmyasuさん と nako_sleep_9hさんに心から感謝します!

myasu さんは電気電子工学についてとても詳しい方で、秋葉原を散策しながら色々教えていただきました。大変勉強になりました。また、myasu さんが執筆された Nerves 開発の入門書もあります。

nako_sleep_9h さんは、日本各地で元氣になる楽しい技術イベント(オンライン及びオフライン)を企画し、また司会も務められています。

Nerves Livebook

Nerves をプラットフォームとして IoT プロジェクトを始めるにはいくつかの方法がありますが、最も簡単な方法の 1 つは、ビルド済みの Nerves Livebook ファームウェアを使用することです。Nerves Livebook を使用すると、Web ブラウザー上で Elixir コーディングを行いながら、Raspberry Pi などのターゲット デバイスを制御できます。

有志の方々が Nerves Livebook のセットアップ方法ついてのビデオを制作してくださっています。ありがとうございます。

circuits_gpio を使って LED を操作

circuits_gpio は、Elixir コードで GPIO を制御できるようにするものです。

Nerves Livebook には、circuits_gpio パッケージを使用して GPIO を出力として制御し、LED を点滅させる方法についての良いチュートリアル ノートブックがあります。作業に取り掛かる前に、最低限必要な電気部品と、それらの配線方法を示しています。

LED 点滅ロジックを GenServer でラップ

点滅を永遠に繰り返したいが、デバイスを再起動せずにある段階で停止したい場合、LED の点滅を管理する GenServer を用意すると便利です。

まず、GPIO 関連のロジックをラップする簡単なモジュールを作成し、LED を操作するための関数を定義します。

defmodule LedBlink do
  def open(led_pin) do
    Circuits.GPIO.open(led_pin, :output)
  end

  def close(led_pin) do
    Circuits.GPIO.close(led_pin)
  end

  def toggle(gpio, 1), do: off(gpio)
  def toggle(gpio, 0), do: on(gpio)
  def toggle(gpio, _), do: on(gpio)

  def on(gpio) do
    :ok = Circuits.GPIO.write(gpio, 1)

    {:ok, 1}
  end

  def off(gpio) do
    :ok = Circuits.GPIO.write(gpio, 0)

    {:ok, 0}
  end
end

次に、LED を永久に点滅させるGenServerを作成します。

defmodule BlinkServer do
  use GenServer, restart: :temporary

  require Logger

  @run_interval_ms 1000

  ## Client

  @doc """
  Start a BlinkServer for the provided GPIO pin. It lets the LED blink forever.

  ### Examples

      iex>  BlinkServer.start_link(led_pin: "GPIO17")

  """
  def start_link(opts) do
    GenServer.start_link(__MODULE__, opts, name: __MODULE__)
  end

  def stop() do
    GenServer.stop(__MODULE__)
  end

  ## Callbacks

  @impl true
  def init(opts) do
    led_pin = Access.fetch!(opts, :led_pin)

    initial_state = %{
      gpio_ref: nil,
      led_state: 0,
      led_pin: led_pin
    }

    {:ok, initial_state, {:continue, :init_gpio}}
  end

  @impl true
  def handle_continue(:init_gpio, state) do
    case LedBlink.open(state.led_pin) do
      {:ok, gpio_ref} ->
        new_state = %{state | gpio_ref: gpio_ref}

        send(self(), :toggle_led_state)

        {:noreply, new_state}

      {:error, error} ->
        {:stop, error}
    end
  end

  @impl true
  def handle_info(:toggle_led_state, state) do
    {:ok, new_led_state} = LedBlink.toggle(state.gpio_ref, state.led_state)

    new_state = %{state | led_state: new_led_state}
    Logger.debug("toggled LED: #{new_state.led_state}")

    Process.send_after(self(), :toggle_led_state, @run_interval_ms)

    {:noreply, new_state}
  end

  @impl true
  def terminate(reason, state) do
    LedBlink.close(state.gpio_ref)
    Logger.debug("terminated: #{reason}")

    :ok
  end
end

論より Run

BlinkServer の使用方法は次のとおりです。

BlinkServer を起動するときに、LED の繋がっている GPIO ピンを指定します。

実行中の BlinkServer ワーカーを停止することもできます。

Logger.configure(level: :debug)

BlinkServer.start_link(led_pin: "GPIO17")

Process.sleep(10_000)

BlinkServer.stop()

今回の BlinkServer の実装はシングルトンの GenServer です。つまり、複数の BlinkServer ワーカーを起動することはできません。そのため、たとえば使用する GPIO ピンを変更する場合は、ワーカーを停止し、異なるオプションで新しいワーカーを起動する必要があります。

Wrapping up

LED を点滅させるシンプルな GenServer を作成しました。

世の中にはいろんな LED 点滅のロジックや楽しみ方があります。あなたもぜひ共有してみてください!

5
1
2

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
5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?