ATOM EchoでGoogle Homeにブロードキャストするやつ移植したけどマイクの音質も問題なさそう。
— ode (@odetarou) June 10, 2020
コンパクトでいい感じ! pic.twitter.com/bCixT13Y0Q
※ 追記:2020/06/14 録音完了後のmp3再生対応しました。
ATOM EchoからGoogle Homeにブロードキャストするやつに効果音付けてみました。
— ode (@odetarou) June 14, 2020
公式のファンファーレのやつに対抗で〜。
ボタン長押しで効果音を楽しむモードに。
後は小型バッテリーが発売されるのをお祈り(˘人˘) pic.twitter.com/qkJZjEFMCc
仕組み
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している理由
ESP32で大きいメモリ扱う際に分割してnewしてるコードが謎だったのだけど
— ode (@odetarou) December 1, 2019
512KBが実はSRAM3つに分割されてるせいっぽい。あとは連続して確保が必要なので断片化してても減るそうな。
heap_caps_get_largest_free_block関数でnewできる最大容量が取得可能と。 pic.twitter.com/26hO2QYSIJ
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が発売されたので移植したんですが小さくて良いですね。
Google Homeに「OK Google ブロードキャストして」と言わないでブロードキャストする装置できた!
— ode (@odetarou) December 11, 2019
毎回言うの面倒だったのがボタン1つで。#ESP32 #I2SMic pic.twitter.com/BzUHnqPh4w