Elixir × Nerves でLチカを実装する

# 概要

**Nerves**は、Elixirで組み込みシステムを開発するためのフレームワークです。Raspberry Piなどのデバイス上でElixirコードを直接動作させることができ、ファームウェアの作成やデプロイもmixを使って行えます。

本記事では、学習の一環としてNervesを使い、Lチカ（LEDの点滅） を試してみます。
Lチカの方法として、以下の2種類があります。

* NervesLiveBookを使用する方法
    * ファームウェアを作成せず、LiveBook上で手軽に試せる

* Nervesでファームウェアを作成する方法
    * microSDカードにファームウェアを書き込み、ラズパイを起動時に実行する

まずは、セットアップが簡単な NervesLiveBook を使った方法から試してみます。

また記事中の内容は下記のリポジトリで公開しています

https://github.com/BlackTanuTanuNinjas/tutorial_nerves

# 環境

本記事の内容はMac環境で検証しています。Debian等のLinuxでも同様に実行できると思いますが、Windows環境については未検証です。

使用しているElixirやErlangのバージョンは以下の通りです。

```
Name            Version         Source                       Installed
elixir          1.18.2-otp-27   /Users/tanuki/.tool-versions true
erlang          27.2.2          /Users/tanuki/.tool-versions true
```

# 必要なもの

Nervesは 複数のRaspberry Piモデル に対応していますが、本記事では Raspberry Pi 4Bを使用します。

