ウチのワンコが「しっぽ振ってるよー」と離れた飼い主に知らせてくれるプロトタイプを、Node-REDとobniz、LINEを連携させて作ってみる①

自分が作りたいもののイメージが大分固まってきましたので、今回はその全体像をご紹介しつつ、実際のシステム環境を準備していきます。

##全体のイメージ

**家に仕掛けたセンサーからトリガ情報を受け取り、離れた場所にある端末を制御したい**というイメージです。

<img width="80%" alt="構成イメージ" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1264253/68fe0342-1c9d-f68c-3ee9-62af6004a134.png">

「ウチのワンコがしっぽを振っている」というアクションを取得し、それを離れた場所にいる飼い主さんへ届けるサービスになります。サービスの企画イメージは別途noteに投稿していく予定です。プロトタイプ1号機の動画も付けておきます('ω')

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">ローカルでなくAzureにホストしたNode-REDから、obnizでサーボモーターを動かしてみました。私の中では、ワンコのしっぽプロト1号機です(&#39;ω&#39;)<br> <a href="https://twitter.com/hashtag/protoout?src=hash&amp;ref_src=twsrc%5Etfw">#protoout</a> <a href="https://twitter.com/hashtag/obniz?src=hash&amp;ref_src=twsrc%5Etfw">#obniz</a> <a href="https://twitter.com/hashtag/IoT?src=hash&amp;ref_src=twsrc%5Etfw">#IoT</a> <a href="https://twitter.com/hashtag/Azure?src=hash&amp;ref_src=twsrc%5Etfw">#Azure</a> <a href="https://twitter.com/hashtag/%E3%81%84%E3%81%9A%E3%82%8C%E3%83%AF%E3%83%B3%E3%82%B3%E3%81%AE%E3%81%97%E3%81%A3%E3%81%BD?src=hash&amp;ref_src=twsrc%5Etfw">#いずれワンコのしっぽ</a> <a href="https://t.co/l9N74s9fMA">pic.twitter.com/l9N74s9fMA</a></p>&mdash; ranchi1977 (@ranchi1977) <a href="https://twitter.com/ranchi1977/status/1392813565690613771?ref_src=twsrc%5Etfw">May 13, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

##Node-RED環境の準備

###Node-REDをAzure上で動かす

冒頭イメージの②サーバー環境ですが、これまではローカルPCでNode.jsを動かし、トンネリングサービスを使ってLINE Messaging APIなど外部サービスと連携させていました。しかし、毎回公開サーバのアドレスが変更されてしまい修正が面倒でしたので、今回の開発向けに**サーバーをクラウドサービス上にホスティング**してしまうことにしました。また、**プロトタイピングに向いている「Node-RED」を採用**して、各種サービスの処理ノードを使って開発していくスタイルにします。

Azureサブスクリプション環境があるので、以下GitHubリポジトリから、Azure App ServiceへNode-RED環境をデプロイしていきます。

