Raspberry Pi 備忘録 / Mbedもあるよ!

Raspberry Pi であれこれやった事の記録

金魚に餌をあげる装置を作る(成功)

pongsuke.hatenablog.com

から。

軌道修正

USBモデムを使えるまで頑張るのが面倒になったのと、そもそも、USBモデムでインターネットに接続できた際に、外部から接続できるように設定可能なのかも、不明なので、軌道修正する。

  1. 動画ストリーミングは、FFmpeg & FFserver(別の固定IPを持っているサーバーを使用) にする
  2. 操作コマンドは、サーバーにSSHトンネルを張りっぱなしにして、実現する

SSHポートフォワーディング

今回は、root での ssh は、さすがに避けて、一般ユーザーでの ssh ポートフォワーディングをします。

RPIマシンから、接続する。

-L -R で、逆向きなので注意する。

サーバーにログインして、トンネルを作る。

ssh -f -N -R 10022:localhost:22 _USER_@_SERVER_IP_

サーバー側で、トンネルを試す

/usr/bin/ssh _USER_@localhost -p 10022 

ログインできる。はず。

公開鍵認証にしておく

鍵を作って、その鍵情報を、ログイン先の .ssh/authorized_keys に足す。

$ ssh-keygen

$ cat .ssh/id_rsa.pub
(中身を、ログイン先の .ssh/authorized_keys に追記する)

常時SSHトンネル@クライアント側(RPI)

SSHトンネル、SSHポートフォワーディング

「常時」は、autossh -N を活用する。

autossh -f -N -R 10022:localhost:22 _USER_@_SERVER_IP_

autossh の自動起動は、

ユーザー の crontab で、@reboot を使用する。

自動でログインできるように、公開鍵認証にしておく。

これで、お互いに公開鍵認証になった。

※補足

環境によっては? sshトンネル越しにログインし、そのまま(ログインしたまま) OS を reboot かけたりすると、SSHが正常に切断されず?、OS立ち上がり後にログインできなくなります。

回避策として

#!/bin/sh

sleep 5s
killall autossh
sleep 3s
sudo reboot

を実行して速やかに切断(ログアウト)してます。

ssh でコマンドを投げるテスト

# /usr/bin/sudo -u _user_ /usr/bin/ssh _user_@localhost -p 10022 "ls"

などをテストする。

最終的には、RIP側でも、user から sudo で GIPO の操作を行う。

sudoの環境整備@サーバー側

まず、流れは、

(ざっくり)
スマホのブラウザ > WEBサーバー > RPI


(細かく)
スマホのブラウザ > WEBサーバー:wwwrun > sudo -u _user_ ssh ===ssh接続(ポートフォワーディング)=== _user_@RPI > sudo GPIO操作

WEBサーバーから、RPIにコマンドを投げるに辺り、sudoを使用するので、WEBサーバーから sudo できるように、sudo の使用環境を整える。

WEBサーバーが立っている opensuse の apache2 は、ユーザー wwwrun に成っていた(デフォルトのまま)。

なので、wwwrun から、```sudo できるようにする。

visudo

visudo で、使用するプログラムに許可を与える。実行するスクリプトではない。

wwwrun ALL=(_USERNAME_) NOPASSWD: /usr/bin/ssh

sudoの環境整備@RPI側

GPIOを触るので、 user で、sudo できるようにしておく。

スクリプトにしておく

手順が多く、頭がこんがらがるので、触るファイルを少なくするため、スクリプトにしておく。

サーバー側

> cat kingyo.sh 
#!/usr/bin/sh

if [ "$1" = "TEMPERATURE" ]; then
        /usr/bin/sudo -u _user_ /usr/bin/ssh _user_@localhost -p 10022 "/usr/bin/sudo /root/temperature.sh"
elif [ "$1" = "AC_LED" -a "$2" = "ON" ]; then
        /usr/bin/sudo -u _user_ /usr/bin/ssh _user_@localhost -p 10022 "/usr/bin/sudo /root/ac_led__on.sh"
elif [ "$1" = "AC_LED" -a "$2" = "OFF" ]; then
        /usr/bin/sudo -u _user_ /usr/bin/ssh _user_@localhost -p 10022 "/usr/bin/sudo /root/ac_led__off.sh"
elif [ "$1" = "FEED" ]; then
        /usr/bin/sudo -u _user_ /usr/bin/ssh _user_@localhost -p 10022 "/usr/bin/sudo /root/feeding.py"
fi

RPI側

# cat ac_led__on.sh 

#!/bin/sh
/usr/bin/irsend SEND_ONCE ac_led on


root@raspberrypi:~# cat ac_led__off.sh 

#!/bin/sh
/usr/bin/irsend SEND_ONCE ac_led off

# cat temperature.sh 

#!/bin/sh
/bin/cat /sys/bus/w1/devices/w1_bus_master1/28-00000724377c/w1_slave | grep t= | /usr/bin/tr -s ' ' , | /usr/bin/cut --delimiter , -f 10 | sed -e "s/t=\(..\)\(...\)/\1.\2/"


# cat feeding.py 

#!/usr/bin/python
# coding: utf-8 

import RPi.GPIO as GPIO
import time
import signal
import sys 

def exit_handler(signal, frame):
        # Ctrl+Cが押されたときにデバイスを初期状態に戻して終了する。
        print("\nExit")
        servo.ChangeDutyCycle(2.5)
        time.sleep(0.5)
        servo.stop()
        GPIO.cleanup()
        sys.exit(0)

# 終了処理用のシグナルハンドラを準備
signal.signal(signal.SIGINT, exit_handler)

GPIO.setmode(GPIO.BCM)

# GPIO 21番を使用
gp_out = 21

GPIO.setup(gp_out, GPIO.OUT)
# pwm = GPIO.PWM([チャンネル], [周波数(Hz)])
servo = GPIO.PWM(gp_out, 50) 


# 初期化
servo.start(0.0)

val = [2.5, 3.6875, 4.875, 6.0625, 7.25, 8.4375, 9.625, 10.8125, 12]

# 初期位置
servo.ChangeDutyCycle(val[0])
time.sleep(1.0)

# 餌を落とす
servo.ChangeDutyCycle(val[8])
time.sleep(1.0)

# 初期位置
servo.ChangeDutyCycle(val[0])
time.sleep(0.5)

# 終了
servo.stop()
GPIO.cleanup()

sys.exit(0)

固定IPをDHCPに直してテストする

/etc/dhcpcd.conf の、固定IPに設定した部分をコメントアウトする。

リブートして、動作確認する。

ffmpeg による動画 feeding が、reboot した際に上手く動いていない。

DHCPに変えたからなのか、エラーログを確認できず、確証は無いが、DHCPで、IPが確定する前に、コマンドが実行されてしまっているのではないかと考える。

そこで、しかたがないので、ffmpeg を起動する前に、 sleep 10s して、10秒またせて対処しました。

仕上げ

  1. FFserver の ACLの変更

  2. FFserver の Feed URL などを、推定されずらいようにハッシュ文字列を使用する。