Check! Node-RED から Azure IoT Hub へデータを送るには？

こんばんは、 @dz_ こと大平かづみです。

Prologue - はじめに
====
[Node-RED Advent Calendar 2016](http://qiita.com/advent-calendar/2016/nodered) の 5日目の投稿です。昨日は [FlowEditorをカスタマイズ (settings.jsで指定)](http://qiita.com/utaani/items/a1d1fc104ad1639dd4d8) という記事で、複数運用のコツが紹介されていました！

さて、本記事では [Node-RED](http://nodered.org/ "Node-RED") から [Azure IoT Hub](https://azure.microsoft.com/ja-jp/services/iot-hub/ "Azure IoT Hub | Microsoft Azure") にデータを送る方法をご紹介します。（すでにご存知の方も多いかとは思いますが…！）

Azure IoT Hub とは？
----

IoT デバイスのデータ送受信や、監視、管理ができる Azure のサービスです。

- デバイス → クラウド、クラウド → デバイスの双方向で通信ができる
- デバイス別に共有アクセスキーや接続文字列を発行することができ、それらを用いて接続する
- [Azure Stream Analytics](https://azure.microsoft.com/ja-jp/services/stream-analytics/ "Stream Analytics - リアルタイム データ分析 | Microsoft Azure") を介して、 Azure の各サービスにつなげることができる

### 余談

ちなみに、IoT でよく使われる似たサービスの Azure Event Hub は、デバイス → クラウドへの単方向の通信をサポートしています。


Node-RED から Azure IoT Hub を利用する方法
----
Node-RED から Azure IoT Hub を利用するには、ざっくり 2つの方法があります。

- デフォルトの MQTT ノードを利用する
- Azure IoT Hub 用ノードを利用する

### デフォルトの MQTT ノードを利用する

Azure IoT Hub は、 MQTT プロトコルをサポートしているので、デフォルトの MQTT ノードでもデータを送受信することができそうです。

- [IoT Hub の MQTT サポート | Microsoft Docs](https://docs.microsoft.com/ja-jp/azure/iot-hub/iot-hub-mqtt-support "IoT Hub の MQTT サポート | Microsoft Docs")

### Azure IoT Hub 用ノードを利用する

MQTT 専用ノードの方がより簡単に Azure IoT Hub を利用できます。2016年12月の時点では 下記の 2つのノードが見つかりました。

- [node-red-contrib-azureiothubnode](http://flows.nodered.org/node/node-red-contrib-azureiothubnode) ← Microsoft 公式？
- [node-red-contrib-azure-iot-hub](http://flows.nodered.org/node/node-red-contrib-azure-iot-hub)

`node-red-contrib-azureiothubnode` は、メンテナが [azure-cli](https://www.npmjs.com/package/azure-cli) と同じ `windowsazure` なので、こちらがマイクロソフト公式のようですね？

ということで、今回は Azure IoT Hub 用ノードを使ってみましょう！

Azure IoT Hub 用ノードを使ってみよう
====

どちらの使い方も、他のノードと同様、さほど難しくないのですが、IoT Hub 特有のポイントを中心にご紹介します。

### 作業環境
- Node.js, npm インストール済み
- Node-RED 構築済み
- Azure IoT Hub 作成済み

以下はそれぞれのノードの大まかな流れです。「デバイスID」「接続文字列」「SASトークン」に関しては、後述の [Azure IoT Hub デバイスID の新規作成、情報取得について]() をご参照ください。

なお、この二つは名前が同じノードがあるので、同時には利用できないようです (´･ω･`)

`node-red-contrib-azureiothubnode` を使ってみる
----
- `node-red-contrib-azureiothubnode` ノードをインストールする
- Azure IoT Hub に デバイスID を作成し、接続文字列を得る
- `azureiothub` ノードを配置し、ノードの設定を行う
    - 作成したデバイスID の `ConnectionString` に接続文字列を設定する

![20161205_node-red_azure-iot-hub_003.png](https://qiita-image-store.s3.amazonaws.com/0/3812/e6d92eb0-e77f-5a97-d09d-ac3f07fa7b1f.png)

`node-red-contrib-azure-iot-hub` を使ってみる
----
こちらは 2つのノード ( `azureiothub`, `azureiothubregistry` ) がインストールされます。

- `node-red-contrib-azure-iot-hub` ノードをインストールする
- ポータルなどから Azure IoT Hub の「ホスト名」を取得する
- `azureiothub` ノードを配置し、ノードの設定を行う
    - `Hostname` にホスト名を入力する
- `azureiothub` ノードの入力に対して、`template` ノードを接続し、設定する
    - `template` ノードに、下記フォーマットに合わせた JSON を入力する

### `azureiothub` ノードに入力する JSONフォーマットについて
[こちら](http://flows.nodered.org/node/node-red-contrib-azure-iot-hub#azure-iot-hub) のフォーマットで、「デバイスID」「デバイスIDのキー」「プロトコル」そして「送信データ」を指定します。

```json
{
  "deviceId": "xxx",
  "key": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=",
  "protocol": "mqtt",
  "data": "{value1: 0, value2: \"test\"}"
}
```
| 設定項目 | 設定値 |
|----------|-------| 
| `deviceId` | デバイスID |
| `key` | デバイスID のプライマリキー、またはセカンダリキー。取得方法は後述参照。 |
| `protocol` | `amqp`, `amqpws`, `mqtt`, `http` から選択可能 |
| `data` | 送信データ |


![20161205_node-red_azure-iot-hub_006.png](https://qiita-image-store.s3.amazonaws.com/0/3812/83693461-b0e6-842a-7f07-4b073da4e4d0.png)

Azure IoT Hub デバイスID の新規作成、情報取得について
====

Azure IoT Hub を利用するには、デバイスごとの「デバイスID」を作成する必要があります。Azure IoT Hub のデバイスIDの作成は、ポータルではなく、以下のようないくつかの方法で行います。

-  [iothub-explorer](https://www.npmjs.com/package/iothub-explorer "iothub-explorer")
    -  Node.js 製CLIツール
    - Windows, Mac OS, Linux で利用可能
    - 参考: [Use the iothub-explorer tool to provision a device](https://github.com/Azure/azure-iot-sdks/blob/master/doc/manage_iot_hub.md#use-the-iothub-explorer-tool-to-provision-a-device)
- Node.js [azure-iothub](https://www.npmjs.com/package/azure-iothub "azure-iothub") モジュール
    - Node.js でコーディング
    - 参考: [Node.js で Azure IoT Hub を使用する方法 | Microsoft Docs](https://docs.microsoft.com/ja-jp/azure/iot-hub/iot-hub-node-node-getstarted "Node.js で Azure IoT Hub を使用する方法 | Microsoft Docs")
- Device Explorer
    - GUIツール
    - Windows のみで利用可能
    - [Azure IoT SDKs のリリースページ](https://github.com/Azure/azure-iot-sdks/releases) から `SetupDeviceExplorer.msi` をダウンロード、インストールして利用する
    - 参考: [Use the Device Explorer tool to provision a device](https://github.com/Azure/azure-iot-sdks/blob/master/doc/manage_iot_hub.md#use-the-device-explorer-tool-to-provision-a-device "Use the Device Explorer tool to provision a device")

`iothub-explorer` を使ってみる
----
### 大まかな流れ
1. ポータルから IoT Hub の接続文字列を取得する
2. IoT Hub の接続文字列を用いて、ログインする
3. デバイスIDを作成する
4. デバイスIDの情報を取得する

### 解説
ポータルから IoT Hub の接続文字列を取得します。

![20161205_node-red_azure-iot-hub_002.png](https://qiita-image-store.s3.amazonaws.com/0/3812/651a5c06-2d92-e3bc-a456-271a4998dddf.png)

接続文字列 ( connectionString ) は、このような文字列です。

```
HostName=xxx.azure-devices.net;SharedAccessKeyName=iothubowner;SharedAccessKey=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
```

そして、 そのの接続文字列を用いて、ログインします。

```bash
# ログインする
$ iothub-explorer login <iothub-connection-string>
```

デバイスIDを新規作成するには、 `iothub-explorer create` コマンドを使います。

デバイスIDのキーは `symmetricKey` として表示され、プライマリキー、セカンダリキーを持っています。このとき `--connection-string` または `-c` とオプションをつけると、このデバイスIDの接続文字列を表示してくれます。

```bash
# デバイスIDを作成する
$ iothub-explorer create <device-name> --connection-string

Created device xxx
deviceId:                   xxx
generationId:               012345678901234567

...<略>...

authentication:
  symmetricKey:
    primaryKey:   xxxx=
    secondaryKey: xxxx=

...<略>...

connectionString:           HostName=xxx.azure-devices.net;DeviceId=xxx;SharedAccessKey=xxxxx=
```

作成済みのデバイスID の場合は、 `iothub-explorer get` コマンドで、キーなどの各情報を得ることができます。 `--connection-string` または `-c` オプションを指定することで接続文字列も取得できます。

```bash
# 作成済みのデバイスIDの接続文字列を取得する
$ iothub-explorer get <device-name> --connection-string

deviceId:                   xxx
generationId:               012345678901234567

...<略>...

authentication:
  symmetricKey:
    primaryKey:   xxxx=
    secondaryKey: xxxx=

...<略>...

connectionString:           HostName=xxx.azure-devices.net;DeviceId=xxx;SharedAccessKey=xxxxx=
```

トラブルシューティング
====
最後に、私が陥ったトラブルについて共有しておきます。

前述のドキュメント ( [Node.js で Azure IoT Hub を使用する方法](https://docs.microsoft.com/ja-jp/azure/iot-hub/iot-hub-node-node-getstarted "Node.js で Azure IoT Hub を使用する方法 | Microsoft Docs")) のデバイスIDを作成する Node.js のコードや、バージョン `1.1.0` 時点の `iothub-explorer` では、以下のエラーが発生します。ご注意くださいませ。

`primaryKey` が定義されていないというエラー
----
### 概要
デバイスIDに紐づく情報の構造が `deviceInfo.authentication.SymmetricKey.primaryKey` から `deviceInfo.authentication.symmetricKey.primaryKey` (`symmetricKey` の頭文字が小文字になった) に変わったようで、これに対応していないと `TypeError: Cannot read property 'primaryKey' of undefined` というエラーが発生します。

### 対処
#### ドキュメント内のサンプルコードの場合
以下を読み替えて実行して下さい。

```diff
...<略>...

function printDeviceInfo(err, deviceInfo, res) {
  if (deviceInfo) {
    console.log('Device id: ' + deviceInfo.deviceId);
-   console.log('Device key: ' + deviceInfo.authentication.SymmetricKey.primaryKey);
+   console.log('Device key: ' + deviceInfo.authentication.symmetricKey.primaryKey);
  }
}
```

#### バージョン `1.1.0` 時点の `iothub-explorer` の場合
`1.1.0` までのバージョン `iothub-explorer` を利用すると、以下のエラーが発生するようです。当初は直接モジュール内のソースコードを書き換えて回避してましたが、今では `1.1.1` 以上にバージョンアップすれば問題ありません。

```bash
$ iothub-explorer sas-token "myFirstDevice"

C:\Users\user\AppData\Roaming\npm\node_modules\iothub-explorer\iothub-explorer-sas-token.js:44
    var key = device.authentication.SymmetricKey.primaryKey || device.authentication.SymmetricKey.secondaryKey;
                                                ^

TypeError: Cannot read property 'primaryKey' of undefined
    at C:\Users\user\AppData\Roaming\npm\node_modules\iothub-explorer\iothub-explorer-sas-token.js:44:49
    at C:\Users\user\AppData\Roaming\npm\node_modules\iothub-explorer\node_modules\azure-iothub\lib\registry.js:223:7
    at C:\Users\user\AppData\Roaming\npm\node_modules\iothub-explorer\node_modules\azure-iothub\lib\rest_api_client.js:109:7
    at IncomingMessage.onResponseEnd (C:\Users\user\AppData\Roaming\npm\node_modules\iothub-explorer\node_modules\azure-iot-http-base\lib\http.js:73:7)
    at emitNone (events.js:91:20)
    at IncomingMessage.emit (events.js:185:7)
    at endReadableNT (_stream_readable.js:974:12)
    at _combinedTickCallback (internal/process/next_tick.js:74:11)
    at process._tickCallback (internal/process/next_tick.js:98:9)
```

Epilogue - おわりに
====
簡単ゆえにいろいろ情報を載せようとしたところ、てんこ盛りに…！

とはいえ、簡単につなげられるので、ぜひご利用くださいませ～！ヾ(\*･ω･\*)o