ATOM EchoでGoogleHomeへブロードキャストする


<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">ATOM EchoでGoogle Homeにブロードキャストするやつ移植したけどマイクの音質も問題なさそう。<br>コンパクトでいい感じ！ <a href="https://t.co/bCixT13Y0Q">pic.twitter.com/bCixT13Y0Q</a></p>&mdash; ode (@odetarou) <a href="https://twitter.com/odetarou/status/1270778547947233280?ref_src=twsrc%5Etfw">June 10, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

※ 追記：2020/06/14 録音完了後のmp3再生対応しました。
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">ATOM EchoからGoogle Homeにブロードキャストするやつに効果音付けてみました。<br>公式のファンファーレのやつに対抗で〜。<br>ボタン長押しで効果音を楽しむモードに。<br>後は小型バッテリーが発売されるのをお祈り(*˘人˘*) <a href="https://t.co/qkJZjEFMCc">pic.twitter.com/qkJZjEFMCc</a></p>&mdash; ode (@odetarou) <a href="https://twitter.com/odetarou/status/1272062449265475585?ref_src=twsrc%5Etfw">June 14, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>


# 仕組み

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132679/1724331d-d56a-5bc2-d790-ed5799b2c9ae.png)

horihiroさんのesp8266-google-home-notifierライブラリを使用しGoogleHomeへWaveファイルのURLを送ることで音声を再生できます。
WaveファイルのURLはATOM上にWebサーバーを立ち上げてWaveファイルを送信します。
WaveファイルはMhageさんのマイクからWaveファイル作成するクラスを参考にしました。

Mhageさんのesp32_CloudSpeech
https://github.com/MhageGH/esp32_CloudSpeech
Google Cloud Speech-to-Text APIを使いマイクで録音したWaveファイルをテキスト化しています。
今回はマイクでの録音からWaveファイル化する部分を参考にさせてもらいました。

horihiroさんのesp8266-google-home-notifier
https://github.com/horihiro/esp8266-google-home-notifier
https://qiita.com/horihiro/items/4ab0edf415916a2cd542
GoogleHomeへテキストからmp3を再生するURLを送ることでテキストメッセージを再生できるライブラリです。
使用方法には載っていないのですがplayメソッドを使うことで任意の音声URLをGoogleHomeに送ることができます。

# コード
githubに置きました。
https://github.com/ode1022/m5atom_echo_google_home_notifier

下記ライブラリに依存しているのでgit clone等してください。
https://github.com/horihiro/esp8266-google-home-notifier
https://github.com/m5stack/M5Atom
https://github.com/FastLED/FastLED (M5Atomが依存している)
https://github.com/earlephilhower/ESP8266Audio

config.h.sampleをconfig.hにリネームし、Wifi設定、GoogleHome設定をしてください。
GoogleHomeは名前かIPのどちらかを指定します。
名前の場合はmDNSからIP探す処理が入るためIP指定のほうが3秒ほど早いです。
バッテリーで動作などする場合は起動時間を早めるためにIP指定が良さそうです。電源稼働後に1秒ほどで録音処理に入れます（Wifi接続処理に1秒はかかります）

録音完了の効果音のmp3ファイルはSPIFFSに保存しています。
mgo-tecさんの記事を参考にdataフォルダを事前にSPIFFSに書き込んで下さい。
https://www.mgo-tec.com/blog-entry-spiffs-uploader-plugin-arduino-esp32.html

macの場合はarduino-esp32fs-pluginを利用するとエラーがでるため
下記フォルダをコピー(リネームだけでもいいかも？)する必要がありました。

```
~/Documents/Arduino/hardware/espressif/esp32/tools/esptool/
↓
~/Documents/Arduino/hardware/espressif/esp32/tools/esptool_py/
```
参考
https://github.com/me-no-dev/arduino-esp32fs-plugin/issues/15

## 動作説明

起動後に自動で録音を初めてGoogleHomeにブロードキャストします。（ACアダプタ稼働の場合等はconfigで電源接続時に録音再生しないようにできます）
ATOM Echoのボタンを押すことで何度も録音＆ブロードキャストできます。
録音時間はバッファの確保した容量に依存します（無音検知で自動終了などはしていません）

ATOM Echo上のIPをブラウザで見ることで
esp8266-google-home-notifierのTTSの機能でテキスト入力したものを音声再生することも可能です（horihiroさんのサンプル通りです）

おまけ機能としてボタンを長押しするとmp3を何度も再生できるモードになります。ボタン押すごとに再生できるので効果音を楽しめます😁

## コード説明

**メインのinoファイル**
大まかな流れは図で書いたとおりのためコードをみると大体わかるかと思います。

