obniz苦闘/「玄関開けたら競馬のゲート音鳴らしたかった」→「コーヒー飲んじゃダメアラート」


1.ドアの近くに来たらファンファーレ→ドアを開けたらゲート音鳴らしたい

昔仕事の社内ブレストでこの案出して、笑いだけ取って却下された案。
いやわかってたけども。却下されるって。
その時は鉄道会社がクライアントで、「(とある駅の)改札にゲート音つけましょうよ♪」という案を出した。会議が和んだ。だけだった。
あれから16年。
やっと日の目を見るのね・・・

出勤もゴミ出しも、すべてがオレのG1レース

出勤もゴミ出しもスーパーにネギ買いに行くのもコンビニにカップ麺買いにいくのもワクチン打ちに行くのも、すべてがG1レースになってしまう。
さあ行こう、武者震いと共に(近所のコンビニに)。
「さあ、今からオレのG1レースが始まる・・・」と、見慣れたその玄関で自分を震い立たせるがいい!

環境はこちら

Node version v17.6.0
npm 8.5.1

音楽を出すには一体どうしたら?

探したら、まさに私のやりたいことと似てることをやっている方を発見。
「obnizを使用して、ドアセンサーを作った開閉時音声再生クライアント編」

https://www.ccbaxy.xyz/blog/2019/02/17/obniz4/
ほー、どうやらnode-wav-playerというのをインストールしなきゃいけないようです。
npm i node-wav-player
をターミナルに入れて実行。入ったぽい。
新しいファイルを作って、一回この方の作ったプログラムをまんま真似、しようかと思ったけど、この方が使ってるのと一緒なのかな。
あなたはなあに?
わたしはNode.js。
あ、あなたもNode.jsね。
よかった。
const mqtt = require("mqtt");
const player = require("node-wav-player");

//wavファイル再生関数定義
const play = async (sound) => {
  await player
    .play({
      path: `${sound}.wav`,
      sync: true,
    })
    .then(() => {
      console.log(`played ${sound}.wav.`);
    })
    .catch((error) => {
      console.error(error);
    });
};

//mqttサーバー接続
var client = Mqtt.connect("ws://[サーバーアドレス]:[ポート]", {
  clientId: "[クライアントID]",
  username: "[クライアント名]",
  password: "[パスワード]",
});
//接続時処理
client.on("connect", function () {
  client.subscribe("[トピック名]", function (err) {
    if (!err) {
      console.log(`OK Subscribe`);
    }
  });
});

//ドアステータス
let state = "CLOSE";

//多重再生回避フラグ
let processing = false;

//メッセージ受信時処理
client.on("message", async (topic, message) => {
  if (!processing) {
    processing = true;

    let temp = JSON.parse(message.toString()).data.door;
    if (temp != state && temp == "OPEN") {
      await play("okaeri");
    }
    if (temp != state && temp == "CLOSE") {
      await play("korekara");
    }
    state = temp;
    processing = false;
  }
});

新キャラ「MQTTサーバー」

待って、//mqttサーバー接続って何・・・
調べたら、「TCP/IPネットワークで利用できる通信プロトコル」と。
いやホントやめて、新しい概念と単語を都度都度ぶっこんでくるの。
しかもさらに調べると、mqttサーバーについて「セキュリティに問題が」とか出てくる。
どうやら無料で使えるIBMが提供しているIoT用のサーバーらしい、ということまでは私でも理解できた。
だ、大丈夫なのかな、コレ・・・
(ここでいったん他の方法を探りながら詳しい人に相談する)
どうやらmqttサーバーを使うこと自体は問題なさそう。
じゃあやってみるか。
(やってみる)

そしてサーバーを使う設定をほぼ終えようとしたその時、私は気が付いた。
うち、スマートスピーカーない
じゃあどうやって音出力したらいいの???
(調べる)
なるほど・・・・・私のこのアイデアの一番の問題は、
「どう音を出力するか」
なんだ。

