読者です 読者をやめる 読者になる 読者になる

RaspberryPiでも消臭力してみた

RaspberryPiでも消臭力してみた

毎回、ダメなパターンのエントリですが、今回はそれに輪をかけて駄目なタイトルになっています。

最近、Amazon Dash Buttonを使ってみたくなったので購入を考えていました。ただ、本体にくっついているデザイン面が日用品ばかりで今ひとつ気が乗らず、購入までには至りませんでいた。この時点で正当な使い方は考えておらず、ネット情報でAmazon Dash Buttonを押してWebHookを行ったりできるネタがあったので、自分でもぜひやってみようと思っていました。

あとでネットの情報を読んでいてわかったこととしては、構造それなりなのに500円と安価で、少し溜まったAmazonポイントで購入できるのもいいなと思っています。実質はものを買えば500円割引になるので、そっちのほうがオトクなんですけど。

中身の詳しい内容は以下が詳しかったです。

itpro.nikkeibp.co.jp

1週間ほど考えて、やっと購入するものを決定しました。

ムーニーと迷っての購入です。決しておむつプレイが好きなわけではないのであしからず。

ネット情報ではDasherというnode.jsのモジュールを使っている情報が多かったのですが、これは以下のnode-dash-buttonの処理をWebhookに特化したラッパーのようですので、自分はRaspberryPiのコマンドなどを実行したかったので、オリジナルのものを使うことにしました。

github.com

IFFTとかSlackへなにかを行うのであればDasherのほうが便利です。

github.com

node-dash-buttonをインストールする

上記のリンクからreadme.mdのInstallation Instructions節で方法が書かれています。

# dependancy on libpcap for reading packets
$ sudo apt-get install libpcap-dev
$ npm install node-dash-button

これでいいようです。…が。

自分の環境では少し気になっていることがありました。そもそもnode.jsはデフォルトのRasbianではバージョンが古いため、nvmをインストールすることでバージョンを上げていました。デフォルトのバージョンではnode-dash-buttonは動作しないようです。

uepon.hatenadiary.com

ユーザからはこれで問題はありません。 しかし、インストール時に使用しているlibpcap-devは、管理者権限でないとこれを使用したプログラムは動作しません。(sudoをつける必要があるということです。)sudoで呼び出されるnode.jsはnvmでインストールしたバージョンのものは適応されず、デフォルトのものが呼び出されてしまいます。(pathなどの環境が異なるので致し方ないのですが)

そこで、根本的にはバージョンアップをすることにしました。 Dasherのreadme.mdに

note Raspberry Pi users may need to update node arm which will automatically remove nodejs-legacy. Credit @legotheboss

なる記載があり以下のコマンドでnodeを比較的新しいバージョンに変更できます。

$ wget http://node-arm.herokuapp.com/node_latest_armhf.deb 
$ sudo dpkg -i node_latest_armhf.deb
$ $ node -v
v4.2.1

これで、比較的新しいnode.jsが準備でき(node.jsは2017/02/18現在v6.9.5まで来ているようですので気休めなのかもしれませんが、デフォルトのバージョンよりはいいと思います)、node-dash-buttonの利用ができるようになりました。 あとはnode-dash-buttonをインストールする処理を行うことになります。

$ sudo apt-get install libpcap-dev
$ npm install node-dash-button

これでOKです。

DashButtonの設定を行う

以下のページのボタンセットアップ節を見てください。このページが分かりやすかったと思います。

qiita.com

具体的には、Dash ButtonのWi-Fi設定を行った後に、ボタン押下時に注文する商品を選択する画面でキャンセルしましょう。(商品との紐付けをしないようにする点) 商品と紐付けさえ行わなければ基本的には注文はされません。(buttonを押すとエラーが発生し、白色のLEDの点滅後に赤いLEDが点灯します。)

後はnode-dash-buttonの設定と実行になります。

node-dash-buttonの設定

node-dash-buttonはarpパケットを受け取ってその中にあるMACアドレスを判別して処理を行うので、入手したDashButtonが通信するときに使うMACアドレスを取得する必要があります。node-dash-buttonにはそのMACアドレスを取得するプログラムも含まれています。(Dasherモジュールにもこのプログラムが含まれています。)

$ cd node_modules/node-dash-button
$ sudo node .bin/findbutton
Watching for arp & udp requests on your local network, please try to press your dash now
Dash buttons should appear as manufactured by 'Amazon Technologies Inc.'
Possible dash hardware address detected: XX:XX:XX:XX:XX:XX Manufacturer: Amazon Technologies Inc. Protocol: udp
Possible dash hardware address detected: XX:XX:XX:XX:XX:XX Manufacturer: Amazon Technologies Inc. Protocol: arp

実行後にボタンを押してください。Amazon Technologies Inc.のベンダネームとしてXX:XX:XX:XX:XX:XXというような形でMACアドレスが取得されます。(MACアドレスは伏せ字にしていますので、適宜読み替えてください。)

このMACアドレスをトリガーにして処理を実行するすることになります。

node-dash-buttonの実行

node-dasu-buttonモジュールをインストールしたディレクトリで以下のようなスクリプトを作成します。

var dash_button = require('node-dash-button');
var dash = dash_button("XX:XX:XX:XX:XX:XX", null, null, 'all'); //address from step above
dash.on("detected", function (){
    console.log("omg found");
});

MACアドレスの入ったarpパケットを検出するとdetectedイベントを検出して処理を実行します。このスクリプトの場合にはconsole.log("omg found");の処理が実行されます。この処理を別のものにすることで、buttonを押すと色々な処理が行える様になります。

node-dash-buttonで検出した際にコマンドを実行する

