はじめてNerves(1) 電源ONでLチカアプリを起動させる

ラズパイ3Bを用意する以外にできるだけハードウェアに触らずにLチカが出来るようにしてみましょう。用いるのはもちろん我らが Elixir の IoT フレームワーク、Nerves です。今回は Nerves 1.5 (Elixir 1.9) を用いました。

# まずはターゲットのファームウェアを焼けるようにする

自分の PC をホスト (host) と、IoT 的に動かすマシンをターゲット (target) と言います。何はともあれターゲットが動くようにしましょう。なお Nerves での基本的な用語については [公式ページの Common terms](https://hexdocs.pm/nerves/getting-started.html#common-terms) を見てください。

## PC を Nerves Ready にする
まずは PC を Nerves 対応にします[^installation]。ここで注意すべきは版管理です。Nerves だけじゃなくて Elixir を本体で動かすことも多いと思います。そして Nerves が必要とする Elixir と Erlang/OTP は必ずしも最新とは限りません。場面々々で版を適切に設定する必要があります。

[^installation]: [公式ドキュメント Installation](https://hexdocs.pm/nerves/installation.html) も見てください。

これは例えば MAC なら asdf コマンドとかで版管理をすることができます。asdf をざっくりとやるには [Elixirのバージョン管理環境をasdfを使って作った](https://qiita.com/nishiuchikazuma/items/b9d319732ddb540fd990) をご覧ください。

以下では

- elixir 1.9.1-otp-22
- erlang 22.0.7

の版で試しています[^version]。

[^version]: この他、Nerves 自体が 1.5.4 で、Nerves Bootstrap が 1.7.0 です。

## ターゲットのファームウェアを書く準備をする
PCを Nerves 対応にしたら以下を実行します。ここではターゲットマシンのアーキテクチャをラズパイ3Bとしてディレクトリを appstart とします[^formaldocs]。

[^formaldocs]: 公式ドキュメントはこちら [Getting Started](https://hexdocs.pm/nerves/getting-started.html) を、インストール可能なターゲットのアーキテクチャはこちらの [Supported Targets and Systems](https://hexdocs.pm/nerves/targets.html#supported-targets-and-systems) をどうぞ。

以下のコマンドを順番に投下します。mix コマンドするたびに（正常に行ったとしても）大量にメッセージが出てきますので、エラーしてないかをちゃんと見ておいてください。

- mix nerves.new appstart
- cd appstart
- export MIX_TARGET=rpi3
- mix deps.get
- mix firmware

とまあ、ここまでエラーなく通るようにしてください。

## ターゲット用のファームウェアを試しに焼いてみる
さあ、これで準備ができました。ターゲット用の SD カードにファームウェアを焼きましょう。PC 本体に SD カードを刺して以下を実行します。

- mix firmware.burn

私の経験では、焼く先は正しく SD カードを見つけてくれるので、どのデバイスが SD カードなのかとあらためて調べたりはしませんでしたが、良きプログラマのふるまいとしては一応確認はしておくべきでしょう。ホストの本体 OS を上書きする可能性が全くのゼロとは言えませんからね。

# ターゲット用のプログラムを書く
SD カードにファームウェアを焼いてラズパイの電源をオンにしても、ターゲットで何が起こってるのかは分かりません。実はターゲットはコンソールの準備をして iex を使えるように準備しています。ここにシリアルIFなりEthernetなりでアクセスできれば `hello/0` 関数を呼び出すことで `:world` という返り値が戻るのを観察できます。

しかしながら、シリアルケーブルとかEthernetケーブルとかつないで確認しようとするとちょこちょこハマったりします。特にハードウェア周りがあまり得意でないユーザは迷宮に入ってしまう可能性があるので、今回は多少コードを書いてハードウェア的な作業をできるだけ避けるようにしてみます。

具体的には、ここではラズパイの電源をオンにすると自動的にLチカが起こるように仕込んでみます。

## LED点灯用のライブラリを読み込む設定
ラズパイのLEDを動作確認用に使います。これには [Nerves.Leds](https://github.com/nerves-project/nerves_leds) ライブラリを使います。まず、このライブラリを使うのに依存関係を `mix.exs` ファイルに指定します。

```exs:mix.exs
  ...
  defp deps do
    [
      ... # 途中省略
      {:nerves_leds, "~> 0.8.0"},
    ]
  end
  ...
```

つぎに、これを書いたのちに `mix deps.get` コマンドを投入してください。

```
New:
  nerves_leds 0.8.0
* Getting nerves_leds (Hex package)
```
とか出てきて読み込まれたのがわかります。沢山出てくるメッセージの一部ですので注意して見てください。

## LEDの定義をする
つぎに、どの LED をどう言う名前にするのかを宣言します。これは `config/config.exs` ファイルに記述します。以下をファイルに追加してください[^nervesleds]。追加する位置はファイルの最後とかで良いです。

[^nervesleds]: [Nerves.Leds の公式ドキュメント](https://hexdocs.pm/nerves_leds/Nerves.Leds.html) の赤LEDと緑LEDの指定は、ラズパイ3Bについては逆になってます(2020.02.03現在)。

```exs:config.exs
config :nerves_leds, names: [ green: "led0", red: "led1" ]
```

このあとは特にコマンドの投入は不要です。

## 自動起動の設定をする
Nerves でプログラムを自動起動したい場合は `lib/プロジェクト名/application.ex` ファイルに起動したいプログラムを記述します。このファイルはすでに雛形があるはずです。今回は `lib/appstart/application.ex` ファイルを編集します。中に `children/1` 関数の定義がありますので、そこに呼び出したい関数を記述します。

```exs:lib/appstart/application.exs
  def children(_target) do
    [
      GoForIt.test()
    ]
  end
```

ここでは `GoForIt` モジュール内の `test/0` 関数を呼び出すようにしました[^nosupervisor]。

[^nosupervisor]: 通常ここでは Supervisor ツリーにプロセスをぶら下げるところですが、ここでは簡単のため、そのようにしていません。

## 起動されるプログラムを作成する
この `GoForIt` モジュールのプログラムも application.ex があるのと同じディレクトリに作ります。今回の場合は `lib/appstart/goforit.ex` になります。これは自分のオリジナルのファイルですので新規に作成してください。ここでは赤色LEDと緑色LEDとが異なる周期で点滅するようにします。

```exs:goforit.ex
defmodule GoForIt do
  def test() do
    Nerves.Leds.set(red: :fastblink, green: :slowblink)
  end
end
```

# 実行してみる
さあ、これで準備ができました。ファームウェアを焼いて実行してみましょう。先程の繰り返しになりますが、PC 本体に SD カードを刺して以下を実行します。

- mix firmware
- mix firmware.burn

焼き上がったら SD カードをラズパイに装着して電源を入れます。起動すると赤色LEDが速く点滅して緑色LEDが遅く点滅するようになります。

![led.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260320/3f805f08-4946-2282-e078-cf154e2860c1.gif)


## 色々なパターンのLチカを楽しむ
上の例では特定のパターンを点けっぱなしにしました。もちろんもっと複雑にもできます。次の場合では5秒(5000ms)後にLEDを消灯します。5秒待つのに [Process.sleep/1](https://hexdocs.pm/elixir/Process.html#sleep/1) 関数を使います。なお、ラズパイの場合はたまにシステムが LED を点灯させようとするせいか、期待通りに点かない・消えない場合があります。


```exs:goforit.ex
defmodule GoForIt do
  def test() do
    Nerves.Leds.set(red: :fastblink, green: :slowblink)
    Process.sleep(5000)
    Nerves.Leds.set(red: :false, green: :false)
  end
end
```

点灯パターンはいくつかあるので色々試してみてください。

- :true 点灯する
- :false 消灯する
- :slowblink ゆっくり（大体1秒に2回）点滅する
- :fastblink はやく（大体1秒に7.5回）点滅する
- :slowwink ほとんど点灯してて大体1秒ごとにちょっとウィンク風に消灯する
- :hearbeat 鼓動するような点滅をする（ドッドッ…、ドッドッ…、という風に）

例えば、2種類の点滅パターンを交互に繰り返す… と言った場合は以下のように書くことができます。

```exs:goforit.ex
defmodule GoForIt do
  def test() do
    Nerves.Leds.set(red: :fastblink, green: :slowblink)
    Process.sleep(5000)
    Nerves.Leds.set(red: :heartbeat, green: :slowwink)
    Process.sleep(5000)
    test()
  end
end
```

なお、ここでもOS側が並行してLEDを点灯・消灯しようとしてくるので、期待通りに点かない場合もあります。このほか、自分なりのアレンジも多少はできるようになってます。どのように設定するとどの様に光るかとかは [Nerves.Leds の公式ドキュメント](https://hexdocs.pm/nerves_leds/Nerves.Leds.html) を見てください。大変短いのですぐに読み切れます。

#まとめ
自分のPCをホストとし、ラズパイ3Bをターゲットとして、それ以上にハードウェアを増やすことなくLチカをさせてみました。このときにターゲットがブートすると自動的にアプリケーションが来どいうするように仕込んでみました。

#参考文献
- [Nerves Project 公式ページ](https://nerves-project.org)
- [Elixir 公式ページ](https://elixir-lang.org)
- [はじめてNerves(0) ElixirによるIoTフレームワークNervesがとにかく動くようになるためのリンク集](https://qiita.com/kikuyuta/items/9dee85c32004c8e827e7)