【obniz】推される側になりたくて熱烈なファンを自作してみた

# 推し活、してますか？
私には推しがたくさんいる。
推しを応援することで、私も日々元気をもらっている。
・・・それで充分だったのに、欲が出てしまった。
**私も誰かに推されてみたい。**

# 野望
いつか、私のパフォーマンスで誰かに感動を与えたい。
**その第一歩として「ファン１号」を自作し、来たる日のためにイメトレしておきたい。**
ファン１号には、以下の役割を担ってもらう。

:::note info
【目標】
　■ うちわを振り、熱烈に応援してほしい。
　■ 黄色い声援を送り、盛り上げてほしい。
　■ ペンライトで、会場をカラフルに彩ってほしい。
:::

# 技術・環境
Node-RED
obniz＋関連部品（後述）

# つくりかた
## ■ 使った部品
![obniz_parts_no.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3563752/1ca85eb1-5408-0042-1c6d-abcbbf0f2019.jpeg)

:::note info
【obniz＋関連部品】
　① obniz
　② ブレッドボード
　③ サーボモータ（腕：うちわを左右に振る）
　④ スピーカー（黄色い声援：うちわを振るたびに異なる音を発する）
　⑤ フルカラーLED（ペンライト：うちわを振るたびに赤・緑・青に点灯する）
　※各部品をつないでいる線は『ジャンパーワイヤ』  
:::

## ■ Node-RED
フローのイメージとJSONコードは以下のとおり。
![Node-RED_flow_JPEG.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3563752/3ede16b8-e1e3-142e-654b-55daa4468bcf.jpeg)

obnizIdは固有情報のため、「xxxx-xxxx」に置換。
```JSON
[{"id":"cd6509d1adb32410","type":"tab","label":"フロー 1","disabled":false,"info":"","env":[]},{"id":"6d59d686cd85715e","type":"switch","z":"cd6509d1adb32410","name":"０：左　1：中　2：右　","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"left","vt":"str"},{"t":"else"},{"t":"eq","v":"right","vt":"str"}],"checkall":"true","repair":false,"outputs":3,"x":340,"y":100,"wires":[["d2cf9b4d.518638"],["4669063f2e182159"],["14c9cc8861912483"]]},{"id":"fd4d9924.ded5e8","type":"obniz-repeat","z":"cd6509d1adb32410","obniz":"a8916095c50b67ba","name":"","interval":"100","code":"msg.payload = await obniz.switch.getWait();\r\nreturn msg;","x":110,"y":100,"wires":[["6d59d686cd85715e"]]},{"id":"d2cf9b4d.518638","type":"obniz-function","z":"cd6509d1adb32410","obniz":"a8916095c50b67ba","name":"左：赤","code":"let degrees = context.get('degrees')||90; // 角度を保持する変数（無ければ初期化）\r\n\r\nobniz.display.clear(); // 画面を消去\r\n\r\n// 左にスイッチを倒したとき\r\n//サーボモータ\r\n degrees = 180.0;\r\n context.set('degrees',degrees);//現在の角度をコンテキストへ保存\r\n // ディスプレイに角度を表示\r\n obniz.display.print(`Current: ${degrees} deg`);\r\n // サーボを指定の角度まで動かす\r\n obnizParts.servo.angle(degrees);\r\n\r\n//LED：赤\r\nobnizParts.WS2811.rgb(255, 0, 0);\r\n\r\n//スピーカー\r\nobnizParts.Speaker.play(1000);","x":590,"y":100,"wires":[["698a24e126b41b50"]]},{"id":"4669063f2e182159","type":"obniz-function","z":"cd6509d1adb32410","obniz":"a8916095c50b67ba","name":"中：緑","code":"let degrees = context.get('degrees')||90; // 角度を保持する変数（無ければ初期化）\r\n\r\nobniz.display.clear(); // 画面を消去\r\n\r\n// スイッチが押されていない状態\r\n//サーボモータ\r\n degrees = 90.0;\r\n context.set('degrees',degrees);//現在の角度をコンテキストへ保存\r\n // ディスプレイに角度を表示\r\n obniz.display.print(`Current: ${degrees} deg`);\r\n // サーボを指定の角度まで動かす\r\n obnizParts.servo.angle(degrees);\r\n\r\n//LED:緑\r\nobnizParts.WS2811.rgb(0, 255, 0);\r\n\r\n//スピーカー\r\nobnizParts.Speaker.stop(); // 音をとめる","x":590,"y":200,"wires":[["698a24e126b41b50"]]},{"id":"14c9cc8861912483","type":"obniz-function","z":"cd6509d1adb32410","obniz":"a8916095c50b67ba","name":"右：青","code":"let degrees = context.get('degrees')||90; // 角度を保持する変数（無ければ初期化）\r\n\r\nobniz.display.clear(); // 画面を消去\r\n\r\n// 右にスイッチを倒したとき\r\n//サーボモータ\r\n degrees = 0.0;\r\n context.set('degrees',degrees);//現在の角度をコンテキストへ保存\r\n // ディスプレイに角度を表示\r\n obniz.display.print(`Current: ${degrees} deg`);\r\n // サーボを指定の角度まで動かす\r\n obnizParts.servo.angle(degrees);\r\n\r\n//LED：青\r\nobnizParts.WS2811.rgb(0, 0, 255);\r\n\r\n//スピーカー\r\nobnizParts.Speaker.play(3000);","x":590,"y":300,"wires":[["698a24e126b41b50"]]},{"id":"698a24e126b41b50","type":"debug","z":"cd6509d1adb32410","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":770,"y":100,"wires":[]},{"id":"a8916095c50b67ba","type":"obniz","obnizId":"xxxx-xxxx","deviceType":"obnizboard1y","name":"","accessToken":"","code":"// 画面を消去\r\nobniz.display.clear();\r\n\r\n//サーボモータ\r\nobnizParts.servo = obniz.wired(\"ServoMotor\", { gnd: 9, vcc: 10, signal: 11 });\r\n\r\n//LED\r\nobnizParts.WS2811 = obniz.wired('WS2811', { gnd: 0, vcc: 1, din: 2 });\r\n\r\n//スピーカー\r\nobnizParts.Speaker = obniz.wired(\"Speaker\", { signal: 5, gnd: 6 });"}]
```
* 「obniz repeat」ノードを設定し、obnizからのスイッチ押下ステータスを受取る
* 「switch」ノードにて、スイッチ押下ステータスを判定し、後続ノートに引継ぐ
* 各「function」ノードにて、サーボモータを指定の角度に動かす＆スピーカーを鳴らす＆LEDを点灯させる処理を行う（※1)

