はじめに
この記事はIoTLT Advent Calendar 2018(Neo)の21日目の記事です。
初投稿です。5年ほど家族の誕生日に何かを作って披露するという活動をしています。
今までの活動は、ファミリーテックIoTLT vol1(発表資料)でまとめて発表させて頂きました。
今年はGoogle Home MiniとRaspberry Pi、Nefry BTを使って寿司屋の大将と弟子というネタで演出しました。
内容
まずは、動画をご覧ください。
Google Home+IFTTT+beebotte+Node-RED+Nefry BTで誕生日を演出してみました。すみっコぐらしのねこが寿司屋さんの弟子になって注文を取ってくれます。#IoTLT #ファミリーテックIoTLT #MakersHappyBirthdayProject #すみっコぐらし #GoogleHome #beebotte #NefryBT #NodeRed pic.twitter.com/4v9f5JNFkc
— TOMANDO (@TOMANDOO) 2018年12月21日
私が寿司屋の大将になって寿司を握ります。
ぬいぐるみの「ぐうぐる」が弟子として注文を取ってくれます。
「ぐうぐる」は「わさびたっぷり」や「ネタちょっぴり」など勝手にアレンジして大将に注文を伝えます。大将は「ぐうぐる」の注文で寿司を握りつつ、様子を見て手元のボタンを押し「ぐうぐる」に話をするよう促します。
「ぐうぐる」の声はGoogle Homeのものではなく、VOICEROID2 琴葉 茜・葵で予め作成した声を使っています。VOICEROID2はかなり自然な言葉で話すことができます(関西弁が怪しい部分は私の調整不足です(汗))。
用意したもの
- IoT
- Google Home Mini
- Raspberry Pi Type-B
- Nefry BT + ボタン
- クラウド
- IFTTT(スマホアプリ)
- Beebotte
- PCソフト
- VOICEROID2 琴葉 茜・葵
- ぬいぐるみ
- すみっコぐらし ねこ
仕組み
(1) Google Homeへの命令をGoogle Assistant連携を通じてIFTTTに伝えます。
(2) IFTTTはWebhook連携を通じて内容をBeebotteに伝えます。
(3) Beebotteは予め繋いでいたRaspberry Pi上のNode-REDにIFTTTからの内容を伝えます。
(4) Node-REDは内容を確認しGoogle Homeに音声再生を命令します。
(5) Google HomeはRaspberry Pi上のNode.jsから音声ファイルを取得し再生します。
(6) ボタンを押すとNefryがWebhook連携を通じIFTTTに伝えます。後は同様の仕組みでGoogle Homeに音声再生を命令します。
作り方
主に以下の記事を参考に作っています。
大学生の初めての電子工作 スマートリモコン編(外部サービス)
[GoogleHome に電車遅延情報を喋ってもらう(google-home-notifier編)]
(https://qiita.com/nori-dev-akg/items/bad7eb0c41617110cfa4)
[Nefry BTとIFTTTでスイッチを押したらLINEを送る仕組みを作ってみよう]
(https://dotstud.io/blog/nefry-ifttt-push-line/)
[Nefryでデジタル入力を試す!]
(https://qiita.com/wamisnet/items/dd0fe5a8bac9221edef0)
Beebotte
BeebotteはIoTなどで使われるリアルタイム接続用のCloud Platformです。REST API, Websockets, MQTTに対応しています。
- https://beebotte.com/ でアカウントを作成
- "My Channels"で[Create New]をクリック
- "Create a new channel"で以下の設定をして[Create channel]をクリック
- SushiDeshi
- ぐうぐる君が話すためのPub/Sub
- Publicはチェックしない
- Configured Resources:neta 寿司ネタ string (SoSはチェックしない)
- "My Channels"に追加された"SushiDeshi"をクリック
- "Channel Token:"の右の文字列を控える
IFTTT
IFTTTは様々なサービスやデバイスと連携するためのサービスです。if this, then thatというシンプルな定義が可能でとても便利です。今回はGoogle HomeとBeebotteを繋ぎます。
Nefry連携用
- https://ifttt.com/ でアカウントを作成
- "My Applets" - "Applets" で[New Applet]をクリック
- "if this, then that" の[this]をクリック
- "Choose a service" で[Webhooks]をクリック
- "Choose trigger" で[Receive a web request]をクリック
- "Receive a web request"の各項目に入力
- Event Name:SushiDeshiNefryBT
- "if this, then that" の[that]をクリック
- "Choose action service" で[Webhooks]をクリック
- "Choose action" で[Make a web request]をクリック
- "Make a web request" の各項目に入力
- URL:https://api.beebotte.com/v1/data/publish/SushiDeshi/neta?token=[Beebotteで控えたChannelToken]
- Method:POST
- Content Type (optional):application/json
- Body (optional):{"data" : "kobanashi"}
Google Home連携用
- "My Applets" - "Applets" で[New Applet]をクリック
- "if this, then that" の[this]をクリック
- "Choose a service" で[Google Assistant]をクリック
- "Choose trigger" で[Say a simple phrase]をクリック
- "Say a simple phrase" の各項目に入力
- What do you want to say?:まぐろ頂戴
- What's another way to say it? (optional):マグロ
- And another way? (optional):マグロちょうだい
- What do you want the Assistant to say in response?:ん
- Language:Japanese
- "if this, then that" の[that]をクリック
- "Choose action service" で[Webhooks]をクリック
- "Choose action" で[Make a web request]をクリック
- "Make a web request" の各項目に入力
- URL:https://api.beebotte.com/v1/data/publish/SushiDeshi/neta?token=[Beebotteで控えたChannelToken]
- Method:POST
- Content Type (optional):application/json
- Body (optional):{"data" : "maguro"} ← ネタにより変わります
ネタの数だけアプレットを作ります。無駄に見えますね?
「OK Google、注文、XX」などXXの部分でネタを言って変数化するのが一般的だとは思いますが、少しでも短く「ねえ ぐうぐる、まぐろ」みたいな注文にすることを目指しました。
一つ困ったことがあり、炙りマグロなどの「炙り」を認識できませんでした。結局、注文するときは「焼きマグロ」などに言い換えてもらいました。
Raspberry Pi
OS
Raspbian GNU/Linux 9.1 (stretch)
手持ちの古いRaspberry Pi 1 Model Bを使いました。参考にならない。。
Raspberry Pi3 ModelB初期セットアップSSHまで(Mac) などをご参照下さい。
Node-RED
Node-REDはハードウェアデバイス/APIおよびオンラインサービスを接続するためのツールです(Node-RED User Group Japanから引用)。
Running on Raspberry Pi を参照してインストールします。
http://[RaspberryPiのIPアドレス]:1880/ にアクセスし、フローを作成します。
フローのイメージはこんな感じです。
-
SushiDesu/neta
- ノード:"入力" - "mqtt"
- サーバ:新規にmqtt brokerを追加
- 接続
- サーバ:mqtt.beebotte.com
- ポート:8883
- SSL/TLS接続を使用:チェック
- TLS設定:新規にTLS接続を追加
- その他:デフォルト
- 接続
- トピック:SushiDeshi/neta
- QoS:2
- 名前:(空白)
-
json
- ノード:"機能" - "json"
- 動作:常にJavaScriptオブジェクトに変換
- プロパティ:msg.payload
- 名前:(空白)
-
set.msg.payload
- ノード:"機能" - "change"
- 名前:(空白)
- 値の代入:msg.payload
- 対象の値:msg.payload.data
-
msg.payload(デバッグ用)
- ノード:"出力" - "debug"
- 設定:全てデフォルト
-
Notify
- ノード:"機能" - "function"
- 名前:Notify
- コード:Google Home Notifierの設定を前提としています。家庭内イベント用ということでセキュリティは甘いです。
node.log("MSG:" + msg.payload);
const language = 'ja';
const googlehome = global.get('googlehome');
const fs = global.get('fs');
const pt = global.get('path');
googlehome.device('[Google Home名]', language);
googlehome.ip('[Google HomeのIPアドレス]');
const fullpathdir = '[audioファイルの保存場所]';
const baseurl = '[Node.jsのURL]';
// メイン処理
try {
subdir = msg.payload.toString(); //IFTTTで指定するdata(maguroなど)
dirPath = fullpathdir + subdir; //data毎にフォルダがあり、WAVファイルが格納されている
filenames = fs.readdirSync(dirPath);
//指定フォルダ内のWAVファイルのランダム選択
var random = Math.floor( Math.random() * filenames.length );
url = baseurl + subdir + '/' + filenames[random];
node.log(url);
// Google HomeにNotifyでWAVファイルの再生を指示
googlehome.play(url, (notifyRes) => {
node.log(notifyRes);
});
// エラー処理
} catch(err) {
// ログ出力
node.log(err);
}
return msg;
node-red-start / node-red-stop で起動 / 停止します。
Google Home Notifier
npmでgoogle-home-notifierをインストールしNode-RED用の環境設定をします。
$ cd ~/.node-red
$ npm install google-home-notifier
$ npm install fs
$ npm install path
$ vi ~/.node-red/setting.js
setting.jsのfunctionGlobalContextにgoogle-home-notifier等を追加。
functionGlobalContext: {
googlehome:require('google-home-notifier'),
fs:require('fs'),
path:require('path')
// os:require('os'),
// jfive:require("johnny-five"),
// j5board:require("johnny-five").Board({repl:false})
},
このままではkey.jsでエラーが発生します。
~/.node-red/node_modules/google-tts-api/lib/key.js
の内容を
https://github.com/ncpierson/google-tts/blob/master/lib/key.js
に書き換えました。
Node.js
Google Notifyで指定したWAVファイルの配信をするWebサーバです。
const express = require('express');
const bodyParser = require('body-parser');
const fs = require('fs');
const app = express();
const serverPort = 8080;
const urlencodedParser = bodyParser.urlencoded({ extended: false });
const dirname = "[任意のベースディレクトリ]";
// CORSを許可
app.use(function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
next();
});
/**
* マグロ
*/
app.get('/googlehome/maguro/:audioName', (req, res) => {
const audioName = req.params.audioName;
if (!audioName) {
res.status(400).send('Invalid Parameters.' + dirname);
}
const file = fs.readFileSync(dirname + '/public/audio/maguro/' + audioName, 'binary');
res.setHeader('Content-Length', file.length);
res.write(file, 'binary');
res.end();
});
//以降、maguroと同様に魚の種類などが並ぶ(IFTTTで指定するdataに対応)
app.listen(serverPort, () => {
console.log(`Start api-server. Port is ${serverPort} Dir is ${dirname}`);
})
Nefry BT
Wi-Fi・BLE通信モジュール「ESP-WROOM-32」を搭載したIoTデバイスです。
初期設定等はdotstudio documentsに詳しく書かれているため、ここでは割愛します。
#include <Nefry.h>
#include <NefryIFTTT.h>
String Event, SecretKey;
int counter =0; //送信データのカウンタ
void setup() {
Serial.begin(115200);
Nefry.setStoreTitle("SecretKey",0); //Nefry DataStoreのタイトルを指定
Nefry.setStoreTitle("Event",1); //Nefry DataStoreのタイトルを指定
SecretKey = Nefry.getStoreStr(0); //Nefry DataStoreからデータを取得
Event = Nefry.getStoreStr(1); //Nefry DataStoreからデータを取得
Nefry.enableSW(); //SW有効化
pinMode(D2, INPUT); //D2のピンのデータを入力モードにします。
}
void loop() {
int pinState;
pinState = digitalRead(D2);
if (pinState == HIGH) { //SWを押した時
counter++; //送信回数加算
bool sendData = IFTTT.send(Event, SecretKey,"Nefry",(String)(micros()/1000000)+"秒",(String)counter);//IFTTTにデータを送信
//Value1:Nefry,Value2:Nefryが起動してからの秒数,Value3:送信カウンタ
if (!sendData) {//IFTTTにデータを送信が成功したか失敗したかの判定
Nefry.setLed(255, 0, 0); //Errの時、赤色点灯
}
Nefry.ndelay(1000); //送信後1秒間待つ
}
}
NefryにWeb接続してSecretkeyとEventを登録しています。
この辺りは、Nefry BTとIFTTTでスイッチを押したらLINEを送る仕組みを作ってみよう の通りです。
GPIOの部分は、Nefryでデジタル入力を試す! を参考にしました。
回路はこんな感じです。
感想
IoTLT を通じて知った様々な技術要素を使って今年も無事誕生日を演出することができました。
Notifyを利用することでスマートスピーカーっぽくない演出ができたかなと思います。声は重要ですね。個性的な声が充実すると面白いなと思いました。