さくらIoT x node-RED on Bluemix で会議室の空き状況を表示させるまでの3日間(1日目)

さくらのIoT PlatformとIBM Bluemix上のnode-REDを接続してみた記録を、半分日記形式で記載しています。
[1日目](http://qiita.com/kkoiwai/items/196cf8136c5a633493c0) では、IBM BluemixにNode-REDを導入し、（シミュレータを作って）WebSocketでデータを受信して、Ambientというグラフ表示サービスにデータを連携するところまで進みました。
[2日目](http://qiita.com/kkoiwai/items/7b311708c42dbdae6aca)では、受信したデータを No-SQL DBにとりあえず保存するのと、Webサイトを作るところまで行きました。
[2.5日目](http://qiita.com/kkoiwai/items/d058c47ef75101088f93)で、IoT Platformが届いたので、Arduinoを実際に接続してみました。
[3日目](http://qiita.com/kkoiwai/items/3b0f2acaadd81128ae7a)では、保存したデータを読める形にして、利用率を計算するところまで行きました。
すごく長くて恐縮ですが、適当にかいつまんで眺めていただき、何かの参考になればうれしいです。


## 目次

- あらまし
- 現状
- ０日目
    - IoT Platform の注文
    - Arduinoの注文
- 1日目
    - node-REDの導入
        - パスワードの設定
    - Ambientの導入
        - 登録
        - node-REDへのモジュール追加
    - WebSocketのテスト
        - 想定データ仕様
    - WebSocketで受信したデータをAmbientに送信する
        - 一旦global変数に格納
        - 定期的にAmbientに送信


---
## あらまし

+ 会議室あるある
    + なかなか予約が取れない
    + 使わないかもしれないけど先に予約しておこう
    + 予約したけど使わなかった。でもキャンセル忘れてた
    + 使わないのに予約済みの会議室だけ増えていく
    + 以下無限ループ
    
---
とりあえず、状況の把握だけでもやってみることにしました。

---
## 現状
まだ統計情報まではできていません。それは3日目にでもやる予定。
<img width="803" alt="スクリーンショット 2017-01-06 20.04.40.png" src="https://qiita-image-store.s3.amazonaws.com/0/135431/bc010469-2af8-7a7d-f256-487bdcc54f56.png">


---

## ０日目

---
## IoT Platform を買った。
- [https://iot.sakura.ad.jp/](https://iot.sakura.ad.jp/)
<img width="749" alt="スクリーンショット 2016-12-31 16.34.12.png" src="https://qiita-image-store.s3.amazonaws.com/0/135431/501d857f-a843-c95e-2d59-b75f7756fd71.png">

会社内なのに有線無線LANを使わなかったのは、セキュリティの問題があるためです。
完全に社内ネットワークと切り離して、すべてをイントラ側ではなく、インターネット側で実現しようと考えました。

---
## Arduinoを買った。
<img width="764" alt="スクリーンショット 2016-12-31 16.37.56.png" src="https://qiita-image-store.s3.amazonaws.com/0/135431/ce9b7c17-e88d-b909-3742-9aa5557daeef.png">

Arduino UNO互換であれば特になんでもいいとたぶん思います。
純正のUNOを一台持っているので、今回はお試しで安いのを買ってみた。

届くまで少し時間がかかるので、その間にWebサイト側を作ってしまいましょう。

---
## 1日目
---
## node-REDの導入
Bluemixは日進月歩で画面が新しくなるので、毎回迷います。
サービスカタログから、 Node-RED Starter を追加します。
<img width="1280" alt="スクリーンショット 2016-12-31 16.59.34.png" src="https://qiita-image-store.s3.amazonaws.com/0/135431/f3a08380-0aca-cb4e-f392-aa0db9178824.png">

---
ダッシュボードを開き、管理者用のユーザ名、パスワードを環境変数に設定し、保存します。
アプリが再起動するので数分待ちます。

<img width="1280" alt="スクリーンショット 2016-12-31 19.07.14のコピー.png" src="https://qiita-image-store.s3.amazonaws.com/0/135431/b77d8a7a-cad4-59a6-3507-5ec069ce4d74.png">



---
## Ambientの導入

[Ambient](https://ambidata.io)という、データを送るだけで最大8系統のデータを自動で履歴保存し、グラフにしてくれる素晴らしい無料のサービスがあります。これを利用させてもらい、０と１を送信することで、利用時間と空き時間を可視化しようと思います。

<img width="1128" alt="スクリーンショット 2016-12-31 19.17.29.png" src="https://qiita-image-store.s3.amazonaws.com/0/135431/408a7f33-805c-cef3-5094-f0870b9b1a40.png">

---
### Node-REDからAmbientに送信するモジュールのインストール

Ambientにデータを送るには、APIにPOSTすればいいのですが、Node-REDで簡単にその送信ができるモジュールがあるので、それをインストールします。
[Wio node + Groveセンサー + Node-RED + Ambientで超簡単IoT](http://qiita.com/AmbientData/items/d92d8d5fe54bf631e3bb#ibm-bluemix-%E3%81%A7%E5%8B%95%E3%81%8B%E3%81%99) を参考にさせていただきました。なお、現在はBluemixのDevOpsサービスは、Continuous Delivery サービスに変更になっており、 ~~ギブハブ~~ GitHubとの連携も簡単にできるようになっています。


---
### Bluemixの　Continuous Deliveryサービスの利用
ダッシュボードから、「継続的デリバリー」の有効化をクリックします。

<img width="1280" alt="スクリーンショット 2016-12-31 20.20.13.png" src="https://qiita-image-store.s3.amazonaws.com/0/135431/9e3d93eb-9aa3-2e84-d45c-99bcbc11f955.png">

---
「Create」をおして、GitHubとのアカウントを紐付けると、

<img width="1280" alt="スクリーンショット 2016-12-31 20.23.13.png" src="https://qiita-image-store.s3.amazonaws.com/0/135431/1e746785-fd73-b8ef-d651-079bd5294688.png">


---
GitHubに新しいレポジトリが登録されます。

<img width="1280" alt="スクリーンショット 2016-12-31 20.23.56.png" src="https://qiita-image-store.s3.amazonaws.com/0/135431/04678da1-e5d1-ad45-860b-6aa3bd4072e3.png">


---
package.json にAmbientを追加し、Commitすると、自動でBluemixに連携され、Node-REDが再起動します。
<img width="1280" alt="スクリーンショット 2016-12-31 20.27.25のコピー.png" src="https://qiita-image-store.s3.amazonaws.com/0/135431/5eea0e45-9318-bbdb-b1e8-bfbc81c27504.png">


---
## WebSocketのテストをする。

他の例をみてみると、さくらIoTとはWebSocketでつなぐのが鉄板のようですので、今回もWebSocketで接続することにします。
しかしながら、まだIoT Platformの発送通知すら来ていないので、先にテストができるよう、テストデータを送信できるようにしてみます。

先ほど設定したユーザ名とパスワードでNode-REDにログインします。

<img width="861" alt="スクリーンショット 2016-12-31 21.07.33.png" src="https://qiita-image-store.s3.amazonaws.com/0/135431/0e6ac22e-92a7-642a-e7b6-073345dcdd09.png">

---

Node-REDの便利な機能の一つに、部品をjson形式で書き出す機能があります。
私が作った稚拙な部品をここに置いておきますので、よろしかったらご利用ください。

```json

[{"id":"9f1852ae.9260a8","type":"websocket out","z":"396465eb.1d8e22","name":"test websocket (/ws/test)","server":"75254220.fd7a5c","client":"","x":700.0000343322754,"y":190.9999828338623,"wires":[]},{"id":"a00899b6.1c5b48","type":"template","z":"396465eb.1d8e22","name":"sakura-iot","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"{\"module\":\"1234567890ab\",\"type\":\"channels\",\"datetime\":\"{{payload.datetime}}\",\"payload\":{\"channels\":[{\"channel\":{{payload.channel}},\"type\":\"I\",\"value\":{{payload.value}}}]}}","x":478.5714797973633,"y":188.14291858673096,"wires":[["9f1852ae.9260a8","88ccffee.743da8"]]},{"id":"99d5057c.56db98","type":"inject","z":"396465eb.1d8e22","name":"15秒おき","topic":"","payload":"true","payloadType":"bool","repeat":"15","crontab":"","once":true,"x":105,"y":102.4285888671875,"wires":[["1566d30d.eb7c95"]]},{"id":"1566d30d.eb7c95","type":"function","z":"396465eb.1d8e22","name":"ランダムなチャンネルにランダムな値を生成","func":"var pdata = {}\n\n    \n// 15秒 * 1/4 = 1分に一度の割合で値を更新\nif(Math.random() < 1/4){\n    \n    // 0 - 5のいずれか\n    pdata.channel =  Math.floor( Math.random() *6 ) ;\n    \n    // 0 , 1のいずれか\n    pdata.value =  Math.round( Math.random()) ;\n    \n    pdata.datetime = new Date().toJSON();\n    msg.payload = pdata;\n\n    return msg;\n\n} else {\n    return null;\n}\n\n\n","outputs":1,"noerr":0,"x":403.57142639160156,"y":102.42860794067383,"wires":[["a00899b6.1c5b48"]]},{"id":"88ccffee.743da8","type":"debug","z":"396465eb.1d8e22","name":"WebSocket送信データ","active":true,"console":"false","complete":"payload","x":680.714282989502,"y":263.8572196960449,"wires":[]},{"id":"50fc2bf8.59c0b4","type":"comment","z":"396465eb.1d8e22","name":"さくらiotのWebSocketのモック","info":"","x":135,"y":51,"wires":[]},{"id":"6ac7390e.c86848","type":"comment","z":"396465eb.1d8e22","name":"モックのWebSocket から受信","info":"","x":137.8571548461914,"y":355.28572368621826,"wires":[]},{"id":"3b4df7a2.862328","type":"websocket in","z":"396465eb.1d8e22","name":"WebSocket (/ws/test)","server":"","client":"df973c4.18d10c","x":114.28571755545477,"y":405.28572368621826,"wires":[["38a72b0c.319a0c"]]},{"id":"38a72b0c.319a0c","type":"debug","z":"396465eb.1d8e22","name":"WebSocket受信データ","active":true,"console":"false","complete":"payload","x":362.32144927978516,"y":404.5715093612671,"wires":[]},{"id":"75254220.fd7a5c","type":"websocket-listener","z":"","path":"/ws/test","wholemsg":"false"},{"id":"df973c4.18d10c","type":"websocket-client","z":"","path":"ws://your-app-name.mybliemix.net/ws/test","wholemsg":"false"}]

```

こちらをコピーして右上のメニュー（横線3本のアイコン）からImportできます。
<img width="574" alt="スクリーンショット 2016-12-31 21.08.47.png" src="https://qiita-image-store.s3.amazonaws.com/0/135431/dbd6037d-11fa-62c0-bd2b-8a0a91c56355.png">


---

なお、まだ見ぬAPIから送られてくるデータ仕様は、 [さくらのIoT Platformを試してみた](http://dev.classmethod.jp/hardware/sakura-iot-ataglance/)を参考にさせていただきました。

右上の「Deploy」をおすと、debugタブにデータがどんどん貯まっていくのが分かります。
<img width="358" alt="スクリーンショット 2016-12-31 21.12.38.png" src="https://qiita-image-store.s3.amazonaws.com/0/135431/6eb2e833-b43c-5dcf-8f69-ab8e078faa92.png">

---
### 想定データ仕様

IoT Platformには、128のチャンネルがあり、そのチャンネルそれぞれに、8バイト（オクテット）のデータを送ることができます。

当社の会議室は、利用中以外はドアを開けておくルールになっています。
なので、Arduinoにドアセンサ（[こんなの](http://akizukidenshi.com/catalog/g/gP-04025/)）を接続して、一つの部屋にひとつのチャンネルを割り当て、ドアが開いていればゼロ、ドアが閉まっていればイチを送るようにしました。

また、なるべくリアルタイムにデータを取得できるよう、5秒おきにループを回すことにしたのですが、毎回通信していたらそれなりに通信量がかさんでしまうので、状態が変わったときにだけデータを送ることにします。

実際のArduinoのコードは、０日目に注文したものが届いて、動くことを確認した後にアップします。


---
### WebSocketで受信したデータをAmbientに送信する

前述の通り、IoT Platformからは、ドアの状態に変化が起こったときだけデータを受信することにしました。受け取ったデータを、そのままAmbientに転送し、棒グラフを描画させると、こんな風に、いつ使われているのか全く分からないデータになります。
![スクリーンショット-2016-12-31-22.28.49.png](https://qiita-image-store.s3.amazonaws.com/0/135431/7e3b379e-fddf-5ed5-2eac-dddd1ac9887c.png)
（画像はイメージです）

そのため、Node-REDの中で、IoT Platformから受信したデータを現在の状態として一時的に貯めておき、Ambientには定期的（1分おきとか）に現在の状態を送信することにしました。

----

まず、状態を蓄積するために、グローバル変数を定義し、初期化します。
左のNode（それぞれの部品をNodeと呼ぶんですね）の一覧から、「Inject」と「Function」を取り出し、2つを線でつないでください。
それぞれのNodeをダブルクリックすると、設定が出てきます。
<img width="533" alt="スクリーンショット 2016-12-31 22.45.59.png" src="https://qiita-image-store.s3.amazonaws.com/0/135431/696e86b5-36e0-906f-5048-f2621776ca86.png">

---
Injectの設定では、PayloadでBooleanのtrueを送るようにし、「Inject once at start?」にチェックを入れてください。これで、再起動時(左上のデプロイを押したとき）に必ず実行されます。
<img width="525" alt="スクリーンショット 2016-12-31 22.44.51.png" src="https://qiita-image-store.s3.amazonaws.com/0/135431/8385b755-83ca-7e1e-e594-3d44551341b9.png">
Functionの設定では、下記のように、global変数にArrayを追加して、ゼロで初期化しておきます。
<img width="531" alt="スクリーンショット 2016-12-31 22.45.20.png" src="https://qiita-image-store.s3.amazonaws.com/0/135431/c77e5cff-a8ce-bcc3-27c3-90000bca5883.png">

設定を変更したら、反映するために「Deploy」を忘れず押しましょう。

---
つぎに、WebSocketから受信したデータでglobal変数を更新する部分を作ります。
せっかちな方は下記をコピーしてImportしてください。

```json
[{"id":"1603f091.8c119f","type":"debug","z":"396465eb.1d8e22","name":"Global変数に保存された値","active":true,"console":"false","complete":"payload","x":602.7500076293945,"y":473.0000066757202,"wires":[]},{"id":"c6245639.e87ce8","type":"function","z":"396465eb.1d8e22","name":"Global変数の更新","func":" msg.payload.payload.channels.forEach(function(v) {\n     context.global.data[v.channel] = v.value;\n\n});\n\nmsg.payload = JSON.stringify(context.global.data);\n\nreturn msg;","outputs":1,"noerr":0,"x":343.75000762939453,"y":472.5000066757202,"wires":[["1603f091.8c119f"]]},{"id":"3e6f4d17.8847f2","type":"json","z":"396465eb.1d8e22","name":"","x":136.875,"y":472.5,"wires":[["c6245639.e87ce8"]]}]
```

Nodeのリストの上に、検索ボックスがあります。必要なNodeが見つからない場合はここから検索してください。
jsonというNodeは、文字列からjson オブジェクトに変換してくれます。WebSocketから受信した状態だと文字列なので変換が必要です。
<img width="808" alt="スクリーンショット 2016-12-31 23.31.26.png" src="https://qiita-image-store.s3.amazonaws.com/0/135431/9d5a38cc-b662-0874-33fb-361cb6384276.png">

---

これで、global変数に各会議室の利用状態が保存されたので、情報を定期的にAmbientに送信します。

<img width="838" alt="スクリーンショット 2017-01-01 0.17.28.png" src="https://qiita-image-store.s3.amazonaws.com/0/135431/d64e39bd-ae85-6bd9-4300-a8e56176fae5.png">

```json

[{"id":"3f480fb6.370088","type":"Ambient","z":"396465eb.1d8e22","name":"Ambient：会議室状況","channelId":"123","writeKey":"0123456789abcdef","x":521.5000019073486,"y":731,"wires":[]},{"id":"30e286bf.69737a","type":"function","z":"396465eb.1d8e22","name":"Arrayから値を取得","func":"\n\nvar data = {\n    // 0 or 1 \n    \"d1\" : context.global.data[0] ,\n    \"d2\" : context.global.data[1] ,\n    \"d3\" : context.global.data[2] ,\n    \"d4\" : context.global.data[3] ,\n    \"d5\" : context.global.data[4] ,\n    \"d6\" : context.global.data[5]  \n};\nmsg.payload = data;\nreturn msg;","outputs":1,"noerr":0,"x":276,"y":785.0000123977661,"wires":[["3f480fb6.370088","2b6720b9.34f1f8"]]},{"id":"cb556381.39e78","type":"inject","z":"396465eb.1d8e22","name":"1分おきに実行","topic":"","payload":"true","payloadType":"bool","repeat":"60","crontab":"","once":true,"x":109,"y":727,"wires":[["30e286bf.69737a"]]},{"id":"2b6720b9.34f1f8","type":"debug","z":"396465eb.1d8e22","name":"Ambient送信データ","active":false,"console":"false","complete":"payload","x":504,"y":785.0000123977661,"wires":[]}]

```

これをコピペして、ChannelIDとWriteKeyを適宜書き換えてください。
AmbientのチャネルIDとライトキーはログインすると出てきます。
<img width="1184" alt="スクリーンショット 2017-01-01 0.32.32.png" src="https://qiita-image-store.s3.amazonaws.com/0/135431/3a96b543-e43d-36d2-3f04-88052d7be454.png">

そうこうやっている間に年が明けてしまったので、2日目に続きます。

---
## [2日目](http://qiita.com/kkoiwai/items/7b311708c42dbdae6aca)に続く