Nerves上で動作するLivebookをデバイスとローカルマシンとで同期する
課題
Nerves Livebookをデバイス上で動かして、livebookでコードを書いたとします。その結果をリポジトリ管理しようと思っても、livebookのファイル(.livemd
ファイル)はデバイス上にあるので、そのままではうまくいきません。デバイスから手元のマシンになんらかの方法で持ってくる必要があります。
解決策
そこで、Nerves Livebookのデフォルト設定でサポートされているSFTP経由で、デバイス上のファイルを手元のマシンに取得するようにしましょう。
(本記事ではMacで検証しています。他のOSについては適宜読み替えてください)
Nerves Livebookを準備する
まずはNerves Livebookを、ドキュメントに従って動かせるようにしましょう。
下記のコマンドでデバイスにSSHできるようになればOKです。
$ ssh livebook@nerves.local
livebookを置くリポジトリを準備する
自分で作ったlivebookを管理するGitリポジトリを作っておきましょう。ディレクトリ構成は、こんな感じになりました。
- Nerves Livebookのクローン先:
/Users/antipop/src/github.com/fhunleth/nerves_livebook
- 自分のLivebookを置くローカルリポジトリ:
/Users/antipop/src/github.com/kentaro/my_nerves_livebooks
以下、適宜ご自身の環境に読み替えてください。
センサーデータを扱う準備をする
せっかくNerves Livebookを使うので、何かセンサーデータを取得してみたいですよね。そこで、CO2濃度を取得できるMH-Z19というセンサーをElixirから扱えるkentaro/mh_z19_exを使ってみることにしましょう。
Raspberry Piへのセンサーの接続については解説しません。mh-z19 · PyPIに図解されていますので、ご覧ください。
以下のようにして依存モジュールを追加します。
diff --git a/mix.exs b/mix.exs
index 316355a..9151cbd 100644
--- a/mix.exs
+++ b/mix.exs
@@ -54,7 +54,7 @@ defmodule NervesLivebook.MixProject do
{:kino, "~> 0.2.1"},
# Dependencies for all targets except :host
- {:circuits_uart, "~> 1.3", targets: @all_targets},
+ {:circuits_uart, "~> 1.3", targets: @all_targets, override: true},
{:circuits_gpio, "~> 0.4", targets: @all_targets},
{:circuits_i2c, "~> 0.3", targets: @all_targets},
{:circuits_spi, "~> 0.1", targets: @all_targets},
@@ -74,7 +74,10 @@ defmodule NervesLivebook.MixProject do
{:nerves_system_bbb, "~> 2.10", runtime: false, targets: :bbb},
{:nerves_system_osd32mp1, "~> 0.6", runtime: false, targets: :osd32mp1},
{:nerves_system_x86_64, "~> 1.15", runtime: false, targets: :x86_64},
- {:nerves_system_npi_imx6ull, "~> 0.2", runtime: false, targets: :npi_imx6ull}
+ {:nerves_system_npi_imx6ull, "~> 0.2", runtime: false, targets: :npi_imx6ull},
+
+ {:mh_z19, "~> 0.1.0"}
]
end
上記でcircuits_uart
の設定をいじっている箇所は、mh_z19
の依存とバッティングしてエラーになったので上書きするようにしました。
SDカードをデバイスから取り出してパソコンにセットした上で、ファームウェアをビルドしてSDカードに焼きます。
$ mix deps.get
$ mix firmware.burn
SDカードをデバイスに差し戻して起動しましょう。
livebookを作成する
デバイスが無事起動したら、 http://nerves.local/ からアクセスできるはずです。
右上のNew livebookというボタンから新規作成します。作成したページで、以下のようにMH-Z19からデータを読み出すコードを動かしてみました。
無事に動きました!
作成したlivebookは、忘れずにファイルに保存しておきましょう。
デバイスから手元にファイルを取得する
さて、上記で作成したファイルはデバイス上にあります。自分で作ったlivebookを管理するGitリポジトリにファイルをコミットするには、デバイス上の自分が作成したlivebookが置かれているディレクトリに含まれる.livemd
ファイルを手元のMacに全部持ってくる必要があります。ここではSFTPを使って取得するようにしましょう。
以下のような内容でsync-my-livebooks
としてファイルを作っておきます。
cd /data/livebooks/notebook
get *
livebookを管理したいリポジトリから、以下のコマンドを実行します。パスワードの入力を求められるので、表示されている通りnerves
を入力しましょう。
$ sftp livebook@nerves.local < sync-my-livebooks
Nerves Livebook
https://github.com/fhunleth/nerves_livebook
ssh livebook@nerves.local # Use password "nerves"
Password:
そうすると、sync-my-livebooks
の内容がSFTPコマンドとして実行され、デバイス上のlivebookのファイルを手元に取得できます。あとは、取得したファイルをいつも通りgit commit
してやればよいでしょう。
補足
SCPではだめなのか
Nervesは、デフォルトでシェルをiex(ElixirのREPL)に置き換えています。また、SSH経由で送信された内容をデフォルトではElixirのコードとして実行します。そのため、普通にはSCPが使えません。SFTPを使うのがおすすめです。
NervesのSSHの設定についてはNervesSSH — nerves_ssh v0.2.2をご覧ください。
SSHFSではだめなのか
Embedded Elixirによるといけるっぽいのですが、試してみたところうまくマウントできませんでした。SSHFSがいけるなら自動的に同期できるので、その方がよいのですが。
デバイスにSSHできない場合
この検証ではRaspberry Pi 3(rpi3
)を使っているのですが、デフォルトの設定ではイーサネット経由でのSSHができませんでした。そこで、rpi3
用の設定ファイルを以下のように書き換えて、自前でファームウェアをビルドしてデバイスに適用しました。
diff --git a/config/rpi3.exs b/config/rpi3.exs
index c3049d8..d2fbc47 100644
--- a/config/rpi3.exs
+++ b/config/rpi3.exs
@@ -5,6 +5,6 @@ import Config
config :vintage_net,
regulatory_domain: "US",
config: [
- {"eth0", %{type: VintageNetEthernet, ipv4: %{method: :dhcp}}},
+ {"eth0", %{type: VintageNetDirect}},
{"wlan0", %{type: VintageNetWiFi}}
]
これで、イーサネット経由でSSHできるようになりました。
Discussion