リモート機器制御

WiringPiを使ってCGI経由のGPIOポート制御はできたので、これを以前に試したMotionアプリによるWebカメラ配信と組み合わせれば、遠隔地の機器をブラウザでOn/Offさせて、その結果をリアルタイムにブラウザで確認する事ができる。

実はこれがやりたくてWebカメラやCGIの実験をあれこれと行ってきたのだ。

WebカメラWiringPi(2) CGI編で書いた内容に従って、ブラウザでWebカメラの画像の表示と、CGIでLEDのOn/Offが行える状態にようになっていれば、あとはそれを組み合わせれば良いのでそれほど面倒ではない。
HTMLファイルにWebカメラの動画表示と、その下にインラインフレームでLED制御のCGIを実行できるように記述する。

<html>
<head>
<style type="text/css">
    body { 
    text-align:center;
    background-color:#ffffff;
    }
    .container {
    width:100%;
    }
    .motion {
    border: 0;
    width: auto;
    height: auto;
    }
    .clear {
    clear:both;
    }
</style>
</head>
<body>
<h1>WebCam View</h1>
<a href="http://192.168.1.9:8081"><img class="motion" src="http://192.168.1.9:8081" /></a>
<iframe src="/cgi-bin/wiringcgi.py" name="cgiframe" width="100%" frameborder="0"></ iframe> ※
</body>
</html>

(※blogの制限でiframeタグを記述すると消されてしまうので、閉じるタグに余分なスペースを入れてある)

このHTMLファイルをcgi-binフォルダの上の階層にあるフォルダに保存したら

$ sudo mkdir -p /var/run/motion
$ sudo motion
$ gpio export 23 out
$ gpio export 24 out
$ gpio export 25 out
$ python -m CGIHTTPServer

でMotionとCGIサーバを起動。

ブラウザから”[Raspberry PiのIPアドレス]:8000/motion.html”にアクセスすれば、以下のようにWebカメラの画像とLEDのOn/Offボタンが表示されるはずだ。

webled

ブラウザをクライアントとしてLEDをOn/Offさせるだけなら、Arduino+ネットワークシールドでも可能だが、Webカメラ配信まで自力でやろうと思ったらかなり困難だろう。
もちろんArduinoにはArduinoの良さがあるし、部品を買ってきて自分で安価に作れてしまうのは非常に大きなメリットなので、用は適材適所。

WiringPi(2) CGI編

PythonのGPIOモジュールはルート権限でしか扱えず、Pythonの内蔵CGIサーバ機能はルート権限でスクリプトを実行できないので困ってしまい、ユーザ権限でもGPIOにアクセスできるWiringPiを試してみた。

事前にポートをexportしておく必要はあるが、これならCGIでも使えそうなことが分かった。と言うことで次は実際にRaspberry PiのGPIOに接続したLEDをOn/OffするCGIを作ってみる。

LED一個ではあまり面白くないので、今回はLEDを三個に増やしてGPIOの23~25に接続。ピンヘッダの番号で言うと16、18、22番になる。ピン番号の対応はこのページにある表が分かりやすい。コピーして保存しておくと良いだろう。

ブレッドボード上の配線はこんな感じ。スイッチは今回使わない。

ledbread

LEDのマイナス極(カソード)を電流制限抵抗(470Ω)経由でRaspberry Piのポートに接続してあるので、ポートに0を出力すると点灯、1を出力すると消灯する。一般的なOn/Offのイメージとは逆になるが、ピンに出力できる電流には制限があるので吸い込み電流で点灯させたほうが安全。

LED三個ぐらいなら吐き出し電流でも問題にならないが、負荷が大きくなると出力電圧が下がってしまう場合があるからだ。

WiringPiを使ったCGIプログラムは以下の通り

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

# Prepare:
#   gpio export 23 out
#   gpio export 24 out
#   gpio export 25 out

import cgi
import wiringpi

def imgbtn(no, onoff):
    img = ['../swon.jpg', '../swoff.jpg']
    name = ['L1', 'L2', 'L3']
    sw = ['ON', 'OFF']
    print '<input type="image" src="'+img[onoff]+'"',
    print 'style="border:none; background-color: #ffffff"', 
    print 'name="'+name[no]+sw[onoff]+'"',
    print 'value="submit" />'

