Orange Pi Zero で書類読み上げ機を作ってみました

# 書類読み上げ機って何？

こんなものです。
[![[Orange Pi Zero] 書類読み上げ機](http://img.youtube.com/vi/y_GsXR9g85Q&feature/0.jpg)](http://www.youtube.com/watch?v=y_GsXR9g85Q&feature)
（１分ぐらいの動画です。音声が流れますが音が少し遠いです）

![스크린샷 2017-04-26 02.41.25.png](https://qiita-image-store.s3.amazonaws.com/0/67057/d27b5300-a920-fb18-e1ce-ad0220b8c1d3.png)

## 仕組み
1. A4サイズの紙をケースに入れる
2. motionで画像検知＆キャプチャ
3. node.jsが実行される
    1. Google Cloud Vision APIでテキスト抽出
    2. Amazon Polly APIでテキストを音声に変換
4. スピーカーから音声が流れる

# 材料

## ハードウェア

### Orange Pi Zero側

- Orange Pi Zero（$6.99）
- USBカメラ（自宅にあったもの、Logicool C270）
- スピーカー（自宅にあったもの、アンプ機能あり）
- 3.5mmジャック（ない場合は頑張ればなんとかできる）
- ブレッドボード、ジャンパーコード、ヒートシンク

### ケース側

- ケース（¥100）
- アーム（¥100*2個=¥200）
- ナット（¥100）
- 磁石（¥100）
- テープ

## ソフトウェア
- Google Cloud Vision
    - 無料：OCR　1～1,000 ユニット/月　
- Amazon Polly
    - 無料：音声の最初のリクエストから 12 か月間は 1 か月あたり 500 万文字まで
- あと、node.jsで実装。。

## その他
- A4サイズの書類
    - ここではちょうど息子の保育園から届いた案内状あったのでそれを使いました

# 工作

## ハードウェア

### ケース作り
![IMG_20170425_013153.jpg](https://qiita-image-store.s3.amazonaws.com/0/67057/ac0cd451-63b8-7853-2b04-e1000ca8e63b.jpeg)

- ケースにUSBカメラを固定するアームを装着
- アームは、ステンレス取り付け金具２つを、ナットとボルトで固定
- 片方のアームは９０度に曲げ、先端にはカメラを簡単に脱着できるように磁石をくっつけテープで固定
- 上記の素材は、すべて100円ショップで入手可能

###　Orange Pi Zero側
![IMG_20170425_013223.jpg](https://qiita-image-store.s3.amazonaws.com/0/67057/fc535903-b483-0997-f358-331f84f40fb1.jpeg)

- Orange Pi Zeroには、スピーカーと繋ぐ3.5mmジャックがないため
    - 1x13 Headerの２番（GND）、７番（LINEOUTR）、８番（LINEOUTR）のピンを、それそれ3.5mmフラグに繋ぎます
    - 一般的な3極プラグだと、先端からL-R-GND順
    - 基板取付用のジャックがあれば便利
    - もしスピーカーから雑音がひどい場合は、UARTピンのGNDも繋いであげる
- あと、LANケーブル、USBカメラ

#### 注意
- Orange Pi Zeroは、CPUがかなり熱くなります
    - ここでは、ヒートシンクを使いました

### USBカメラのフォーカス調整
- Logicool C270の場合
    - https://www.youtube.com/watch?v=fJYqHhkq42M

## ソフトウェア
### 下記の作業はここでは割愛します
- OSインストール
- sshもしくはシリアル接続
- awsアカウント作成、アクセス鍵設定
- gcpアカウント作成、アクセス鍵設定

### OS設定変更
- wifiはスピードも遅いしおすすめできません。そして技適問題もあるのでここでは無効にします

```bash
$ sudo echo "blacklist xradio_wlan" > /etc/modprobe.d/disable_xradio_wlan.conf
$ sudo reboot
```

- USBカメラデバイスの確認

```bash
ls /dev/video0
```

### 各種インストール

```bash
# motion（画像キャプチャ）
$ sudo apt-get install motion

# mpg321（mp3再生）
$ sudo apt-get -y install mpg321

# nvm, node.js
$ curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.1/install.sh | bash
$ nvm install v6.10.2

# google vison（画像→テキスト変換）
# Orange Pi Zeroのメモリが２５６MBのモデルは、事前にswap領域の設定が必要
npm install @google-cloud/vision

# aws-sdk（テキスト→音声）
npm install aws-sdk

# node.jsから音声を再生
npm install lame serialport
sudo apt-get install libasound2-dev
npm install speaker
```

### motion（カメラの画像を感知しキャプチャ保存）設定

- motion.confから下記の値を修正します。

```bash:/etc/motion/motion.conf
daemon off

# ログ
logfile /tmp/motion.log
log_level 6
log_type all

# 使用するカメラデバイス
videodevice /dev/video0

# キャプチャの解像度
width 1920
height 1080

framerate 2

# ５００００pixelに変化があればキャプチャを取る
threshold 50000
minimum_motion_frames 4

# 1イベント間隔
event_gap 2

# 保存する画像
output_pictures center

# カメラの位置補正のため
stream_port 3001

# 案内ボイス（それぞれmp3ファイルを制作し配置）
;on_event_start mpg321 event-first.mp3
;on_event_end mpg321 event-end.mp3

# キャプチャを取ったあと実行するnode.jsのファイル
on_picture_save node ocr.js %f
```

### node.jsファイル
- 下記のファイルを作成します。

```js:ocr.js
const vision = require('@google-cloud/vision')({
    projectId: 'ocr-api-xxxxxx',
    keyFilename: 'ocr-api-xxxxxxxxxxxx.json'
});

const aws = require('aws-sdk');
aws.config.loadFromPath('aws_key.json');
var polly = new aws.Polly({ apiVersion: '2016-06-10', region: 'us-east-1' });

const fs = require('fs');
const Stream = require('stream');
const Speaker = require('speaker');

const Player = new Speaker({
    channels: 1,
    bitDepth: 16,
    sampleRate: 22050
})

// motionからの引数（キャプチャファイル名）
const fileName = process.argv[2];

// google vision
return vision.detectText(fileName, function (err, text, apiResponse) {
    if (err) {
        console.log(err);
    }

    if (text === undefined) {
        console.log('no text: ' + fileName);
        return;
    }
    var textMsg = text[0].replace(/\n/g, '');
    console.log(textMsg);

    var speechParams = {
        OutputFormat: 'pcm',
        VoiceId: 'Mizuki',
        Text: textMsg
    };

    // aws polly
    return polly.synthesizeSpeech(speechParams, function(err, data) {
        if (err) {
            console.log(err);

        } else {
            if (data.AudioStream instanceof Buffer) {
                var bufferStream = new Stream.PassThrough()
                bufferStream.end(data.AudioStream)
                bufferStream.pipe(Player)
            }
        }
    });
});
```

## 実行
```bash
$ motion
```

- http://192.168.x.x:3001 に接続し、動画を見ながらカメラの位置を調整
    - Orange Pi Zeroと同じネットワーク上のデバイスから接続してください

# 反省点
- スイッチとしてmotionの動き検知を使うのはやはり無理（勝手にキャプチャを撮ったり、誤動作が多い）
    - それっぽいスイッチを発見したので、次のバージョンで挑戦！
    - http://akizukidenshi.com/catalog/g/gP-09187/

# 参考サイト
- Xunlong Orange Pi Zero
    - http://linux-sunxi.org/Xunlong_Orange_Pi_Zero
- Getting Started with Amazon Polly using Node.js
    - https://medium.com/@anaptfox/getting-started-with-amazon-polly-using-node-js-345e84dbd23d
- Google Cloud Vision API 料金
    - https://cloud.google.com/vision/docs/pricing?hl=ja
- Amazon Polly API 料金
    - https://aws.amazon.com/jp/polly/pricing/

# その他
- これ。。ラズパイでは作れないの？
    - もちろんできますｗ