plant-raspberrypi3のブログ

ラズベリーパイ3とPythonに挑戦して、植物を愛でたり画像を加工したりします。最近はscikit-imageの勉強してます。

ラズパイ起動時にキャラクタLCDでIPアドレスを表示する

ラズパイさんの起動時にIPアドレスをキャラクタLCDに表示できるように設定しました。

が、完成するまでにかなり苦戦しました。。。

エラーが出て調べて修正して、またエラーが出ての繰り返し(>人<;)

全体の流れ

  1. シェルのワンライナーIPアドレスを取得
  2. IPアドレスPythonプログラムで受け取り、キャラクタLCDに出力
  3. 上記2つを組み合わせた実行用のshファイルの作成
  4. 起動時プログラム自動実行の設定

実行環境

  • 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

参考にさせていただいたページはこちら。

www.asahi-net.or.jp

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ファイルは絶対パスで指定

ループのあたりは再び、こちらのページを参考に。

www.asahi-net.or.jp

初心者的はまりポイント

  • ループ問題
    ループしないと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とは使い方が全然違って大混乱(> <)

  • コードの標準出力を変数に代入する問題
    これはシングルクォーテーションやダブルクォーテーションではなく、バッククォーテーションなんですね。見間違いやすいポイント、、、

このあたりはプログラム言語から入った人にはよくあるはまりポイントらしく、まとめた記事を見つけました。

mollifier.hatenablog.com

また、シェルスクリプトでの変数の取り扱いについてはこちらの記事が参考になります。

shellscript.sunone.me

4. 起動時プログラム自動実行の設定

起動時にプログラムを自動で走らせるため、systemdを使いました。うまくいかないときに、原因がステータス情報から簡単にログとして見られたのがよかったです。systemdの使い方は、このあたりのページを参考に。

qiita.com

www.asahi-net.or.jp

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になった :(´ཀ`」 ∠):

どうもこのあたりが原因の可能性がありそう。

kometchtech.blog.fc2.com

調べてみると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というところが気になります。この文面でググってみたところ、以下のページが状態として近そうでした。

takeg.hatenadiary.jp

参考にさせていただいて、Restart = alwaysRestart = 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.

ラズパイの起動とともにキャラクタLCDIPアドレスを表示させるには、サービスを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アドレスを出力させることができました。

長かった、、、 (;´Д`)