if __name__ == '__main__':
    io = wiringpi.GPIO(wiringpi.GPIO.WPI_MODE_SYS)
    pins = [23, 24, 25]
    name = ['L1OFF', 'L2OFF', 'L3OFF', 'L1ON', 'L2ON', 'L3ON']
    hilo = [io.LOW, io.HIGH]    # HIGH:off,LOW:on
    [io.pinMode(pin,io.OUTPUT) for pin in pins]
    form = cgi.FieldStorage()
    for i in range(6):
        if form.getfirst(name[i]):
            no = i % 3
            onoff = i / 3
            io.digitalWrite(pins[no], hilo[onoff])
            break
    # html
    print \
'''Content-type: text/html; charset=UTF-8\n
<html>
<head>
<title>CGI Test</title>
</head>
<body bgcolor=#ffffff>
<div style="text-align:center">
<h1>LED Remote On/Off</h1>
<form action="wiringcgi.py" method="post">
''',
    for i in range(3):
        imgbtn(i, io.digitalRead(pins[i]))
    print \
'''</form>
</div>
</body>
</html>
'''

せっかくなのでフリー素材で適当なボタン画像を探してきて使った。
swon swoff

この画像ファイルとスクリプトを、以下のように配置する

.
├── cgi-bin
│   └── wiringcgi.py
├── swoff.jpg
└── swon.jpg

画像ファイルもスクリプトと同じ場所に置きたかったのだが、Pythonの内蔵CGIサーバはcgi-binフォルダにPythonスクリプト以外は置くことができないので、上の階層に置いた。

スクリプトファイル wiringcgi.py には、chmodコマンドで実行属性を付けておく。

$ chmod 755 wiringcgi.py

画像ファイルを置いたフォルダで、以下のようにポートのexportとCGIサーバの起動を行う

$ gpio export 23 out
$ gpio export 24 out
$ gpio export 25 out
$ python -m CGIHTTPServer

これで準備完了。ブラウザからRaspberry PiのIPアドレスと、スクリプトファイル名を http://192.168.1.9:8000/cgi-bin/wiringcgi.py と直接指定してアクセスすれば、以下のように表示されるはず。

ledremote

ボタンをクリックするとLEDがOn/Offされて、同時にボタン画像が切り替わればOK。

CGIスクリプトはプログラムがHTMLファイルを動的に生成する形になるので少しややこしいが、最終的にクライアントへ送信されるHTMLファイルは以下のような形になる。

<html>
<head>
<title>CGI Test</title>
</head>
<body bgcolor=#ffffff>
<div style="text-align:center">
<h1>LED Remote On/Off</h1>
<form action="wiringcgi.py" method="post">
<input type="image" src="../swoff.jpg" style="border:none; background-color: #ffffff" name="L1OFF" value="submit" />
<input type="image" src="../swoff.jpg" style="border:none; background-color: #ffffff" name="L2OFF" value="submit" />
<input type="image" src="../swoff.jpg" style="border:none; background-color: #ffffff" name="L3OFF" value="submit" />
</form>
</div>
</body>
</html>

スクリプトは出力ポートの現在値を読み取って、対応するボタンを持つHTMLファイルを生成。ブラウザで画面上のボタンがクリックされると、その情報をパラメータとして同じスクリプトが再度実行される。スクリプトはパラメータに従ってLEDのOn/Offを行い、またHTMLファイルを生成。
という手順で処理が続いていく事になる。

LEDが制御できれば、これを赤外線リモコンに替えてエアコンのリモート制御等も、理論的には可能になるはずだ。実際にやるとしたら他にも色々と考慮する必要があるが、何もない所から始めるよりもとりあえず動かせる状態にして、そこから考えるほうが近道。

I2C液晶(2) Webアプリ版

Webブラウザをクライアントとして動作するプログラム、いわゆるWebアプリを動かす場合はPCでWebサーバを走らせておく必要がある。

Raspberry PiでももちろんWebサーバをインストールしてWebアプリを動かす事は可能だが、Pythonには標準で簡易CGIサーバ機能が備わっているので、もっと簡単にWebアプリを試してみる事ができる。

このへんの必要と思われる機能が予め用意されていて、面倒なお膳立ての必要無しに気軽に試せるのがPythonの真骨頂。教育目的で開発されたRaspberry Piには、非常に似合った開発環境だろう。最初からこんな良いモノが使える今の子供たちが、ちょっと羨ましい。

PythonのCGIサーバを起動するには、単にコンソールから

python -m CGIHTTPServer

