概要
ESP32にはI2Sインターフェースが内蔵されており、音声の入出力が可能です。ESP32のI2SはDMA経由で動作し、低いCPUの負荷で動作します。8bitのDACも内蔵されており、I2Sに接続して使用できます。
I2Sのライブラリ、サンプルプログラムは esp-idf開発環境[1]で提供されています。使用方法もProgramming Guide[2]に説明してありますが、そこに書いてある方法では、内蔵DACから音声を出力することができませんでした。試行錯誤の結果、若干修正することで出力できるようなったので、その修正内容を紹介します。
esp-idfのドキュメントの更新が追い付いていないだけで、いずれ解決される問題ではないかと思いますが、現時点(2017年5月)では、この情報は、提供されていないようなので、紹介します。
ドキュメントにあるI2S+DACの使い方
ドキュメント、Programming GuideのI2Sのページ[3]にあるI2SとDACを組み合わせて使う方法を以下に示します。大変簡単です。
#include "driver/i2s.h"
#include "freertos/queue.h"
static const int i2s_num = 0; // i2s port number
static const i2s_config_t i2s_config = {
.mode = I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN,
.sample_rate = 44100,
.bits_per_sample = 8, /* must be 8 for built-in DAC */
.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
.communication_format = I2S_COMM_FORMAT_I2S_MSB,
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, // high interrupt priority
.dma_buf_count = 8,
.dma_buf_len = 64
};
...
i2s_driver_install(i2s_num, &i2s_config, 0, NULL); //install and start i2s driver
i2s_set_pin(i2s_num, NULL); //for internal DAC
i2s_set_sample_rates(i2s_num, 22050); //set sample rates
i2s_driver_uninstall(i2s_num); //stop & destroy i2s driver
補足すると、
- i2s_driver_install()で設定した値と違わなければ、i2s_set_sample_rates()は不要。
- i2s_driver_uninstall()も、特にメモリーを解放したいというようなことが無ければ不要。
- データの書き込みは、1サンプルづつ i2s_push_sample() か、まとめてi2s_write_bytes()で行う。
その問題点
ドキュメントどおり作成したプログラムでは、DACの出力から、ちゃんとした音声が出力されません。
私は、次のような現象に遭遇しました。
- i2s_set_sample_rates()で、エラーになります。メッセージは"Invalid bits per sample"
- esp-idf/components/driver/i2s.cを改造し、上記のエラーが出ないようにしても、
- DAC1から、なんか波形は出るが、いろいろ変
- DAC2からは、ノイズしか出力されない
対策
試行錯誤の結果、わかった対策は、以下の通りです。
- .bits_per_sample は 16, "must be 8 for built-in DAC" と書いてあるけど16
- i2s_push_sample(), i2s_write_bytes()でデータを書き込む場合も1サンプルあたり 16bit x 2 = 32bitづつ書き込む。但しDACは8bitなので、下位8bitは無視される
- .dma_buf_countは大目に取る。参考にしたプログラム[4]では14
当初、 .dma_bufの数など2つあれば十分、余裕を持たせて4程度にし、.dma_buf_lenを1024で試したが、うまくいきませんでした。参考にしたプログラム[4]で14となっていたので、同じにしたところ急にうまくいくようになりました。14がマジックナンバーというわけでもなく16でも問題無く動作しました。
デモ・プログラム
実際に、I2S+内蔵DACで動作するプログラムの例として、bluetooth speakerを作成しました。Tw_Mhageさんの「ESP32でBluetoothイヤホンを作ってみた」に触発され、esp-idfのサンプルプログラム a2dp_sinkを改造しています。
サンプリングレート44.1kHz, 12bit stereoに決め打ちです。環境によっては、うまく動かないかもしれませんが、サンプルプログラムとして参考になれば幸いです。
動作の様子をYouTube, ソースプログラムをgithubで公開しています。
- デモ・プログラム・動画 (YouTube)
- https://youtu.be/7-ZSAkkyPiY
- デモ・プログラム・ソース (GitHub)
- https://github.com/h-nari/ESP32_bt_speaker
必要な回路は、下の図程度です。GPIO25,GPIO26にDAC1,DAC2の信号が出力されます。これは固定されていて変更できません。
最後に
ESP32での音声出力は、前からやりたかったことなので、出来て嬉しいです。次は、音声入力に挑戦したいと考えています。音声入出力の機能がArduinoライブラリなどに、まとめられて、簡単に使えるようになるのが理想ですが、現状のIDFやesp32-arduinoの状態を見ると、それには、まだまだ時間がかかりそうな気がします。
参照
- [1]esp-idf 開発環境
- https://github.com/espressif/esp-idf
- [2]ESP-IDF Programming Guide:
- http://esp-idf.readthedocs.io/en/latest/#
- [3]ESP-IDF Programming Guide I2Sのページ:
- http://esp-idf.readthedocs.io/en/latest/api-reference/peripherals/i2s.html
- [4]参考にしたプログラム、ESP32_MP3_Decoder
- https://github.com/MrBuddyCasino/ESP32_MP3_Decoder