前回は HubotでMQTTを使いPubSubをするサンプルを書きました。それぞれに対応するMQTTクライアントのPubSubをBeagleBone BlackとRaspberry Pi 2に実装します。BeagleBone BlackはHubotからLEDライトをオンオフするメッセージをsubscribeします。USB連動電源タップのUSBポートが接続されたUSBハブの特定のポートの電源を操作することで、植物育成LEDの電源を制御します。またDS18B20で計測したアボカドが入っているガラス瓶の水温をfreeboardのダッシュボードにpublishします。Raspberry Pi 2はBME280からアボカド周辺の気温、湿度、気圧をfreeboardにpublishします。
BeagleBone Black BeagleBone Blackには防水仕様のDS18B20で計測した水温をMQTTブローカーにpublishするプロセスと、Slackから植物育成LEDライトをon/offするメッセージをsubscribeするプロセスを起動します。リポジトリはこちら です。
Supervisor プロセスのデモナイズにSupervisor を使います。
$ sudo apt-get update $ sudo apt-get install supervisor
DS18B20 1-wireのDS18B20をBeagleBone Blackのcapemgrの設定は毎回必要です。Supervisorから管理するプロセスの起動スクリプトを用意して最初に有効化の処理を書きます。その後にDS18B20から計測した水温をMQTTブローカーにpublishするPythonスクリプトを実行します。
~/python_apps/glow-light-py/start_w1.sh #!/bin/bash sudo sh -c 'echo BB-W1:00A0 > /sys/devices/bone_capemgr.9/slots' /home/debian/python_apps/glow-light-py/w1_publish.py
DS18B20で計測した温度をMQTTブローカーにpubishするPythonのプログラムです。
~/python_apps/glow-light-py/w1_publish.py import paho.mqtt.client as mqttfrom time import sleepimport jsonimport sysfrom config import confw1="/sys/bus/w1/devices/{}/w1_slave" .format (conf["W1_ID" ]) def sensing (): raw = open (w1, "r" ).read() celsius = float (raw.split("t=" )[-1 ])/1000 retval = dict (temperature="{:.2f}" .format (celsius)) return retval def on_connect (client, userdata, rc ): print("Connected with result code {}" .format (rc)) def on_publish (client, userdata, mid ): print("publish: {}" .format (mid)) def main (): client = mqtt.Client(client_id='' , clean_session=True , protocol=mqtt.MQTTv311) client.username_pw_set(conf["TRIGGER_5_UUID" ], conf["TRIGGER_5_TOKEN" ]) client.on_connect = on_connect client.on_publish = on_publish client.connect(conf["IDCF_CHANNEL_URL" ], 1883 , 60 ) while True : retval = sensing() if retval: message = json.dumps({"devices" : conf["ACTION_5_UUID" ], "payload" : retval}) print(message) client.publish("message" ,message) sleep(5 ) if __name__ == '__main__' : main()
DS18B20のIDはデバイス毎に異なるため、/sys/bus/w1/devices
に作成される28*
のIDを設定ファイルに記述します。
~/python_apps/glow-light-py/config.py conf = { ... "W1_ID" : "28-xxx" , ... }
PahoのPythonクライアントをインストールします。
$ sudo pip install paho-mqtt
Supervisorの設定ファイルを書きます。debian
ユーザーが実行するプロセスになります。上記のラップしたシェルスクリプトを実行コマンドにします。
/etc/supervisor/conf.d/w1.conf [program:w1] command =/home/debian/python_apps/glow-light-py/start_w1.shnumprocs=1 redirect_stderr=true stdout_logfile=/var/log /w1.log user=debian
w1の設定ファイルの変更をrelead
でSupervisorに伝えてから、add
で子プロセスを追加します。
$ sudo supervisorctl reread w1: available $ sudo supervisorctl add w1 w1: added process group
植物育成LED DS18B20と違いこちらはSupervisorの設定ファイルに直接debian
ユーザーが実行できるPythonのプログラムをcommand
に指定します。
/etc/supervisor/conf.d/led.conf [program:led] command =/home/debian/python_apps/glow-light-py/led_subscribe.pynumprocs=1 redirect_stderr=true stdout_logfile=/var/log /led.log user=debian
Hubutからの指示をsubscribeしてLEDライトをon/offします。
~/python_apps/glow-light-py/led_subscribe.py import paho.mqtt.client as mqttimport jsonimport subprocessimport osfrom config import confdef on_connect (client, userdata, rc ): print("Connected with result code {}" .format (rc)) client.subscribe(conf["ACTION_3_UUID" ]) def on_subscribe (client, userdata, mid, granted_qos ): print("Subscribed: " +str (mid)+" " +str (granted_qos)) def led_on_off (on_off ): cmd = os.path.abspath(os.path.join(os.path.dirname(__file__), "hub-ctrl" )) p = subprocess.Popen(["sudo" ,cmd, "-b" , "1" , "-d" , conf["DEVICE_NO" ], "-P" , conf["PORT_NO" ], "-p" , on_off], stdout=subprocess.PIPE) out, err = p.communicate() print(out) def on_message (client, userdata, msg ): print(msg.topic+" " +str (msg.payload)) payload = json.loads(msg.payload) on_off_str = payload["data" ] if on_off_str == "led-on" : led_on_off("1" ) elif on_off_str == "led-off" : led_on_off("0" ) else : pass def main (): client = mqtt.Client(client_id='' , clean_session=True , protocol=mqtt.MQTTv311) client.username_pw_set(conf["ACTION_3_UUID" ], conf["ACTION_3_TOKEN" ]) client.on_connect = on_connect client.on_subscribe = on_subscribe client.on_message = on_message client.connect(conf["IDCF_CHANNEL_URL" ], 1883 , 60 ) client.loop_forever() if __name__ == '__main__' : main()
hub-ctrlで指定して電源をコントロールするUSBのデバイス番号とポート番号は変わる場合があるので、lsusbコマンドで確認します。NEC Corp. HighSpeed Hub
がコントロール先のデバイスです。
$ sudo lsusb Bus 001 Device 002: ID 0409:005a NEC Corp. HighSpeed Hub Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub Bus 002 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub Bus 001 Device 003: ID 2019:ab2a PLANEX GW-USNano2 802.11n Wireless Adapter [Realtek RTL8188CUS]
確認したデバイス番号と、USB連動電源タップを接続したUSBハブのポート番号の1(左端)を設定ファイルに記述します。
~/python_apps/glow-light-py/config.py conf = { ... "DEVICE_NO" : "2" , "PORT_NO" : "1" }
同様にSupervisorの子プロセスに追加します。
$ sudo supervisorctl reread led: available $ sudo supervisorctl add led led: added process group
Supervisorに追加した時点で子プロセスが起動します。status
でステータスを確認すると「RUNNING」になっています。
$ sudo supervisorctl status led RUNNING pid 3218, uptime 0:00:03 w1 RUNNING pid 3190, uptime 0:03:51
Raspberry Pi 2 Raspberry Pi 2にはBME280の環境センサーをMQTTブローカーにpublishするプロセスを起動します。リポジトリはこちら です。
Supervisor Supervisorをインストールします。
$ sudo apt-get update $ sudo apt-get install supervisor
Supervisorでは子プロセスをuser
を指定せずにrootで実行したりsudoをつけてプログラムを実行することは推奨されないようです。sudoなしでI2Cを使うプログラムを実行できるように、piユーザーをi2cグループに追加します。
$ sudo usermod -aG i2c pi
Supervisorの設定ファイルはBeagleBone Blackのときと同じです。
/etc/supervisor/conf.d/bme280.conf [program:bme280] command =/home/pi/python_apps/bme280-meshblu-py/bme280_publish.pynumprocs=1 redirect_stderr=true stdout_logfile=/var/log /bme280.log user=pi
ACTION_1_UUID
とACTION_2_UUID
にBME280から計測した環境データをpublishします。ACTION_1_UUID
はHubotのMQTTがsubscribeしているuuidです。ACTION_2_UUID
はfreeboardのダッシュボードがWebSocketでsubscribeしているuuidです。
~/python_apps/bme280-meshblu-py/bme280_publish.py import paho.mqtt.client as mqttfrom time import sleepimport jsonimport sysimport bme280from config import confdef sensing (): return bme280.readData() def on_connect (client, userdata, rc ): print("Connected with result code {}" .format (rc)) def on_publish (client, userdata, mid ): print("publish: {}" .format (mid)) def main (): client = mqtt.Client(client_id='' , clean_session=True , protocol=mqtt.MQTTv311) client.username_pw_set(conf["TRIGGER_1_UUID" ], conf["TRIGGER_1_TOKEN" ]) client.on_connect = on_connect client.on_publish = on_publish client.connect(conf["IDCF_CHANNEL_URL" ], 1883 , 60 ) while True : retval = sensing() if retval: message = json.dumps({"devices" : [conf["ACTION_1_UUID" ], conf["ACTION_2_UUID" ]], "payload" : retval}) print(message) client.publish("message" ,message) sleep(5 ) if __name__ == '__main__' : main()
PahoのPythonクライアントをインストールします。
$ sudo pip install paho-mqtt
子プロセスをSupervisorに追加して起動します。
$ sudo supervisorctl reread bme280: available $ sudo supervisorctl add bme280 bme280: added process group
trigger-1はデフォルトでaction-1にメッセージを送信できますが、action-2への送信も許可します。DockerホストのMeshbluコンテナが起動しているディレクトリで実行します。
$ docker-compose run --rm iotutil whiten -- -f trigger-1 -t action-2 > iotutil@0.0.1 start /app > node app.js "whiten" "-f" "trigger-1" "-t" "action-2" trigger-1 can send message to action-2
freeboard freeboardのダッシュボードにアボカド周辺の環境データが4種類表示できました。