Google Home+サーボモータで照明のスイッチを操作できるようにした話
自室の照明のスイッチなんですが、ベットが置いてあるところに対して部屋の対角線上のちょうど反対側にあるんですよね。ベットでゴロゴロしながらあ~眠くなってきたなってときに照明を消すために立ち上がらないとイケないわけで不便でした。せっかくGoogle Homeが部屋にあるのだからベットでゴロゴロしながら「OK Google, 電気を消して」「パチッ(電気が切れる音)」とかやりたいですよね。仮にも情報系学生を名乗るからにはこれくらい自作してナンボですね。ということで作ってみたという話です。
動作の様子
結果からお見せすると、こんな感じです。ベッドの上からまさにゴロゴロしながら操作してます。
機材
- Google Home Mini
- Raspberry Pi 3(raspbian、4.14.52-v7+)
- Arduino nano互換ボード(CH340+ATmega328P、中華製の偽物です)
- USBケーブル(miniB)
- サーボモータ(SG90)
- ジャンプワイヤ3本(オスメス)
- ガムテープ
構成
Google Homeに話しかけてからサーボモータが動作するまでの流れは以下のような構成です。
- Google Homeがフレーズを拾う
- iftttがGoogle Homeからwebhookに転送
- webhookがraspiで動いているnodeサーバーにhttpリクエスト送信
- raspi上のnodeはhttpリクエストを受け取ってArduinoにシリアル通信でコマンド送信
- Arduinoはraspiからのシリアル通信を受信したらサーボモータを動かす。
Google Home
何もしていません。初期設定のままです。
Arduino nanoとサーボモータ
サーボモータ(SG90)とArduino nano(の偽物)をジャンプワイヤで結びました。赤をVcc(5V)、黄をDigital Outの3ピン、茶をGNDに繋げます。以下のようなプログラムを書いて書き込みました。
元々は電気を消すだけじゃなくて電気をつけることもしたかったのですが、どうしてもスイッチのところにうまく設置できなくて断念しました。その名残で若干実装が気持ち悪いですが、許してください。あとはサーボモータとArduinoを動画のようにスイッチのところにガムテで固定しました。
Raspberry Pi
まずArduino(偽物)とRaspiを繋げます。他の方の記事を見ているとドライバを入れないと動かないというのを多く見たのですが、新しいカーネルだからでしょうか、普通にUSBケーブルで繋げるだけで認識しました。
ll /dev
僕の場合は/dev/ttyUSB0
という名前で認識されていました(更新日時で確認)。sudoなしで操作できるようにdialoutグループにぶち込みました。
sudo gpasswd -a pi dialout
次にnodeでサーバーを立ち上げます。chiachu gammaを入れていた関係でnode v8.xとpm2が既に入っていました。
mkdir roomapp echo {} >> package.json npm install express --save npm install serialport --save npm install body-parser --save vim main.js
main.js
const express = require("express") const morgan = require('morgan') const SerialPort = require('serialport') const Readline = require('@serialport/parser-readline') const bodyParser = require('body-parser') const app = express() app.use(morgan('combined')) app.use(bodyParser.urlencoded({ extended: false })) const port = new SerialPort('/dev/ttyUSB0', { baudRate: 9600 }) const parser = port.pipe(new Readline({ delimiter: '\r\n' })) parser.on('data', function(data) { console.log('Data recieved: %s', data) }); app.post('/light', function(req, res, next) { switch(req.body.switch) { case 'on': port.write('o') break; case 'off': port.write('x') break; default: break; } res.json({}) }); app.listen(3002, function(){ console.log('Server starting.'); });
最初GETメソッドで立ち上げたのですが、Line等にリンクを貼り付けただけでhttpリクエストが発生してモータが動いてしまうという悲しい事態が発生したのでPOSTに変えました。あとは電気を消すだけじゃなくて点けることも実現しようとした名残が残っています。以下コマンドでサーバーを立ち上げます。
node main.js
常駐させるなら
pm2 start main.js
nginxでリバプロを設定して外から見られるようにします。ワイルドカード証明書を既に取得してあります。
cd /etc/nginx/sites-available vim hoge.example.com.conf
hoge.example.com.conf(ここのサーバー名は適宜)
server { listen 443; server_name hoge.example.com; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-Host $host; proxy_set_header X-Forwarded-Server $host; proxy_set_header X-Forwarded-For $remote_addr; ssl on; ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; location / { proxy_pass http://192.168.0.100:3002/; } }
nginxでconfを有効にします。
cd /etc/nginx/sites-enabled sudo ln -s /etc/nginx/sites-available/hoge.example.com.conf hoge.example.com.conf sudo nginx -s reload
ifttt
New Appletから新しいアプレットを作ります。this
にはGoogle Assistantを選択し、Say a simple phrase
を選択しました。以下のように設定しました。
レスポンスには何も入力しませんでしたが、実際に使ったときにはGoogle Homeは「OK!アクションを実行します。」と軽快な返事をくれました。that
にはwebhookを選択します。以下のように設定しました。
URLはリバプロで設定したサーバー名を入れます。switch=off
のボディをnodeに渡します。iftttを設定すると、一連のプログラムが動くようになります。
終わりに
ガムテでのサーボモータの設置はお世辞にもスマートとは言えないものになってしまいましたが、まあ、こういうことも出来るよって知見は溜まったのかなとは思います。あとはこういうのはピタゴラスイッチみたいで楽しいです。