ラズパイ起動時にキャラクタLCDでIPアドレスを表示する
ラズパイさんの起動時にIPアドレスをキャラクタLCDに表示できるように設定しました。
が、完成するまでにかなり苦戦しました。。。
エラーが出て調べて修正して、またエラーが出ての繰り返し(>人<;)
全体の流れ
実行環境
- Raspberry pi3 Model B
- Raspbian Jessie
- Python3.4.2
1. シェルのワンライナーでIPアドレスを取得
まずはip aコマンドと一部取り出しのためのコマンド(grepとcut)をパイプで組み合わせて、IPアドレスのみを取得してみます。
$ ip a show wlan0 | grep "inet " | cut -f6 -d ' ' | cut -f1 -d '/' #192.168.xx.xx
参考にさせていただいたページはこちら。
2. IPアドレスをPythonプログラムで受け取り、キャラクタLCDに出力
IPアドレスを受け取るために2つの方法を考えました。
a. 標準入力から受け取れるようにする。
input
関数を使って標準出力を入力値として取得する形で、前述のワンライナーと組み合わせて使う予定でした。
この方法、ターミナルではうまくいきましたが、起動時に呼び出すようにした場合にはうまくいかなかったです(詳細は割愛)。
b. 引数として受け取れるようにする。
前回作成したキャラクタLCDで文字を表示するためのpythonコードを、表示する文字列を引数としてターミナルから受け取れるように変更しました。1行目に日時、2行目に引数に入力した文字列が表示されるようにしています。
plant-raspberrypi3.hatenablog.com
引数として文字列を受け取るためには、sys
モジュールを使います。ファイル名はip_display_lcd.py
としました。
#!/usr/bin/python3 # coding: utf-8 import Adafruit_CharLCD as LCD from datetime import datetime import sys args = sys.argv dt_now = datetime.now() # ピンの設定. GPIO番号を入れる lcd_rs = 23 lcd_en = 24 lcd_d4 = 22 lcd_d5 = 27 lcd_d6 = 4 lcd_d7 = 17 # 16x2桁. lcd_columns = 16 lcd_rows = 2 #LCD.Adafruit_CharLCDのインスタンスを生成 lcd = LCD.Adafruit_CharLCD(lcd_rs, lcd_en, lcd_d4, lcd_d5, lcd_d6, lcd_d7, lcd_columns, lcd_rows) # 液晶クリア. lcd.clear() # メッセージ表示. time_message = dt_now.strftime("%m/%d/%H:%M:%S") ip_address = args[1] message_text = time_message+"\n"+ip_address lcd.message(message_text)
ターミナルでpython3 ip_display_lcd.py Hello
などのコマンドを入力すると、液晶で日時とHelloが表示されます。
3. 上記2つを組み合わせた実行用のshファイルの作成
組み合わせて書いたシェルスクリプトは以下。
ファイル名はip_display_autorun2.sh
で保存。
#!/bin/sh ipadd="" while [ "$ipadd" = "" ] do ipadd=`ip a show wlan0 | grep "inet " | cut -f6 -d ' ' | cut -f1 -d '/'` wait 1 done python3 /home/pi/Adafruit_Python_CharLCD_2/ip_display_lcd.py "$ipadd" #.pyファイルは絶対パスで指定
ループのあたりは再び、こちらのページを参考に。
初心者的はまりポイント
ループ問題
ループしないとIPアドレスが取得できず引数に何も入力されないので、.pyファイルの実行に失敗します(慣れている人には当たり前?)。イコール前後のスペース問題
シェルスクリプトの勝手がわからず、ipadd=
をipadd =
とスペースを空けていたら、変数ipadd
に値が代入されず、延々ループが繰り返される現象が起こりました (旦那ちゃんのアドバイスにより原因特定)。出ていたエラーは以下。
ip_display_autorun2.sh: 5: ip_display_autorun2.sh: ipadd: not found ip_display_autorun2.sh: 5: ip_display_autorun2.sh: ipadd: not found ip_display_autorun2.sh: 5: ip_display_autorun2.sh: ipadd: not found ip_display_autorun2.sh: 5: ip_display_autorun2.sh: ipadd: not found ...
変数の取り扱い問題
ipadd
の変数の引数としての渡し方がわからなかったため、単純にipadd
として渡したら、本当にただのipaddという文字列としてキャラクタLCDから出力されるだけでした。シェルでは$ipadd
としないとダメなんですね。Pythonとは使い方が全然違って大混乱(> <)コードの標準出力を変数に代入する問題
これはシングルクォーテーションやダブルクォーテーションではなく、バッククォーテーションなんですね。見間違いやすいポイント、、、
このあたりはプログラム言語から入った人にはよくあるはまりポイントらしく、まとめた記事を見つけました。
また、シェルスクリプトでの変数の取り扱いについてはこちらの記事が参考になります。
4. 起動時プログラム自動実行の設定
起動時にプログラムを自動で走らせるため、systemd
を使いました。うまくいかないときに、原因がステータス情報から簡単にログとして見られたのがよかったです。systemd
の使い方は、このあたりのページを参考に。
crontab
も使ってみましたが、うまくいかないときに原因がわかりづらかったので、今回は保留にしました。
まず、設定ファイルを作成します。xxx.service
といった形で/etc/systemd/system
内に作成して保存。今回はautorun.service
という名前にしました。.shファイルをコマンドなしで動かせるようにしていなかったので、/bin/sh
とコマンドの絶対パスを指定。
[Unit] Description = ip_display_autorun by LCD [Service] ExecStart = /bin/sh /home/pi/ip_display_autorun2.sh Restart = always Type = simple [Install] WantedBy = multi-user.target
この設定ファイル、Restart = always
のところは良くなかったのですが、その点は後述します。
次に、この設定ファイルを有効にします。デーモンにリロードし、スタートしてみて、ステータスを確認すると、うまくいっているか見ることができます。
$ sudo systemctl daemon-reload $ sudo systemctl start autorun $ sudo systemctl status autorun ● autorun.service - ip_display_autorun by LCD Loaded: loaded (/etc/systemd/system/autorun.service; disabled) Active: failed (Result: start-limit) since Thu 2017-11-23 10:32:59 JST; 616ms ago Process: 1861 ExecStart=/bin/sh /home/pi/ip_display_autorun2.sh (code=exited, status=0/SUCCESS) Main PID: 1861 (code=exited, status=0/SUCCESS)
なんかfailedになった :(´ཀ`」 ∠):
どうもこのあたりが原因の可能性がありそう。
調べてみるとRaspbian Jessieではsystemd-networkd-wait-online.service
が存在しているようだったので、以下のコマンドをターミナルに入力。
$ sudo systemctl enable systemd-networkd-wait-online.service Created symlink from /etc/systemd/system/network-online.target.wants/systemd-networkd-wait-online.service to /lib/systemd/system/systemd-networkd-wait-online.service.
とりあえず、一旦autorun.service
をストップして、もう一度スタートしてから、ステータスをチェック。
$ sudo systemctl stop autorun $ sudo systemctl start autorun $ sudo systemctl status autorun ● autorun.service - ip_display_autorun by LCD Loaded: loaded (/etc/systemd/system/autorun.service; disabled) Active: failed (Result: start-limit) since Thu 2017-11-23 10:46:28 JST; 2s ago Process: 2094 ExecStart=/bin/sh /home/pi/ip_display_autorun2.sh (code=exited, status=0/SUCCESS) Main PID: 2094 (code=exited, status=0/SUCCESS) Nov 23 10:46:28 raspberrypi systemd[1]: autorun.service holdoff time over, scheduling restart. Nov 23 10:46:28 raspberrypi systemd[1]: Stopping ip_display_autorun by LCD... Nov 23 10:46:28 raspberrypi systemd[1]: Starting ip_display_autorun by LCD... Nov 23 10:46:28 raspberrypi systemd[1]: autorun.service start request repeated too quickly, r...rt. Nov 23 10:46:28 raspberrypi systemd[1]: Failed to start ip_display_autorun by LCD. Nov 23 10:46:28 raspberrypi systemd[1]: Unit autorun.service entered failed state. Hint: Some lines were ellipsized, use -l to show in full.
またfailed ( ̄Д ̄;)
しかし、メッセージが少し変わっています。autorun.service start request repeated too quickly
というところが気になります。この文面でググってみたところ、以下のページが状態として近そうでした。
参考にさせていただいて、Restart = always
をRestart = no
に変更。
そのあと前述の.shファイルの修正(イコール前後のスペース問題など)を行い、やっとエラーなく動くようになりました。うまくいったときのステータスは以下の通り。
$ sudo systemctl stop autorun $ sudo systemctl start autorun $ sudo systemctl status autorun ● autorun.service - ip_display_autorun by LCD Loaded: loaded (/etc/systemd/system/autorun.service; disabled) Active: inactive (dead) since Thu 2017-11-23 12:59:02 JST; 2s ago Process: 3634 ExecStart=/bin/sh /home/pi/ip_display_autorun2.sh (code=exited, status=0/SUCCESS) Main PID: 3634 (code=exited, status=0/SUCCESS) Nov 23 12:59:02 raspberrypi systemd[1]: Started ip_display_autorun by LCD.
ラズパイの起動とともにキャラクタLCDにIPアドレスを表示させるには、サービスをenableにします。ちゃんとenableになっているかの確認も一緒に。
$ sudo systemctl enable autorun Created symlink from /etc/systemd/system/multi-user.target.wants/autorun.service to /etc/systemd/system/autorun.service. $ systemctl list-unit-files --type=service | grep autorun autorun.service enabled
これで完成です。無事、ラズパイ起動時にキャラクタLCDからIPアドレスを出力させることができました。
長かった、、、 (;´Д`)