Google Home+サーボモータで照明のスイッチを操作できるようにした話

自室の照明のスイッチなんですが、ベットが置いてあるところに対して部屋の対角線上のちょうど反対側にあるんですよね。ベットでゴロゴロしながらあ~眠くなってきたなってときに照明を消すために立ち上がらないとイケないわけで不便でした。せっかくGoogle Homeが部屋にあるのだからベットでゴロゴロしながら「OK Google, 電気を消して」「パチッ(電気が切れる音)」とかやりたいですよね。仮にも情報系学生を名乗るからにはこれくらい自作してナンボですね。ということで作ってみたという話です。

機材

構成

Google Homeに話しかけてからサーボモータが動作するまでの流れは以下のような構成です。

  1. Google Homeがフレーズを拾う
  2. iftttがGoogle Homeからwebhookに転送
  3. webhookがraspiで動いているnodeサーバーにhttpリクエスト送信
  4. raspi上のnodeはhttpリクエストを受け取ってArduinoにシリアル通信でコマンド送信
  5. 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を設定すると、一連のプログラムが動くようになります。

終わりに

ガムテでのサーボモータの設置はお世辞にもスマートとは言えないものになってしまいましたが、まあ、こういうことも出来るよって知見は溜まったのかなとは思います。あとはこういうのはピタゴラスイッチみたいで楽しいです。