* 「msg.payload」ノードにて、デバッガにメッセージを表示する（※2）

（※1)いろいろ試したが、サーボモータの「ハンチング」と呼ばれる、細かい振動を起こし停止せずにブルブル動き続ける挙動が解消できず。「ファンの興奮具合が表現できて逆にありかな」と受容。**これがのちに悲劇を生む。**

（※2)任意。ファン1号の挙動には無関係。

## ■ フュージョン
obniz＋関連部品に命を吹き込む。これでも図工の成績はずっと5だ。

![fusion.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3563752/51b07a46-6ab0-9a9f-aea0-5145a656e430.png)

## ■ ファン1号、爆誕
ついに完成した。
いい感じの背景も表示させ、部屋も暗くし、撮影した動画がこちら。

https://twitter.com/xe_ay51938/status/1720371529165521150

**フラグ立ってたもんね。**

# おわりに
obniz初体験、ちょっとしたハプニングもあったが、すべての目標を満たした「ファン1号」は完成した。いずれは、私のオリジナル曲を奏でさせたり、オタ芸を打てるようにパワーアップなどさせたい。

やりきることで成長し、推される側に近づくと信じています。初心者のみなさま、一緒に頑張っていきましょう。最後までお読みいただき、ありがとうございました！

# 参考
セリアのぬいぐるみ用うちわを購入、日常・ライブ用作成【素材配布】ネップリ再登録（8/20まで）
(https://tofu56.info/2022/06/19/7451/)

無配うちわの作り方【素材ダウンロードあり】
(https://tofu56.info/2017/08/11/2421/)

ぱくたそ
(https://www.pakutaso.com/)