出力問題~我が家にはスマートスピーカーがない~

obnizのスピーカーは、「ブー」とかしか出ない。
ファンファーレを鳴らし、G1の開幕曲を鳴らすには、有効な出力(この方はスマートスピーカー)が必要。
私はBluetoothのスピーカーを持っているが、これに出力するためには、PCかスマホをいずれにしろ経由する必要がある。
(調べる)
そもそも普通のスピーカーで音楽出力かつ同じ条件(obniz board 1Y、Node.js)でやっている事例が見つからない上、相談するとすごい大がかり(今の私のレベルには)になってしまうことが判明。
wavファイルをNode.jsで再生するだけならできるらしい(下記の方の記事参照)けど、これだけでは私は今回はイヤなのです。
例:【Node.js 2021】 WAVファイルを Node.js で再生する(Mac で node-wav-player を利用する)

https://qiita.com/youtoy/items/ea270d180d17094807f5
const player = require('node-wav-player');
player.play({
  path: './【WAVファイルの名前】.wav',
}).then(() => {
  console.log('The wav file started to be played successfully.');
}).catch((error) => {
  console.error(error);
});

2.急遽方向転換「超音波センサー×スピーカーで、今日はコーヒー飲んだらダメよアラート」

私は今3日に一回ほど通院をしている。
とある病気になり、7,8つのこれまでやったことがない大がかりな検査をやっている最中だ。
検査によっては、食事制限や飲み物制限がある。
私はコーヒー中毒かつお水大好き女(お酒は全く飲めない)なのだが、
検査前はコップ一杯程度の水しか口にしてはいけない
という私にとって拷問のような決まりがちょいちょいある。

朝起きたらまずコーヒー二杯とお水を飲むのが習慣になっているので、ナチュラルにこれを忘れそうになる。しかも毎回。
パートナーに「私がコーヒー飲みそうになったら止めてね!」と言っても、彼もナチュラルに忘れる。
危険だ。

そこで「飲むな!」と知らせてくれるものがあったら便利だな、と、遊び心皆無の「今本当に必要な実装」にチャレンジすることにした。

コーヒー豆が入ってるBOXに近づいたらスピーカーをならすコード

const Obniz = require('obniz');
const obniz = new Obniz('×××××××××');

obniz.onconnect = async () => {
    // 超音波距離センサを利用
    const hcsr04 = obniz.wired('HC-SR04', {
        gnd: 3,
        echo: 2,
        trigger: 1,
        vcc: 0,
    });

    // スピーカーを利用
    const speaker = obniz.wired('Speaker', { signal: 9, gnd: 10 });

    // obnizディスプレイ(初期表示)
    obniz.display.clear();
    obniz.display.print('obniz Ready');

    // setIntervalで定期実行
    setInterval(async () => {
        // 距離を取得
        let distance = await hcsr04.measureWait();
        // 小数点以下がたくさんあるのでここでは整数にします
        distance = Math.floor(distance);
        // 距離をコンソールに表示
        console.log(distance + ' mm');
        // 距離をobnizディスプレイに表示
        obniz.display.clear();
        obniz.display.print(distance + ' mm');
        // 距離によって判定
        if (distance < 30.0) {
            // 30mm = 3cm 未満の場合 obnizディスプレイ表示
            obniz.display.clear();
            obniz.display.print('No coffee!!');
            // 200Hz で音を鳴らす
            speaker.play(200);

        } else {
            // 音を停止する
            speaker.stop();
        }
    }, 1000); // 1000ミリ秒 = 1秒ごとに実行
};

これでどうだ!!
私がコーヒー缶に3センチ以上近づくと、ブザーが鳴ってくれるはず。

超音波センサは0-3、スピーカーを9,10にして離したけど、超音波センサでかくてスピーカーがむぎゅってなってる。
さあ実行!

