takustaquのアイコン画像
takustaqu 2021年05月16日作成
製作品 製作品 閲覧数 451
takustaqu 2021年05月16日作成 製作品 製作品 閲覧数 451

ライブ配信用の警告・通知ランプを作ってみた

ストーリー

前々から興味こそあったObnizのコンテストがあるとのことなので、
当初トイレの蓋の閉め忘れを強制的に閉めて通知するデバイスをつくろとしたものの、
それ以外の家庭の課題が出てしまったので、そっちの解決にピボットしました。

我が家は嫁さんが所謂「VTuber」というものをやっており、本人の撮影部屋や
リビングで配信を行っています。(ちなみにこれです。日本以外でちょっと人気です)

その際問題なのは、リビングで普通にゲームをやってるのかと思いきや配信中だったり、
配信中なのはわかっているものの、配信上の問題があって教えてあげたいものの
邪魔にならない方法で伝えようにもなかなか難しかったりと色々問題がありました。

  • 音声が出ていないよ!
  • スパチャ貰ってるけど拾えてないよ!
  • (主に私に対して)配信中だから静かに!

といった状態を簡単に示すサインがあればいいな、と思い今回作ってみました。

それがこれ。

カンペシグナル

カンペシグナル!

デモ動画

こんな感じで使えます。遅延なく切り替わるし、薄く作れたのでモニタのフチだったり、ドアに貼り付けたりと使い所を選ばない感じにできました。

ここに動画が表示されます

部品

さて、恐ろしくお手軽に作ってしまったので、部品らしいものがNeoPixelとObnizBoardぐらいなんですが....一応ちゃんと書き出すと以下の通りです。

  • NeoPixelテープ (10個付きのもの) 1点
  • プラ段(黒)
  • スプレー糊
  • ケーブルとピン(3本)
  • obniz Board 1Y

我が家ではObniz board 1Y以外はご家庭にあるものでございました。

作り方

筐体

光を拡散させるために白アクリルだったりいろいろ検討したもののの、薄く軽く作りたかったので
シンプルにプラ段に穴あけをし、発光部以外を黒塗りした紙をスプレー糊で貼ります。
私は手元のレーザプリンタで出力しましたが、意外とNeoPixelは輝度が高くて黒い箇所でも光が透けるため透ける部分は裏からマジックで塗っています。

プラ段にシワがよらないように貼ったら、LCD部分にカッターで穴あけして、裏側からはObnizの
厚み分少し前に出すためにプラ段を削っています。

カットして部品をはめた状態

こんな感じのカットガイドを作りました

個人的には手軽に結構子綺麗にできたんでないかな、と思ってます。
厚みも1cm程度なのでとてもスリムです。

仕上がり

回路

シンプルに、NeoPixelのテープをDI, + , - にそれぞれ接続するのみです。のちに出てくるソースコードにもあるように、ピンアサインはそれぞれ2、1、0番ピンにアサインしています。
10連のNeoPixelを発光させるにあたっては特段電源を別に確保する必要もないようです。
結線図を書くとしたらこうでしょうか。

キャプションを入力できます

うん、簡単。感動。

ソースコード

Slack連携とか、YoutubeAPI拾って勝手に発光、みたいなのとかやはり考えてはしまうものの、
今回はシンプルにHTML1枚でまずは済ませることにしました。
我が家には放送用の便利ツールをいろいろHTMLで作ってWebサーバに置いてあり、そこに置いておく運用イメージです。