**Audio.cpp**
Waveファイルを生成するクラスです。
音量が小さいためオートゲイン(自動音量調整）を付けました。
録音時間を長くするためにバッファサイズはなるべく確保できる大きい値を指定しています。
mp3再生(非同期再生、再生中のを停止して再生にも対応)

mp3を非同期再生するためにクラスインスタンスのメソッドをxTaskCreatePinnedToCoreに渡しているところが見どころです。
参考URL
https://forum.arduino.cc/index.php?topic=658230.0
mp3再生中に割り込みで停止して別のを再生するなども対応しました。

ATOM Echoのマイクとスピーカーは排他使用のようです。ESP32自体はI2Sは2つ使用できるのですがチャンネルを0,1で分けて利用しようとしても使えませんでした。
恐らく線が一部共用のためかと思われます。
https://docs.m5stack.com/#/en/atom/atomecho?id=schematic



**I2S.cpp**
I2Sでマイクの初期化処理をします。
ATOM Echo用の設定を加えました。

### バッファの確保について
Audioクラスでなぜ一度に大きいサイズのバッファを確保せずに配列で分割してnewしている理由
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">ESP32で大きいメモリ扱う際に分割してnewしてるコードが謎だったのだけど<br>512KBが実はSRAM3つに分割されてるせいっぽい。あとは連続して確保が必要なので断片化してても減るそうな。<br>heap_caps_get_largest_free_block関数でnewできる最大容量が取得可能と。 <a href="https://t.co/26hO2QYSIJ">pic.twitter.com/26hO2QYSIJ</a></p>&mdash; ode (@odetarou) <a href="https://twitter.com/odetarou/status/1201075393379176450?ref_src=twsrc%5Etfw">December 1, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

### mp3再生に使うESP8266Audioライブラリについて

公式サンプルですとESP8266Audioをソースごと丸コピーして使用していて不便そうでした。
ソースの違いを見てみましたがデフォルトのpinや一部設定が違うためライブラリを直接修正しているようです。（ライブラリ側が引数対応していないのが原因っぽい。。）
https://github.com/m5stack/M5-ProductExampleCodes/blob/a73fadf7a6ccfc089f9d727b513a0338ac5ce718/Core/Atom/AtomEcho/Arduino/StreamHttpClient_ECHO/src/AudioOutputI2S.cpp#L65
```channel_format = I2S_CHANNEL_FMT_ALL_RIGHT```
https://github.com/m5stack/M5-ProductExampleCodes/blob/a73fadf7a6ccfc089f9d727b513a0338ac5ce718/Core/Atom/AtomEcho/Arduino/StreamHttpClient_ECHO/src/AudioOutputI2S.cpp#L81
```SetPinout(19, 33, 22)```

https://github.com/earlephilhower/ESP8266Audio/blob/e59319f605d45d06ae0c6a7f983872c0366dd11c/src/AudioOutputI2S.cpp#L66
```channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT```
https://github.com/earlephilhower/ESP8266Audio/blob/e59319f605d45d06ae0c6a7f983872c0366dd11c/src/AudioOutputI2S.cpp#L81
```SetPinout(26, 25, 22)```

ライブラリ直接編集はソースを含めるのもいやなのと、最新ver追従がしずらく扱いづらいのでライブラリをそのまま利用する方向で対応しました。
AudioOutputI2Sをnewしたあとに別途ATOM Echo向けのI2Sの初期化を行うことで対応可能です。

(追記:2020/10/11)
AudioOutputI2SをnewするとGPIO25でI2Sが初期化されしまい後からEcho用のピンでi2s_set_pinしてもGPIO25は使用できなくなるようでした。
数少ない貴重なAtomEchoのGPIOが利用できなくなるのは困るので
バッドノウハウですが回避策として
```new AudioOutputI2S(I2S_NUM_0, AudioOutputI2S::INTERNAL_PDM);```
で初期化することでGPIO25でi2s_set_pinされないようにできます。
恐らくi2sで一度初期化されるとpinの状態が通常とは違くなると予想されます。

(追記:2021/01/01)
ESP8266AudioでPin番号きめうちじゃない任意の番号で初期化できるようになったため特殊な処理は不要になりました。
下記のpull reqにて対応された模様
https://github.com/earlephilhower/ESP8266Audio/pull/331

コンストラクタでI2Sの初期化がされていたのをやめて後回しにしたことで下記のコードで動くようになりました。

```
out = new AudioOutputI2S(I2S_NUM_0);
out->SetPinout(19, 33, 22);
```

# 雑記

以前にAmazon Dashボタンを押してブロードキャストするのを作りました（コード未整理）。
ESP32とI2SマイクとSDカードからのmp3再生を組み合わせましたがサイズ的にごちゃごちゃしてあまり使わず。
今回ATOM Echoが発売されたので移植したんですが小さくて良いですね。
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">Google Homeに「OK Google ブロードキャストして」と言わないでブロードキャストする装置できた！<br>毎回言うの面倒だったのがボタン１つで。<a href="https://twitter.com/hashtag/ESP32?src=hash&amp;ref_src=twsrc%5Etfw">#ESP32</a> <a href="https://twitter.com/hashtag/I2SMic?src=hash&amp;ref_src=twsrc%5Etfw">#I2SMic</a> <a href="https://t.co/BzUHnqPh4w">pic.twitter.com/BzUHnqPh4w</a></p>&mdash; ode (@odetarou) <a href="https://twitter.com/odetarou/status/1204792874451095558?ref_src=twsrc%5Etfw">December 11, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>