とだけ入力すれば良い。CGIサーバが起動されて、ブラウザからの接続待ち状態になる。

pi@raspberrypi ~ $ python -m CGIHTTPServer
Serving HTTP on 0.0.0.0 port 8000 …

今回はCGIサーバ起動時に別な処理を行いたいので、起動用のスクリプトを作ってそこから行う。^C(Ctrl+C)でPythonの実行を中断し、作業用のフォルダに移動して以下の起動用ファイルを作成する。ファイル名は CGIServer.py とした。

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

import CGIHTTPServer
import smbus
import time

# LCD initialize
i2c = smbus.SMBus(1)
addr = 0x3e
contrast = 42   # 0~63
i2c.write_byte_data(addr, 0, 0x38)  # function set(IS=0)
i2c.write_byte_data(addr, 0, 0x39)  # function set(IS=1)
i2c.write_byte_data(addr, 0, 0x14)  # internal osc
i2c.write_byte_data(addr, 0,
                    (0x70 | (contrast & 0x0f))) # contrast
i2c.write_byte_data(addr, 0,
                    (0x54 | ((contrast >> 4) & 0x03)))  # contrast/icon/power
i2c.write_byte_data(addr, 0, 0x6c)  # follower control
time.sleep(0.2)
i2c.write_byte_data(addr, 0, 0x38)  # function set(IS=0)
i2c.write_byte_data(addr, 0, 0x0C)  # Display On
i2c.write_byte_data(addr, 0, 0x01)  # Clear Display
i2c.write_byte_data(addr, 0, 0x06)  # Entry Mode Set
time.sleep(0.2)

# CGI Server start
CGIHTTPServer.test()

CGIHTTPServerを起動する前に、I2C液晶の初期化を行っている。

次に同じフォルダに index.html ファイルを作成。

</pre>
<form action="/cgi-bin/cgilcd.py" method="GET">
Send to I2C LCD

1<input type="text" name="line1" />

2<input type="text" name="line2" />
<input type="submit" /></form>
<pre>

このフォルダの直下にcgi-binフォルダを作成(mkdir cgi-bin)して、そこに cgilcd.py ファイルを作成する。

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

import cgi
import smbus
import time

i2c = smbus.SMBus(1)
addr = 0x3e
# LCD Clear
i2c.write_byte_data(addr, 0, 0x38)  # function set(IS=0)
i2c.write_byte_data(addr, 0, 0x0C)  # Display On
i2c.write_byte_data(addr, 0, 0x01)  # Clear Display
i2c.write_byte_data(addr, 0, 0x06)  # Entry Mode Set
time.sleep(0.2)

# Send to LCD
form = cgi.FieldStorage()
if form.has_key('line1'):
    line1 = form['line1'].value
    [i2c.write_byte_data(addr, 0x40, ord(c)) for c in line1]
else:
    line1 = 'none'
if form.has_key('line2'):
    line2 = form['line2'].value
    i2c.write_byte_data(addr, 0, 0xc0)  # 2nd line
    [i2c.write_byte_data(addr, 0x40, ord(c)) for c in line2]
else:
    line2 = 'none'

print 'Content-type: text/html\n'
print ''
print 'Send to LCD
'
print '1:',line1,'
'
print '2:',line2,'
'
print '<a href="../index.html">Back</a>'
print ''

これで準備完了。I2C液晶が接続された状態で、CGIServer.pyを起動する。

pi@raspberrypi ~/PythonProjects $ python CGIServer.py
Serving HTTP on 0.0.0.0 port 8000 …

Raspberry Piと同一ネットワーク上にあるPCのブラウザから、Raspberry PiのIPアドレスとポート番号を指定してアクセスする。ポート番号はデフォルトでは8000番。

cgilcd1

index.htmlに記述した入力フォームが表示されるので、文字列を入力して送信。

cgilcd2

I2C液晶に入力した文字列が表示されればOK。

2013-02-18 22.34.27

非常に簡単なプログラムで、WebブラウザからRaspberry Piに接続したデバイスへ情報を渡すことができた。同じような仕組みで接続したセンサ類の情報をブラウザで読み出すような事も可能だろう。

ブラウザからアクセスした時に、Raspberry Piのコンソールに「code 404, message File not found」というエラーメッセージが出る場合があるが、これはブラウザがfavicon.icoファイルを要求しているのに、そのファイルが無いというエラーなので、無視してかまわない。