IBM Bluemix で IoT (1) - Pythonからpublish/subscribe

IBM IoT Foundation
======================

IBM Bluemixでは、IoTデバイス接続のための「Internet of Things Foundation」が利用できる。これとBluemix上で動くNode-REDを組み合わせればIoTのプロトタイプがお手軽にできそう、ということでいくつかプロトタイプしてみた。
プロトタイプのコードは[fnishio/mqtt-sample](https://github.com/fnishio/mqtt-sample)を参照。

Pythonからpublish/subscribe
===========================
まず、pythonで動くMQTTライブラリ[paho-mqtt](https://pypi.python.org/pypi/paho-mqtt)を使ってIoT Foundationに対してpublish/subscribeしてみる。
プロトタイプの内容は、以下のサンプルをpythonに焼き直したもの。
http://www.ibm.com/developerworks/jp/cloud/library/cl-mqtt-bluemix-iot-node-red-app/

- 機器側
  - 定期的にインクリメントされるカウンター値をpublishする
  - カウンター値リセットのコマンドをsubscribeする
- サービス側
  - Deviceからのカウンター値をモニター(subscribe)
  - カウンター値が5になったらリセットするようDeviceにコマンドをPublishする

機器側
------------

### 接続機器の登録 ###
IoT Foundationのdashboardを開いて接続する機器を登録する。
登録後に表示される認証用のAuthentication TokenはMQTT Broker接続時の認証パスワードとして使う。
![device_reg.png](https://qiita-image-store.s3.amazonaws.com/0/88598/1e0ae055-26ca-0d31-5812-6a1b01c328eb.png)

### MQTT Brokerにつなぐ ###

```python:device.py
client_id = "d:{org_id}:{type_id}:{device_id}"
endpoint = "{org_id}.messaging.internetofthings.ibmcloud.com"
client = mqtt.Client(client_id)
client.username_pw_set("use-token-auth", PASSWORD)
client.on_connect = on_connect
client.on_message = on_message
client.connect(endpoint, 1883)
```

``org_id, type_id, device_id, PASSWORD``はIoT Foundationへの登録情報を元に適切に設定する。


### Event Publish ###
1.5秒ごとに``count``をインクリメントしながら、トピック`iot-2/evt/eid/fmt/json`にカウンタ値をpublishする。

```python:device.py
count = 0
while client.loop() == 0:
    msg = json.dumps({ "d" : { "count" : count } });
    client.publish("iot-2/evt/eid/fmt/json", msg, 0, True)
    print("sent: " + msg)
    time.sleep(1.5)
    count = count + 1
```


### Command subscribe
- Brokerに接続したら`iot-2/cmd/cid/fmt/json`トピックをsubscirbe
- ``reset``コマンドが来たら``count``をコマンドに含まれている値に再設定!

```python:device.py
def on_connect(client, userdata, flags, rc):
    client.subscribe("iot-2/cmd/cid/fmt/json")

def on_message(client, userdata, msg):
    global count
    payload = json.loads(msg.payload.decode())
    if payload['cmd'] == "reset":
        count = payload['count']
```

サービス側
----------------------------
サービス側のアプリはNode-REDで以下のフローを実装する。
![monitor_app_flow.png](https://qiita-image-store.s3.amazonaws.com/0/88598/4a308831-d135-94d3-c5c6-f7c759d4c846.png)

### Event Subscribe ###
``MQTT Device``から送信されるトピック``iot-2/evt/eid/fmt/json``のイベントを処理する。
![monitor_app_in.png](https://qiita-image-store.s3.amazonaws.com/0/88598/153e12be-a646-35f1-bf98-9e49d5b57263.png)

### Monitor
イベントをモニターして、カウンター値が4を超えたら次のフローへ進む
![monitor_app_switch.png](https://qiita-image-store.s3.amazonaws.com/0/88598/beb7d68a-cdf2-9d52-0a99-33d385cc93c0.png)

### Command Payload
resetコマンドを組み立てる
![monitor_app_reset.png](https://qiita-image-store.s3.amazonaws.com/0/88598/44c6f64d-22c6-9845-b7bc-219f98211875.png)

### Command Publish
deviceIdの機器に対して、トピック``iot-2/cmd/cid/fmt/json``にコマンドをpublishする。
![monitor_app_out.png](https://qiita-image-store.s3.amazonaws.com/0/88598/4abb391f-c311-7de4-7a6e-7176acbbdbab.png)

実行
---------
`{"count": 5}`のデータを送信した後、
カウンターをリセットするコマンド`{"cmd":"reset", "count": 0}`を受信している。

```shell
$ python3 device.py
sent: {"d": {"count": 0}}
sent: {"d": {"count": 1}}
sent: {"d": {"count": 2}}
sent: {"d": {"count": 3}}
sent: {"d": {"count": 4}}
sent: {"d": {"count": 5}}
iot-2/cmd/cid/fmt/json b'{"cmd":"reset", "count": 0}'
sent: {"d": {"count": 0}}
sent: {"d": {"count": 1}}
```