node.jsからシェルコマンドを実行する方法としては以下を参考にしました。

tkybpp.hatenablog.com

sudo環境での実行なのでpathが心配だったのですが、特に問題はなかったみたいです。 例えば実行するディレクトリのファイルを取得するlsコマンドを実行するには以下のように記述します。

const execSync = require('child_process').execSync;
const result =  execSync('ls -la ./').toString();
console.log(result);

node.jsは非同期実行なので同期的な実行にしたほうがわかりやすくなるかなとは思いますのでこちらを使用しました。

スクリプト名をnode dash-button_shell.jsした場合

const dash_button = require('node-dash-button');
const execSync = require('child_process').execSync;

var dash = dash_button("XX:XX:XX:XX:XX:XX", null, null, 'all'); //address from step above
dash.on("detected", function (){
    var result =  execSync('ls -la ./').toString();
    console.log(result);
});

このようになります。これを実行すると

$ sudo node dash-button_shell.js
omg found
合計 656
drwxr-xr-x  3 pi pi   4096  215 09:40 .
drwxr-xr-x 45 pi pi   4096  220 23:33 ..
-rw-r--r--  1 pi pi    204  215 01:10 dash-button.js
-rw-r--r--  1 pi pi    341  215 09:40 dash-button_play.js
-rw-r--r--  1 pi pi    336  215 09:23 dash-button_shell.js
-rw-r--r--  1 pi pi    348  215 09:32 dash-button_talk.js
-rwxr--r--  1 pi pi    503  215 09:27 jtalk.sh
drwxr-xr-x  4 pi pi   4096  215 01:05 node_modules
-rwxr--r--  1 pi pi 635436  28 23:00 riki.wav

うまくカレントディレクトリのファイル一覧を取得できています。気にしていた実行pathの問題も問題はなさそうです。

node-dash-buttonでボタンを押したら【消臭力】の音声ファイルを鳴らす。

音声を鳴らすコマンドはaplayで行えるので今回は慣らしたい音声ファイルriki.wavとして鳴らしてみます。

const dash_button = require('node-dash-button');
const execSync = require('child_process').execSync;

var dash = dash_button("XX:XX:XX:XX:XX:XX", null, null, 'all');
dash.on("detected", function (){
    var result =  execSync('aplay riki.wav').toString();
});

これでOKです! 「しょ~しゅ~りき~♪」どこでもミゲルくんが歌ってくれる・・・素敵です!

node-dash-buttonで検出した際に音声合成で言葉を話す

以前、Open JTalkを使いましたので、今回もそこで作成したshを使用することにします。

参考: uepon.hatenadiary.com

今回使用するシェルスクリプトは以下のようなものです。(ファイル名はjtalk.sh

#!/bin/bash

#HTSVOICE=/usr/share/hts-voice/nitech-jp-atr503-m001/nitech_jp_atr503_m001.htsvoice
HTSVOICE=/usr/share/hts-voice/mei/mei_normal.htsvoice

voice=`tempfile`
option="-m $HTSVOICE \
  -s 16000 \
  -p 100 \
  -a 0.03 \
  -u 0.0 \
  -jm 1.0 \
  -jf 1.0 \
  -x /var/lib/mecab/dic/open-jtalk/naist-jdic \
  -ow $voice"

if [ -z "$1" ] ; then
  open_jtalk $option
else
  if [ -f "$1" ] ; then
    open_jtalk $option $1
  else
    echo "$1" | open_jtalk $option
  fi
fi

aplay -q $voice
rm $voice

node.jsからこのスクリプトを呼び出すことになるので以下のようなスクリプトを記述します。

const dash_button = require('node-dash-button');
const execSync = require('child_process').execSync;

var dash = dash_button("XX:XX:XX:XX:XX:XX", null, null, 'all');
dash.on("detected", function (){
    console.log("omg found");
    var result =  execSync('./jtalk.sh \'こんにちは\'');
});

ボタン一つで話したい音声が話せます。す・て・き(うっとり)

蛇足

今回はnode.jsを使用していましたが、通信時のarpパケットさえ捉えられればよいのでtcpdumpでも同様の処理を行えます。というか両者ともlibpcapを使っているのであれば同様なことが可能です。

まずは、libpcapを使用するtcpdumpをインストールします。

$ sudo apt-get install tcpdump

インストールが終わったら以下のようなスクリプトを作成します。 ファイル名はbutton.shとしました。

#!/bin/bash
tcpdump -i wlan0 -e | while read line
do
    if [[ $line = *XX:XX:XX:XX:XX:XX*ethertype\ ARP* ]]; then
        echo "button pressed"
    fi
done

XX:XX:XX:XX:XX:XXはDashボタンのMACアドレスを設定してください。 このシェルスクリプトは指定されたMACアドレスのパケットを受信したら標準出力にbutton pressedという表示を行うものになります。 実行は以下のように行います。(ファイルの編集が終わったらchmodで実行権限を与えてください。)

$ sudo ./button.sh
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on wlan0, link-type EN10MB (Ethernet), capture size 262144 bytes
button pressed
^C74 packets captured
75 packets received by filter
0 packets dropped by kernel

(Ctrl-Cで処理を中断していますので一回となっていますが、基本は無限ループになります。)

おわりに

無事に同一のWi-Fiエリアであれば、ミゲルくんが歌ってくれる素敵な家になりました。それにしても、Amazon Dash Buttonはこんな価格で出してもいいもんなんでしょうか?

難点を上げるとしては、ボタンを押してからARPパケットの送出するまでの反応が遅いかなと思います。まあ、値段からみれば十分問題のない範囲ですけどね。

あとで動画を追加する予定です。