ElixirでIoT#4.1.1：WSL 2でNerves開発環境を整備する

# はじめに

Nervesとは，モダンな関数型言語ElixirでIoT！ができる[ナウでヤングなcoolなフレームワーク](https://qiita.com/takasehideki/items/94820516ec95b85bae32)です．
推奨されるホスト開発環境はmacOSとLinux(Ubuntu)で，永らくWinodwsユーザは~~虐げられて~~Nervesで遊べずに来ていました．~~素直な~~macOS/Ubuntuユーザの方は，[ElixirでIoT#4.1](https://qiita.com/takasehideki/items/88dda57758051d45fcf9)にて開発環境の構築方法を紹介していますので，ぜひ遊びましょう！！

ところが朗報です！！
2020年4月下旬にあった[Nerves v1.6.1のcommit](https://github.com/nerves-project/nerves/commit/5d058cd432f537706e4f80f2b356f971bf6b07e0)にて，Windowsがホスト開発環境として正式サポートされました！hexdocsもすでに公開されています．

- [Nerves on hexdocs: Installation#Windows](https://hexdocs.pm/nerves/installation.html#windows)

本記事では，WindowsユーザをNervesの世界に招待します！！

## 要点

せっかちさんのために先に要点をまとめます．

- WSL2でUbuntu 18.04 LTSを用意し，Nervesビルド環境を構築する
- fwupはWindowsとUbuntuの両方にインストールする
- ラズパイゼロかBeagleBoneボードをcoolに使いたい場合は，USB Ethernet/RNDIS Gadget Driverをインストールする

これだけの説明で分かる「Nerves完全に理解している」ヒトは，以降は読み飛ばして大丈夫です．

## 蛇足：Nerves開発環境の壁

ちなみに本題に入る前に，Nervesの開発環境の構築には，以下の課題がありました．

1. Nervesアプリのビルド環境の構築（クロスコンパイラの用意）
2. NervesファームのmicroSDへの書き込み（microSDの認識）
3. Nervesデバイスとのssh通信

ここで3.については，特にラズパイゼロやBeagleBoneでは，microUSBケーブルをVirtualEthernetとみなしてホストPCとssh通信ができる機能が提供されています．これがナウいです．

これまでにもWSLやVirtualBox，Dockerなどの仮想環境でWindows上にNerves開発環境を構築する取り組みはありました．しかしながら，上記3点を完全に乗り越えられる方法はこれまでになく，Winodowsネイティブと仮想OSを行ったり来たりしないといけませんでした．

今回のWSL 2対応により，Windowsネイティブ環境だけでNerves開発ができるようになります！
\# WSL 2は，まぁWindowsネイティブって言っていいよね？？

# WSL 2の用意

WSL (Windows Subsystem for Linux)とは，LinuxのELF64バイナリをWindows 10上で実行できるようになる機構です．Windows公式のLinux仮想化環境ということで，いろいろ安心な方も多いのかもしれません．
その2世代目となるWSL 2は，Windows 10 ビルド 18917以降で利用可能です．この記事を書いている時点では，Insider Preview Programでしかまだ入手できないバージョンとなります．"Windows 10 May 2020 Update (ver. 2004)"で正式リリースとして同梱されるそうなので，早期版では不安な方はもう少しお待ちください．

WSL 2の導入にあたっては，下記を参考にしてください．

- [Microsoft Docs: WSL 2 のインストール手順](https://docs.microsoft.com/ja-jp/windows/wsl/wsl2-install)
- [Microsoft Docs: WSL 2 Linux カーネルの更新](https://docs.microsoft.com/ja-jp/windows/wsl/wsl2-kernel)
- [Qiita: WSL1からWSL2に乗り換える](https://qiita.com/rootpia/items/f49b9e903216cd6dfb51)

LinuxディストリビューションはUbuntu 18.04 LTSを用います．管理者権限でPowerShellを起動し，下記でWSL 2に切り替えてください．

```powershell
> wsl --set-version Ubuntu-18.04 2
```

下記のとおりとなっていれば，Ubuntu18 on WSL2の準備が完了しています．

```powershell
> wsl -l -v
  NAME            STATE           VERSION
  Ubuntu-16.04    Stopped         1
* Ubuntu-18.04    Running         2
```

# 開発環境の構築

## Elixir/Erlang/Nervesのインストール

大事なことなので２度言います．
WindowsネイティブにはElixir/Erlangのインストールは必要ありません．
WindowsネイティブにはElixir/Erlangのインストールは必要ありません．

ここはUbuntuユーザと同じ方法です．
[ElixirでIoT#4.1の「Linuxの場合」](https://qiita.com/takasehideki/items/88dda57758051d45fcf9#linux%E3%81%AE%E5%A0%B4%E5%90%88)を参考にして，必要なパッケージのapt installやasdfでのElixir/Erlangのインストール，`mix archive.install hex nerves_bootstrap`でのNerves Bootstrapインストールまでを行ってください．

## fwupのインストール

`fwup`は，NervesのファームウェアをmicroSDに書き込むツールです．
Nervesファームウェアは，堅牢性を高めるために，ブート領域とファイルシステムがA/Bの二重化構造を取っています．このため書き込みツールも専用化されています．

fwupは，WindowsネイティブとWSL 2内のUbuntuの両方にインストールします．

### Windowsネイティブへのインストール

Windows側ではChocolateyでインストールします．まず[公式ページのガイド](https://chocolatey.org/install)を参考にしてChocolateyをインストールしてください．

その後，管理者権限のあるPowerShellで下記を実行します．

```powershell
> choco install fwup /y
Chocolatey v0.10.15
Installing the following packages:
fwup
By installing you accept licenses for the packages.
Progress: Downloading fwup 1.5.2... 100%

fwup v1.5.2 [Approved]
fwup package files install completed. Performing other installation steps.
 ShimGen has successfully created a shim for fwup.exe
 The install of fwup was successful.
  Software install location not explicitly set, could be in package or
  default install location if installer.

Chocolatey installed 1/1 packages.
 See the log for details (C:\ProgramData\chocolatey\logs\chocolatey.log).
```

わざわざChocolateyのインストールがめんどい場合は，[fwupのGitHubのReleases](https://github.com/fhunleth/fwup/releases)から`fwup.exe`を直接ダウンロードしてきて，よろしくシステム環境変数Pathを通すだけでもよいです．


### WSL 2へのインストール

`.deb`を落としてきてdpkgインストールするだけです．

```shell
$ wget https://github.com/fhunleth/fwup/releases/download/v1.7.0/fwup_1.7.0_amd64.deb
$ sudo dpkg -i fwup_1.7.0_amd64.deb
```

# Nervesアプリのビルド

以降はもうWSL 2だけの操作です！
Nervesアプリをビルドしていきます．詳細はカッツアイ:eye:します．

```shell
# Nervesプロジェクトの作成
$ mix nerves.new hello_nerves
# プロジェクトのディレクトリに移動
$ cd hello_nerves/
# ターゲットのボードを指定（ラズパイゼロの例）
$ export MIX_TARGET=rpi0
# 依存パッケージのダウンロードと依存関係の解消
$ mix deps.get
# Nervesファームウェアのビルド
$ mix firmware
```

最後に
`Building /home/takase/hello_nerves/_build/rpi0_dev/nerves/images/hello_nerves.fw...`
とか出てきたらビルド成功です．

ちなみに作業ディレクトリはWSL 2配下のところで構いません．つまり `~` で全然よくって， Windowsネイティブと共有する `/mnt/c/` 以下とかにわざわざ移動しなくてよいです．

# microSDへの書き込み

Linuxからのデバイス認識・接続が安定化されたのが，Nervesでも嬉しいWSL 2のイイトコロその１です．
microSDをホストPCに接続し，WSL 2上で`mix burn`を実行してください．


すると，PowerShellが管理者権限で立ち上がり，さらにコマンドプロンプトが立ち上がり，microSDカードにNervesファームウェアがこんがりと焼けます:fire::fire::fire:

```shell

~/hello_nerves$ mix burn

Nerves environment
  MIX_TARGET:   rpi0
  MIX_ENV:      dev

'\\wsl$\Ubuntu-18.04\home\takase\hello_nerves'
CMD.EXE was started with the above path as the current directory.
UNC paths are not supported.  Defaulting to Windows directory.
Use 7.31 GiB memory card found at \\.\PhysicalDrive3? [Yn] Y
```

# ボード上でNervesを実行！

今回はcoolなラズパイゼロを使います．
Nervesファームウェアがこんがり焼けたmicroSDをラズパイゼロに刺して，microUSBでホストPCと接続してください．下記の通りラズパイゼロの内側のポートに接続することに注意してください．

![image2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/240349/78ea1a38-da9a-c253-afb1-ba8defdc9b97.jpeg)

ゲストLinuxがネイティブWindowsのネットワーク構成をほとんどそのまま継承できることが，Nervesでも嬉しいWSL 2のイイトコロその２です．
そして，冒頭で説明した通り，ラズパイゼロやBeagleBoneではmicroUSBケーブルでホストPCとssh通信ができるのがナウいです．

初回のラズパイゼロ接続時は，USBがCOMポートとして認識されてデバイス設定されることがあります．この場合は，microUSBによるssh接続の機能を使うために，「USB Ethernet/RNDIS Gadget Driver」をインストールしてください．

加えて，`~/.ssh/config`に下記を記述してください（ファイルが存在しない場合は作成してください）

```~/.ssh/config
Host nerves.local
  UserKnownHostsFile /dev/null
  StrictHostKeyChecking no
  IdentityFile ~/.ssh/id_rsa
```




以上の設定を行って，microUSB接続して10秒ほど経ってから，**WSL 2上から**`nerves.local`にssh接続してみましょう．

```shell
$ ssh nerves.local
Warning: Permanently added 'nerves.local,172.31.77.101' (RSA) to the list of known hosts.
Interactive Elixir (1.10.2) - press Ctrl+C to exit (type h() ENTER for help)
Toolshed imported. Run h(Toolshed) for more info.
RingLogger is collecting log messages from Elixir and Linux. To see the
messages, either attach the current IEx session to the logger:

  RingLogger.attach

or print the next messages in the log:

  RingLogger.next

iex(1)>
```

うごいた！！:raised_hands::raised_hands::raised_hands::raised_hands::raised_hands:

ちなみにNervesでは公開鍵認証方式によるssh接続なので，公開鍵と秘密鍵のペアをWindowsネイティブにも渡しておけば，Windowsネイティブのsshクライアントからも接続可能ですｗ


ssh接続できているので，もちろんVirtualEther越しのファームウェアのアップデートも可能です．これもWSL 2からできるのがエグい．

```
# ファームウェア書き込みスクリプトの作成
$ mix firmware.gen.script

Nerves environment
  MIX_TARGET:   rpi0
  MIX_ENV:      dev

Writing upload.sh...

# ssh越しのファームウェアのアップデート
$ ./upload.sh
Path: ./_build/rpi0_dev/nerves/images/hello_nerves.fw
Product: hello_nerves 0.1.0
UUID: 1db61ebe-913e-577b-7d4c-6930c974e413
Platform: rpi0

Uploading to nerves.local...
Warning: Permanently added '[nerves.local]:8989,[172.31.77.101]:8989' (RSA) to the list of known hosts.
Running fwup...
fwup: Upgrading partition B
100% [====================================] 31.02 MB in / 33.12 MB out
Success!
Elapsed time: 19.948 s
Rebooting...
```


うまく繋がらない場合は，デバイスマネージャー画面で，ネットワークアダプターとして`USB Ethernet/RNDIS Gadget`が認識されているかを確認してみてください．

![devicemanager.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/240349/38de718e-66ac-f012-0c73-bc254f74b9c2.jpeg)

あるいは，`nerves.local`の名前解決はmDNSで行われていますので，ファイアウォール設定が適切か確認してください．mDNS機能も最近のWindows Updateで標準搭載されたそうです．
mDNSのポート番号5353が受信許可されている必要があります．

![firewall.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/240349/91a887f3-5011-3547-e2de-ee4a401974c8.jpeg)

ちなみに`listener`がファイアウォール設定されているのは，ROS屋さんあるあるですよね～（これを見せたかっただけ^^;

# おわりに

筆者は今までNervesの布教活動を進めてきて，全国各地でハンズオンを実施してきました．
でも「Windowsでこなれてないなら使えないよ～」の声を割りと多めにいただいてきました，，，

さぁこれでどうでしょうか？ ~~もう言い訳できませんよね！？？~~
Windows屋さん，一緒にNervesしましょう！！

Elixir/Nervesをもっと知りたい方，Nervesでなんか作ってみたい方，Nerves Projectに貢献したい方，『[NervesJP](https://nerves-jp.connpass.com/)』にれっつじょいなす！！

[![NCJP_Logo1_V1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/240349/a4c2e825-fb18-018b-9e56-051e43b1c29d.png)](https://nerves-jp.connpass.com/)