Raspberry Pi 2でAmazon Echoを作る

AmazonからAlexa Voice Service(AVS)の使い方についてのサンプルが公開されていたので、試してみました。
https://github.com/amzn/alexa-avs-raspberry-pi

### 追記(2017/3/31)
この記事を書いた頃はボタン押してAlexaを使っていましたが、その後ウェイクワードに対応したサードパーティー製のサービスが組み込まれ、ボタン押さなくても `Alexa, what is the weather` みたいな感じで、動くようになっています。
インストールの手順もコマンドライン一発みたいに改善されているので、↑のgithubを参照ください。
raspberry piを利用する場合は、こちらのページになっています。https://github.com/alexa/alexa-avs-sample-app/wiki/Raspberry-Pi

## サンプル動画
出来上がりはこんな感じで、Alexaが質問に反応して、答えてくれるようになります。
[![Raspberry Pi2 and Alexa Voice Service](http://img.youtube.com/vi/Pz64qvF_kKs/0.jpg)](http://www.youtube.com/watch?v=Pz64qvF_kKs)

## いきなり感想
Raspberry Pi用のサンプルでは、GUIでクライアントを起動する必要があり、音声の認識には事前にボタンを押してといった操作が必要なので、Amazon Echoみたいに好きなタイミングで話しかけて、反応するといったことが出来ませんでした。
Amazon Echoのようにトリガーキーワードである `Alexa` を言わなくても反応するのは、ボタンで音声認識を開始させているからだと思います。

音声だけで反応させるには、常時オーディオを監視して、特定のキーワードが見つかったら、それ以降の音声を認識させるといった作りこみを、クライアントアプリで出来るようにすれば、よりAmazon Echoの様なデバイスが作れるのではないでしょうか。

Amazon Fire Stickのリモコンも、Alexaを使う時はボタンを押してる間だけ音声認識させるので、実装するデバイスの用途次第で、トリガーをどうするか決めれば良さそうです。

# サンプルを元に進めます
https://github.com/amzn/alexa-avs-raspberry-pi

## 準備するもの

- Raspberry Pi 2
- マイク
- スピーカー（私の例では、HDMIでTVに繋いだので、音声はTVから出力されます）


### Raspberry PiのOS

```
$ uname -a
Linux raspberrypi 4.1.17-v7+ #838 SMP Tue Feb 9 13:15:09 GMT 2016 armv7l GNU/Linux
```

## VNCサーバの設定
AVSのサンプルを動かす際にはGUIが必要となりますので、別途モニターを用意しない場合は、元の手順のようにVNCサーバのセットアップをしてリモートデスクトップを使ってサンプルを動かす必要があります

## VLCをセットアップ

```
sudo apt-get update
sudo apt-get install vlc-nox vlc-data
```

VLCの場所を確認

```
$ whereis vlc
vlc: /usr/bin/vlc /usr/lib/vlc /usr/share/vlc /usr/share/man/man1/vlc.1.gz
```

環境変数の設定

```
export LD_LIBRARY_PATH=/usr/lib/vlc
export VLC_PLUGIN_PATH=/usr/lib/vlc/plugins
```

## nodejsのインストール

```
curl -sL https://deb.nodesource.com/setup | sudo bash -
sudo apt-get install nodejs

$ node -v
v0.10.43
```

## JDKのインストール
[こちら](http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html)からJDKをダウンロードします。
今回ダウンロードしたのは、Linux ARM 32 Soft Float ABI（jdk-8u77-linux-arm32-vfp-hflt.tar.gz 新しいバージョンを使う場合は適宜ファイル名を置き換えて進めてください）
注:2016/6/5) リンク先から`Linux ARM 32 Soft Float ABI`がない場合がありますが、Oracleが数週間毎にSoft/Hardと名称を変えるので、最新の`Linux ARM 32 Hard Float ABI` or `Linux ARM 32 Hard Float ABI`をダウンロードして使ってください。 （@jhotta さんありがとうございます）

```
sudo tar zxvf jdk-8u77-linux-arm32-vfp-hflt.tar.gz -C /opt

sudo update-alternatives --install /usr/bin/javac javac /opt/jdk1.8.0_77/bin/javac 1
sudo update-alternatives --install /usr/bin/java java /opt/jdk1.8.0_77/bin/java 1
```

### インストールしたものを使うように設定

```
sudo update-alternatives --config javac
sudo update-alternatives --config java
```

確認

```
$ java -version

java version "1.8.0_77"
Java(TM) SE Runtime Environment (build 1.8.0_77-b03)
Java HotSpot(TM) Client VM (build 25.77-b03, mixed mode)

$ javac -version

javac 1.8.0_77
```

## Mavenのインストール

[こちら](https://maven.apache.org/download.cgi) ダウンロードしてくる

```
sudo tar zxvf apache-maven-3.3.9-bin.tar.gz -C /opt

export M2_HOME=/opt/apache-maven-3.3.9
export PATH=$PATH:$M2_HOME/bin
```

確認

```
mvn -version

Apache Maven 3.3.9 (bb52d8502b132ec0a5a3f4c09453c07478323dc5; 2015-11-11T01:41:47+09:00)
Maven home: /opt/apache-maven-3.3.9
Java version: 1.8.0_77, vendor: Oracle Corporation
Java home: /opt/jdk1.8.0_77/jre
Default locale: en_US, platform encoding: ANSI_X3.4-1968
OS name: "linux", version: "4.1.17-v7+", arch: "arm", family: "unix"
```

## Alexa Voice Serviceの準備

手順ページ（3 - Getting started with Alexa Voice Service）に書かれている通りにデバイスを追加します
https://github.com/amzn/alexa-avs-raspberry-pi#3---getting-started-with-alexa-voice-service

### サンプルをRaspberry上にダウンロード

```
wget https://github.com/amzn/alexa-avs-raspberry-pi/archive/master.zip

unzip master.zip
sudo mv alexa-avs-raspberry-pi-master /opt/alexa-avs-raspberry-pi
```

### 自己証明書の準備

```
sudo apt-get install openssl

cd /opt/alexa-avs-raspberry-pi/samples/javaclient
```

ssl.cnfを編集して証明書の情報を入力します
`countryName` は2文字じゃないと証明書の生成に失敗します。
commonNameは、`/opt/alexa-avs-raspberry-pi/samples/companionService/config.js` のcompanionService.serviceUrlと一致させておく必要があります。（デフォルトからいじらなければOK）

```
[req]
distinguished_name      = req_distinguished_name
prompt                  = no

[v3_req]
subjectAltName          = @alt_names

[alt_names]
DNS.1                   = localhost
IP.1                    = 127.0.0.1
IP.2                    = 10.0.2.2

[req_distinguished_name]
commonName              = $ENV::COMMON_NAME
countryName             = JP
stateOrProvinceName     = Tokyo
localityName            = tokyo
organizationName        = sparkgene
organizationalUnitName  = dev
```

証明書生成スクリプトに実行権限を付けて実行する

```
chmod +x generate.sh

./generate.sh

Product ID: pi2_alexa
Serial Number: 123456
Password for Keystores (won't echo): Generating RSA private key, 4096 bit long modulus
.................................................................................................................................++
...............................................................................................................................................................................++
e is 65537 (0x10001)
Generating RSA private key, 2048 bit long modulus
.+++
...................................+++
e is 65537 (0x10001)
Signature ok
subject=/CN=sparkgene/C=JP/ST=Tokyo/L=tokyo/O=SPARKGENE/OU=development
Getting CA Private Key
Generating RSA private key, 2048 bit long modulus
...............................+++
...............................................................................................................................................+++
e is 65537 (0x10001)
Signature ok
subject=/CN=sparkgene/C=JP/ST=Tokyo/L=tokyo/O=SPARKGENE/OU=development
Getting CA Private Key
Generating RSA private key, 2048 bit long modulus
..................+++
................................................+++
e is 65537 (0x10001)
Signature ok
subject=/CN=sparkgene/C=JP/ST=Tokyo/L=tokyo/O=SPARKGENE/OU=development
Getting CA Private Key
```

nodejsの設定ファイルに生成した証明書のパスを指定する。

```
sslKey: '/opt/alexa-avs-raspberry-pi/samples/javaclient/certs/server/node.key',
sslCert: '/opt/alexa-avs-raspberry-pi/samples/javaclient/certs/server/node.crt',
sslCaCert: '/opt/alexa-avs-raspberry-pi/samples/javaclient/certs/ca/ca.crt',
```

javaクライアントの設定ファイルに生成した証明書のパスを指定する。
パスワード指定していないので、Passphraseは空にしてます。

```
{
    "productId":"",
    "dsn":"",
    "provisioningMethod":"",
    "companionApp":{
        "localPort":8443,
        "sslKeyStore":"/opt/alexa-avs-raspberry-pi/samples/javaclient/certs/server/jetty.pkcs12",
        "sslKeyStorePassphrase":"",
        "lwaUrl":"https://api.amazon.com"
    },
    "companionService":{
        "serviceUrl":"https://localhost:3000",
        "sslClientKeyStore":"/opt/alexa-avs-raspberry-pi/samples/javaclient/certs/client/client.pkcs12",
        "sslClientKeyStorePassphrase":"",
        "sslCaCert":"/opt/alexa-avs-raspberry-pi/samples/javaclient/certs/ca/ca.crt"
    }
}
```

## Raspberry Piをデバイス登録するためのアプリを準備

```
cd /opt/alexa-avs-raspberry-pi/samples/companionService

npm install
```

### Security Profileを有効にする

https://github.com/amzn/alexa-avs-raspberry-pi#6---enable-security-profile
を参考に、有効にします。

### 各種configファイルに設定する

/opt/alexa-avs-raspberry-pi/samples/companionService/config.jsを編集して、clientId、clientSecret、productsを設定します。
元のサンプル通りの命名で進めるとproductsは `products: {"my_device": ["123456"]}` と指定します。

/opt/alexa-avs-raspberry-pi/samples/javaclient/config.jsonを編集して、productId、dsn、provisioningMethodを設定します。

/opt/alexa-avs-raspberry-pi/samples/javaclient/pom.xmlのdependenciesに以下を追加する

```
<dependency>
  <groupId>net.java.dev.jna</groupId>
  <artifactId>jna</artifactId>
  <version>4.1.0</version>
  <scope>compile</scope>
</dependency>
```

## Companion Serviceサーバを実行する

```
cd /opt/alexa-avs-raspberry-pi/samples/companionService/
npm start

# サーバが実行され、ポート3000でリッスンを開始します

> alexa-voice-service-sample-companion-service@1.0.0 start /opt/alexa-avs-raspberry-pi/samples/companionService
> node ./bin/www

This node service needs to be running to store token information memory and vend them for the AVS app.

Listening on port 3000

```

## クライアントを起動
別のターミナルウィンドウを起動してクライアントを実行します

```
cd /opt/alexa-avs-raspberry-pi/samples/javaclient/
chmod +x install-java8.sh
./install-java8.sh

mvn validate
mvn install

mvn exec:exec
```

## アクティベーションする

クライアントが起動すると、デバイスを登録するためのURLが表示されるので、それをブラウザに貼り付けて、有効にします。

![IMG_7187.JPG](https://qiita-image-store.s3.amazonaws.com/0/36184/e9a49b99-4861-2b4b-7786-703d8e52c983.jpeg)

アクティベーションがされると、クライアントの画面で`Start Listening` をクリックして音声認識させます。