もう師走だなんて信じられない!11月31日はどこにいった!!
なんて言ってても時は戻らないので,雑ネタを投下したいと思います.
2023年は一本もQiita記事をアウトプットしておらず,そして #NervesJP Advent Calendar 2023 も飛ばしてしまったので,その罪滅ぼしも兼ねまして,,,
みなさんれっつじょいなす!!
Zenohexとは??
ZenohのElixir APIライブラリです.
@ShintaroHosoai や @pojiro と楽しくつくってます:D
いろんなご縁がありまして7月の daimon.ex にて講演させてもらいました.YouTubeアーカイブも公開してくれていて,Alchemist向け前提ではあるものの,ZenohやZenohexの解説内容としては一番まとまっているのではと思っています.
このへんの記事も参考になるかと思います,というか私も参考にしています:D
ネイティブなElixirで試されている記事も!(Zenohexバージョンが v0.1.3 で古めです)
という感じで Zenohex on Nerves はまだ記事がなさそうなのでセーフセーフ,ということでやっていきます〜
下準備
ホスト環境
ホストは MacBook Pro 2021 / Apple M1 Max でやっていきます.
Elixir/Erlang/Nerves 関係の環境は構築済みであることを前提とします.ちなバージョンはこんな感じ.
% asdf current
elixir 1.17.3-otp-27 /Users/takasehideki/.tool-versions
erlang 27.1.2 /Users/takasehideki/.tool-versions
Nerves mix プロジェクトの作成
いつものように Nerves mix プロジェクトを作成して初期ビルドしておきます.
% mix nerves.new zenohex_nerves
* creating zenohex_nerves/config/config.exs
* creating zenohex_nerves/config/host.exs
* creating zenohex_nerves/config/target.exs
* creating zenohex_nerves/lib/zenohex_nerves.ex
* creating zenohex_nerves/lib/zenohex_nerves/application.ex
* creating zenohex_nerves/test/test_helper.exs
* creating zenohex_nerves/test/zenohex_nerves_test.exs
* creating zenohex_nerves/rel/vm.args.eex
* creating zenohex_nerves/rootfs_overlay/etc/iex.exs
* creating zenohex_nerves/.gitignore
* creating zenohex_nerves/.formatter.exs
* creating zenohex_nerves/mix.exs
* creating zenohex_nerves/README.md
Fetch and install dependencies? [Yn] y
* running mix deps.get
* running mix format
Your Nerves project was created successfully.
You should now pick a target. See https://hexdocs.pm/nerves/supported-targets.html
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 zenohex_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 burn
Plug the SDCard into the target and power it up. See target documentation
above for more information and other targets.
mix deps.get
これもいつものやつですね.
ターゲットは目の前に転がっていたラズパイ4にしました.
% cd zenohex_nerves
% export MIX_TARGET=rpi4
% mix deps.get
Resolving Hex dependencies...
Resolution completed in 0.09s
Unchanged:
beam_notify 1.1.1
castore 1.0.10
circular_buffer 0.4.1
elixir_make 0.9.0
gen_state_machine 3.0.0
jason 1.4.4
mdns_lite 0.8.11
muontrap 1.5.0
nerves 1.11.2
nerves_logging 0.2.2
nerves_motd 0.1.15
nerves_pack 0.7.1
nerves_runtime 0.13.7
nerves_ssh 1.0.0
nerves_system_bbb 2.25.0
nerves_system_br 1.29.1
nerves_system_grisp2 0.13.0
nerves_system_mangopi_mq_pro 0.11.0
nerves_system_osd32mp1 0.20.0
nerves_system_rpi 1.29.0
nerves_system_rpi0 1.29.0
nerves_system_rpi2 1.29.0
nerves_system_rpi3 1.29.0
nerves_system_rpi3a 1.29.0
nerves_system_rpi4 1.29.0
nerves_system_rpi5 0.4.0
nerves_system_x86_64 1.29.0
nerves_time 0.4.8
nerves_toolchain_aarch64_nerves_linux_gnu 13.2.0
nerves_toolchain_armv6_nerves_linux_gnueabihf 13.2.0
nerves_toolchain_armv7_nerves_linux_gnueabihf 13.2.0
nerves_toolchain_ctng 1.10.0
nerves_toolchain_riscv64_nerves_linux_gnu 13.2.0
nerves_toolchain_x86_64_nerves_linux_musl 13.2.0
nerves_uevent 0.1.0
one_dhcpd 2.0.3
property_table 0.2.5
ring_logger 0.11.3
shoehorn 0.9.2
ssh_subsystem_fwup 0.6.2
toolshed 0.4.1
uboot_env 1.0.1
vintage_net 0.13.5
vintage_net_direct 0.10.7
vintage_net_ethernet 0.11.2
vintage_net_wifi 0.12.5
All dependencies are up to date
==> jason
Compiling 10 files (.ex)
Generated jason app
==> elixir_make
Compiling 8 files (.ex)
Generated elixir_make app
==> castore
Compiling 1 file (.ex)
Generated castore app
==> nerves
HOST_CC port.o
HOST_LD port
Compiling 51 files (.ex)
Generated nerves app
==> zenohex_nerves
Nerves environment
MIX_TARGET: rpi4
MIX_ENV: dev
Checking for prebuilt Nerves artifacts...
Checking nerves_system_rpi4...
[GitHub] nerves_system_rpi4-portable-1.29.0-FB67112.tar.gz
|==================================================| 100% (274 / 274) MB
=> Success
Found nerves_toolchain_aarch64_nerves_linux_gnu in cache
/Users/takasehideki/.nerves/artifacts/nerves_toolchain_aarch64_nerves_linux_gnu-darwin_arm-13.2.0
Zenohex のインストール
いつものように mix.exs
を編集します.
# Run "mix help deps" to learn about dependencies.
defp deps do
[
<snipped...>
{:nerves_system_mangopi_mq_pro, "~> 0.6", runtime: false, targets: :mangopi_mq_pro},
# Add zenohex hex package
{:zenohex, "~> 0.3.2"}
]
end
改めて mix deps.get
しておきます.
% mix deps.get
Resolving Hex dependencies...
Resolution completed in 0.109s
New:
rustler_precompiled 0.8.2
toml 0.7.0
zenohex 0.3.2
Unchanged:
<snipped...>
* Getting zenohex (Hex package)
* Getting rustler_precompiled (Hex package)
* Getting toml (Hex package)
==> nerves
==> zenohex_nerves
Nerves environment
MIX_TARGET: rpi4
MIX_ENV: dev
Checking for prebuilt Nerves artifacts...
Found nerves_toolchain_aarch64_nerves_linux_gnu in cache
/Users/takasehideki/.nerves/artifacts/nerves_toolchain_aarch64_nerves_linux_gnu-darwin_arm-13.2.0
Found nerves_system_rpi4 in cache
/Users/takasehideki/.nerves/artifacts/nerves_system_rpi4-portable-1.29.0
さてビルド
Zenoh は Rust 実装されていますが,NIF的な Rustler と Rustler Precompiled のおかげでユーザはそこをあんまり気にする必要ありません.ビルド時に prebuild binary をよしなにダウンロードしてきてくれます.
% mix firmware
==> nerves
<snipped...>
==> rustler_precompiled
Compiling 4 files (.ex)
Generated rustler_precompiled app
==> zenohex
Compiling 13 files (.ex)
18:13:10.932 [debug] Copying NIF from cache and extracting to /Users/takasehideki/research/zenoh/zenohex_nerves/_build/rpi4_dev/lib/zenohex/priv/native/libzenohex_nif-v0.3.2-nif-2.15-aarch64-unknown-linux-gnu.so.tar.gz
Generated zenohex app
<snipped...>
==> zenohex_nerves
Compiling 2 files (.ex)
Generated zenohex_nerves app
|nerves| Building OTP Release...
* [Nerves] validating vm.args
* skipping runtime configuration (config/runtime.exs not found)
* creating _build/rpi4_dev/rel/zenohex_nerves/releases/0.1.0/vm.args
Updating base firmware image with Erlang release...
Copying rootfs_overlay: /Users/takasehideki/research/zenoh/zenohex_nerves/_build/rpi4_dev/nerves/rootfs_overlay
Copying rootfs_overlay: /Users/takasehideki/research/zenoh/zenohex_nerves/rootfs_overlay
Building /Users/takasehideki/research/zenoh/zenohex_nerves/_build/rpi4_dev/nerves/images/zenohex_nerves.fw...
Firmware built successfully! 🎉
Now you may install it to a MicroSD card using `mix burn` or upload it
to a device with `mix upload` or `mix firmware.gen.script`+`./upload.sh`.
burn
これもいつものやつですね.
microSDカードにNervesファームウェアを焼きます.
% mix burn
==> nerves
==> zenohex_nerves
Nerves environment
MIX_TARGET: rpi4
MIX_ENV: dev
Use 14.65 GiB memory card found at /dev/rdisk4? [Yn] y
100% [====================================] 45.06 MB in / 54.37 MB out
Success!
Elapsed time: 5.560 s
実行
SSHアクセス
microSDカードを Raspberry Pi 4 に挿入し,Ethernetでよしなに接続しておきます.
これもいつものやつですね.電源を入れてちょっと待ってから ssh nerves.local
で Nerves 上の IEx に入ります.
% ssh nerves.local
Warning: Permanently added 'nerves.local' (ED25519) to the list of known hosts.
Interactive Elixir (1.17.3) - press Ctrl+C to exit (type h() ENTER for help)
████▄▄ ▐███
█▌ ▀▀██▄▄ ▐█
█▌ ▄▄ ▀▀ ▐█ N E R V E S
█▌ ▀▀██▄▄ ▐█
███▌ ▀▀████
zenohex_nerves 0.1.0 (22c33f4a-52b4-56c7-1d3e-159ed97caaa2) arm rpi4
Serial : 10000000b39a11d8
Uptime : 23.652 seconds
Clock : 2024-11-26 20:49:47 UTC (unsynchronized)
Temperature : 47.2°C
Firmware : Valid (A) Applications : 41 started
Memory usage : 152 MB (4%) Part usage : 288 MB (2%)
Hostname : nerves-11d8 Load average : 0.80 0.19 0.06
eth0 : 192.168.11.100/24, fe80::e65f:1ff:fe65:8172/64
usb0 : 172.31.166.73/30
Nerves CLI help: https://hexdocs.pm/nerves/iex-with-nerves.html
Toolshed imported. Run h(Toolshed) for more info.
iex(1)>
とりま Pub/Sub してみる
Zenohex リポジトリの README にある Getting Started を :muishiki: にやってみます.
iex(1)> {:ok, session} = Zenohex.open()
{:ok, #Reference<0.1198495918.1163788306.186120>}
iex(2)> {:ok, publisher} = Zenohex.Session.declare_publisher(session, "demo/example/test")
{:ok, #Reference<0.1198495918.1163788290.187264>}
iex(3)> {:ok, subscriber} = Zenohex.Session.declare_subscriber(session, "demo/**")
{:ok, #Reference<0.1198495918.1163788290.187298>}
iex(4)> Zenohex.Publisher.put(publisher, "Hello Zenoh Dragon")
:ok
iex(5)> Zenohex.Subscriber.recv_timeout(subscriber, 1000)
{:ok,
%Zenohex.Sample{
key_expr: "demo/example/test",
value: "Hello Zenoh Dragon",
kind: :put,
reference: #Reference<0.1198495918.1163788306.186121>
}}
やったぜいごいたぜっ!!
ちなリポジトリには,より Elixir 成分の感じられる practical な Examples もご用意しております.
注意点というかお誘いというか
本稿執筆時点での Zenohex の最新版は v0.3.2 となります.
Zenoh への対応は v0.11.0 になります.
Zenoh は割りとバージョン番号にセンシティブ a.k.a 絶賛成長中で,異なるバージョン間では通信できないことが多いです.なので他ライブラリや言語モジュールと疎通させたい場合はご注意ください.
もちろん v1.0 対応は進行中でっす!
ですが,,, たいへん辛みがツラく,,, v0.11.0 to v1.0 はけっこう内部構造から変わってるんですよねぇ,,,
Elixir と Rust と Rustler を完全に理解されている方がいましたら,ぜひとも手伝ってください!!
まとめ
Zenoh の Elixir API である Zenohex を用いて Nerves で Pub/Sub をしてみました。簡単に Pub/Sub できました。
今回は NAT越えやネットワーク間通信をするための Zenoh ルータを用いていません。また Zenoh 独自の機能である pull や get も使っていません。今後、試していきたいと思います。
という既視感たっぷりな締めにて筆を置きたいと思います〜