LoginSignup
8
0

More than 1 year has passed since last update.

Elixir/Nerves/rpi0でLチカする +α

Last updated at Posted at 2022-10-02

はじめに

備忘録です。
ElixirのIoTフレームワークであるNervesにてようやくLチカしました。
2年前のALGYANハンズオンの内容1を途中まで追っかけただけです。

環境

  • 母艦機: antiX21(debian GNUlinux11/bullseye) on sony VAIO VGN-FW72JGB / 4GB
  • target機: RaspberryPI-0 WH
  • Erlang: 25.0.4
  • Elixir: 1.14.0-otp-25

コマンド操作はすべて母艦機上で行います。

asdf のインストール

erlangとelixirのバージョン管理ツールにasdfを使います。
asdfの公式サイトgetting startedに従います。

$ sudo apt install curl git
$ git clone https://github.com/asdf-vm/asdf.git ~/.asdf --branch v0.10.2

私はbash+gitのケースなので、公式サイトの指示に従って、~/.bashrc を編集し、bashを再度立ち上げます。

~/.bashrc
  :
. $HOME/.asdf/asdf.sh
. $HOME/.asdf/completions/asdf.bash

Erlang / Elixir のインストール

nishiuchiさんの記事に従って、以下を進めていきます。
Erlang/Elixirとも最新バージョンのインストールでいいと思います。
【オンライン】豪華プレゼント付!Elixir/Nerves(ナーブス)体験ハンズオン!

# プラグイン
$ asdf plugin-add erlang https://github.com/asdf-vm/asdf-erlang.git
$ asdf plugin-add elixir https://github.com/asdf-vm/asdf-elixir.git
# erlang/elixirインストール
$ asdf install erlang 25.0.4
$ asdf install elixir 1.14.0-otp-25
# 使用するバージョンを指定
$ asdf global erlang 25.0.4
$ asdf global elixir 1.14.0-otp-25

erlangのバージョンは公式サイトで調べます。

asdf installでエラーが出る場合は、エラー内容に従って、インストールします。

:
Building Erlang/OTP 25.0.2 (asdf_25.0.2), please wait...
WARNING: It appears that a required development package 'libssl-dev' is not installed.
WARNING: It appears that a required development package 'make' is not installed.
WARNING: It appears that a required development package 'automake' is not installed.
WARNING: It appears that a required development package 'autoconf' is not installed.
WARNING: It appears that a required development package 'libncurses5-dev' is not installed.
WARNING: It appears that a required development package 'gcc' is not installed.
:
$ sudo apt install libssl-dev make automake autoconf libncurses5-dev gcc

Nervesのインストール

公式のhexdocページの内容に基づいて進めます。

$ sudo apt install build-essential automake autoconf git squashfs-tools ssh-askpass pkg-config curl

fwupのインストール
fwupはmicroSDカードにfirmwareを書き込むツールです。
公式ページに従って、debパッケージをブラウザ上でダウンロードしてから、以下のコマンドでインストールします。

# download fwup.deb
$ sudo dpkg -i ~/Downloads/fwup_1.9.0_amd64.deb

引き続きNervesのインストールを続けます。

$ sudo apt install libssl-dev libncurses5-dev bc m4 unzip cmake python
$ mix local.hex
$ mix local.rebar
$ mix archive.install hex nerves_bootstrap

Nervesのインストールはこれで終了です。

Nervesのプロジェクトの作成

公式ページにかかれている通りに新規プロジェクトを作成します。
mix nerves.new {プロジェクト名}で作成できます。

$ mix nerves.new Hellonerves
$ cd Hello_nerves

wifiの設定

vintage.netを使います。
config/target.exsに設定します。
wifiの設定は"wlan0"の箇所をデフォルトの状態から書き換えます。

config/target.exs
config :vintage_net,
  regulatory_domain: "US",
  config: [
    {"usb0", %{type: VintageNetDirect}},
    {"eth0",
     %{
       type: VintageNetEthernet,
       ipv4: %{method: :dhcp}
     }},
    {"wlan0",
      %{type: VintageNetWiFi,
        vintage_net_wifi: %{
          networks: [
            %{ key_mgmt: :wpa_psk,
               ssid: System.get_env("MY_NERVES_SSID"),
               psk: System.get_env("MY_NERVES_PSK")
            }
          ]
        },
        ipv4: %{method: :dhcp}
      }}
  ]

