できること

第34回「WebIOPiでIoT!(5)プログラミング応用編~デジタルデバイスとの連携~」

raspberrypi34_main

WebIOPiでIoTシリーズ5回目は、電子部品との連携した入出力処理の実装です。
第32回「WebIOPiでIoT!(3)プログラミング基礎編~値の入出力~」で紹介した、ブラウザからの入出力をおさらいしつつ、自作プログラムの作成に挑戦してみたいと思います!

使用する電子部品は、この連載ではおなじみの温度センサDS18B20です。使い慣れたデバイスを制御することで、WebIOPiの理解をより深めていきましょう。

 

温度センサの準備

Tutorial : Using devices

公式サイトには「TMP102」という温度センサを使ったチュートリアルも用意されていますが、DS18B20でも同じ手順で温度を取得することができます。DS18B20の使い方は、第18回「ラズベリーパイで手作り温度計!」で詳しく解説しています。

それでは、DS18B20を使う準備をしていきます。2015年9月24日リリースの新しいRaspbianである「Raspbian Jessie」でこの部品を扱うのは初めてなので、動作確認も兼ねてもう一度手順を振り返ってみましょう。
まずは配線!これはいつもどおりでOKです。

raspberrypi34_img01

図1

次に、「1-Wire」を有効化するために、2つの設定ファイルを調整する必要があります。
1つ目は /etc/modules ファイルの設定です。

sudo nano /etc/modules

ファイルを開いて、下記の2行を追記します
/etc/modules
w1-gpio
w1-therm

2つ目も同様の手順です。これは、Raspbian 2015-01-31(NOOBS 1.3.12)以降のOSを利用している場合に必要な設定なのですが、「Raspbian Jessie」でも必要のようです。

sudo nano /boot/config.txt

/boot/config.txt に追記
dtoverlay=w1-gpio-pullup,gpiopin=4

最後に再起動!

sudo reboot

再起動後は、温度センサがきちんと認識されているかどうか確認をしておきましょう。ファイルマネージャで /sys/bus/w1/devices/デバイスID のディレクトリを開き、「w1_slave」というファイルが確認できればOKです!「w1_slave」に、温度が記述されています。

raspberrypi34_img02

図2

 

configファイルの設定

公式サイトにはDS18B20についての説明ページがありますが、この記述方法だとうまくいかなかったので、チュートリアルを参考に進めました。

Testing Devices – Tutorial : Using devices

WebIOPiでデバイスを使用するためには、configファイルを調整する必要があります。わたしの環境では、コメントアウトされているサンプル行があったため、文頭の「#」を削除してそのまま利用することにしました。

sudo nano /etc/webiopi/config

raspberrypi34_img03

図3 /etc/webiopi/config

/etc/webiopi/config (一部抜粋)

[DEVICES]

#temp0 = TMP102
#temp1 = TMP102 slave:0x49
temp2 = DS18B20
#temp3 = DS18B20 slave:28-0000049bc218

「temp2」にDS18B20が設定されているので、コメントアウトを解除。変数名は任意のものに変えてもOKです。また、同じデバイスを複数使用する場合は「temp3」のように、「slave:デバイスID」を追記して区別します。
また、接続していないデバイスやPythonファイル内で使用していないデバイスを有効にしていると、エラーが発生して、WebIOPiが起動しない場合があります。プログラムを差し替える際は注意が必要です(デバッグモードで実行すると、コンソール上でエラーの内容を確認できます)。

設定ファイルの準備はこれだけ!次は、Pythonファイルにプログラムを書き込んでいきましょう!

 

デバッグモードで温度を取得

Devices in Python scripts with REST mapping
When devices are named in the configuration file, drivers are automatically called to initialize the device and mapped to the REST API. We need to use the webiopi.deviceInstance(name) function in the Python script to retrieve the device object instance for the given name. Then we can use functions of the drivers, depending of interfaces supported.

上記説明にあるように、configファイルで設定したデバイスを利用するには、

webiopi.deviceInstance(name)

このような記述で、インスタンスを作成する必要があります。先程、configファイルで「temp2」という名前でDS18B20を設定したので、今回の場合は次のように記述します。

tmp = webiopi.deviceInstance("temp2")

温度センサのインスタンスでは、次の関数が利用できます。

Temperature

