obnizでわが子と一緒に「オリジナル目覚ましBot」を作って、実際に起きられるかどうかを試した結果

##今回の目的　～わが子に"オリジナル目覚まし"を作りたい！～
来年の今頃は、わが家の息子は小学校１年生。
今後のために、朝は１人で起きられるようになって欲しい！！
そんな思いから、先日学習したobnizで「**オリジナル目覚ましBot**」を作ってみよう！と考えました。

そもそも、わが家の息子は、朝がとっても苦手。
毎日、必死に起こすのが日課となっていますが、寝起きがとっても不機嫌です💦
本人いわく、「パパやママの声だと、ビックリするから嫌」なのだとか。
その後の支度もグダグダとなってしまうので、なんとか**笑顔で朝を迎えてくれたら**・・・と、模索しており、このあたりもBotで解消できればと思います。

##完成品（「わが子専用 目覚ましBot」）
完成品のご紹介です。
LINEBotからの合図とともに、obniz電圧スピーカーからメロディーが流れる仕様。
[前回の記事](https://qiita.com/kokano23/items/b34b7d0361b8aaa6ad1f)で作ったLINEBotを、一部アレンジしています。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">わが子専用「オリジナル目覚まし<br>Bot」が完成！😁<br>リズムが所々不自然ですが、個人的には苦労した部分なので、かえって愛着が湧いてます。笑<br>さて、わが子は起きられるようになるのでしょうか？🧐<a href="https://twitter.com/hashtag/obniz?src=hash&amp;ref_src=twsrc%5Etfw">#obniz</a> <a href="https://twitter.com/hashtag/LINEBot?src=hash&amp;ref_src=twsrc%5Etfw">#LINEBot</a> <a href="https://twitter.com/hashtag/%E5%AD%90%E3%81%A9%E3%82%82?src=hash&amp;ref_src=twsrc%5Etfw">#子ども</a><a href="https://twitter.com/hashtag/%E3%83%9D%E3%82%B1%E3%83%A2%E3%83%B3?src=hash&amp;ref_src=twsrc%5Etfw">#ポケモン</a> <a href="https://twitter.com/hashtag/%E3%82%81%E3%81%96%E3%81%BE%E3%81%97?src=hash&amp;ref_src=twsrc%5Etfw">#めざまし</a> <a href="https://twitter.com/hashtag/Javascript?src=hash&amp;ref_src=twsrc%5Etfw">#Javascript</a> <a href="https://t.co/ZPyClWlFi3">pic.twitter.com/ZPyClWlFi3</a></p>&mdash; くるみ (@Kokano23) <a href="https://twitter.com/Kokano23/status/1393805254341185540?ref_src=twsrc%5Etfw">May 16, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

- メロディーは、「めざせ ポケモンマスター」。
- **息子本人のモチベーションがなにより大事**になると思ったので、「どんな曲なら起きられそう？」と本人にヒアリングして一緒に決めました。
- 前回作ったピカチュウBotが好評で、「**今度は、ミュウに起こして欲しい！**」と要望もありました。幻のポケモン"ミュウ"が、今は一番好きなのだそう。
- そこで、今回もLINEのリッチメニューを活用しました。（ポケモンの画像は、[pokeAPI](https://pokeapi.co/)より取得）
- ミュウをタッチすると、テーマソングの再生とともに応援メッセージが届く仕組みです。実際は、わが子の名前も入れて、オリジナル感を演出しました。
（これを見れば、きっと笑顔になれる！）

##作り方
###①obnizの設定
準備したもの

- obniz Board 1Y
- 圧電スピーカー（圧電サウンダ）（13ｍｍ）PKM13EPYH4000-A0

事前設定や接続は、[obniz公式ドキュメント（起動とWi-Fi設定）](https://obniz.com/ja/doc/reference/board-1y/quick-start/)や[obniz公式ドキュメント（Speaker）](https://obniz.io/ja/sdk/parts/Speaker/README.md)を参考にしました。

###②LINEBotの準備
Botアカウントの準備は、前回と同様に、[こちらの記事](https://qiita.com/n0bisuke/items/ceaa09ef8898bee8369d)を参考にしています。

###③メロディーの設定
[音階周波数](https://www.tomari.org/main/java/oto.html)を参考にしました。
「めざせポケモンマスター」の楽譜から、メロディーをソースコードに組み込みました。
リズムが不自然になってしまったところがあり、手作り感が満載です。（テンポの調整など、何度も再生しながら試行錯誤しました。難しかったです。）

##ソースコード
<details><summary>ソースコード全文です。（セクション内参照）</summary><div>

```javascript
'use strict';

// obniz呼び出し
const Obniz = require('obniz');
const obniz = new Obniz("XXXXXXXXX");  // Obniz_ID に自分のIDを入れます

const express = require('express');
const line = require('@line/bot-sdk');
const PORT = process.env.PORT || 3000;

// Messaging APIで利用するクレデンシャル（秘匿情報）です。
const config = {
  channelSecret: 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
  channelAccessToken: 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
};

const app = express();

// HTTP POSTによって '/webhook' のパスにアクセスがあったら、POSTされた内容に応じて様々な処理をします
app.post('/webhook', line.middleware(config), (req, res) => {

  // 検証ボタンをクリックしたときに飛んできたWebhookを受信したときのみ以下のif文内を実行
  if (req.body.events.length === 0) {
    res.send('Hello LINE BOT! (HTTP POST)'); // LINEサーバーに返答します（なくてもよい）
    console.log('検証イベントを受信しました！'); // ターミナルに表示します
    return; // これより下は実行されません
  } else {
    // 通常のメッセージなど … Webhookの中身を確認用にターミナルに表示します
    console.log('受信しました:', req.body.events);
  }

  // あらかじめ宣言しておいた "handleEvent" 関数にWebhookの中身を渡して処理してもらい、
  // 関数から戻ってきたデータをそのままLINEサーバーに「レスポンス」として返します
  Promise.all(req.body.events.map(handleEvent)).then((result) => res.json(result));
});

const client = new line.Client(config);

// obniz接続
obniz.onconnect = async function () {
  obniz.display.clear();
  obniz.display.print("obniz meets LINE Bot!");
}

function handleEvent(event) {
  if (event.type !== 'message' || event.message.type !== 'text') {
    return Promise.resolve(null);
  }

  let mes = ''
  if(event.message.text === 'めざましかけて'){
    mes = 'すこしまってね';  //メッセージだけ先に処理
    getAskObnizSpeaker(event.source.userId);  //スクレイピング処理が終わったらプッシュメッセージ
  }else{
    mes = event.message.text;
  }

  return client.replyMessage(event.replyToken, {
    type: 'text',
    text: mes
  });
}

const getAskObnizSpeaker = async (userId) => {
 // スピーカーを呼び出す
 const speaker = obniz.wired("Speaker", {signal:0, gnd:1});
 console.log("Speaker")
 await client.pushMessage(userId, {
  type: 'text',
  text: "おはよう！きょうもがんばろう！",
});

 // 音階とhzの参考サイト https://tomari.org/main/java/oto.html
 // 「めざせポケモンマスター」
 await speaker.play(880.000); // A5 ラ
 await obniz.wait(800);
 await speaker.stop();
 await obniz.wait(80);
 

 await speaker.play(783.991); // G5 ソ
 await obniz.wait(150);
 await speaker.stop();
 await obniz.wait(60);
 await speaker.play(783.991); // G5 ソ
 await obniz.wait(150);
 await speaker.stop();
 await obniz.wait(60);
 await speaker.play(783.991); // G5 ソ
 await obniz.wait(150);
 await speaker.stop();
 await obniz.wait(60);
 await speaker.play(880.000); // A5 ラ
 await obniz.wait(150);
 await speaker.stop();
 await obniz.wait(60);
 await speaker.play(783.991); // G5 ソ
 await obniz.wait(1200);
 await speaker.stop();
 await obniz.wait(500);

 await speaker.play(783.991); // G5 ソ
 await obniz.wait(150);
 await speaker.stop();
 await obniz.wait(80);
 await speaker.play(880.000); // A5 ラ
 await obniz.wait(150);
 await speaker.stop();
 await obniz.wait(80);
 await speaker.play(880.000); // A5 ラ
 await obniz.wait(150);
 await speaker.stop();
 await obniz.wait(80);
 await speaker.play(880.000); // A5 ラ
 await obniz.wait(150);
 await speaker.stop();
 await obniz.wait(80);
 await speaker.play(880.000); // A5 ラ
 await obniz.wait(140);
 await speaker.stop();
 await obniz.wait(80);
 await speaker.play(783.991); // G5 ソ
 await obniz.wait(150);
 await speaker.stop();
 await obniz.wait(80);
 await speaker.play(783.991); // G5 ソ
 await obniz.wait(150);
 await speaker.stop();
 await obniz.wait(80);
 await speaker.play(880.000); // A5 ラ
 await obniz.wait(100);
 await speaker.stop();
 await obniz.wait(80);
 await speaker.play(783.991); // G5 ソ
 await obniz.wait(1200);
 await speaker.stop();
 await obniz.wait(600);

 await speaker.play(783.991); // G5 ソ
 await obniz.wait(150);
 await speaker.stop();
 await obniz.wait(60);
 await speaker.play(880.000); // A5 ラ
 await obniz.wait(150);
 await speaker.stop();
 await obniz.wait(60);
 await speaker.play(783.991); // G5 ソ
 await obniz.wait(150);
 await speaker.stop();
 await obniz.wait(60);
 await speaker.play(880.000); // A5 ラ
 await obniz.wait(150);
 await speaker.stop();
 await obniz.wait(60);
 await speaker.play(783.991); // G5 ソ
 await obniz.wait(900);
 await speaker.stop();
 await obniz.wait(300);

 await speaker.play(783.991); // G5 ソ
 await obniz.wait(150);
 await speaker.stop();
 await obniz.wait(60);
 await speaker.play(783.991); // G5 ソ
 await obniz.wait(150);
 await speaker.stop();
 await obniz.wait(60);
 await speaker.play(783.991); // G5 ソ
 await obniz.wait(140);
 await speaker.stop();
 await obniz.wait(60);
 await speaker.play(659.255); // E5 ミ
 await obniz.wait(150);
 await speaker.stop();
 await obniz.wait(80);
 await speaker.play(783.991); // G5 ソ
 await obniz.wait(900);
 await speaker.stop();
 await obniz.wait(300);

 await speaker.play(783.991); // G5 ソ
 await obniz.wait(150);
 await speaker.stop();
 await obniz.wait(60);
 await speaker.play(880.000); // A5 ラ
 await obniz.wait(140);
 await speaker.stop();
 await obniz.wait(60);
 await speaker.play(783.991); // G5 ソ
 await obniz.wait(150);
 await speaker.stop();
 await obniz.wait(60);
 await speaker.play(880.000); // A5 ラ
 await obniz.wait(140);
 await speaker.stop();
 await obniz.wait(60);
 await speaker.play(783.991); // G5 ソ
 await obniz.wait(600);
 await speaker.stop();
 await obniz.wait(80);
 await speaker.play(1046.502); // C6 ド
 await obniz.wait(200);
 await speaker.stop();
 await obniz.wait(80);
 await speaker.play(987.767); // B5 シ
 await obniz.wait(1200);
 await speaker.stop();
 await obniz.wait(80);
 
 speaker.stop();
}

app.listen(PORT);
console.log(`ポート${PORT}番でExpressサーバーを実行中です…`);
```
</div></details>

##結果 ～メロディー再生では起きず。でも、笑顔は達成！～

- さっそく「目覚ましBot」を使ってみた結果、**メロディー再生ではピクリとも動かず、いつも通りに名前を連呼しながら起こす**ことになりました。（わが子、手強いです）
- 実際に使ってみると、目覚ましの割にはボリュームが小さい点など、さっそく改善したい部分が見つかりました。
- 一方、寝起きには「これ、大好きな歌だ！」と反応していて、ミュウの画像やひらがなのメッセージを読んで、**笑顔で目覚める**ことができました。ここは狙いどおりです！

- メロディーに慣れてきた場合、飽きてしまいそうなので、子どもが好きな曲のランダム再生などの機能が追加できたら面白そう・・・と新たな妄想が膨らみました。
- 今後は、一定時間になったら起動するタイマーやスヌーズ機能なども付けてみたいと思っています。


##参考
▶[LINEでメッセージを送ると メロディを奏でるBOT【IoT連携】【LINE BOT】](https://qiita.com/Naru0607/items/b523cf9f67fa18bcf7d7)
▶[1時間でLINE BOTを作るハンズオン (資料+レポート) in Node学園祭2017 #nodefest](https://qiita.com/n0bisuke/items/ceaa09ef8898bee8369d)