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文と同じことができますが、同じではないことも処理できます。