<html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <script src="https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.js"></script> <script src="https://unpkg.com/obniz@3.14.0/obniz.js" crossorigin="anonymous" ></script> <style> body,html { background-color: #333; color:#fff; font-family:sans-serif; } .color-controls { margin:0; padding:0; display:flex; flex-wrap:wrap; color:#333 } .color-controls > li { list-style-type:none; width:calc(100%/3 - 1em); max-width:200px; margin:0.5em; box-sizing:border-box; } .color-controls >li button { width:100%; position: relative; padding-top:100%; overflow:hidden; border-radius:1em; border:5px solid rgba(0,0,0,0.2); box-shadow:0 0.5em 0 rgba(0,0,0,0.1); } .color-controls >li button div { display: flex; flex-direction: column; justify-content: center; align-items: center; width:100%; height:100%; position: absolute; left:0; top:0; text-shadow: rgb(255, 255, 255) 3px 0px 0px, rgb(255, 255, 255) 2.83487px 0.981584px 0px, rgb(255, 255, 255) 2.35766px 1.85511px 0px, rgb(255, 255, 255) 1.62091px 2.52441px 0px, rgb(255, 255, 255) 0.705713px 2.91581px 0px, rgb(255, 255, 255) -0.287171px 2.98622px 0px, rgb(255, 255, 255) -1.24844px 2.72789px 0px, rgb(255, 255, 255) -2.07227px 2.16926px 0px, rgb(255, 255, 255) -2.66798px 1.37182px 0px, rgb(255, 255, 255) -2.96998px 0.42336px 0px, rgb(255, 255, 255) -2.94502px -0.571704px 0px, rgb(255, 255, 255) -2.59586px -1.50383px 0px, rgb(255, 255, 255) -1.96093px -2.27041px 0px, rgb(255, 255, 255) -1.11013px -2.78704px 0px, rgb(255, 255, 255) -0.137119px -2.99686px 0px, rgb(255, 255, 255) 0.850987px -2.87677px 0px, rgb(255, 255, 255) 1.74541px -2.43999px 0px, rgb(255, 255, 255) 2.44769px -1.73459px 0px, rgb(255, 255, 255) 2.88051px -0.838247px 0px; font-weight:bold; font-size:1.5em; } </style> </head> <body> <h1>カンペシグナル操作パネル</h1> <div id="app"> <ul class="color-controls"> <template v-for="control in controls"> <li> <button v-bind:style="{background: createBackgroundString(control.colors)}" @click="setStatus(control)"> <div> <span> {{control.label}} </span> </div> </button> </li> </template> </ul> </div> <script> var obniz = new Obniz("7051-4334"); var T = {}; obniz.onconnect = async function () { obniz.display.clear(); // ここでパーツライブラリのWS2812Bを呼び出し、ピンアサインを設定。 const led = obniz.wired("WS2812B", {gnd:0,vcc:1,din: 2}); const led_count = 10; let timer = null;  // メッセージ表示だったりLED制御したりをざっくり関数化しておきます T = { message : (msg) => { obniz.display.clear(); obniz.display.pos(4,8); obniz.display.font('sans-serif',40) obniz.display.print(msg); }, setAllLED : (colorArr) => { let led_arr = new Array(led_count); for(i=0; i< led_count; i++){ led_arr[i] = colorArr; } led.rgbs(led_arr); }, setHalfLED : (colorA,colorB) => { let led_arr = new Array(led_count); for(i=0; i< led_count; i++){ led_arr[i] = i < led_count*0.5 ? colorB : colorA; } led.rgbs(led_arr); }, ledOFF : () => { T.setAllLED([0,0,0]) }, killTimer : () => { clearTimeout(timer); }, setLEDFlash : (colorA,colorB,interval,isHalf) => { let i = 0; var setColor = () => { if(i%2){ if(isHalf){ T.setHalfLED(colorA,colorB); } else { T.setAllLED(colorA,colorB); } } else { if(isHalf){ T.setHalfLED(colorB,colorA); } else { T.setAllLED(colorB); } } timer = setTimeout(function(){ setColor(); },interval); i++; }; setColor(); } } }; // ここからはUI用のVueのコード。 new Vue({ el:"#app", methods: { createBackgroundString(colors) { let tmpColors = colors.length == 0 ? [[0,0,0],[0,0,0]] : colors.length == 1 ? [colors[0] , colors[0]] : colors; return `linear-gradient(146deg, rgb(${tmpColors[0][0]},${tmpColors[0][1]},${tmpColors[0][2]}) 50%,rgb(${tmpColors[1][0]},${tmpColors[1][1]},${tmpColors[1][2]}) 50%)`; }, setStatus (control){ T.killTimer(); T.message(!!control.label ? control.label : ""); let tmpInterval = control.interval ? control.interval : 1000; if(!control.style || control.style == "default" || control.style == "" ){ T.setAllLED(control.colors[0]); }else if(control.style == "strobe"){ T.setLEDFlash(control.colors[0],control.colors[1],tmpInterval,false); }else if(control.style == "strobeHalf"){ T.setLEDFlash(control.colors[0],control.colors[1],tmpInterval,true); }if(control.style == "off" ){ T.setAllLED([0,0,0]); } } }, data(){ return { controls : [ // ボタンとカラー定義の追加編集する場合はここをイジイジします。 {label : "休止中", colors : [] , style : "off" }, {label : "準備中", colors : [[255,255,255],[100,100,100]] , style:"strobe" , interval : 1000 }, {label : "要注意", colors : [[255,0,0],[0,0,255]] , style:"strobeHalf" , interval : 500 }, {label : "SC有", colors : [[255,255,0],[255,125,0]] , style:"strobeHalf" , interval: 300}, {label : "MS有", colors : [[0,255,0],[0,255,100]] , style:"strobeHalf" , interval: 300}, {label : "放送中", colors : [[255,0,0]] , style : "default" , interval : null}, {label : "無音!", colors : [[255,0,255],[0,0,255]] , style : "strobeHalf" }, {label : "休憩中", colors : [[0,0,255],[0,0,255]] , style : "default" }, {label : "5分前", colors : [[255,0,140]] , style : "default" }, ] } } }) </script> </body> </html>

感想

RaspberryPiで百葉箱的な物を作ったりやらはしているので、Obnizの存在は
興味こそあったものの、いざ触ってみると想像していた以上に衝撃的なボードでした。
今回ちょっと見た目に地味なものを作ってしまった感はあるのですが、
このようなちょっとした「これほしい」をすぐに手軽に実装させてくれるのは、
ちょうどいい敷居の高さだなって思います。

実際、これをRPiで作ろうと思ったらこんなもんじゃ済まないですからね。
レゴブロックとArduinoの間ぐらいをとってくれてる、というか、うん。
何にしても惚れ込みました。手持ちのESP32+ObnizOSとかも試したいですね。

ログインしてコメントを投稿する