WebIOPiを使ってIoT!2回目は、操作画面のカスタマイズ編です!
WebIOPiデフォルトのGPIO画面も分かりやすくて良いですが、せっかくブラウザで表示するのであれば、ボタンを設置したりCSSで飾ったり……画面ごと自分でカスタマイズしたいですよね!
そんなときのために、公式サイトでは、IoTアプリを作るためのチュートリアルとサンプルソースが用意されています。
まずはこのチュートリアルに沿って、LEDの点灯・消灯ができるボタンを作成してみましょう!
そして、最終目標であるアナログ値の取得に向けて、Pythonについても少し勉強していきたいと思います。
プログラムソースの準備
前述のとおり、チュートリアルのページのサンプルソースを使って進めていきます。
ファイルの置き場所は、WebIOPiの設定ファイルで指定できるので、任意の場所でも問題なさそうです。今回は、チュートリアルと同じ /home/pi/ ディレクトリ下に「webiopi_sample」というフォルダを作成し、その中にファイルを置くことにしました。
PythonとHTMLのサンプルソースをそれぞれコピー&ペーストで取得し、下記のように格納します。
/home/pi/webiopi_sample/python/script.py
import webiopi import datetime GPIO = webiopi.GPIO LIGHT = 17 # GPIO pin using BCM numbering HOUR_ON = 8 # Turn Light ON at 08:00 HOUR_OFF = 18 # Turn Light OFF at 18:00 # setup function is automatically called at WebIOPi startup def setup(): # set the GPIO used by the light to output GPIO.setFunction(LIGHT, GPIO.OUT) # retrieve current datetime now = datetime.datetime.now() # test if we are between ON time and tun the light ON if ((now.hour >= HOUR_ON) and (now.hour < HOUR_OFF)): GPIO.digitalWrite(LIGHT, GPIO.HIGH) # loop function is repeatedly called by WebIOPi def loop(): # retrieve current datetime now = datetime.datetime.now() # toggle light ON all days at the correct time if ((now.hour == HOUR_ON) and (now.minute == 0) and (now.second == 0)): if (GPIO.digitalRead(LIGHT) == GPIO.LOW): GPIO.digitalWrite(LIGHT, GPIO.HIGH) # toggle light OFF if ((now.hour == HOUR_OFF) and (now.minute == 0) and (now.second == 0)): if (GPIO.digitalRead(LIGHT) == GPIO.HIGH): GPIO.digitalWrite(LIGHT, GPIO.LOW) # gives CPU some time before looping again webiopi.sleep(1) # destroy function is called at WebIOPi shutdown def destroy(): GPIO.digitalWrite(LIGHT, GPIO.LOW)
/home/pi/webiopi_sample/html/index.html
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>WebIOPi | Light Control</title> <script type="text/javascript" src="/webiopi.js"></script> <script type="text/javascript"> webiopi().ready(function() { // Create a "Light" labeled button for GPIO 17 var button = webiopi().createGPIOButton(17, "Light"); // Append button to HTML element with ID="controls" using jQuery $("#controls").append(button); // Refresh GPIO buttons // pass true to refresh repeatedly of false to refresh once webiopi().refreshGPIO(true); }); </script> <style type="text/css"> button { display: block; margin: 5px 5px 5px 5px; width: 160px; height: 45px; font-size: 24pt; font-weight: bold; color: white; } #gpio17.LOW { background-color: Black; } #gpio17.HIGH { background-color: Blue; } </style> </head> <body> <div id="controls" align="center"></div> </body> </html>
配線
サンプルソースで使用されているGPIO17(11番ピン)に、LEDを配線しておきましょう。
設定ファイルの編集
WebIOPiの設定ファイルは、 /etc/webiopi/config にあります。
設定ファイルの編集には管理者権限が必要になるので、rootユーザー以外でログインしている場合は、コマンドラインから編集を行いましょう。今回はpiユーザーの場合を想定して、nanoエディタから編集することにしました。
sudo nano /etc/webiopi/config
修正するのは2箇所です。
まずは21行目付近にある [SCRIPTS] という項目を探しましょう。
/etc/webiopi/config (21行目付近から)
[SCRIPTS] # Load custom scripts syntax : # name = sourcefile # each sourcefile may have setup, loop and destroy functions and macros #myscript = /home/pi/webiopi/examples/scripts/macros/script.py
ここに、次のように1行追記します。
myproject = /home/pi/webiopi_sample/python/script.py
「myproject」に、先程作成したPythonファイルのパスを指定します。
チュートリアルページのサンプルでは「myproject」となっていますが、コメントアウトされている部分を参考に「myscript」と設定しても動作しました。(どんな違いがあるのかまでは分かりません……)
次は、29行目付近にある [HTTP] を探しましょう。
/etc/webiopi/config (29行目付近から)
[HTTP] # HTTP Server configuration enabled = true port = 8000 # File containing sha256(base64("user:password")) # Use webiopi-passwd command to generate it passwd-file = /etc/webiopi/passwd # Change login prompt message prompt = "WebIOPi" # Use doc-root to change default HTML and resource files location #doc-root = /home/pi/webiopi/examples/scripts/macros # Use welcome-file to change the default "Welcome" file #welcome-file = index.html
先程と同じく、次のように1行追記します。
doc-root = /home/pi/webiopi_sample/html
「doc-root」に、「html」ディレクトリまでのパスを指定します。こちらは先程とは違い、ファイル名を含めないので、間違えないように注意が必要です。42行目にサンプルソースがコメントアウトされているので、このフォーマットを参考に記述しましょう。
実行するだけであれば、設定はここまででOKです。
チュートリアルでは [REST] の値を設定していますが、これはGPIOの使用制限を設定する部分なので、デフォルトのままでも問題ありません。アプリとしてしっかり作り込みたい場合は、設定した方が良いかもしれません。
それではデバッグ実行をしてみましょう!
sudo webiopi -d -c /etc/webiopi/config
ここでエラーになってしまう場合は、WebIOPiがすでに起動中の可能性があります。自動起動の設定をしている場合も含めて、一度WebIOPiを停止させてから、もう一度実行してみましょう。うまくいかない場合は、自動起動設定を一度解除してから行うことをおすすめします。
停止コマンド
sudo /etc/init.d/webiopi stop
自動起動を解除するコマンド
sudo update-rc.d webiopi remove
デバッグ実行が開始されると、コンソールが自動で動き始めます。
「 http://raspberrypi:8000/ 」にアクセスしてみましょう!
Lightボタンが表示されています。
クリックすると、
ボタンの表示が切り替わり、
このようにLEDが点灯します!(スマートフォンからのアクセスにはIPアドレスを使用しました)
エラーが発生しないことを確認できたら、LX Terminalで[Ctrl]+[C]キーを入力し、デバッグを終了しましょう。
次にWebIOPiを起動したときにはこの画面からスタートできます。自動起動設定をしている場合も同様です。
Pythonを分析!
サンプルプログラムを実行するところまではできましたが、肝心のソースコードの中身が分からないままではちょっと物足りない……。
Pythonの知識はまったく無いわたしですが、サンプルのソースコードには非常に細かくコメント文が書かれているので、コメント文を翻訳しながら読み進めればなんとか理解できるかも!?というわけで、いつも通りの手探り電子工作、はじめます!
予備知識として、Wikipediaを見ておきました。
Pythonには、構文規則としてインデントが使われるという特徴があります。他の言語ではあまり見られない仕様なので、Python初心者の方は要注意です!(知らずに進めてしまい、長時間つまづいてしまいました……)
日本Pythonユーザ会
Pythonのドキュメントを日本語に翻訳するプロジェクトです。もっとしっかり勉強してから進めたい方にはこちらのサイトがオススメです。
それでは、Pythonのサンプルソース(script.py)を見ていきましょう。
最初の数行、import文や変数定義、if文などは、他のプログラム言語にも共通するような部分なので、なんとなく想像がつくので割愛して……まずは全体的な作りを確認!
大きく3つのブロックに分けられています。先頭の「def」は、Pythonの関数を定義するときに使用するキーワードです。定義されている関数は、以下の3つです。コメント文を翻訳してみると、次のようになりました。
setup() | WebIOPi起動時に自動的に呼ばれる関数。 |
loop() | WebIOPi起動中に繰り返し呼ばれる関数。 |
destroy() | WebIOPi終了時に呼ばれる関数。 |
初期処理、繰り返しのメイン処理、終了処理、という3本立てです。Arduinoのプログラムの書き方にちょっと似てますね。
その他に、「GPIO」から始まる関数も気になりますね!これはWebIOPi用の関数っぽい……と思って調べてみると、公式サイトに解説ページが用意されていました!
サンプルソースで使われているものを、いくつかピックアップして解説してみます。
GPIO.setFunction(LIGHT, GPIO.OUT)
setFunction(channel, func)
Setup the given digital channel with the given function
channel (int) : digital channel number
func (int) : GPIO.IN or GPIO.OUT
REST API : Use setFunctionString instead.
14行目、setup()の中で登場するsetFunction関数は、使用するGPIOのIN/OUTを設定しています。変数「LIGHT」には「17」が格納されているので、14行目は「GPIO17をOUTに設定」という命令になります。
if (GPIO.digitalRead(LIGHT) == GPIO.LOW)
digitalRead(channel)
Returns the value of the given digital channel.
REST API : GET /devices/name/channel/value
name (str) : device name from configuration file
channel (int) : digital channel number
30行目、35行目の条件式で使用されているdigitalReadは、GPIOの値を取得する関数です。デジタル値なので、HIGHまたはLOWのどちらかになります。
GPIO.digitalWrite(LIGHT, GPIO.HIGH)
digitalWrite(channel, digit)
Write the value of the given digital channel.
REST API : POST /devices/name/channel/value/digit
name (str) : device name from configuration file
channel (int) : digital channel number
digit (int) : digital value (0 or 1)
21行目のdigitalWriteは、digitalReadとは逆に、値を設定する関数です。GPIO.HIGHまたはGPIO.LOWのどちらかを指定できます。
サンプルソースでは、これらの関数を使って、自動でLEDを点灯・消灯させる処理を実行させています。
処理の内容をまとめると、次のようになります。
・08:00~18:00の間に起動した場合に点灯
・08:00になったら点灯
・18:00になったら消灯
・WebIOPi終了時に消灯
HTML・Javascriptを分析!
続きまして、HTMLのソースコードを見ていきましょう。
と言っても、HTMLで書かれた部分はほとんどありませんね。WebIOPiに関する部品は基本的にJavascriptで書き出しているようです。Javascriptの関数についてもドキュメントが用意されています。
Javascript Library
なんとサンプルソース付き!
var button = webiopi().createGPIOButton(17, "Light");
createGPIOButton
Returns a button that change the state of a GPIO at each click.
(int) gpio : GPIO number from 0 to 53
(string) label : label of the button
10行目に登場する「createGPIOButton」は、その名の通り、GPIOの状態を変更するボタンを作成する関数です。第1引数はピン名の数字(11番ピンのGPIO17であれば17を指定)、第2引数はボタンに表示するラベル用の文字列を渡します。
<button class="HIGH" type="button" id="gpio17">Light</button>
書き出されるボタンのHTMLは上記のようになります。「id」にピン名、「class」はGPIOの状態に応じてHIGHまたはLOWが設定されるので、CSSで個別に装飾ができますね!
$("#controls").append(button);
createGPIOButtonで作成したボタンは、13行目でjQueryを使って画面上に表示させます。WebIOPiで使用する部品は、基本的にこの方法を使います。
17行目の「refreshGPIO」は、ドキュメントには記載が無いのですが、ボタンを有効にするためにはこの記述が必要のようです(この行を削除して試してみたとボタンをクリックできなくなってしまいました)。
こうして見てみると、HTML(Javascript)が画面上の操作、Pythonが時間に応じた点灯・消灯の自動処理、というように、それぞれが独立して、GPIOに対して別々の処理をしているような印象ですね。両者間で値をやりとりするにはどうするの……?と更に興味がわいてきたところで、次回に続きます!
まとめ
同じLチカでも、自分でプログラムを組み込むと達成感が違いますね!
前回使ったデフォルト画面も、裏側ではこんなプログラムが動いていたんだなと、実感しながら進めることができました。便利さや手軽さも大切ですが、電子工作の醍醐味はやっぱり手作りですね!
そして、電子工作をしながら同時にPythonも勉強できて一石二鳥!
公式サイトはドキュメントがとても充実している上に、便利な関数があらかじめ用意されているので、Python初心者のわたしでもプログラミングができました。特にチュートリアルのサンプルソースは、コメント文が細かく書き込まれていて、あまりの親切さにすっかりWebIOPiファンになってしまいました。笑。
次回は、このチュートリアルの続きで、ブラウザからの入力値をWebIOPi側で受け取る機能の実装です。マクロというものを使うのですが……もう少しPythonの知識が必要になりそうですね。入出力をマスターしたら、自作プログラムにも挑戦してみたいと思います!