wifiのssid, psk を環境変数に設定してから渡します。各自の状況に合わせて設定します。
ソースコードをgithubなどの公の場所におく場合に、パスワード類をコードに記載することを避けるためです。
環境変数名は他の変数とかぶらない限り、コードと一致していれば何でもいいです。

$ export MY_NERVES_SSID=*****
$ export MY_NERVES_PSK=+++++

ssh鍵の作成

ssh鍵を作成し、公開鍵のファイル名をコードに記載します。

$ ssh-keygen -t ed25519 -f ~/.ssh/id_nerves_key -N ""
:
# ~/.ssh下に id_nerves_key(秘密鍵)と id_nerves_key.pub(公開鍵)が作成される。

作成されたssh公開鍵をconfig/target.exskeys=[...の箇所に、他の例に従って記載します。

config/target.exs
keys =
  [
    Path.join([System.user_home!(), ".ssh", "id_nerves_key.pub"]),  # <-- 追加
    Path.join([System.user_home!(), ".ssh", "id_rsa.pub"]),
    :
  ]

target環境変数の設定

私の場合はtarget機にrpi0を使いますので、以下の設定にします。

他のtarget機器を使用する場合はこちらに従って設定します。

$ export MIX_TARGET=rpi0

mix deps.get, mix firmware

コンパイルしていきます。
mix deps.getはdepsに依存するライブラリの取得をします。
先ほど環境変数を設定したシェルと同じシェルで実施する必要があります。
違うシェルでは環境変数が反映されません。

$ mix deps.get  # ライブラリの読み込みなど
$ mix firmware # コンパイル

microSDカードの準備

microSDカードのリードライターを接続する前後でlsblk -a | grep -v loopコマンドの結果の差異と、microSDカードの容量の情報から、microSDカードのデバイスファイル名を確認しておきます。恐らく/dev/sdbとか/dev/sdcなどになると思います。私の場合は、/dev/sddでした。
間違えると母艦機のシステムを壊してしまう可能性があるので、十分注意します。

$ lsblk -a | grep -v loop

mix burn

microSDカードへの書き込みです。

$ mix burn

前項で確認したデバイスファイル名を聞かれるので、合っていることを確認してenterで進めます。
自分のlinux上でのログインパスワードを聞かれるので、入力します。
パスワードが合っていれば、すぐにmicroSDカードへの書き込みが始まり、5秒間くらいで終了します。

RaspberryPIの起動

microSDカードをリーダーライターから抜き、RaspberryPI0に挿入します。
microUSBポートに電源ケーブルを接続して、5V電源を供給します。rpi0なら1Aくらいの供給能力があれば十分かと思います。電源を供給すると電源の緑色LEDの2度点滅を繰り返します。

母艦機からのssh接続

RaspberryPIを起動してから約30秒間してから、pingでアクセス可否を確認します。

$ ping -c 3 nerves.local

RaspberryPI起動からの時間が短いと、名前解決エラーが出ます。さらに10秒間くらい待ってから再度確認してください。

ping エラー例
PING nerves.local (***.***.***.***) 56(84) byes of data.
(ここでしばらく止まって)
--- nerves.local ping statistics ---
3 packets transmitted, 0 received, 100% packet loss, time ****ms

pingから3回とも応答があり、packet lossが0であればアクセス成功です。

ping 成功例
PING nerves.local (***.***.***.***) 56(84) byes of data.
64 bytes from nerves-**** (***.***.***.***): icmp_seq=1 ttl=64 time=8.87 ms
64 bytes from nerves-**** (***.***.***.***): icmp_seq=2 ttl=64 time=9.91 ms
64 bytes from nerves-**** (***.***.***.***): icmp_seq=3 ttl=64 time=21.3 ms

--- nerves.local ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2002ms
rtt min/avg/max/mdev = 8.871/13.356/21.291/5.626 ms

私の場合はnerves.localでは名前解決できませんでした。あるあるのようです。23

$ ping -c 3 nerves.local
ping: nerves.local: Name or service not known

なので、別のパソコンからルーターにログインして、ホスト名が "nerves-****" というものを探しました。
****は英数4桁のようです。

次にssh接続します。

$ ssh nerves-****

するとパスワードを延々と聞かれ続ける状態になりました。

SSH server
Enter password for "user"
password: 

高瀬先生4の記事を参考にして、以下のように~/.ssh/configに設定を追加すると、うまく接続できるようになりました。5
あとのmix uploadするときもnerves.localとしてアクセスするので、nerves.localでアクセスできるようにしておくと後々楽だと思います。6

~/.ssh/config
Host nerves.local
  Hostname nerves-****
  StrictHostKeyChecking no
  IdentityFile ~/.ssh/id_nerves_key

ssh接続が成功すれば、nervesのロゴが出てきてiexのプロンプト待機になります。

$ ssh nerves.local
Interactive Elixir (1.14.0) - press Ctrl+C to exit (type h() ENTER for help)
████▄▄    ▐███
█▌  ▀▀██▄▄  ▐█
█▌  ▄▄  ▀▀  ▐█   N  E  R  V  E  S
█▌  ▀▀██▄▄  ▐█
███▌    ▀▀████
hellonerves 0.1.0 (.....................................) arm rpi0
  Uptime       : 1 minutes and 12 seconds
  Clock        : 2022-09-23 06:56:09 UTC

  Firmware     : Valid (B)               Applications : 36 started
  Memory usage : 41 MB (13%)             Part usage   : 0 MB (0%)
  Hostname     : nerves-****             Load average : 0.34 0.13 0.04

  wlan0        : **.**.**.**/**, **:**:**:**:**:**:**:**/**, **::**:**:**:**/**
  usb0         : **.**.**.**/**

Nerves CLI help: https://hexdocs.pm/nerves/using-the-cli.html

Toolshed imported. Run h(Toolshed) for more info.
iex(1) >

ssh接続に失敗する場合は、sshの鍵の作成に失敗している可能性があるので、そこからやり直す必要があります。
元々準備されているHellonerves.hello()関数を呼び出して、worldアトムが返ることを確認します。

iex> Hellonerves.hello()
:world

exitと入力して、一旦ssh接続を終了してシェルに戻ります。

iex> exit
Connection to nerves.local closed.
$

コードを書き換えてmix upload

何でもいいのですが、Hellonerves.hello()関数をちょこっと書き換えます。

lib/hello_nerves.ex
defmodule Hellonerves do
  def hello do
    "Hello Nerves!"
  end
end

mix firmwareでコンパイルします。
以降でmix deps.get, mix firmware, mix uploadを実行するときは、必ず環境変数(MIX_TARGET, wifiのssid/psk)が設定されている必要があります。

$ mix firmware

microSDカードをRaspberryPIに挿したまま、mix uploadコマンドでファームウェアを書き換えます。
mix uploadの場合はmix burnに比べて書き込みスピードが少し遅いようです。

$ mix upload

mix uploadから再び約30秒間待ってからssh接続して、Hellonerves.hello()コマンドを実行して、
書き換えたところが反映されていることを確認します。

$ ssh nerves.local
:
iex> Hellonerves.hello()
"Hello Nerves!"
iex> exit
Connection to nerves.local closed.
$

Lチカとスイッチ状態検出

参考資料1.のALGYANハンズオン基本編1.にあるデバイスを利用します。
添付写真のように、LEDはD16に、プッシュスイッチはD5に接続されています。
circuits.GPIOのライブラリを使います。
mix.exs内のdepsに以下を追加します。

defp deps do
  [
    : 
    {        .....},   # <-- 前行の最後にコンマも入れてください
    {:circuits_gpio, "~> 0.4"}  # <-- 追加
  ]
end

mix deps.getcircuits_gpioのライブラリを取得してきます。
mix firmware, mix.uploadを実行し、再度ssh接続します。

$ mix deps.get
$ mix firmware
$ mix upload
$ ssh nerves.local
 :
iex>

LEDは16番に接続されています。
GPIOのwriteコマンドの引数でLEDのon/offが制御できることを確認します。

iex> {:ok, gpio} = Circuits.GPIIO.open(16, :output)
iex> Circuits.GPIO.write(gpio, 1)  # LED点灯
iex> Circuits.GPIO.write(gpio, 0)  # LED消灯

プッシュスイッチもやってみます。5番に接続しています。

iex> {:ok, gpio} = Circuits.GPIO.open(5, :input)
iex> Circuits.GPIO.read(gpio)  # ボタンを押しながら実行すると1が返る
1
iex> Circuits.GPIO.read(gpio)  # ボタンを押さずに実行すると0が返る
0
iex> exit
$

プッシュスイッチを押したときにLED点灯

ハンズオンの資料に基づいてやってみます。
以下のコードを追加します。
ファイル名はlib/下にあれば何でもOKです。

lib/buttonServer.ex
defmodule NervesjpBasis.ButtonServer do
  use GenServer

  require Logger

  alias Circuits.GPIO

  @button 5
  @led 16

  def start_link(_) do
    GenServer.start_link(__MODULE__, [])
  end

  def init(_) do
    {:ok, button} = GPIO.open(@button, :input, pull_mode: :pullup)
    GPIO.set_interrupts(button, :both)

    {:ok, led} = GPIO.open(@led, :output)

    {:ok, %{button: button, led: led}}
  end

  def handle_info({:circuits_gpio, @button, _timestamp, value}, state) do
    Logger.debug("Button is now #{value}")

    GPIO.write(state.led, value)

    {:noreply, state}
  end
end

mix.deps.get, mix.firmware, mix upload, ssh nerves.localを順に実行します。

iex> NervesjpBases.ButtonServer.start_link([])
{:ok, #PID<****.0>}
iex>

この状態でプッシュスイッチを押している間だけLEDが点灯することを確認します。

UART経由でiexコンソールの表示

NishiguchiさんのElixir/Nerves UARTでシリアルコンソール接続をマネしてやってみました。

UART-TTL変換ケーブルの入手

私は以下を入手しました。
Prolific PL2303使用品
単なるUSBケーブルの一端をばらしたモノかと錯覚しましたが、USB接続(typeA形状)のコネクタ部にICを含む回路が内蔵されているようです。
私の環境はlinuxだったため?か、デバイスドライバのインストールは不要でした。デバイスファイル名は/dev/ttyUSB0として認識されました。ls /dev/tty*で確認。

ケーブルの接続

前述のNishiguchiさんのqiita記事のリンクにある記事の内容に従って、変換ケーブルの3本をラズパイのGPIOピンに接続します。赤線(5V)のみ非接続です。

エミュレータのインストール

私はscreenを使用しました。

$ sudo apt install screen

エミュレータの起動

$ screen /dev/ttyUSB0 115200

画面が真っ黒のままの場合は、enterを何度が押すとプロンプトが出現します。
文字化けする場合はボーレートの設定(上記コマンド中の115200の箇所)を入れてみるといいと思います。

エミュレータ端末を切断するときは、ctrl-a + kを押すと、kill screen?という確認が入るので、yを押下して終了すると、bashプロンプトに戻ります。
ctrl-a + kの前にexitを入力してしまうと、UART接続のターミナルが終了してしまうようで、再度screenコマンドを叩いても端末表示はされなくなりました。
NervesではUARTのデバイスファイルは、rpi0ではデフォルトで/dev/ttyAMA0と設定されるようです。その後screenコマンド実行後にNerves側で/dev/ttyAMA0-->/dev/ttyUSB0に自動接続されるのかな?と思っています。

参考資料

  1. ALGYAN Elixir/Nerves(ナーブス)体験ハンズオン!
  2. ALGYAN x Seeed x NervesJPハンズオン!に向けた開発環境の準備方法(高瀬先生の記事)
  3. NervesのSSH環境を整えた 〜upload.shを使ってファームウェア更新も〜
  4. nerves_pack(vintage_net含む)を使ってNervesのネットワーク設定をした〜SSHログインまで〜
  1. 2020/12/27【オンライン】豪華プレゼント付!Elixir/Nerves(ナーブス)体験ハンズオン!

  2. Nerves講義ノート・始めてみる回の、3(6)通信状態の確認の項を参照。

  3. 高瀬先生の記事:ElixirでIoT#4.3:Nervesアプリ開発時のよくあるトラブルをシューティング nerves.localが見つからない

  4. 高瀬先生の記事:ElixirでIoT#4.3:Nervesアプリ開発時のよくあるトラブルをシューティング ssh接続できない

  5. ssh接続時にパスワードを延々と聞かれるのは、ssh接続時の秘密鍵がどれかわからないためと思われます。ssh接続時の秘密鍵を明示すればうまくいくのかと。

  6. ~/.ssh/configへの設定記載がなくても、mix firmware,mix uploadコマンドにて、mix firmware nerves-****, mix upload nerves-****などと記述して実行できるようです。

8
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
0