IoTって面白い
この度、obnizというIoT機器が手に入ったので色々触ってみました。
これまで自分でコードを書いたりしても画面の中で動作するのを見るのみだったのですが、IoTは現実世界で自分の作ったものが動作しているのを見ることができるので、その点が本当に面白いなと感じました。
推しを動かしてみたい
obnizには色々なセンサーやモーター、LEDといったパーツがありますが、これらの触り方を一通り知って真っ先に思ったことがあります。
「推しを動かしてみたい」
…はい。何を言っているか分からない方もいらっしゃると思いますが、obnizを使えば光と音と動きを簡単に制御できる、ということは推しキャラクターのフィギュアやアクリルスタント(以下、アクスタ)を使ってライブを再現できるのではないかと思い至ったわけです。
実際にやってみた
思い立ったら後は行動するのみ。という訳で実際に作ったものが以下になります。
「ライブを再現」ということで、ペンライトの光を照度センサーで捉えて動き出す仕組みにしています。
メロディーも作りこんで、照明もこのキャラのイメージカラーである青にして、見事に(?)ライブを再現することができました!
それでは、以下どのように制作したか書いていきたいと思います。
環境
obniz
Node-RED(Heroku環境上で実装)
JavaScript
obniz構成
obnizの組み方、使用したパーツについては以下をご覧ください。
使用したパーツ
- obniz Board 1Y
- 照度センサ(CdS) * 1
- サーボモーター * 1
- スピーカー * 1
- LED(青) * 2
渡辺曜アクスタ(Hop? Stop? Nonstop! ver)
Node-RED
今回は、以下のようなフローを作成しました。
フローをJSON形式で出力したものは以下になります。
[{"id":"47059c7b.003b54","type":"tab","label":"曜ちゃんライブ","disabled":false,"info":""},{"id":"28c3949b.90707c","type":"obniz-repeat","z":"47059c7b.003b54","obniz":"ac5898c.7d1c768","name":"動作開始","interval":"100","code":"//照度センサーからのインプット\nvar voltage = await obniz.ad1.getWait();\nmsg.payload = voltage;\n\nreturn msg;","x":120,"y":80,"wires":[["7ebb593a.3048b8"]]},{"id":"7ebb593a.3048b8","type":"obniz-function","z":"47059c7b.003b54","obniz":"ac5898c.7d1c768","name":"メイン動作","code":"//角度を保持する変数\r\nvar degrees = context.get('degrees')||70;\r\n//メロディーの演奏に使用するカウント\r\nvar count = context.get('count')||0;\r\n//照度\r\nvar voltage = msg.payload\r\n\r\n//メロディーの配列(周波数)\r\nconst melody = [1046.502,\r\n 1046.502,1046.502,932.328,932.328,\r\n 783.991,783.991,698.456,622.254,\r\n 698.456,698.456,783.991,783.991,\r\n 830.609,830.609,783.991,783.991,\r\n 698.456,698.456,783.991,783.991,\r\n 830.609,830.609,783.991,783.991,\r\n 698.456,698.456,783.991,783.991,\r\n 830.609,830.609,932.328,932.328,\r\n 1046.502,1046.502,1046.502,622.254,\r\n 622.254,622.254,0,622.254,\r\n 587.330,587.330,1046.502,1046.502,\r\n 932.328,932.328,783.991,783.991,\r\n 783.991,698.456,0,783.991,\r\n 698.456,0,783.991,698.456,\r\n 0,1046.502,1046.502,932.328,\r\n 1046.502,1046.502,932.328,0,\r\n 0,0,0,0,\r\n 0,0,0,0,\r\n 0,0,0,0,\r\n 0,0,0,0,\r\n 0,0,0,0,\r\n 0,0,0,0]\r\n\r\n//obnizの画面に照度を表示\r\nobniz.display.clear();\r\nobniz.display.print(voltage);\r\n\r\n//照度が0.03以上の場合にメイン動作開始\r\nif (voltage > 0.03) {\r\n //LED点灯\r\n obnizParts.led1.on();\r\n obnizParts.led2.on();\r\n //サーボモーターの角度制御\r\n if (degrees === 70) {\r\n degrees = 110.0;\r\n } else {\r\n degrees = 70.0;\r\n }\r\n //スピーカーでメロディ演奏開始\r\n obnizParts.speaker.play(melody[count]);\r\n context.set('count',count + 1);\r\n //サーボモーター動作\r\n obnizParts.servo.angle(degrees);\r\n context.set('degrees',degrees);\r\n} else {\r\n //動作停止\r\n obnizParts.led1.off();\r\n obnizParts.led2.off();\r\n obnizParts.speaker.stop();\r\n}\r\n\r\n//メロディの演奏終了判定\r\nif (count > 64) {\r\n msg.flg = true\r\n obnizParts.led1.off();\r\n obnizParts.led2.off();\r\n obnizParts.speaker.stop();\r\n} else {\r\n msg.flg = false\r\n}\r\n\r\nreturn msg;","x":290,"y":80,"wires":[["52b4dae5.f040c4"]]},{"id":"f5c06e2e.1fb14","type":"debug","z":"47059c7b.003b54","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":370,"y":160,"wires":[]},{"id":"89abe67f.d502b8","type":"obniz-function","z":"47059c7b.003b54","obniz":"ac5898c.7d1c768","name":"動作終了","code":"//obniz動作停止\nawait obniz.wait(1000); \nobniz.close();\n\nreturn msg;","x":360,"y":220,"wires":[["bcaccb68.768c78"]]},{"id":"bcaccb68.768c78","type":"debug","z":"47059c7b.003b54","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":530,"y":220,"wires":[]},{"id":"52b4dae5.f040c4","type":"switch","z":"47059c7b.003b54","name":"動作終了判定","property":"flg","propertyType":"msg","rules":[{"t":"false"},{"t":"else"}],"checkall":"true","repair":false,"outputs":2,"x":180,"y":180,"wires":[["f5c06e2e.1fb14"],["89abe67f.d502b8"]]},{"id":"ac5898c.7d1c768","type":"obniz","obnizId":"XXXX-XXXX","deviceType":"obnizboard1y","name":"","accessToken":"","code":"obniz.display.clear(); // 画面を消去\r\nobnizParts.led1 = obniz.wired('LED', { anode:3, cathode:4 });\r\nobniz.io0.output(true); //io0を5vに\r\nobniz.io2.output(false); //io2をGNDに\r\nobnizParts.speaker = obniz.wired(\"Speaker\",{ signal:5, gnd:6 });\r\nobnizParts.led2 = obniz.wired('LED', { anode:7, cathode:8 });\r\nobnizParts.servo = obniz.wired(\"ServoMotor\",{ gnd:9, vcc:10, signal:11 });"}]
フローの中身、ソースコード
では、ここからフローの中身について順番に解説していきます。
obniz初期化
まずはobnizの初期化コードから。各パーツについて初期化していきます。
obniz.display.clear(); // 画面を消去
obniz.io0.output(true); //io0を5vに
obniz.io2.output(false); //io2をGNDに
obnizParts.led1 = obniz.wired('LED', { anode:3, cathode:4 });
obnizParts.speaker = obniz.wired("Speaker",{ signal:5, gnd:6 });
obnizParts.led2 = obniz.wired('LED', { anode:7, cathode:8 });
obnizParts.servo = obniz.wired("ServoMotor",{ gnd:9, vcc:10, signal:11 });
動作開始
obniz-repeatノードで照度センサーのインプットを受け取ります。
//照度センサーからのインプット
var voltage = await obniz.ad1.getWait();
msg.payload = voltage;
return msg;
なお、今回実行間隔は100msに設定しています。
メイン動作
モーターの動作、スピーカーのメロディー再生、LEDの点灯といったメインの動作を制御しているノードです。
//角度を保持する変数
var degrees = context.get('degrees')||70;
//メロディーの演奏に使用するカウント
var count = context.get('count')||0;
//照度
var voltage = msg.payload
//メロディーの配列(周波数)
const melody = [1046.502,
1046.502,1046.502,932.328,932.328,
783.991,783.991,698.456,622.254,
698.456,698.456,783.991,783.991,
830.609,830.609,783.991,783.991,
698.456,698.456,783.991,783.991,
830.609,830.609,783.991,783.991,
698.456,698.456,783.991,783.991,
830.609,830.609,932.328,932.328,
1046.502,1046.502,1046.502,622.254,
622.254,622.254,0,622.254,
587.330,587.330,1046.502,1046.502,
932.328,932.328,783.991,783.991,
783.991,698.456,0,783.991,
698.456,0,783.991,698.456,
0,1046.502,1046.502,932.328,
1046.502,1046.502,932.328,0,
0,0,0,0,
0,0,0,0,
0,0,0,0,
0,0,0,0,
0,0,0,0,
0,0,0,0]
//obnizの画面に照度を表示
obniz.display.clear();
obniz.display.print(voltage);
//照度が0.03以上の場合にメイン動作開始
if (voltage > 0.03) {
//LED点灯
obnizParts.led1.on();
obnizParts.led2.on();
//サーボモーターの角度制御
if (degrees === 70) {
degrees = 110.0;
} else {
degrees = 70.0;
}
//スピーカーでメロディ演奏開始
obnizParts.speaker.play(melody[count]);
context.set('count',count + 1);
//サーボモーター動作
obnizParts.servo.angle(degrees);
context.set('degrees',degrees);
} else {
//動作停止
obnizParts.led1.off();
obnizParts.led2.off();
obnizParts.speaker.stop();
}
//メロディの演奏終了判定
if (count > 64) {
msg.flg = true
obnizParts.led1.off();
obnizParts.led2.off();
obnizParts.speaker.stop();
} else {
msg.flg = false
}
return msg;
メロディーの再生については以下の記事を参考にしました。
Node-REDではどうも一度のプログラム実行でスピーカーの再生を順番に実行するということができないようなので、メロディーの各音を予め配列に格納し、一度のプログラム実行で一音ずつ再生するという手法をとりました。
(恐らくobniz-repeatノードが設定した間隔で動作し、その度にプログラムが実行されることと関係していると推測しています。この辺り、うまく非同期的にプログラム実行する方法があるのかも知れませんが、現時点ではそこまで理解が及んでいないので力業で何とかしています。)
メロディーの最後に周波数"0"(=無音)を入れているのも、それを入れないとプログラムの実行タイミング的にこの後説明するobnizの動作終了処理が先に動いてしまって、メロディーの途中で動作が終わってしまうための処置になります。
なお、メロディーはアクスタの衣装に合わせて「Hop? Stop? Nonstop!」のサビ部分にしています!
動作終了判定
メイン動作ノードで設定したフラグを判定します。
フラグが立っていなかったら特に何もせず動作を継続します。
動作終了
動作終了と判定された場合は、obnizの動作を停止させて終了です。
//obniz動作停止
await obniz.wait(1000);
obniz.close();
return msg;
あとがき
趣味全開な開発でしたが、やっぱりこういうのが一番楽しいですね。
特にメロディーのところの制御が色々工夫を求められてかなり苦労しましたが、完成した時はとても嬉しかったです!
今回作ったものが何かの役に立つとは正直あまり思えないのですが、、、obnizの動かし方について学べたので、今後も色々触ってみたいなと思います。
以上、ここまでお読みいただきありがとうございました!