この中で、温度を取得する関数はこちらです。

getCelsius()
Returns the temperature in celsius.
REST API : GET /devices/name/sensor/temperature/c
name : device name from configuration file

このgetCelsius関数を使って、温度を取得するPythonプログラムを書いてみました。

/home/pi/webiopi_sample/python/script.py

import webiopi
def loop():
    tmp = webiopi.deviceInstance("temp2")
   celsius = tmp.getCelsius()
    print("Temperature: %f" % celsius)
    webiopi.sleep(5)

5秒ごとに「print」を使って、値をコンソールに表示させています。
デバッグモードで起動して、温度の値が取得できているかを見てみましょう。

sudo webiopi -d -c /etc/webiopi/config

raspberrypi34_img04

図4

指で直接触れて、温度を変化させてみました。値が少しずつ変化している様子を確認できます。

今まではDS18B20から温度を取得するには、w1_slaveファイルにアクセスして2行の文字列を取り出し、その中から温度の値のみを抽出し、さらに1/1000に変換して……と手間のかかる作業でしたが、WebIOPiを使えば温度の数値だけを簡単に取り出すことができました!

 

HTMLと連携!

温度が取得できたら、ブラウザ画面に表示させるための値を受け渡す関数を用意して、ブラウザ画面と連携してみましょう。

/home/pi/webiopi_sample/python/script.py

import webiopi

@webiopi.macro
def getTemp():
    tmp = webiopi.deviceInstance("temp2")
   celsius = tmp.getCelsius()
    print("Temperature: %f" % celsius)
    webiopi.sleep(5)

ブラウザ画面に値を渡すために、Pythonを少し調整しました。変更点は、3~4行目。loop()をgetTemp関数に差し替えています。
loop()の中で温度を取得し続けても問題はありませんが、数秒ごとに急激に温度が変化する状況はあまり無さそうなので、取得のタイミングはブラウザからの入力に任せるような流れに変更しています。

次に、このgetTemp関数を呼び出す処理を、JavaScriptで記述していきます。

/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</title>
    <script type="text/javascript" src="/webiopi.js"></script>
    <script type="text/javascript">
        webiopi().ready(function() {
            //温度取得処理のコールバック関数(画面上に表示)
            var updateTemp = function(macro, args, response) {
                $("#temp").val(response);
            }
            //温度の初期表示
            webiopi().callMacro("getTemp", [], updateTemp);
            //更新ボタンを作成
            var updateButton = webiopi().createButton("btnUpdate", "update", function() {
                webiopi().callMacro("getTemp", [], updateTemp);
            });
            //更新ボタンを画面上に表示
            $("#controls").append(updateButton);
        });
    </script>
    <style type="text/css">
        #temp{font-size:30px;font-weight:bold;color:red;text-align:center;}
        button {width: auto;height: auto;color: white;margin:10px;}
    </style>
</head>
<body>
<div align="center">
<h1>Temperature</h1>
        <input type="text" id="temp"></span>
        <div id="controls"></div>
</div>
</body>
</html>

温度の値をブラウザ画面に初期表示させるために、14行目でcallMacro関数を使用しています。

webiopi().callMacro("getTemp", [], updateTemp);

callMacroは、第32回でも登場した関数ですね。第1引数はPythonファイルにある関数名、第2引数がPython関数に渡す引数、第3引数はJavaScript側のコールバック関数を指定します。

WebIOPi.callMacro(macro, [args[, callback]])
Call a macro on the server.
(string) macro : name of the macro to call
(string) arg (optional) : array containing arguments
(function) callback (optional) : function called when result received from the server

また、16~20行目で、温度表示を更新するためのボタンを作成しました。createButton関数を使って、ボタンクリック時のイベントを設定しています。イベントの中身は、14行目の初期表示時と同じ内容を設定しました。

createButton
WebIOPi.createButton(id, label[, mousedown[, mouseup]])
Returns a simple button without predefined behavior.
(string) id : id of the button to create
(string) label : label of the button
(function) mousedown (optional) : function called on mousedown/click event
(function) mouseup (optional) : function called on mouseup event

それではプログラムを実行してみましょう!

実行コマンド
sudo /etc/init.d/webiopi start

