IoTで使うPython入門Step2-MQTT (5) アラート
●Subscriber がアラートを出す
raspberrypi-3.localでは、上記のraspberrypi-2.localがBrokerに4種類のデータを送っているうち、温度データを購読します。そして、30℃以上になったらアラートを出すというプログラムを作ります。
発行しているのは4種類です。
- 1個目のLM75Bの温度 raspberrypi-2/lm75b-1/temp
- 2個目のLM75Bの温度 raspberrypi-2/lm75b-2/temp
- 1個目のSHT31の温度 raspberrypi-2/sht31-1/temp
- 1個目のSHT31の湿度 raspberrypi-2/sht31-1/RH
このうち特定なものを抜き出すのは、下記の方法でtopicを記述します。#はワイルド・カードです。通常のプログラムなどでは * に相当します。階層構造のワイルド・カードは + です。
- 全部を購読 '#'
- SHT31の温度と湿度の両方 'raspberrypi-2/sht31-1/#'
- すべてのデバイスの温度 '+/+/temp'
- 二つのLM75Bの温度 'raspberrypi-2/+/temp'
- 2個目のLM75Bの温度だけ 'raspberrypi-2/lm75b-2/temp'
●paho.mqttライブラリのsubscribe
書式:simple(topics, qos=0, msg_count=1, retained=False, hostname="localhost", port=1883, client_id="", keepalive=60, will=None, auth=None, tls=None, protocol=mqtt.MQTTv311) |
raspberrypi-2.localが発行しているTopicをraspberrypi-3.localで温度全部を購読するテスト・プログラムです。
#coding:utf-8
import paho.mqtt.subscribe as subscribe
from time import sleep
#topics = ['#'] # wild card
#topics = ['raspberrypi-2/sht31-1/#']
topics = ['+/+/temp'] # すべてのデバイスの温度
#topics = ['raspberrypi-2/+/temp']
#topics = ['raspberrypi-2/lm75b-2/temp']
host = 'raspberrypi.local'
while 1:
m = subscribe.simple(topics, hostname=host, retained=False, msg_count=1)
print(m.topic)
print(m.payload)
if float(m.payload) > 30.0:
print " 熱い!"
sleep(1)
payloadはstr型です。'25.0'などの値が入っています。浮動小数点なので、floart()で数値に変換します。整数のときはint()で数値に変換できます。
simple()のパラメータのmsg_countは1以上の値を取れます。そのとき、値はlist型で取得できるので、次のように取り出します(コラム参照)。
m = subscribe.simple(topics, hostname=host, retained=False, msg_count=3)
for a in m:
print(a.topic)
print(a.payload)
●警告をLEDで知らせる
GPIO21(物理40番ピン)にLEDをつなぎ、アラートを光で知らせます。raspberrypi-3.localで購読しているのは、すべての温度です。したがって、2個のLM75Bと1個のSHT31のいずれかの温度が30℃以上になったらLEDが点灯します。
# coding:utf-8
import RPi.GPIO as gpio
import paho.mqtt.subscribe as subscribe
from time import sleep
LED_pin = 21 # 40pin
# topics = ['#'] # wild card
# topics = ['raspberrypi-2/sht31-1/#']
# topics = ['+/+/temp']
topics = ['raspberrypi-2/+/temp']
# topics = ['raspberrypi-2/lm75b-2/temp']
host = 'raspberrypi.local'
gpio.setmode(gpio.BCM)
gpio.setup(LED_pin, gpio.OUT)
gpio.output(LED_pin, True)
sleep(1.0)
gpio.output(LED_pin, False)
try:
while 1:
gpio.output(LED_pin, False)
m = subscribe.simple(topics, hostname=host, retained=False, msg_count=1)
print(m.topic)
print(m.payload)
if float(m.payload) > 30.0:
print " 熱い!"
gpio.output(LED_pin, True)
sleep(1.0)
except KeyboardInterrupt:
print('\n end')
gpio.cleanup()
(※)paho-mqttのドキュメント paho.mqtt.pythonのソース
●コラム for文
この連載で初めてfor文が出てきました。forは制御フロー文です。いままでループを回すにはwhile文を使っていました。
while 1: というのは無限ループなので、プログラムを書いているという感覚があまりありません。たとえばA-Dコンバータの変換完了信号(正論理)がGPIOx端子につながっているとしたら、
while GPIO.input(GPIOx) :
データを読み込む変換が完了したら信号がHighになるのでそれまで待っていて、データを読みに行くというプログラムが書けます。
for文はループを回すだけではありません。C言語のように使うときは、
for i in range(8): のように、range()関数を使います。range(8)は、0、1、2、3、4、5、6、7の値を取ります。
for i in [1, 5, 9]: のように変数 i の値をとることもできます。[1, 5, 9]はlistです。
本文で出てきた、
m = subscribe.simple(topics, hostname=host, retained=False, msg_count=3)
for a in m:mは、順番に三つの購読をしたのでイテラブル・データです。具体的にはlist型で、
[<paho.mqtt.client.MQTTMessage object at 0x76c3ddb0>, <paho.mqtt.client.MQTTMessage object at 0x7663ecb0>, <paho.mqtt.client.MQTTMessage object at 0x7663ed30>] それぞれのlistの要素は、
m[0] <paho.mqtt.client.MQTTMessage object at 0x76c3ddb0>
m[1] <paho.mqtt.client.MQTTMessage object at 0x7663ecb0>
m[2] <paho.mqtt.client.MQTTMessage object at 0x7663ed30>です。順番に変数aに入ります。オブジェクトの中身は次の構成になっています。
{
"topic":"raspberrypi-2/sht31-1/temp",
"payload":"23.82",
"qos":0,
"retain":false,
"_msgid":"48ec3bc1.9c8584"
}このままでは利用できないので、オブジェクトの中身を取り出すときは、
print(a.topic)
print(a.payload)
print(a.qos)
print(a.retain)です。本文ではa.topicとa.payloadを利用しています。
mは通常オブジェクトのことです。ここで使えるオブジェクトは、イテレータです。イテレータ・データの特徴は、反復処理が可能なことです。イテレータのもととなるのは、次の2種類です。これらのデータをコピーして、for文で順に取り出されて、使い終わったら中が空っぽになります。
- シーケンス型
- イテラブル・オブジェクト
シーケンス型に含まれるのは、文字列のstr、list、tupleです。イテラブル・オブジェクトとは、並んだデータです。具体的には、list、tuple、range、集合のset?などです。
整理できていないです。間違っているかもしれません。型とオブジェクト、関数がごちゃごちゃになっています。でもまとめると、
for 変数 in <list型もしくは文字列というオブジェクト>: オブジェクトから、入っている値をまず最初の1個を取り出し、次のをまた取り出すというように、順に取り出したのが変数に入るという理解でよいかと思います。結果として、ループを回すwhile文と同じことができますが、同じではないことも処理できます。