enebularの本気
過去記事で enebular + RaspberryPi + Slack を連携させて簡単な遠隔Lチカを実現しました。
【超入門】初めてのenebular! eneblarの始め方から、"hello,world"に"Lチカ"と簡単なチュートリアル |
---|
しかし、**「enebularの本気はまだまだこんなもんじゃない・・・」**と思い、なるべくenebularの特徴を十分に活かした開発をしてみました。
作るもの
今回は、スマホで外出先からも操作できるスマートエアコンを実現しました。
外出先から操作できるスマートエアコンがあれば家に帰る直前にエアコンをONにして、帰宅した時には部屋がポッカポカというわけです。
また、いつも持ち歩いているスマホで操作できるので、部屋のどこに置いたか忘れたリモコンを探し回る必要もありません。
私のように寒い地域 (岩手県:この記事を書いている時点で-9℃) に住む人には嬉しいデバイスだと思います。
完成したもの
で、できたものがこんな感じです。
動画で見ると地味ですが実際に動いた時は結構感動します。
全体の構成
構成を考える
エアコンを操作したり、部屋の温度を取得したりするのはRaspberryPi Zero WH(ラズパイ)
に任せます。
そして、リモコンはWebアプリ
としてHeroku
に作成します。
enebularを活用する
しかし、こうなってくると**「enebularで作成したフローをラズパイのNode-REDにコピーして・・・」「ラズパイと連携するようにHerokuでアプリ作って・・・」と、色んなサービスやハードウェアを行ったり来たりしなければいけません。
そこで、今回ラズパイ
とHeroku
のコーディングとデプロイをenebular
のみで行います。
この、作成したフローのHerokuへのデプロイ**、AWS IoTを経由したRaspberryPiやmbedなどのハードへのデプロイが簡単にできることがenebularの特徴の一つでもあります。
準備するもの
- Raspberry Pi
- 赤外線受光モジュール( GP1UXC41QS )
- 赤外線LED( OSI5FU5111C-40 )
- 電解コンデンサ
47μF
- 抵抗器
130Ω
47Ω
- ブレッドボード + ジャンパーワイヤ
用意する部品の型番を参考までに書きましたが、他の製品でも大体動くと思います。
私は、赤外線受光モジュールは VS838 、赤外線LEDはジャンクのリモコンから抜いたものを使って今回作りましたが、一応動きました。
ラズパイで赤外線を送受信できるようにする
Raspberry Pi Zero で赤外線リモコンを作る
https://qiita.com/ww24/items/32d2a78c20e6dcc2e68c
by @ww24
大部分で、こちらの記事を参考にさせていただきました。ありがとうございます。
ラズパイで赤外線を送受信できるようにする手順は、基本的には上記の記事の通りなのですが、所々簡略化したので内容が多少ダブりますが解説します。
回路図
赤外線受信
受信部は参考記事の回路と同じです。
赤外線送信
ラズパイのセットアップ
回路が組めたら、リモコンの赤外線を学習・送信できるようにラズパイをセットアップしましょう。
lirc のインストールと設定
Linux上から赤外線の制御を簡単にできるようにする、LIRCというデーモンを入れます。
sudo apt update
sudo apt upgrade
sudo apt install lirc
無事インストールできたら、
lirc-rpiを有効化、回路につながっているGPIOピンを設定します。
以下のコマンドで設定ファイルを開いて、
sudo vi /boot/config.txt
50行目あたりを以下のように書き換えます。
# Uncomment this to enable the lirc-rpi module
# dtoverlay=lirc-rpi
↓
# Uncomment this to enable the lirc-rpi module
dtoverlay=lirc-rpi,gpio_in_pin=24,gpio_out_pin=25
設定が完了したら、sudo reboot now
でラズパイを再起動します。
参考記事に書かれていますが、以下のことに注意が必要です。
⚠上記の設定によって lircd は自動起動するので、 systemctl enable 等は不要です。逆に他の方法で自動起動を設定してしまうことで GPIO の割り当てられるピンが変わってしまうなど不具合が起こる事もあり、注意が必要です。
リモコンの学習
以下のコマンドを実行した状態で、赤外線受光モジュールにリモコンを向けてボタンを押してみてください。
mode2 -d /dev/lirc0 | cat
出力(謎の数字列)が得られれば、赤外線を無事に受信できています!
pulse 3774
space 1858
pulse 515
space 426
pulse 569
space 1330
pulse 538
space 405
・・・
受信できていることを確認したら、lircd.conf
というファイルに、受信した信号を整形したものを記述して、ラズパイに記憶させる必要があるのですが・・・
今回はそこらへんの処理を全てshellスクリプト
にまとめました。
赤外線信号を記憶する
【赤外線受信情報を保存するスクリプト】
#!/bin/bash
if [ $# -ne 1 ]; then
echo "ERROR: missing file operand"
echo "Usage: $0 output"
exit 1
fi
# 読み取り
echo "[Send a signal within 5 seconds!!]"
timeout 5 mode2 -d /dev/lirc0 | sed -ue '1d' | tee $1
# 整形
echo "================================="
cat $1 | awk '{if(NR % 30) ORS=" "; else ORS="\n"; print $1}' | { cat -; echo; } | tee $1
コピペして保存したら、chmod +x read_pulse.sh
で実行権限を付与してください。
使い方
以下のように実行して、5秒以内に赤外線受光モジュールに向けてリモコンのボタンを押せば、読み取った数値がon
というファイル名で保存されます。
./parse_pulse.sh on
同じ要領で、off
ボタンの信号も読み取っておきましょう。
【赤外線情報を LIRC に登録するスクリプト】
#!/bin/bash
output="lircd.conf"
# ==========================
head="begin remote
name $1
flags RAW_CODES
eps 30
aeps 100
gap 200000
toggle_bit_mask 0x0
begin raw_codes
"
# ==========================
end=" end raw_codes
end remote
"
# ==========================
if [ $# -lt 2 ]; then
echo "ERROR: Argument is missing"
echo "Usage: $0 name Signal_file [Signal file ...]"
exit 1
fi
echo "$head" | tee $output
shift
for x in "$@"
do
echo " name $x"
cat "$x"
echo
done | tee -a $output
echo "$end" | tee -a $output
上と同じく、コピペして保存したらchmod +x mklircd.sh
で実行権限を付与してください。
使い方
以下のように、./mklircd.sh [リモコンの名前] [登録したい信号ファイル] [登録したい信号ファイル] ...
という形でコマンドを実行するとlircd.conf
というLIRCの設定ファイルを自動で生成してくれます。
./mklircd.sh AIR on off
設定を反映
設定ファイルがうまく生成されたら、以下のコマンドで設定を適用します。
sudo cp lircd.conf /etc/lirc/lircd.conf
設定ファイルを適用したら、/etc/lirc/hardware.conf
を以下のように変更。
# Run "lircd --driver=help" for a list of supported drivers.
DRIVER="UNCONFIGURED"
# usually /dev/lirc0 is the correct setting for systems using udev
DEVICE=""
MODULES=""
↓
# Run "lircd --driver=help" for a list of supported drivers.
DRIVER="default"
# usually /dev/lirc0 is the correct setting for systems using udev
DEVICE="/dev/lirc0"
MODULES="lirc_rpi"
以上が完了したら、LIRC を再起動して設定を反映します。
sudo systemctl restart lirc
sudo systemctl status lirc
信号の送信
以下のように、irsend LIST '' ''
とirsend LIST AIR ''
でリモコンのボタンが登録されたか確認できます。
$ irsend LIST '' ''
irsend: AIR
$ irsend LIST AIR ''
irsend: 0000000000000001 on
irsend: 0000000000000002 off
長かったですが、やっと信号を送出します。
実行するときは赤外線LEDをエアコンの赤外線受信部に十分近づけて実行してください。
irsend SEND_ONCE AIR on
これでエアコンがON
になれば成功です。
とりあえずお疲れ様です・・・
ラズパイをAWS IoTに繋いでenebularと連携する
ここの部分についてはこちらの記事にまとめています。
RaspberryPiをAWS IoTに繋いでenebularと連携する
https://qiita.com/TakedaHiromasa/items/b6828e4ac434bf99325d
いよいよenebularで開発!
ここまで下準備が長かったですがやっとフローの作成です・・・!
今回はSmartAircon
というプロジェクトに Device_raspberrypi
Controller_heroku
という2つのフローを作成しました。
「Device_raspberrypi」 フロー
動きとしては以下のような感じ。
-
MQTTで
"ON"
or"OFF"
のテキストを受信して、それぞれのケースに分岐。 -
execで
irsend SEND_ONCE AIR on
とかを実行。 - 実行エラーなら
0
、成功なら"ON"
or"OFF"
のテキストをMQTTで返す。
フローのソースは以下の通り。
[
{
"id": "c2e2e67c.0eb118",
"type": "switch",
"z": "e5de4f7.bd044b",
"name": "",
"property": "payload",
"propertyType": "msg",
"rules": [
{
"t": "eq",
"v": "ON",
"vt": "str"
},
{
"t": "eq",
"v": "OFF",
"vt": "str"
}
],
"checkall": "true",
"repair": false,
"outputs": 2,
"x": 210,
"y": 140,
"wires": [
[
"41c9a9ae.849118"
],
[
"853d6803.816448"
]
]
},
{
"id": "41c9a9ae.849118",
"type": "exec",
"z": "e5de4f7.bd044b",
"command": "irsend SEND_ONCE AIR on",
"addpay": false,
"append": "",
"useSpawn": "",
"timer": "5",
"oldrc": false,
"name": "AIR_on ",
"x": 360,
"y": 100,
"wires": [
[],
[],
[
"2cd16419.b2c07c"
]
]
},
{
"id": "853d6803.816448",
"type": "exec",
"z": "e5de4f7.bd044b",
"command": "irsend SEND_ONCE AIR off",
"addpay": false,
"append": "",
"useSpawn": "",
"timer": "5",
"oldrc": false,
"name": "AIR_off",
"x": 360,
"y": 180,
"wires": [
[],
[],
[
"b70a29c9.1df7e8"
]
]
},
{
"id": "1c821c08.b3f094",
"type": "mqtt in",
"z": "e5de4f7.bd044b",
"name": "",
"topic": "",
"qos": "0",
"broker": "f6b7453d.c2a968",
"x": 90,
"y": 140,
"wires": [
[
"c2e2e67c.0eb118"
]
]
},
{
"id": "2cd16419.b2c07c",
"type": "switch",
"z": "e5de4f7.bd044b",
"name": "error",
"property": "payload.code",
"propertyType": "msg",
"rules": [
{
"t": "eq",
"v": "0",
"vt": "num"
},
{
"t": "neq",
"v": "0",
"vt": "num"
}
],
"checkall": "true",
"repair": false,
"outputs": 2,
"x": 490,
"y": 100,
"wires": [
[
"1c1b151b.96394b"
],
[
"6dec0c3d.e2aa34"
]
]
},
{
"id": "b70a29c9.1df7e8",
"type": "switch",
"z": "e5de4f7.bd044b",
"name": "error",
"property": "payload.code",
"propertyType": "msg",
"rules": [
{
"t": "eq",
"v": "0",
"vt": "num"
},
{
"t": "neq",
"v": "0",
"vt": "num"
}
],
"checkall": "true",
"outputs": 2,
"x": 490,
"y": 180,
"wires": [
[
"3c7f5948.87ea06"
],
[
"66026380.b53dcc"
]
]
},
{
"id": "1c1b151b.96394b",
"type": "template",
"z": "e5de4f7.bd044b",
"name": "",
"field": "payload",
"fieldType": "msg",
"format": "handlebars",
"syntax": "mustache",
"template": "ON",
"x": 630,
"y": 60,
"wires": [
[
"66471905.764468"
]
]
},
{
"id": "3c7f5948.87ea06",
"type": "template",
"z": "e5de4f7.bd044b",
"name": "",
"field": "payload",
"fieldType": "msg",
"format": "handlebars",
"syntax": "mustache",
"template": "OFF",
"x": 630,
"y": 160,
"wires": [
[
"143a4a17.b41a96"
]
]
},
{
"id": "e74a2173.54aec",
"type": "mqtt out",
"z": "e5de4f7.bd044b",
"name": "",
"topic": "",
"qos": "2",
"retain": "true",
"broker": "f6b7453d.c2a968",
"x": 890,
"y": 120,
"wires": []
},
{
"id": "1a0f4483.07de2b",
"type": "link in",
"z": "e5de4f7.bd044b",
"name": "",
"links": [
"143a4a17.b41a96",
"66026380.b53dcc",
"66471905.764468",
"6dec0c3d.e2aa34"
],
"x": 795,
"y": 120,
"wires": [
[
"e74a2173.54aec"
]
]
},
{
"id": "66471905.764468",
"type": "link out",
"z": "e5de4f7.bd044b",
"name": "",
"links": [
"1a0f4483.07de2b"
],
"x": 715,
"y": 60,
"wires": []
},
{
"id": "6dec0c3d.e2aa34",
"type": "link out",
"z": "e5de4f7.bd044b",
"name": "",
"links": [
"1a0f4483.07de2b"
],
"x": 715,
"y": 100,
"wires": []
},
{
"id": "143a4a17.b41a96",
"type": "link out",
"z": "e5de4f7.bd044b",
"name": "",
"links": [
"1a0f4483.07de2b"
],
"x": 715,
"y": 160,
"wires": []
},
{
"id": "66026380.b53dcc",
"type": "link out",
"z": "e5de4f7.bd044b",
"name": "",
"links": [
"1a0f4483.07de2b"
],
"x": 715,
"y": 200,
"wires": []
},
{
"id": "f6b7453d.c2a968",
"type": "mqtt-broker",
"z": "",
"broker": "iot.eclipse.org",
"port": "1883",
"clientid": "",
"usetls": false,
"compatmode": true,
"keepalive": "60",
"cleansession": true,
"birthTopic": "",
"birthQos": "0",
"birthPayload": "",
"willTopic": "",
"willQos": "0",
"willPayload": ""
}
]
MQTT入力・出力のトピックは自分で他の人と被らなそうなトピック名をつけてください。(名前+日時
とか)
デプロイ
デプロイボタンの右にある矢印を押して出てくるメニューからExport to Other Services
を選択してAWS IoT
経由でラズパイにフローをデプロイしましょう!
デプロイが完了したら、以下のようなフローを別に作ってエアコンがON/OFFするか、"ON "OFF"テキストが返ってくるかテストしてみるといいでしょう。
(先ほど設定したMQTTトピックとトピック名を合わせるようにすること)
「Controller_heroku」 フロー
今回は、WebアプリのUI作成にnode-red-dashboardを使いたいので、enebularのメニューからパレットの管理
を開いて、ノードを追加
でnode-red-dashboard
を検索・追加します。
やってることはフローの形からなんとなくわかると思います。
フローのソースは以下の通り。
[
{
"id": "b5570c93.3f03f",
"type": "ui_button",
"z": "b2efaedb.75299",
"name": "ON",
"group": "720e73f5.43930c",
"order": 2,
"width": 0,
"height": 0,
"passthru": false,
"label": "ON",
"color": "",
"bgcolor": "red",
"icon": "",
"payload": "ON",
"payloadType": "str",
"topic": "",
"x": 90,
"y": 140,
"wires": [
[
"3ca1dc71.6ffbd4"
]
]
},
{
"id": "6ab44bc.2e6a5b4",
"type": "ui_text",
"z": "b2efaedb.75299",
"group": "720e73f5.43930c",
"order": 1,
"width": 0,
"height": 0,
"name": "",
"label": "現在の状態:",
"format": "{{msg.payload}}",
"layout": "row-center",
"x": 360,
"y": 80,
"wires": []
},
{
"id": "8cc4529a.c0c0d",
"type": "ui_button",
"z": "b2efaedb.75299",
"name": "OFF",
"group": "720e73f5.43930c",
"order": 0,
"width": 0,
"height": 0,
"passthru": false,
"label": "OFF",
"color": "",
"bgcolor": "",
"icon": "",
"payload": "OFF",
"payloadType": "str",
"topic": "",
"x": 90,
"y": 220,
"wires": [
[
"3ca1dc71.6ffbd4"
]
]
},
{
"id": "3ca1dc71.6ffbd4",
"type": "mqtt out",
"z": "b2efaedb.75299",
"name": "",
"topic": "",
"qos": "0",
"retain": "true",
"broker": "76ac36e8.8ca7c8",
"x": 330,
"y": 180,
"wires": []
},
{
"id": "939da3fa.9c869",
"type": "mqtt in",
"z": "b2efaedb.75299",
"name": "",
"topic": "",
"qos": "2",
"broker": "76ac36e8.8ca7c8",
"x": 90,
"y": 80,
"wires": [
[
"6ab44bc.2e6a5b4"
]
]
},
{
"id": "720e73f5.43930c",
"type": "ui_group",
"z": "",
"name": "Default",
"tab": "4a68af23.3436b",
"disp": true,
"width": "6",
"collapse": false
},
{
"id": "76ac36e8.8ca7c8",
"type": "mqtt-broker",
"z": "",
"name": "",
"broker": "iot.eclipse.org",
"port": "1883",
"clientid": "",
"usetls": false,
"compatmode": true,
"keepalive": "60",
"cleansession": true,
"birthTopic": "",
"birthQos": "0",
"birthPayload": "",
"closeTopic": "",
"closeQos": "0",
"closePayload": "",
"willTopic": "",
"willQos": "0",
"willPayload": ""
},
{
"id": "4a68af23.3436b",
"type": "ui_tab",
"z": "",
"name": "Home",
"icon": "dashboard"
}
]
Herokuにデプロイ
下記の公式ドキュメント通りの手順を踏むと、作成したフローをHerokuにDeployできます!
Heroku へのデプロイ
https://docs.enebular.com/ja/Deploy/DeployFlow/Heroku/
https://[Herokuアプリ名].herokuapp.com/ui
にアクセスするとエアコン操作画面が出てきます。
ついに完成・・・!
enebularの凄さ
今回は赤外線を扱うために回路作りやモジュールの設定など事前準備があったため、前半やることがみっちりでしたが、enebularでの開発に入ってからはいたってスムーズでした。
エッジデバイス(回路含め)さえ作成・設置すれば、あとは実物が手元になくてもブラウザ1つで開発・反映・確認ができるので、自分の好きな時に好きな場所から修正・検証できます。
また、今回のようにRaspberryPi
, Heroku
と、複数の環境をまたいで開発する場合でも、各環境用のコードがバラバラにならず、プロジェクト
としてenebular上で一括管理できるのも嬉しいポイントです。これによって共有や共同開発も簡単にできます。
実際にenebular中心の開発をしてみることで、**「あらゆるデバイスとクラウドサービスを簡単に『つなぐ』、IoTオーケストレーション・サービス」**と銘打っている理由がなんとなくわかった気がします。
おわりに
私は集合住宅に住んでいて、ポート開放やグローバルIPの取得ができないので、今まで家にラズパイを置いたまま外部から遠隔開発するのは絶望的だったのですが、AWSIoTとラズパイを連携したらグローバルIPがなくても遠隔デプロイできたのが地味に嬉しかったです。
ちょっと長い記事になってしまいましたが最後まで読んでいただきありがとうございました!
みなさん、良きenebularライフをー!