はじめに
関数型言語ElixirでIoTを開発できる**「ナウでヤングなcoolな」**Nervesフレームワーク,楽しいっすよね!!?
ですが初めて触る方には,いろいろ引っかかるトラップがあるかと思います.そこにハマってしまっては面白くない,,, せっかくならIoTアプリの開発で存分に楽しみたい!ということで,よくあるエラーと対処策をまとめてみることにしました.
他にもこんなのもあるんじゃない?こう解決できるよ!とかありましたら,どしどしコメントや筆者へのTwitterにお寄せください.もちろん,こんなんで困ってんだけどどうすんの?てのも大歓迎です!!
適宜で項目やリンクを追加していくつもりです.
環境設定篇
開発環境の構築やNervesプロジェクトの一通りの開発プロセスでのハマりどころです.
nerves.new
コマンドが動かない
Nervesの良いところは,普通のElixirアプリの開発と同じようにmix
でプロジェクト作成と管理ができることです.
ということで喜び勇んでNervesプロジェクト作成のコマンドmix nerves.new
をしてみたら,どうもうまく動きません,,,
症状
$ mix nerves.new hello_nerves
** (Mix) The task "nerves.new" could not be found
Note no mix.exs was found in the current directory
原因と対処法
Nerves向けのmix
コマンドがアーカイブされたnerves_bootstrap
をインストールしてやりましょう.
$ mix archive.install hex nerves_bootstrap
ssh鍵が無い
プロジェクトを作成したら次に実行するコマンドはmix deps.get
です.これでプロジェクトに依存するライブラリをダウンロードしてきて,それらの依存性を解決してくれます.
ところがこれがどうも動きません,,,
症状
$ mix deps.get
** (Mix) No SSH public keys found in ~/.ssh. An ssh authorized key is needed to
log into the Nerves device and update firmware on it using ssh.
See your project's config.exs for this error message.
原因と対策
NervesではホストPCとデバイスとの通信にsshを使います.この通信をセキュアにするために鍵交換方式が用いられます.公開鍵がNervesアプリのビルド時に参照されて,その情報をアプリに取り込みます.
このように,公開鍵と秘密鍵のキーペアを作ってやりましょう.
$ ssh-keygen -t rsa
TIPS: Nervesで使用するssh鍵
Nervesのデフォルトで使えるssh鍵のファイル名は,下記のとおりです.
(カッコ内は鍵作成時のコマンドラインとオプション)
- id_rsa.pub: RSA方式の公開鍵 (
ssh-keygen -t rsa
) - id_ecdsa.pub: ECDSA方式の公開鍵 (
ssh-keygen -t ecdsa
) - id_ed25519.pub: (
ssh-keygen -t ed25519
)
公開鍵とはいえ普段使っている?ものがNervesデバイスに取り込まれるのはコワイ,,, という方もいらっしゃるかと思います.ssh-keygen -f
オプションでファイル名を指定して専用の鍵ファイルを作ることもできます.
ssh-keygen -t rsa -f ~/.ssh/id_rsa_nerves
このようにした場合は,プロジェクトのconfig/target.exsの30行目辺りでファイル名を指定してやってください.Path.join()
の上から順にファイルを探して最初に見つかったものを利用します.
keys =
[
Path.join([System.user_home!(), ".ssh", "id_rsa_nerves.pub"]), # 追加した行
Path.join([System.user_home!(), ".ssh", "id_rsa.pub"]),
Path.join([System.user_home!(), ".ssh", "id_ecdsa.pub"]),
Path.join([System.user_home!(), ".ssh", "id_ed25519.pub"])
]
|> Enum.filter(&File.exists?/1)
nerves_bootstrap
が古い
まだmix deps.get
がうまく動いていないようです,,,
症状
$ mix deps.get
Resolving Hex dependencies...
Dependency resolution completed:
Unchanged:
nerves 1.6.3
shoehorn 0.6.0
All dependencies are up to date
Nerves environment
MIX_TARGET: host
MIX_ENV: dev
Resolving Nerves artifacts...
A new version of Nerves bootstrap is available(1.8.0 < 1.8.1), You can update by running
mix local.nerves
原因と対処法
nerves_bootstrap
を新しいバージョンに更新する必要があります.
mix local.nerves
で最新版に更新することができます.
$ mix local.nerves
Resolving Hex dependencies...
Dependency resolution completed:
New:
nerves_bootstrap 1.8.1
* Getting nerves_bootstrap (Hex package)
All dependencies are up to date
13:31:19.113 [info] Application nerves_bootstrap exited: :stopped
Compiling 11 files (.ex)
Generated nerves_bootstrap app
Generated archive "nerves_bootstrap-1.8.1.ez" with MIX_ENV=prod
Found existing entry: /Users/takase/.asdf/installs/elixir/1.10.3-otp-23/.mix/archives/nerves_bootstrap-1.8.0
Are you sure you want to replace it with "nerves_bootstrap-1.8.1.ez"? [Yn] Y
* creating /Users/takase/.asdf/installs/elixir/1.10.3-otp-23/.mix/archives/nerves_bootstrap-1.8.1
TIPS: バージョンを指定したい
逆に,昔作ったNervesアプリについて,バージョンは保ったままメンテしたいということがあるかと思います(多くの場合はバージョンアップしたほうが良い!ということは置いといて^^;
特定のバージョンをインストールするには,下記のようにします.
$ mix archive.install hex nerves_bootstrap 1.8.0
環境変数が設定されていない
次はmix firmware
です.このコマンドでNervesのファームウェアをビルドします.
ところがどうも真っ赤なメッセージが出てきました,,,
症状
$ mix firmware
==> toolshed
Compiling 10 files (.ex)
Generated toolshed app
==> ring_logger
Compiling 4 files (.ex)
Generated ring_logger app
==> shoehorn
Compiling 8 files (.ex)
Generated shoehorn app
==> hello_nerves
Nerves environment
MIX_TARGET: host
MIX_ENV: dev
NERVES_SYSTEM is unset
NERVES_TOOLCHAIN is unset
** (Mix) Environment variable $NERVES_SYSTEM is not set.
This variable is usually set for you by Nerves when you specify the
$MIX_TARGET. It is unusual to need to specify it yourself.
Some things to check:
1. In your `mix.exs`, is the value that you have in $MIX_TARGET in the
`@all_targets` list? If you're not using `@all_targets`, then the
$MIX_TARGET should appear in the `:targets` option for `:nerves_runtime`
and other packages that run on the target.
2. Do you have a dependency on a Nerves system for the target? For example,
`{:nerves_system_rpi0, "~> 1.8", runtime: false, targets: :rpi0}`
3. Is there a typo? For example, is $MIX_TARGET set to `rpi1` when it should
be `rpi`.
4. Is there a typo in the package name of the system? For example, if you
have a custom system, `:nerves_system_my_board`, does the spelling of the
system in the dependency in your `mix.exs` match the spelling in your
system project's `mix.exs`?
For build examples in the Nerves documentation, please see
https://hexdocs.pm/nerves/getting-started.html#create-the-firmware-bundle
原因と対処法
Nervesではシェル環境変数$MIX_TARGET
で対象のデバイスを指定します.
rpi0
とかbbb
とか設定してやる必要があります.
# ラズパイゼロの例
$ export MIX_TARGET=rpi0
なお,このあとにもういちどmix deps.get
してやってください.
TIPS: ターゲットデバイスの指定
Nerves公式のターゲットデバイスは,このhexdocsページのTAG列を参照してください.
ちなみに,同じNervesのプロジェクトディレクトリを,複数のターゲット向けに共用することができるわけです.環境変数$MIX_TARGET
をポチポチ切り替えるだけです.
Erlang/OTPのバージョンが合っていない
さぁ$MIX_TARGET
も設定したしもう一度!
でも今度は違うエラーが??
症状
$ mix firmware
==> nerves_system_br
Generated nerves_system_br app
==> nerves_toolchain_ctng
Compiling 1 file (.ex)
Generated nerves_toolchain_ctng app
==> nerves_toolchain_arm_unknown_linux_gnueabihf
Generated nerves_toolchain_arm_unknown_linux_gnueabihf app
==> nerves_system_rpi3
Generated nerves_system_rpi3 app
==> hello_nerves
Nerves environment
MIX_TARGET: rpi3
MIX_ENV: dev
** (Mix) Major version mismatch between host and target Erlang/OTP versions
Host version: 22
Target version: 23
This will likely cause Erlang code compiled for the target to fail in
unexpected ways.
The easiest way to resolve this issue is to install the same version of
Erlang/OTP on your host. See the Nerves installation guide for doing this
using the `asdf` version manager.
The Nerves System (nerves_system_*) dependency determines the OTP version
running on the target. It is possible that a recent update to the Nerves
System pulled in a new version of Erlang/OTP. If you are using an official
Nerves System, you can verify this by reviewing the CHANGELOG.md file that
comes with the release. Run 'mix deps' to see the Nerves System version and
go to that system's repository on https://github.com/nerves-project.
If you need to run a particular version of Erlang/OTP on your target, you can
either lock the nerves_system_* dependency in your mix.exs to an older
version. Note that this route prevents you from receiving security updates
from the official systems. The other option is to build a custom Nerves
system. See the Nerves documentation for building a custom system and then
run 'make menuconfig' and look for the Erlang options.
原因と対処法
最新のNervesはErlang/OTP 23に対応しています.このバージョン番号はホストPCのものと一致している必要があります.
@MzRyuKa さんの記事にも詳しい解説があります.OTP 22のまま利用する対処方法も紹介されていますので,あわせてご覧ください.
asdf
でElixir/Erlangのバージョンを管理されている方は,asdf
もv0.7.8以降にしてやる必要があります.ということで,まずはasdfをアップデートしましょう.
$ asdf update
Elixir/Erlangのプラグインもアップデートしておきます.
$ asdf plugin update erlang
$ asdf plugin update elixir
そして,Elixir/Erlangをアップデートしてやります.
$ asdf install erlang 23.0.1
$ asdf install elixir 1.10.3-otp-23
$ asdf global erlang 23.0.1
$ asdf global elixir 1.10.3-otp-23
$ mix local.hex
$ mix local.rebar
$ mix archive.install hex nerves_bootstrap
microSDカードが刺さっていない
無事にファームウェアのビルドができました.
microSDカードへの書込みはmix burn
です.
ところが,なんかおかしいです,,,
症状
$ mix burn
Nerves environment
MIX_TARGET: rpi0
MIX_ENV: dev
** (Mix) Could not auto detect your SD card
原因と対処法
まぁ見てのとおりですね,microSDカードをホストPCに刺してください^^;
あるいは刺し方が甘くて認識していないこともありえますので,もう一度刺し直してみてください.
$ mix burn
Nerves environment
MIX_TARGET: rpi0
MIX_ENV: dev
Use 7.31 GiB memory card found at /dev/rdisk3? [Yn] Y
100% [====================================] 31.71 MB in / 34.21 MB out
Success!
Elapsed time: 3.047 s
なお書込み先がホントにrdisk3
で番号合ってる??と疑心暗鬼な方は,df
とかdiskutil list
でご確認ください^^;
書込みがいったん完了したら,自動でmicroSDカードをアンマウントします.
もう一度書き直したい場合は,microSDカードを刺し直してください.
ssh接続篇
さぁNervesデバイスを実行するぞ!
ホストからの接続は,基本的にはssh接続で通信します.
ラズパイゼロやBeagleBoneファミリでは,USBケーブルの接続がVirtualEtherと認識されて,それだけでssh接続が可能です.
その他のボードでは,ネットワークの設定が必要です.最新のNervesではVintageNetというライブラリを利用します.この設定方法は,下記の記事が詳しいです.
- 「nerves_pack(vintage_net含む)を使ってNervesのネットワーク設定をした〜SSHログインまで〜」 by @nishiuchikazuma さん
- 「備忘録: WSL2 Ubuntu上で NervesとLAN接続してみる by @ShozF さん」
そのときのハマりどころです.
nerves.local
が見つからない
NervesデバイスにはmDNSが動作していて,デフォルトではnerves.local
という名前が自動的に付与されます.
ところが例えばping
してやると,この名前が見つかりません,,,
症状
$ ping nerves.local
ping: cannot resolve nerves.local: Unknown host
原因と対処法
まずは,まだmDNSサービスが立ち上がっていないことが考えられます.経験的にラズパイゼロだと,電源投下してから15秒くらいは掛かります.
そんなせっかちにならずに,,, ちょっとだけお待ちください^^;
$ ping nerves.local
PING nerves.local (172.31.77.101): 56 data bytes
64 bytes from 172.31.77.101: icmp_seq=0 ttl=64 time=0.637 ms
64 bytes from 172.31.77.101: icmp_seq=1 ttl=64 time=0.508 ms
64 bytes from 172.31.77.101: icmp_seq=2 ttl=64 time=0.638 ms
^C
--- nerves.local ping statistics ---
3 packets transmitted, 3 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 0.508/0.594/0.638/0.061 ms
あるいは,デフォルトの設定を変更して,nerves.local
でない名前にされているかもしれません.
この名前の設定は,config/target.exsの63行目辺りのconfig: mdns_lite,
ブロックにあります.
config :mdns_lite,
# The `host` key specifies what hostnames mdns_lite advertises. `:hostname`
# advertises the device's hostname.local. For the official Nerves systems, this
# is "nerves-<4 digit serial#>.local". mdns_lite also advertises
# "nerves.local" for convenience. If more than one Nerves device is on the
# network, delete "nerves" from the list.
host: [:hostname, "nerves"],
ttl: 120,
ちなみに,ここのコメントに書いてあるとおり,nerves-<4 digit serial#>.local
という名前も付いています.この4桁は,ボードのシリアル番号から付けられています.
それでも繋がらない場合は,,, arp -a
などでIPアドレスを探してやって,直接繋いでみてください.(いったんIPアドレスで繋がると,その後は名前解決もできてnerves.local
で繋がるようになることがあります)
ssh接続できない
さぁnerves.local
も見つかっているしNervesデバイスにssh接続するぞ!
でも,いごかない,,, passwordってなにそれ??
症状
$ ssh nerves.local
SSH server
Enter password for "takase"
password:
(省略)
Permission denied (publickey,keyboard-interactive,password).
原因と対処法
これはssh鍵をデフォルトのid_rsa
以外にしているときに発生することが多いです.
例えば前述したこんな感じでid_rsa_nerves
という名前の鍵を作っていたとしましょう.
まずひとつめの対処方法は,コマンドラインで秘密鍵を指定してやることです.
$ ssh nerves.local -i ~/.ssh/id_rsa_nerves
Interactive Elixir (1.10.3) - 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)>
あるいは,~/.ssh/config
に下記のように設定情報を書いておくと,コマンドラインで指定する必要が無くなります.
Host nerves.local
UserKnownHostsFile /dev/null
StrictHostKeyChecking no
IdentityFile ~/.ssh/id_rsa_nerves
実行時篇
さぁ繋がったぞ!さくさく動いてるぞ!!
あれっでもなんかおかしい??
終了できない
ElixirのREPLであるiexの終了時は,Ctrl+Cを2回押しされる方が多いかと.
$ iex
Erlang/OTP 23 [erts-11.0.1] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [hipe]
Interactive Elixir (1.10.3) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)>
BREAK: (a)bort (A)bort with dump (c)ontinue (p)roc info (i)nfo
(l)oaded (v)ersion (k)ill (D)b-tables (d)istribution
^C
あれっでもこれNervesじゃダメじゃね??
症状
iex(1)> ** (EXIT) interrupted
iex(1)> ** (EXIT) interrupted
原因と対処法
なぜなら今はNervesデバイスにssh接続しているからです.
ssh接続を終了する場合はexit
ですよね.
iex(1)> exit
Connection to nerves.local closed.
ちなみに,Nervesデバイスをちゃんと終了したい場合は,System.stop/0
を使うと良いでしょう.
iex(1)> System.stop
:ok
Received disconnect from 172.31.77.101 port 22:11: Terminated (shutdown) by supervisor
Disconnected from 172.31.77.101 port 22
おわりに
さぁ皆さんのNervesデバイス,思ったとおりに**「いごいた」**でしょうか!??
さっくりまとめるつもりだったのが,思ったより長くなってしまった,,,
冒頭にも書きましたが,他にもこんなのもあるんじゃない?こう解決できるよ!とかありましたら,どしどしコメントや筆者へのTwitterにお寄せください.
もちろん,こんなんで困ってんだけどどうすんの?てのも大歓迎です!!