Watson IoT Platform を介してMQTTでデータを送受信する

# はじめに
　Watson IoT Platform (Internet of Things Platform、以降WIOTP) は、IBM Cloud を基盤とする IoT のためのプラットフォームです。センサー等のデバイスから得られるデータの収集・蓄積・可視化など、様々な機能を提供しています。
　デバイスやアプリケーションを接続するための公式のSDKが提供されており、MQTT経由でデータを送受することができます。
　本記事ではその方法について解説します。


# Watson IoT Platform のコンセプト

　まずはWIOTPが扱う「モノ」のコンセプトについて説明します。
WIOTPでは、接続する「モノ」の種類として以下の3つを定義しており、それぞれ実装できる機能や接続方法が異なります。

- アプリケーション
- デバイス
- ゲートウェイ

## アプリケーション

　アプリケーションは、3つモノの中で最も多くの機能を使用することでき、デバイスから送信されたイベントを受信してサービスに活用することも、自らデバイスとしてイベントを送信することもできます。
　アプリケーションとして実装するためには、事前にWIOTPコンソールでAPIキーを発行する必要があります。WIOTPに接続する際にAPIキーで認証することで、接続されているアプリケーションをWIOTPで識別することができます。
　また、WIOTPでは想定するアプリケーションの種類が定義されており、APIキーごとにアプリケーションの種類を選択することで、使用できる機能を制限することができます。

アプリケーションでは、以下の機能を使用することができます。
- デバイスから送信されたイベントを受信する
- デバイスにコマンドを送信する
- デバイスとしてイベントを送信する
- デバイスとしてコマンドを受信する

## デバイス
　センサーなどで得られた周囲の情報をWIOTPに送信するのがデバイスの役割です。
　デバイスとして実装するためには、事前にWIOTPコンソールでデバイスタイプ、デバイスIDを設定する必要があり、デバイスIDは1つのデバイスごとにユニークになるように設定します。
　WIOTPでは、デバイスIDによって接続要求やデータの送信がどのデバイスによって行われたかを識別することができます。

デバイスでは、以下の機能を使用することができます。
- 「人感センサーで人を検知した」のような、イベントをアプリケーションに送信する
- アプリケーションから送信されたコマンドを受信する

## ゲートウェイ
　同じ種類・役割の複数のデバイスが存在する場合に、それらとWIOTPの間でデータを中継するのがゲートウェイの役割です。そうすることで、WIOTPは実際には複数のデバイスから送信されたデータを、一つのデバイス（ゲートウェイ）から送信されたものとしてまとめて扱うことができるようになり、データを処理しやすくなります。
　ゲートウェイもデバイスと同様に、事前にWIOTPコンソールでデバイスタイプ、デバイスIDを設定する必要があります。WIOTPの中ではデバイスの一つとして扱われます。

ゲートウェイでは、以下の機能を使用することができます。
- デバイスから送信されたイベントを受信し、アプリケーションに中継する
- アプリケーションから送信されたコマンドを受信し、デバイスに中継する


# サンプルの作成

　実際に公式のSDKを使用して、WIOTPに定期的にイベントを送受信するサンプルアプリを作成します。今回は送信側をデバイス、受信側をアプリケーションとして作成します。

## 前提

- IBM Cloud のアカウントを所有していること
- IBM Cloud 上で Watson IoT Platform のインスタンスを作成済みであること


## 事前準備

### WIOTPでのデバイスの設定

デバイスを実装するのに必要なデバイスの登録を行います。

- Watson IoT Platform のインスタンスを開き、画面左のメニューから「デバイス」を開きます。
- 「デバイスの参照」と書かれたページが表示されるので、右上の「デバイスの追加」ボタンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/122591/5577efa2-7d2b-e0a8-7fc8-9cf00fe6e7b6.png)

- 「デバイス・タイプ」「デバイスID」に任意の値を設定し、「次へ」ボタンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/122591/725fbd1d-4493-16f4-b391-0f624ea23141.png)

- 次のページでは、何も入力せずにそのまま「次へ」ボタンをクリックします。
-  「自動生成認証トークン」のページが表示されます。「認証トークン」は自動で生成することも、任意の値を設定することも可能です。設定したら「次へ」ボタンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/122591/4fa62fd6-c0f0-fdf3-6793-ab2237af7172.png)

- 要約の画面が表示されるので、「終了」ボタンををクリックします。
- 「デバイスのドリルダウン」のページが表示されるとデバイスの登録は完了です。ここで表示される情報は後ほど使用するのでメモしておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/122591/73e27fa7-277c-ccc1-e5ad-ffc137b9b9b8.png)


### WIOTPでのAPIキーの発行

アプリケーションを実装するのに必要なAPIキーを発行します。
詳細は以下の記事の「1. APIキーの発行」をご覧ください。
https://qiita.com/Motonaga/items/6304f5f66f63cb566943

## 送信側の実装