* Raspberry Pi 4B
* microSDカード
* Raspberry Pi Imager(https://www.raspberrypi.com/software/)
* LED
* 抵抗(本記事では220Ωを使用)
* ブレッドボード
* ジャンパーワイヤ(オス-メス )

## 配線

```
(GPIO17) ─── LED(＋) ─── [220Ω] ─── (GND)
```

# 手順

ファームウェアを作成しないNervesLiveBookを使用する方法と、  
ファームウェアを作成してNervesを動作させる方法では、手順が異なります。

## NervesLiveBookの手順

1. NervesLiveBookのダウンロード
2. microSDカードへの書き込み
3. LiveBookへの接続
4. Lチカの実行

## Nervesの手順

1. Nervesのインストール
2. Nervesプロジェクトの作成
3. Lチカの処理を実装
3. 環境変数の設定
4. ファームウェアの作成と書き込み
5. Nervesの起動

# NervesLiveBookの手順

## NervesLiveBookのダウンロード

**NervesLiveBook**では、使用するRaspberry Piのモデルごとに異なるファイルが必要です。
以下のリンクから、自分のデバイスに対応するファイルをダウンロードしてください。

https://github.com/nerves-livebook/nerves_livebook/releases

本記事ではRaspberry Pi 4Bを使用するため、以下のファイルをダウンロードします。

```nerves_livebook_rpi4.zip```

## microSDカードへの書き込み

ダウンロードしたファイルを、マイクロSDカードに書き込みましょう。

Raspberry Pi Imagerを起動し、以下のように設定します。

| Raspberry Piデバイス | OS | ストレージ |
|---|---|---|
| RASPBERRY PI 4 | Use custom（ダウンロードしたファイルを指定） | 書き込みたいマイクロSDカード |


この画像は、すべての項目を選択し終わった直後のRaspberry Pi Imagerの画面です。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3455723/7c17ff97-ac65-444d-8bbe-99953913c998.png" width="50%" />

「次へ」を押した後、<font color="red">カスタム設定は使用せず、そのまま書き込みを開始してください。</font>

書き込みが完了したら、マイクロSDカードをRaspberry Piに挿入し、有線LANでルーターと接続してください。

## LiveBookへの接続

PCのブラウザで以下のURLにアクセスし、パスワード `nerves` を入力してLiveBookに接続します。

http://nerves.local

複数台で起動している場合は、ルーター等でIPアドレスを調べて接続してください

## Lチカの実行

新しいNotebookを作成し、以下のコードを **改行ごとにセルに分けて入力しながら** 実行してください。  

```elixir
Mix.install([:circuits_gpio])

# GPIOを開く
{:ok, gpio } = Circuits.GPIO.open(17, :output)

# 出力をHIGH(1)にして電流を流す
Circuits.GPIO.write(gpio, 1)

# 出力をLOW(0)にして電流を止める
Circuits.GPIO.write(gpio, 0)

# 5回チカチカする
for _count <- 1..5 do
  Circuits.GPIO.write(gpio, 1)
  Process.sleep(1000)
  Circuits.GPIO.write(gpio, 0)
  Process.sleep(1000)
end

# GPIOを閉じる
Circuits.GPIO.close(gpio)
```

最後のセルを実行すると、LEDを5回点滅することができます。

#  Nervesの手順

## Nervesのインストール

Nervesの公式ドキュメントを参考に、Nervesをインストールしてください。

https://hexdocs.pm/nerves/installation.html

## Nervesプロジェクトの作成

Nervesのセットアップが完了したら、プロジェクトを作成します。  
次のコマンドを実行して、新しいNervesプロジェクトを作成しましょう。

```bash
mix nerves.new tutorial_nerves
```

## Lチカの処理を実装

Lチカの処理を**GenServer**で実装し、<font color="red">起動時に自動的にLEDが点滅するように</font>します。

次のコードを `lib/tutorial_nerves/led_chika.ex` に記述してください。

```elixir:lib/tutorial_nerves/led_chika.ex
defmodule TutorialNerves.LedChika do
  @moduledoc """
  起動時に led_on と led_off を交互に送信して、点滅を行う
  """
  use GenServer

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

  @impl true
  def init(gpio_pin) do
    {:ok, gpio} = Circuits.GPIO.open(gpio_pin, :output)
    Process.send_after(self(), :led_on, 0)
    {:ok, %{gpio: gpio}}
  end

  @impl true
  def handle_info(:led_on, %{gpio: gpio} = state) do
    Circuits.GPIO.write(gpio, 1)
    Process.send_after(self(), :led_off, 1000)
    {:noreply, state}
  end

  def handle_info(:led_off, %{gpio: gpio} = state) do
    Circuits.GPIO.write(gpio, 0)
    Process.send_after(self(), :led_on, 1000)
    {:noreply, state}
  end
end
```

次に、作成した`TutorialNerves.LedChika`モジュールを**Application**の**Supervisorツリー**に追加します。

これにより、Nervesが起動すると自動的にLEDの点滅が開始されます。

`lib/tutorial_nerves/application.ex`のSupervisorの子プロセスのリストに以下を追加してください。

```elixir:/lib/tutorial_nerves/application.ex
children =
    [
        {TutorialNerves.LedChika, 17}
    ] ++ target_children()
```

これにより、Nervesが起動する際にLチカが行われるようになります。

## 環境変数の設定

作業を進める前に、環境変数を設定します。  
本記事では Raspberry Pi 4Bを使用するため、以下のように設定してください。

```bash
# export MIX_TARGET=<Tag>
export MIX_TARGET=rpi4
```

他のRaspberry Piモデルを使用する場合は、以下のリストから対応するターゲットを確認してください。

https://hexdocs.pm/nerves/supported-targets.html#supported-targets-and-systems

環境変数を設定したら、依存関係を取得します。

```bash
mix deps.get
```

## ファームウェアの作成と書き込み

まず、次のコマンドを実行してファームウェアを作成します。

```elixir
mix firmware
```

作成が完了したら、microSDカードをPCに接続し、以下のコマンドで書き込みを行います。

```bash
mix burn
```

書き込みが完了したら、microSDカードを取り出し、Raspberry Pi に挿入してください。

## Nervesを起動する

microSDカードを挿入したら、電源を入れて起動してください。

Nervesが起動すると、自動的にLチカが開始されます。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3455723/bd6a2794-6790-4fb1-8331-0f902ed16ea9.gif" width="25%"/>

### リモートでファームウェアを更新する

Raspberry PiでNervesを一度起動すると、microSDカードを抜き取らずにファームウェアを更新できるようになります。 

コードを修正した後、以下のコマンドを実行すると、リモートでファームウェアを適用できます。

```bash
mix upload nerves.local
```

# まとめ

初めて Lチカ に触れたので、最初は配線の知識がなく不安でしたが、実際に試すことである程度理解できました。  
今後は、その他の周辺機器も活用し、IoT デバイスの開発に応用してみたいと思います。

また、記事中で触れたようにGenServerを使用することで、プロセスを独立して動作させる仕組みを改めて学ぶことができました。 
この知識は、Nervesでの実装だけでなく、Elixirでの並行処理全般にも活かせそうです。

本記事で作成したNervesのコードはGitHubリポジトリで公開しています。  
記事の内容をそのまま試せるように、記述済みのプロジェクトを用意しているので、ぜひ参考にしてください。