-  [jmservera/node-red-azure-webapp](https://github.com/jmservera/node-red-azure-webapp)

あとは、以下のNode-REDのユーザガイドを参考に`adminAuth`プロパティで認証有効化してアカウント設定をすれば、サーバー環境の準備は完了です！

-  [Node-RED User Group Japan](https://nodered.jp/docs/user-guide/runtime/configuration)

懐かしのログイン画面。Node-RED触るの久し振りです。（初心者ですが）
<img width="60%" alt="Node-REDログイン" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1264253/de4bf205-5169-bec9-dc57-969bb9757061.png">

###接続サービスの処理ノードをパレットに追加

obnizとLINE Messaging APIと連携させていきたいので、以下処理ノードをパレットから追加していきます。

|node|Version|
|----|-----|
|node-red-contrib-obniz|0.6.2|
|node-red-contrib-line-messaging-api|0.1.6|

これで、Node-REDでobnizノードとLINEノードが使えるようになりました！
<img width="60%" alt="処理ノード" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1264253/1c72e39f-ac85-3f8c-8ec3-714711ff0454.png">

##外部サービスと連携して動作テストしてみます

それでは早速動作テストしていきます。今回は冒頭イメージの③にある、トリガを受け取ったサーバからLINE通知やobniz操作を試します('ω')

###obnizでサーボモーターを動かしてみよう！

今回はサーバーからのトリガを受け、サーボモータを使ってしっぽフリフリを試してみました。このレベルなら、もっと１つの`obniz function`内で処理を書き込んでフローをスッキリさせても良いと思いますが、何だかNode-REDが懐かしくて、無駄に処理フローを見える化させたくなってしまいました。「サーボを角度指定で動かす→0.5秒待つ」の繰り返しですね。先頭の`inject`ノードのボタンを押せば、処理が実行されます。

<img width="60%" alt="obnizフロー" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1264253/326cc0f1-41e2-58bf-1f44-c91a820e2911.png">

`obniz function`の中はこんなコードになっています。

```js
let servo = obniz.wired("ServoMotor", {gnd:0, vcc:1, signal:2});
servo.angle(90.0);
return msg;
```

ニーズは無いと思いますが、このフローの書き出しJSONも付けておきます。
<details><summary>コード全文はこちら</summary><div>

```json
[
    {
        "id": "d8711099.2c0df",
        "type": "tab",
        "label": "obniz-servo",
        "disabled": false,
        "info": ""
    },
    {
        "id": "6bd62e86.f9a92",
        "type": "obniz-function",
        "z": "d8711099.2c0df",
        "obniz": "b7c6a37d.a77d8",
        "name": "move servo",
        "code": "let servo = obniz.wired(\"ServoMotor\", {gnd:0, vcc:1, signal:2});\r\nservo.angle(90.0); \r\nreturn msg;\r\n",
        "x": 310,
        "y": 160,
        "wires": [
            [
                "74d3c74b.a66118"
            ]
        ]
    },
    {
        "id": "ca64b6cf.9327c8",
        "type": "debug",
        "z": "d8711099.2c0df",
        "name": "",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "x": 530,
        "y": 400,
        "wires": []
    },
    {
        "id": "8d6c6c2b.1aee7",
        "type": "inject",
        "z": "d8711099.2c0df",
        "name": "トリガ",
        "topic": "",
        "payload": "トリガ",
        "payloadType": "str",
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "x": 130,
        "y": 160,
        "wires": [
            [
                "6bd62e86.f9a92"
            ]
        ]
    },
    {
        "id": "74d3c74b.a66118",
        "type": "delay",
        "z": "d8711099.2c0df",
        "name": "",
        "pauseType": "delay",
        "timeout": "500",
        "timeoutUnits": "milliseconds",
        "rate": "1",
        "nbRateUnits": "1",
        "rateUnits": "second",
        "randomFirst": "1",
        "randomLast": "5",
        "randomUnits": "seconds",
        "drop": false,
        "x": 490,
        "y": 160,
        "wires": [
            [
                "b0f92f0f.19db5"
            ]
        ]
    },
    {
        "id": "b0f92f0f.19db5",
        "type": "obniz-function",
        "z": "d8711099.2c0df",
        "obniz": "b7c6a37d.a77d8",
        "name": "move servo",
        "code": "let servo = obniz.wired(\"ServoMotor\", {gnd:0, vcc:1, signal:2});\r\nservo.angle(10.0); \r\nreturn msg;\r\n",
        "x": 330,
        "y": 240,
        "wires": [
            [
                "a7e718c3.bbbd38"
            ]
        ]
    },
    {
        "id": "a7e718c3.bbbd38",
        "type": "delay",
        "z": "d8711099.2c0df",
        "name": "",
        "pauseType": "delay",
        "timeout": "500",
        "timeoutUnits": "milliseconds",
        "rate": "1",
        "nbRateUnits": "1",
        "rateUnits": "second",
        "randomFirst": "1",
        "randomLast": "5",
        "randomUnits": "seconds",
        "drop": false,
        "x": 510,
        "y": 240,
        "wires": [
            [
                "829c940a.11c898"
            ]
        ]
    },
    {
        "id": "829c940a.11c898",
        "type": "obniz-function",
        "z": "d8711099.2c0df",
        "obniz": "b7c6a37d.a77d8",
        "name": "move servo",
        "code": "let servo = obniz.wired(\"ServoMotor\", {gnd:0, vcc:1, signal:2});\r\nservo.angle(90.0); \r\nreturn msg;\r\n",
        "x": 330,
        "y": 320,
        "wires": [
            [
                "a6d4d43c.eb8ce8"
            ]
        ]
    },
    {
        "id": "a6d4d43c.eb8ce8",
        "type": "delay",
        "z": "d8711099.2c0df",
        "name": "",
        "pauseType": "delay",
        "timeout": "500",
        "timeoutUnits": "milliseconds",
        "rate": "1",
        "nbRateUnits": "1",
        "rateUnits": "second",
        "randomFirst": "1",
        "randomLast": "5",
        "randomUnits": "seconds",
        "drop": false,
        "x": 510,
        "y": 320,
        "wires": [
            [
                "63671970.4fc308"
            ]
        ]
    },
    {
        "id": "63671970.4fc308",
        "type": "obniz-function",
        "z": "d8711099.2c0df",
        "obniz": "b7c6a37d.a77d8",
        "name": "move servo",
        "code": "let servo = obniz.wired(\"ServoMotor\", {gnd:0, vcc:1, signal:2});\r\nservo.angle(10.0); \r\nreturn msg;\r\n",
        "x": 330,
        "y": 400,
        "wires": [
            [
                "ca64b6cf.9327c8"
            ]
        ]
    },
    {
        "id": "b7c6a37d.a77d8",
        "type": "obniz",
        "z": "",
        "obnizId": "My obnizId",
        "deviceType": "obnizboard1y",
        "name": "my obniz",
        "accessToken": "",
        "code": ""
    }
]
```

</div></details>

私が使用しているのは「obniz Board 1Y」と「Micro Servo Digital 9G SG90」、`obnizId`はご自身のモノに書き換えをお願いします。しっぽらしさ？を出すために、机の上にあった結束バンドを使ってみました。こういうことを始めると、100円ショップに行きたくなりますね('ω')

###LINE Botへプッシュ通知をしてみよう！

続けて、LINE Botへプッシュ通知を試していきます。以前に作成した**わんこボット**（リンク先参照）がいますので、そこに「ふりふり」と通知を出してみます。

https://qiita.com/ranchi1977/items/fff2dc52def04f6a8cee

フローは以下の通りシンプルです。コードを書くというより、`PushMessage`ノードに、LINE Messaging APIのチャネルシークレットやIDなどを埋めて連携させていきます。これも先頭の`inject`ノードのボタンを押して、処理を実行します。

<img width="60%" alt="LINEフロー" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1264253/abb973f8-838b-187c-b004-3de17f9f3656.png">
<img width="60%" alt="PushMessage" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1264253/782accbf-9e71-979a-48cf-cd9f6de41145.png">

これで以下の通り、LINE Botがプッシュ通知してくれます。以前の使い方のように、こちらから尋ねると応答を返してくれるボットとは違い、サーバ側から一方的にプッシュ通知を受け取るというのは、またユーザー体験が違いますね。

<img width="60%" alt="LINEでプッシュ通知" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1264253/c4bb8b37-2294-0150-d9ef-92e75599539d.png">

##終わりに

なかなか良い感じでプロトタイピング環境の構築と動作テストまで進めることができました。珍しく、記事作成の方が時間取られた･･気がします。今回のハマりポイントは以下二点くらいでしょうか。

- サーボモータを動かす角度と待ち時間の微調整に結構時間を取られる（でも楽しい）
- Node-REDのobnizノードでアクセストークン発行したら、他の経路から一切obniz触れなくなっていて、かなり焦る('ω')

二点目の方は、セキュアに作ろうとしたら対応必須なのでしょうが、まだまだPCローカルのNode.jsから試したり、公式のパーツライブラリから動かして勉強しているような段階ですので、ちょっと利便性優先ですぐに無効に戻してしまいました。この辺りは追々対応していけるようにしたいと思います。

***

さて、今後は今回試した機能のレベルアップを行いながら、冒頭イメージ①のセンサー収集部分もチャレンジしていきます！ここからのハードルが非常に高そうですね・・。いかにワイヤレスで、どれくらいの精度で「フリフリ」を検知するか。いかにワンコに嫌がられない、センサーを外されてしまわない、**ワンコに優しい仕組み**を実現できるか。できれば、画像分析ではない、実際のしっぽフリフリをセンサーで捉まえたいなーと妄想中です('ω')

##参考記事リンク

-  [Azure Web AppでNode-REDを動かす手順](https://qiita.com/zuhito/items/50fc450ad9b827e98d11)
-  [Node-REDのobnizノードがバージョンアップした！](https://qiita.com/wicket/items/e150e78cd787da03493e)
-  [https://flows.nodered.org/node/node-red-contrib-line-messaging-api](https://flows.nodered.org/node/node-red-contrib-line-messaging-api)