ボタンをクリックするたびに、クリックした瞬間の温度を取得し、表示が更新されます。最初のサンプルプログラムと同じように、指で触れてあたためながら実行すると、温度変化が分かりやすいので、試してみてくださいね。

 

カスタマイズ!

先程のプログラムをカスタマイズして、温度に応じてライトのON/OFFを制御するツールを作成してみました!

/home/pi/webiopi_sample/python/script.py

import webiopi

GPIO = webiopi.GPIO
LIGHT = 17
CHECK = 30
CELSIUS = 0

def setup():
    GPIO.setFunction(LIGHT, GPIO.OUT)
    GPIO.digitalWrite(LIGHT, GPIO.LOW)
    checkTemp()

def destroy():
    GPIO.digitalWrite(LIGHT, GPIO.LOW)

def checkTemp():
    global CELSIUS
    tmp = webiopi.deviceInstance("temp2")
    celsius = tmp.getCelsius()
    CELSIUS = celsius
    print("Temperature: %f" % celsius)
    if ((celsius >= CHECK) and (GPIO.digitalRead(LIGHT) == GPIO.LOW)):
        GPIO.digitalWrite(LIGHT, GPIO.HIGH)
    elif((celsius < CHECK) and (GPIO.digitalRead(LIGHT) == GPIO.HIGH)):
        GPIO.digitalWrite(LIGHT, GPIO.LOW)

@webiopi.macro
def getTemp():
    global CELSIUS
    checkTemp()
    return "%f" % (CELSIUS)

/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</title>
    <script type="text/javascript" src="/webiopi.js"></script>
    <script type="text/javascript">
        webiopi().ready(function() {
            //温度取得処理のコールバック関数(画面上に表示)
            var updateTemp = function(macro, args, response) {
                $("#temp").val(response);
            }
            //温度の初期表示
            webiopi().callMacro("getTemp", [], updateTemp);
            //更新ボタンを作成
            var updateButton = webiopi().createButton("btnUpdate", "update", function() {
                webiopi().callMacro("getTemp", [], updateTemp);
            });
            //更新ボタンを画面上に表示
            $("#controls").append(updateButton);
            //5秒間隔で表示更新
            setInterval(function(){
                webiopi().callMacro("getTemp", [], updateTemp);
            },5000);
        });
    </script>
    <style type="text/css">
        #temp{font-size:30px;font-weight:bold;color:red;text-align:center;}
        button {width: auto;height: auto;color: white;margin:10px;}
    </style>
</head>
<body>
<div align="center">
    <h1>Temperature</h1>
    <input type="text" id="temp"></span>
    <div id="controls"></div>
</div>
</body>
</html>
raspberrypi34_img06

図5

配線図はこちらです。実際にはLEDの部分に、乾電池式のライトが接続されています。

raspberrypi34_img05

図6

実際の写真はこちら。GPIO17のジャンパーワイヤーを電池ボックスのプラス極、GNDをマイナス極に接続しています(写真では空の電池でワイヤーを挟んで固定しています)。

執筆中はクリスマスシーズンだったので、クリスマスツリーを使って実験してみました。
右下のスマートフォン画面に注目しながら見てください。5秒ごとに温度の表示が更新されていきます。30℃になったタイミングでイルミネーションライトが点灯し、30℃未満になると消灯します。
今回はテスト用に、温度の基準値を30℃、スリープを5秒として実行していますが、実際に使用する環境に合わせて値を調整してください。

GPIOの出力は3.3Vなので、単三電池2~3本で動くものであれば、ラズベリーパイの電源だけでも制御可能です。身近にあるものでぜひ試してみてくださいね!

 

まとめ

温度センサの制御、そしてライトとの連携!ブラウザからの入力と出力、両方ができるようになって、IoTに近づいてきましたね!
WebIOPiのプログラミングにもかなり慣れてきたので、単に電子工作として遊ぶだけでなく、どのように日常に組み込もうかを考えながら進められるようになりました。WebIOPiのおかげで、ラズベリー活躍の場所がどんどん増えています!

次回は、いよいよアナログ値の取得に挑戦!

raspberrypi34_img06

これらの部品を使って、アナログの光センサをラズベリーパイから制御してみましょう!

アバター画像

プロフィール:プログラミング暦通算4年、最近IT業界に舞い戻ってきたプログラマーです。女子です。

NHK学生ロボコン2018