送信側のデバイスを実装します。

``` python:publish.py
import wiotp.sdk.application
import time
import json

## WIOTPで設定した「デバイス」用の各種パラメータをJSON形式の構成情報（options）に埋め込む
org_id = "xxxx" # WIOTPの組織ID
device_id = "sample_id" # 事前準備で設定した「デバイスID」
device_type = "sample_type" # 事前準備で設定した「デバイスタイプ」
token = "sample-token" # 事前準備で設定した「認証トークン」
event_id = "sample" # 送信するイベントの識別子。任意の値を設定できる。受信側と同じ値にする

options = {
  "identity": {
    "orgId": org_id,
    "typeId": device_type,
    "deviceId": device_id
  },
  "auth": {
    "token": token
  }
}

# SDKを使用して「デバイス」としてWIOTPに接続する
client = wiotp.sdk.device.DeviceClient(options,  logHandlers=None)
client.connect()

# 2秒ごとに {count} をインクリメントしてWIOTPに送信する
myData = {'message': 'foo', 'count': 0}
while True:
  print("data published: ", json.dumps(myData))
  client.publishEvent(event_id, "json", myData)
  myData['count'] += 1
  time.sleep(2)
```


## 受信側の実装

受信側のアプリケーションを実装します。

``` python:subscribe.py
import wiotp.sdk.application
import json
import time

app_id = "sample_app" # アプリケーションの識別子。任意の値を設定する
app_auth_key = "xxxx" # WIOTPで発行したアプリケーションのAPIキー
app_auth_token = "xxxx" # WIOTPで発行したアプリケーションの認証トークン

# WIOTPで設定した「アプリケーション」用の各種パラメータをJSON形式の構成情報（options）に埋め込む
options = {
  "identity": {
    "appId": app_id
  },
  "auth": {
    "key": app_auth_key,
    "token": app_auth_token
  }
}

# SDKを使用して「アプリケーション」としてWIOTPに接続する
client = wiotp.sdk.application.ApplicationClient(options, logHandlers=None)
client.connect()

# イベントを受信した際のコールバック関数を設定。ここでは受信したイベント情報を標準出力に書き出している
def event_callback(event):
  # 受信したデータのbodyはevent.dataで取得できる
  print("{} event '{}' received from device [{}]: {}".format(event.format, event.eventId, event.device, json.dumps(event.data)))
client.deviceEventCallback = event_callback

# サブスクライブするデバイスのパラメーター（送信側と同じもの）を設定し、Subscribeを開始
device_id = "sample_id" # 事前準備で設定した「デバイスID」
device_type = "sample_type" # 事前準備で設定した「デバイスタイプ」
event_id = "sample" # 受信するイベントの識別子。任意の値を設定できる。送信側と同じ値にする

client.subscribeToDeviceEvents(typeId=device_type, deviceId=device_id, eventId=event_id)

# アプリの稼働状態を維持するためにループを回す
while True: 
  time.sleep(3)
```

# 実行結果

送信側と受信側の両方を実行すると、下記のような結果が得られ、期待通りにMQTT経由でデータの送受信ができていることが確認できます。

WIOTPのデバイスのコンソール（送信されたイベントの内容が表示される）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/122591/c3fe055c-5b16-f05c-41e9-46fdddce187a.png)




受信側のコンソール

```
$ python subscribe.py 
2019-12-23 15:41:58,308   wiotp.sdk.application.client.ApplicationClient  INFO    Connected successfully: a:a54k3u:sample_app
json event 'sample' received from device [sample_type:sample_id]: {"message": "foo", "count": 0}
json event 'sample' received from device [sample_type:sample_id]: {"message": "foo", "count": 1}
json event 'sample' received from device [sample_type:sample_id]: {"message": "foo", "count": 2}
json event 'sample' received from device [sample_type:sample_id]: {"message": "foo", "count": 3}
json event 'sample' received from device [sample_type:sample_id]: {"message": "foo", "count": 4}
json event 'sample' received from device [sample_type:sample_id]: {"message": "foo", "count": 5}
json event 'sample' received from device [sample_type:sample_id]: {"message": "foo", "count": 6}
```



# まとめ

　本記事では、Watson IoT Platform と接続する「モノ」のコンセプトの説明と、公式のSDKを使用した接続方法について解説しました。
　接続方法については、送信側をデバイス、受信側をアプリケーションとして作成しましたが、実際の現場で使用する際は複数のデバイスをゲートウェイで中継して使用する実装も多くなると思います。


# 参考資料

- Github: https://github.com/ibm-watson-iot/iot-python
- Python SDK: https://ibm-watson-iot.github.io/iot-python/
- 公式ドキュメント：https://cloud.ibm.com/docs/services/IoT?topic=iot-platform-connect_devices_apps_gw&locale=ja
- 初めてのMQTT：https://gist.github.com/voluntas/89000a06a7b79f1230ab