ElixirでIoT#2.1：Nervesって何者？ラズパイでLチカできんの！？

注意：
本記事は2019年6月に執筆したものなので，最新版のNervesとは異なるところがあるかもしれません（なにか差分を見つけたらコメントでお知らせくださいm(_ _)m
少なくとも開発環境の構築については [『ElixirでIoT#4.1』](https://qiita.com/takasehideki/items/88dda57758051d45fcf9)をご参照ください．こちらの記事は常に最新版に追従するようにしています．

---

この記事は「[Elixir or Phoenix Advent Calendar 2017](https://qiita.com/advent-calendar/2017/elixir-or-phoenix)」の22日目です．
昨日は@zacky1972さんの「[ZEAM開発ログv0.1.3 AI/MLを爆速にしたい！ Flow のコードを OpenCL で書いてみる〜GPU編](https://qiita.com/zacky1972/items/05ab840561657da1e154)」でした．

:black_square_button::black_large_square::black_square_button:　お知らせ　:black_square_button::black_large_square::black_square_button:
**[「fukuoka.ex#11：DB／データサイエンスにコネクトするElixir」](https://fukuokaex.connpass.com/event/87241/)**を6/22(金) 19時に開催します
私も**『ElixirでIoTやってみた』**ネタで発表します！是非ともご参加下さい！！

[![https://fukuokaex.connpass.com/event/87241/](https://camo.qiitausercontent.com/bcffdf573a5efe4bf524e0bce325dfc175e74fc5/68747470733a2f2f636f6e6e706173732d746f6b796f2e73332e616d617a6f6e6177732e636f6d2f7468756d62732f63622f31332f63623133306130333334336263333130623037323434633330386239343064622e706e67)](https://fukuokaex.connpass.com/event/87241/)


# はじめに

こんにちは．
組込み界隈の"Hello, World!"はLチカ（LEDの点滅制御）と謂われています．
えぇーっ！？たった4日間で組込みビギナーを卒業できる開発合宿があるんだって！？？

[![https://swest.toppers.jp/LED-Camp/](https://swest.toppers.jp/LED-Camp/wp-content/uploads/2017/04/logo.jpg)](https://swest.toppers.jp/LED-Camp/)

さてステマはさておき，先日の勉強会「[fukuoka.ex#8(福岡Elixir会)：2018年 春のElixir入学式](https://fukuokaex.connpass.com/event/85038/)」にて発表した内容を加筆編集しながら，**ElixirをIoTボードで動かしたらどうなんの？**という異色ネタをお届けしています．

[これまでの連載記事](https://qiita.com/takasehideki/items/8f43f1853ce88cbbe82e#elixirでiot1iotボード上でのelixirphoenix環境構築と性能評価篇)では，IoTボード上でElixirが実行できる環境を構築し，それぞれの性能をベンチマークアプリで評価してきました．
　|> [ElixirでIoT#1.0：IoTボードへのLinux環境の準備](https://qiita.com/takasehideki/items/9e3db412a40659233384)
　|> [ElixirでIoT#1.1：IoTボードへのElixir環境の構築とEEloTツールキットの紹介](https://qiita.com/takasehideki/items/fc570ae92a895caed213)
　|> [ElixirでIoT#1.2：いろいろ分かるベンチマークを整備してみる](https://qiita.com/takasehideki/items/e1dcb4058c7d0b51d554)
　|> [ElixirでIoT#1.3：IoTボードで動いた！Phoenixが立った！性能評価と考察](https://qiita.com/takasehideki/items/0aa644639f81c2ad0bbc)

残念ながらボード／アーキテクチャの性能評価までに留まり，IoT屋さんには物足りないところかもしれません．

今回から新章に突入します！
ElixirでIoTボードのデバイスプログラミング可能な環境を提供している[Nerves](https://nerves-project.org)というのを調査してみて，まずは**IoTの醍醐味？であるLチカをしてみましょう！！**


# Nervesって何者？

うーんHPのロゴがcool!!
<a href="https://nerves-project.org">
<img width="1044" alt="Nerves Projects" src="https://qiita-image-store.s3.amazonaws.com/0/240349/19c1518d-9b7b-e912-0063-7ff230beaa23.png"></a>

HPのTopでは，
**「Craft and deploy bulletproof embedded software in Elixir」**
と謳われています．bulletproofをどう解釈するか悩ましいところですが，
**堅牢な組込みソフトウェアをElixirでお手軽に開発！**
を目指しているようです．

 * Platform: たったの12MBでLinuxブートローダとErlang VM/Elixir実行環境を実現！
 * Framework: ネットワーク，I/Oなどのデバドラ機能を提供！
 * Tooling: `mix new`対応ですぐにプロジェクト開発を開始！

要するにNervesとは，**IoTボード上で動作する最小構成のLinuxブートローダ＋Elixir実行環境＋各種デバドラのパッケージセット**と理解してよいかと思います．

このプロジェクト，[v1.0.0の正式リリースが2018年5月3日](https://embedded-elixir.com/post/2018-05-03-nerves-v1.0.0/)と新しめでありつつ，[initial commitは2013年10月](https://github.com/nerves-project/nerves_system_br/tree/dcd9a16edc39146a505c6e854579a03145c2d439)と，長期的かつ活発に活動されているようです．素晴らしい！！

[現状の対応ボード](https://hexdocs.pm/nerves/targets.html)は，歴代のラズパイに加えて，BeagleBone familyのボード~~やLEGO Mindstorms EV3~~も使えるようです．Linuxブート可能なものなら他のIoTボードへの移植もできるのかも．（EV3のサポートは終了したようですT T;
この辺りもそのうちいろいろ試してみたいところです．

# 環境設定

それではNervesを試してみるにあたって，まずは[Installation](https://hexdocs.pm/nerves/installation.html)のページを参考にしてホスト側の開発環境を整えてみたいと思います．
Elixirアプリ／mixプロジェクトの開発はクロス開発[^1]を前提にしています．このため本記事で解説する操作はホストPC上で行います．私はMacとUbuntuの両方で動くことを確認しています．

なお，Erlang/OTPとElixirはインストール済みを前提とします．


```shell-session
$ elixir --version 
Erlang/OTP 20 [erts-9.3] [source] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false]
```

## パッケージの導入

Nervesのツール内では`fwup`，`squashfs`，`ssh-askpass`辺りのパッケージを使っています．それぞれインストールしていきましょう．

* Macの場合

```shell-session
$ brew install fwup squashfs coreutils 
```

* Ubuntuの場合

```shell-session
$ sudo apt install build-essential automake autoconf git squashfs-tools ssh-askpass
$ wget https://github.com/fhunleth/fwup/releases/download/v1.1.0/fwup_1.1.0_amd64.deb
$ sudo dpkg -i fwup_1.1.0_amd64.deb
```

以降の操作はMac/Ubuntuで同じになります．

## ライブラリアップデート

`hex`と`rebar`は最新版にアップデートしといてほしいそうです．

```shell-session
$ mix local.hex
$ mix local.rebar
```

## 環境のインストール

`nerves_bootstrap`という名前で[Hexパッケージ](https://hex.pm/packages/nerves_bootstrap)として公開されています．
もちろんコマンドラインから環境をインストールすることができます．

```shell-session
$ mix archive.install hex nerves_bootstrap
```

これでNervesアプリの開発環境が整いました！


# 使い方

次は，Nervesの使い方を説明していきます．

## プロジェクトの作成

`mix`で作成できます．途中の`Fetch and install dependencies? [Yn]`では気にせず`Y`を打ち込んで良いです．
以下は`hello_nerves`という名前のプロジェクトの作成例です．出力も掲載します．

```shell-session
$ mix nerves.new hello_nerves
* creating hello_nerves/config/config.exs
* creating hello_nerves/lib/hello_nerves.ex
* creating hello_nerves/lib/hello_nerves/application.ex
* creating hello_nerves/test/test_helper.exs
* creating hello_nerves/test/hello_nerves_test.exs
* creating hello_nerves/rel/vm.args
* creating hello_nerves/rootfs_overlay/etc/iex.exs
* creating hello_nerves/.gitignore
* creating hello_nerves/.formatter.exs
* creating hello_nerves/mix.exs
* creating hello_nerves/README.md

Fetch and install dependencies? [Yn] Y
* running mix deps.get
* running mix nerves.release.init
Your Nerves project was created successfully.

You should now pick a target. See https://hexdocs.pm/nerves/targets.html#content
for supported targets. If your target is on the list, set `MIX_TARGET`
to its tag name:

For example, for the Raspberry Pi 3 you can either
  $ export MIX_TARGET=rpi3
Or prefix `mix` commands like the following:
  $ MIX_TARGET=rpi3 mix firmware

If you will be using a custom system, update the `mix.exs`
dependencies to point to desired system's package.

Now download the dependencies and build a firmware archive:
  $ cd hello_nerves
  $ mix deps.get
  $ mix firmware

If your target boots up using an SDCard (like the Raspberry Pi 3),
then insert an SDCard into a reader on your computer and run:
  $ mix firmware.burn

Plug the SDCard into the target and power it up. See target documentation
above for more information and other targets.

```

もうみなまで説明が書かれていて親切ですね．

テンプレートとして出力されるElixirコードを見てみましょう．

```lib/hello_nerves.ex
defmodule HelloNerves do
  @moduledoc """
  Documentation for HelloNerves.
  """

  @doc """
  Hello world.

  ## Examples

      iex> HelloNerves.hello
      :world

  """
  def hello do
    :world
  end
end
```

これをベースとして色々なIoTアプリを開発できそうです．


## プロジェクトのビルド

まずは，ターゲットのボードを環境変数の設定によって指定します．
選択肢は[Targetsのページ](https://hexdocs.pm/nerves/targets.html)にある表のTAG列になります．今回はRaspberry Pi 3 Model Bの`rpi3`を使っています．

```shell_session
$ export MIX_TARGET=rpi3
```

あとはいつも通りのmixプロジェクトのビルドと同じですね．
イメージファイル生成のためのmixタスク`firmware`が用意されています．

```shell_session
$ cd hello_nerves
$ mix deps.get
$ mix firmware
```

これで，ラズパイ3B上で動作するアプリが含まれたイメージファイル（最小構成のLinuxブートローダ＋Elixir実行環境＋各種デバドラ）が生成されました．

なお初回の`mix deps.get`時には，おそらくブートローダとErlang VMをビルドするためのクロスコンパイラ環境が`~/.nerves/artifacts/`にインストールされます．


### 何が生成されたの？

ちなみに生成されるイメージファイルの実態は`_build/rpi3/dev/nerves/images/hello_nerves.fw `です．

```shell-session
$ ls -l _build/rpi3/dev/nerves/images/hello_nerves.fw
-rw-r--r--  1 takase  staff    24M  5 13 18:01 _build/rpi3/dev/nerves/images/hello_nerves.fw
```

確かに24MBと驚くべき小ささです！！

```shell-session
$ file _build/rpi3/dev/nerves/images/hello_nerves.fw 
_build/rpi3/dev/nerves/images/hello_nerves.fw: Zip archive data, at least v2.0 to extract
```

ということは，，，？？

```shell-session
$ unzip _build/rpi3/dev/nerves/images/hello_nerves.fw 

$ ls meta.conf data/
meta.conf

data/:
bcm2710-rpi-3-b.dtb	config.txt		start.elf
bcm2710-rpi-cm3.dtb	fixup.dat		w1-gpio-pullup.dtbo
bootcode.bin		pi3-miniuart-bt.dtbo	zImage
cmdline.txt		rootfs.img

```

なるほどなるほど，確かにいろいろ入っていますね:laughing:

### TIPS: SSH公開鍵の作成

```shell-session
$ mix deps.get 
** (Mix.Config.LoadError) could not load config config/config.exs
    ** (File.Error) could not read file "/Users/takase/.ssh/id_rsa.pub": no such file or directory
```

と言われる場合は，SSH公開鍵を以下のように作ってあげてください．

```shell-session
$ ssh-keygen -t rsa -f ~/.ssh/id_rsa 
```

## microSDへの書き込み

これも簡単です．microSDカードをホストPCに繋いで`$ mix firmware.burn`を実行します．
microSDの接続先は自動認識されますので，接続先が正しければ`Y`を打ち込みます．なお，イメージ書き込み時には管理者パスワードを求められます．

```shell-session
$ mix firmware.burn 

Nerves environment
  MIX_TARGET:   rpi3
  MIX_ENV:      dev

Use 59.8 GiB memory card found at /dev/rdisk2? [Yn] Y
|====================================| 100% (27.50 / 27.50) MB
Success!
Elapsed time: 4.330 s

```

## ラズパイでの実行

イメージファイルを焼いたmicroSDカードをラズパイ3Bに差し込み，HDMIディスプレイとUSBキーボードを接続して電源を投下しました．

そして数秒経つと，，，おー`iex`が立ち上がってきています！
普通のインタラクティブなコードはもちろんのこと，テンプレートのコード`hello_nerves.ex`に入っていた`HelloNerves.hello`だってもちろん動きます！！

![iex.jpg](https://qiita-image-store.s3.amazonaws.com/0/240349/026869fb-ca3f-6fc5-6691-44c7c672b3e2.jpeg)

ちなみにCtrl+C x2をしても`iex`が落ちることはありません^^;

# さあLチカしてみよう！

Nerves Projectが[幾つかのサンプル](https://github.com/nerves-project/nerves_examples)を提供しています．
その中の[`Blinky`](https://github.com/nerves-project/nerves_examples/tree/master/blinky)を使ってみました．

```shell-session
$ git clone https://github.com/nerves-project/nerves_examples.git
$ cd nerves_examples/blinky
$ export MIX_TARGET=rpi3
$ mix deps.get
$ mix firmware
$ mix firmware.burn
```

おーなんとなんと！お分かりになりますでしょうか？？
ボード左下にある緑色のLEDが見事にチカチカしています！！！

![ledtikatika2.gif](https://qiita-image-store.s3.amazonaws.com/0/240349/dcbf6ae2-1c5a-a9af-e331-cb76df7aeddc.gif)


[`hello_gpio`](https://github.com/nerves-project/nerves_examples/tree/master/hello_gpio)や[`hello_phoenix`](https://github.com/nerves-project/nerves_examples/tree/master/hello_phoenix)などなど，GPIOやPhoenixフレームワークを使ったサンプルも提供されています．また，各種センサ／アクチュエータをお手軽に繋げる[Groveモジュールのライブラリ](https://hexdocs.pm/nerves_grove/readme.html)もあるようです．
この辺りを使えばIoTアプリの構築もお手のもの！？これからぜひ触ってみたいと思います．

# 使ってみた感想

Elixir環境でここまでお手軽にさくっとIoTデバイスのアプリ開発を始められるとは，非常に驚きでした．IoTワールドでElixirの生産性の良さや並列性能を活かすことができますので，新たなアプリ創出の機運が高まってきていますね．

ただ，クロス開発なのはちょっと面倒だと感じました．
いちいちmicroSDに焼くのも面倒ですし，いちいち電源を投下して数秒経たないと結果が分からないのはストレスに感じます．複数ボードへの本番環境へのデプロイならお手軽になる状況とも考えられます．調査が及んでいないので提供されているかもですが，ホスト開発[^2]の方法やリモートデバッグの方法が無いとツラいところです．
イメージファイルのサイズの小ささは魅力ではあります．しかしながら，ラズパイのようなLinuxがきびきび動くIoTボードは，そもそもメモリ量が潤沢ですので，メモリサイズはそこまで重視する要素にはなりません．それであれば，Linuxを載せちゃって生のelixir/iex環境でホスト開発したほうが手っ取り早いよなぁと感じました．（なお，Linuxが動かせないクラスのボードを目指すなら，12MBでも全然大きかったりします）

ホスト開発だと[「elixir_ale - Elixir Actor Library for Embedded」](https://github.com/fhunleth/elixir_ale)というのが使えるらしいという情報もあります．王道としてはNIF/BIFに飛び込むのもアリでしょう．
IoTアプリのためのElixir実行環境／開発環境，うーん研究材料としてとっても面白そうだ！！



# まとめ

- ElixirでIoT開発ができます！！
- Nervesを使ってお手軽に組込みソフトウェアを開発！
    - IoTボード上で動作する最小構成のLinuxブートローダ＋Elixir実行環境＋各種デバドラのパッケージセット
    - ホストPC上の`mix`システムでクロス開発を実現
    - テンプレートのアプリ込みでもイメージファイルはたったの24MB！

**皆さんもElixirを片手にIoTワールドへ飛び込んでみませんか！？？**

次回の発表駆動開発の機会である**[「fukuoka.ex#11：DB／データサイエンスにコネクトするElixir」](https://fukuokaex.connpass.com/event/87241/)**までに，さらなる調査や開発を進めたいと思います！私の発表もお楽しみに！！




明日のAdvent Calendarは@piacere_exさんによる
「[Excelから関数型言語マスター5回目：Webにグラフ表示](https://qiita.com/piacere_ex/items/290b76b76d5ff8e019bf)」
です．



[^1]: クロス開発とは，実際の実行環境とは異なるシステム（ホストPC）で開発を行うことを指します．ホストのPCで実装とビルドを行い，バイナリ等をターゲットのIoTボードなどに転送して実行・デバッグを行います．例えばArduino IDEだと，MacやWindows上でコーディングして，そのバイナリをArduinoボードに転送して実行していますよね．
[^2]: 逆にホスト開発とは，実行環境と同じシステム上で開発を行うことを指します．例えばRaspberry PiにSSH接続でログインして，その上のエディタでPythonやElixirのスクリプトをコーディングして実行することを想像してください．