West Gate Laboratory

人生を少しでも,面白く便利にするモノづくり

WebIOPiを使ってRaspberryPiのGPIOを外出先から操作する

はじめに

RaspberryPiに外出先から(LANの外から)スマホなどでアクセスできると工作の幅がグッと広がる。 例えば、GPIOにアクセスできるだけでも外からLEDを操作したり、リレーを操作したり、サーボモータだって操作できるだろう。

RaspberryPiに外からアクセスする方法にもいろいろあるが、今回は手軽な方法として、WebIOPiを使った方法を試してみる。

WebIOPiとは

webiopi.trouch.com

RaspberryPiに外部からアクセスしてGPIOなどを操作できるライブラリらしい。Python+RaspberryPi 2or3で動作し、REST APIもついてくる。

f:id:kaname_m:20200109212946p:plain
WebIOPiの概要(http://webiopi.trouch.com/より)

やはりこれもすでに試した人が多くいる。WebIOPiを使ってブラウザからRaspberry PiのGPIOを操作してみる | Developers.IOなどを参考に実装した。ここでも備忘録を兼ねて動作確認までの手順を述べる。

WebOIPiのインストール、動作確認

まず、WebIOPiをダウンロードする。2020/01/09現在の最新バージョンは0.7.1である。

ダウンロード・インストール手順はWebIOPiを使ってブラウザからRaspberry PiのGPIOを操作してみる | Developers.IOをそのまま実行して問題なし。

sudo systemctl start webiopiでWebIOPiサーバを立てたらまずはLAN内のPCやスマホからアクセスしてみる。

例えば、ブラウザからアクセスする場合はhttp://"RaspberryPiのIPアドレス":8000でアクセスできる。今はLAN内なのでIPアドレスは192.168...のような感じだろう。例えば、http://192.168.0.6:8000のような感じ。アクセスするとIDとパスワードが聞かれる。デフォルトはID:webiopi, pass:raspberryである。

最初に表示されるのは以下の画面。

f:id:kaname_m:20200109215352p:plain
WebIOPiアクセス画面

今回使うのはGPIO Headerである。クリックすると以下のIO一覧が見える。

f:id:kaname_m:20200109215354p:plain
GPIO Header

それぞれのピンの横に書いてあるのが現在のそのピンの属性(INなら入力、OUTなら出力)、数字の色が出力状態を表す(黒:L、黄:H)。 出力がOUTになっているピンなら、数字をクリックするだけでON/OFFが切り替わる。少なくともLAN内からのアクセスなら瞬時に切り替わる。

WebIOPiをRaspberryPiブート時に自動起動

基本的にWebIOPiはRaspberryPiを再起動する度にstartさせる必要があるが、面倒なので以下のコマンドでブート時に自動起動できる。

sudo update-rc.d webiopi defaults

逆に自動起動をやめたい場合は以下のコマンド。

sudo update-rc.d webiopi remove

REST APIで操作する

ピンの状態取得などをGET,POSTで行うことも可能だ。(以下IPアドレス192.168.0.6、GPIO21の場合)

状態取得

GET http://192.168.0.6:8000/GPIO/21/value

属性の変更(OUT:出力に変更)

POST http://192.168.0.6:8000/GPIO/21/function/out

ピンをH

POST http://192.168.0.6:8000/GPIO/21/value/1

ピンをL

POST http://192.168.0.6:8000/GPIO/21/value/0

ブラウザのURLバーからアクセスする場合はGETなので、状態取得はブラウザでできる。以下はGPIO21の状態を取得した場合だが、ただただ数字で0or1が帰ってくる。

f:id:kaname_m:20200109223754p:plain
GPIO21の状態取得

URLバーからPOSTはできないので、ChromeならAdvanced REST client - Chrome Web Storeなどの拡張機能を入れてPOSTの試験ができる。

LANの外からアクセスする

家の中だけでやっていてもつまらないので、外出先からいじりたくなる。そうなると、ルータの設定をいじる必要がある。

基本的に一般的な家の中なら、ルータで各機器にプライベートIPを割り当てているはずである。我が家の場合ならRaspberryPiに192.168.0.6を当てている。 外からアクセスする場合は、グローバルIPでアクセスするわけだが、グローバルIPはルータに割り当てられているわけであって、RaspberryPiには割り当てられていない。なので、グローバルIPだけではプライベートIPしか割り当てられていないRaspberryPiにはアクセスができない。

そこで、ルータのポートマッピング機能を使う。ポートマッピングとは、ルータが持つ機能の一つで、グローバルIPアドレスの特定のポートを、特定のプライベートIPアドレスの特定のポートに転送するものである。

今回の事例では、WebIOPiはポート番号8000を使っているので、グローバルIP宛でポート8000宛のパケットをRaspberryPiに転送してやることで外部からRaspberryPiにアクセスする。

ポートマッピングの設定は一般的にルータの管理画面からできる。

我が家はAtermのルータだが、詳細設定→ポートマッピング設定から設定できる。

f:id:kaname_m:20200109221755p:plain
ポートマッピング設定

これが設定できたら、http://"グローバルIP":8000でWebIOPiにアクセスできる。

なお、グローバルIPはルータ設定画面やアクセス情報【使用中のIPアドレス確認】などでわかる。 CUIグローバルIPを知りたい場合は、What Is My IP Address? - ifconfig.meのサービスを利用できる。

curl ifconfig.me

グローバルIPを取得できる。

WebIOPiのパスワードを変更する

外部からアクセスする場合は、パスワードがデフォルトのままでは不安である。 パスワードは以下の通り変えられる。

$ sudo webiopi-passwd
WebIOPi passwd file generator
Enter Login: webiopi
Enter Password: 
Confirm password: 

Hash: e70c940a189251e9cd4515b3a1a6c6f02aa05c744a456ce360fe14bf2c5c0353
Saved to /etc/webiopi/passwd

パスワードを変更したら、stop,startすること。これで反映される。

参考:Change Password

WebIOPiからサーボモータを操作する

上記の使い方は、あくまでGPIOのIN/OUT, H/Lを設定するものだった。外出先からWebIOPiを使って簡易的にサーボモータを操作できないだろうか?

今回は以下の方法で実装した。

  • GPIO20の出力状態(On/Off)をポーリングで監視する

  • WebIOPiでGPIO20の出力状態を変更する(Off→On)

  • ポーリングでGPIO20がOnになったことを確認したら、サーボモータを操作する関数を呼び出す

少しまどろっこしいが、WebIOPiを使って気軽にサーボモータをいじるならこれが楽ではないだろうか。 例えば、以下は常にGPIO20を監視して、WebIOPiでGPIO20をOnしたらサーボモータを動かすスクリプトだ。

import pigpio
import time

def servoPush():
    moveServo(SERVO_PUSH)
    time.sleep(SERVO_TIME)
    moveServo(SERVO_RELEASE)

def calcServoDuty(ratio):
    duty = 25000 + (120000 - 25000) * ratio
    return duty

def moveServo(ratio):
    duty = calcServoDuty(ratio)
    pi.hardware_PWM(PORT_PWM, SERVO_PERIOD, int(duty))

# pigpioの設定
pi = pigpio.pi()
PORT_PWM = 18
PORT_WEBIOPI = 20
pi.set_mode(PORT_WEBIOPI, pigpio.OUTPUT)

# サーボモータ設定
SERVO_RELEASE = 0.5
SERVO_PUSH = 0.35
SERVO_TIME = 0.2
SERVO_PERIOD = 50
pi.set_mode(PORT_PWM, pigpio.OUTPUT)
moveServo(0.5)


pi.write(PORT_WEBIOPI, 0)

while True:
    try:
        #WebIOPi polling
        if pi.read(PORT_WEBIOPI):
            pi.write(PORT_WEBIOPI, 0)
            servoPush()
        
        time.sleep(1)
    
    except KeyboardInterrupt:
        break 

(2020年1月10日追記)

上のコードでサーボモータを操作してみた。 スマホWiFiを切って、モバイル回線につないでおり、グローバルIPでアクセスしている。