うそーなんで泣
なんでよー
でも表示はちゃんと出てる!
えらいぞ、obniz。
私がそもそも超音波センサーに近いのかな。
超音波センサを何もない宙に向け、さ、リトライ。
ブ―――――――♬
鳴りやがる・・・・・なんで。

鳴き続けるスピーカーの謎

ターミナルも異常なし。
表示も異常なし。
3cm設定が近すぎていけないのかな。
もっと遠くしてみるか。
「10cm以内に来たらブザー」
にif文を変更。

        // 距離によって判定
        if (distance < 1000.0) {
            // 1000mm = 10cm 未満の場合 obnizディスプレイ表示
            obniz.display.clear();
            obniz.display.print('No coffee!!');
            // 200Hz で音を鳴らす
            speaker.play(200);

さあこれでどうだ!!!

ブー-------------------♬

いやああああああああああ!!!

ついに答えにたどり着く

そして同じようなことをやっている人の記事をいくつも見ることおよそ一時間・・・
例えばこの人とか

https://qiita.com/Izumi0711/items/67beedef9df05dfa32c5
この人とか
https://protopedia.net/prototype/2649

そして私はいくつかの記事を見ていたら突然わかった。
超音波センサーを逆向きにつけていた!!
超音波センサーの裏?にgnd、echo、trigger、vccとあり、そこからシルバーのなんか針みたいなのが出てる。
これを刺す基盤の番号とコードに書いてる番号が合ってない!!!
つまり逆さにすればいいだけ・・・・
なにこのケアレス中のケアレスミス。
どゆこと。

そういや大昔に初めてTOEICのテスト受けた時も、前知識もなく、例題も過去問も一切見ずに身一つで挑み、(留学してたのに)まさかの400点台をたたき出した。
何事も準備万端で挑むしっかり者の姉にこのことを話し、「あんなに一日中やるテストだって知らなかった。疲れた。」と言ったら、「え!?テスト時間も知らないで受けたの!?」と、まるで異星人を見るような目つきで見られた。
失礼しちゃう。
そんなことを思い返しながら、粛々とobnizを逆さに付け直す。

・・・さ、逆にしましたよ。どうでしょ、obniz様。

そしてコードのif文も「5cm以内にきたら鳴る」、に戻す。
ああ、ここまでぐるっと回り道。長かった・・・・・

最終的なコードはこちら

const Obniz = require('obniz');
const obniz = new Obniz('×××××××××');

obniz.onconnect = async () => {
    // 超音波距離センサを利用
    // 向きが反転していることに注意
    const hcsr04 = obniz.wired('HC-SR04', {
        gnd: 3,
        echo: 2,
        trigger: 1,
        vcc: 0,
    });

    // LEDを利用
    const speaker = obniz.wired('Speaker', { signal: 9, gnd: 10 });

    // obnizディスプレイ(初期表示)
    obniz.display.clear();
    obniz.display.print('obniz Ready');

    // setIntervalで定期実行
    setInterval(async () => {
        // 距離を取得
        let distance = await hcsr04.measureWait();
        // 小数点以下がたくさんあるのでここでは整数にします
        distance = Math.floor(distance);
        // 距離をコンソールに表示
        console.log(distance + ' mm');
        // 距離をobnizディスプレイに表示
        obniz.display.clear();
        obniz.display.print(distance + ' mm');
        // 距離によって判定
        if (distance < 50.0) {
            // 50mm = 5cm 未満の場合 obnizディスプレイ表示
            obniz.display.clear();
            obniz.display.print('No coffee!!');
            // 200Hz で音を鳴らす
            speaker.play(200);

        } else {
            // 音を停止する
            speaker.stop();
        }
    }, 1000); // 1000ミリ秒 = 1秒ごとに実行
};

ターミナルにもこのように距離が出てきました。

357 mm
430 mm
3426 mm
397 mm
351 mm
432 mm
1346 mm
1315 mm
1367 mm
1304 mm

で、できた!
明日は15時の検査の4時間前からコーヒー飲んじゃダメなので、さっそく活躍してもらおう!!
おしまい!