今回からマイクの接続になります。前回でアンプから音が出せる様になっています。今、ESP32が2台有るので、一方でサイン波を出してもう一方のESP32のマイクでそれを拾い、IDEのシリアルプロッタを使用して画面に波形を出力します。
アンプ同様マイクも、i2s_config_tとi2s_pin_config_tの設定から初めます。アンプの設定と比較して説明して行きます。
#include <driver/i2s.h>
#define I2S_NUM_MIC I2S_NUM_1
#define I2S_PIN_CLK_MIC 27
#define I2S_PIN_WS_MIC 25
#define I2S_PIN_DOUT_MIC I2S_PIN_NO_CHANGE
#define I2S_PIN_DIN_MIC 26
#define I2S_SAMPLE_RATE 32000
#define I2S_BUFFER_COUNT 4
#define I2S_BUFFER_SIZE 512
uint8_t recBuffer[I2S_BUFFER_SIZE];
void setup() {
Serial.begin(115200);
i2s_config_t i2s_config_MIC = {
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX),
.sample_rate = I2S_SAMPLE_RATE,
.bits_per_sample = I2S_BITS_PER_SAMPLE_32BIT,
.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT,
.communication_format = I2S_COMM_FORMAT_I2S,
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
.dma_buf_count = I2S_BUFFER_COUNT,
.dma_buf_len = I2S_BUFFER_SIZE,
.use_apll = false,
.tx_desc_auto_clear = false,
.fixed_mclk = 0
};
i2s_pin_config_t pin_config_MIC = {
.bck_io_num = I2S_PIN_CLK_MIC,
.ws_io_num = I2S_PIN_WS_MIC,
.data_out_num = I2S_PIN_DOUT_MIC,
.data_in_num = I2S_PIN_DIN_MIC,
};
i2s_driver_install(I2S_NUM_MIC, &i2s_config_MIC, 0, NULL);
i2s_set_pin(I2S_NUM_MIC, &pin_config_MIC);
}
void loop()
{
int a;
size_t transBytes;
int32_t* val;
i2s_read(I2S_NUM_MIC, (char*)recBuffer, I2S_BUFFER_SIZE, &transBytes, portMAX_DELAY);
for(a=0; a< I2S_BUFFER_SIZE; a += 4)
{
val = (int32_t*)&recBuffer[a];
Serial.println(*val);
}
}
- 3行:define I2S_NUM_MIC I2S_NUM_1
- 使用するI2Sの番号を宣言している箇所です。
- アンプ時は、I2S_NUM_0を使用しました。
- 今回は、I2S_NUM_1を使用します。
- 4から7行:使用するピンの定義です。回路は、ここを参照下さい。
- アンプの時は、DOUTを使用していましたが、今回はマイクなのでDINのみ使用となります。
- 11、12行
- 以前はっきりと分かっていないバッファーの数と長さですが、取り敢えずアンプと同じ値を使う事にしました。
- 14行: uint8_t recBuffer[I2S_BUFFER_SIZE];
- マイクから読み込んだデータ保管用バッファー。
- 符号無し8ビットで512バイト確保。
- これも幾つが適切かよく分かっていません。
- 19から31行: i2s_config_tの設定。
- 20行:.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX),
- I2Sのモードを設定する箇所。
- マイクなので、I2S_MODE_RXを指定。(アンプの時は、I2S_MODE_TX)
- それ以外は、アンプの時と同じ値を使用。
- 20行:.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX),
- 32から37行: i2s_pin_config_tの設定
- 39,40行: I2Sドライバーのインストールとピン設定。
- 49行:i2s_read(I2S_NUM_MIC, (char*)recBuffer, I2S_BUFFER_SIZE, &transBytes, portMAX_DELAY);
- マイクからデータを読み込むの関数。
- 第一引数: I2Sの番号。今回は、I2S_NUM_1(1番)
- 第二引数: 保存するバッファーのポインター
- 第三引数: バッファーのサイズ
- 第四引数: 読み込まれたデータの数。
- 第五引数: タイムアウト指定。portMAX_DELAY指定でタイムアウト無し。
- 51から55行: 読み込んだデータを32ビットに変換。
- 読み込んだデータが8ビット形式なので32ビットに変換
- 54行で値を表示
一方のESP32に音を出力するプログラムを実行して、もう一方のESP32でこのスケッチを実行して下さい。シリアルプロッタにこんな波形が出ると思います。波形からサイン波が録音出来ている事が分かります。
サンプリングとデータの数から音の周波数は500Hzでは無いかと予想しています。この波形は500Hzなのでしょうか。ちなみに横軸の単位は何なんでしょうか。プロットしている部分のスケッチを確認すると
for(a=0; a< I2S_BUFFER_SIZE; a += 4)
{
val = (int32_t*)&recBuffer[a];
Serial.println(*val);
}
測定した8ビットのデータを32ビットへの変換し、変換が終わったらプロットしなさいって事です。波形はサイン波になると思いますが500Hzには成らない。
横軸はプロットされた数と仮定する。今回は1波長128個。プロットされた波形は1波長約80個(目分量)。少なくとも128ではない。横軸は何なんだろう。
マイクで拾ってアンプに出力
マイクの出力をプロッタに出せる様になったので、今度はアンプに繋ぎます。スケッチは、アンプとマイクの設定を行えば良いので、こんな感じになります。
#include <driver/i2s.h>
// AMP Port
#define I2S_NUM_AMP I2S_NUM_0
#define I2S_PIN_WS_AMP 4
#define I2S_PIN_CLK_AMP 16
#define I2S_PIN_DOUT_AMP 17
#define I2S_PIN_DIN_AMP I2S_PIN_NO_CHANGE
// MIC Port
#define I2S_NUM_MIC I2S_NUM_1
#define I2S_PIN_CLK_MIC 27
#define I2S_PIN_WS_MIC 25
#define I2S_PIN_DOUT_MIC I2S_PIN_NO_CHANGE
#define I2S_PIN_DIN_MIC 26
#define I2S_SAMPLE_RATE 32000
#define I2S_BUFFER_COUNT 2
#define I2S_BUFFER_SIZE 512
//DMA Buffer
uint8_t recBuffer[I2S_BUFFER_SIZE];
// Initiate AMP
void i2s_AMP_Init()
{
i2s_config_t i2s_config_AMP = {
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX),
.sample_rate = I2S_SAMPLE_RATE,
.bits_per_sample = I2S_BITS_PER_SAMPLE_32BIT,
.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT,
.communication_format = I2S_COMM_FORMAT_I2S,
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
.dma_buf_count = I2S_BUFFER_COUNT,
.dma_buf_len = I2S_BUFFER_SIZE,
.use_apll = false,
.tx_desc_auto_clear = false,
.fixed_mclk = 0
};
i2s_pin_config_t pin_config_AMP = {
.bck_io_num = I2S_PIN_CLK_AMP,
.ws_io_num = I2S_PIN_WS_AMP,
.data_out_num = I2S_PIN_DOUT_AMP,
.data_in_num = I2S_PIN_DIN_AMP,
};
i2s_driver_install(I2S_NUM_AMP, &i2s_config_AMP, 0, NULL);
i2s_set_pin(I2S_NUM_AMP, &pin_config_AMP);
}
// Initiate MIC
void i2s_MIC_Init()
{
i2s_config_t i2s_config_MIC = {
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX),
.sample_rate = I2S_SAMPLE_RATE,
.bits_per_sample = I2S_BITS_PER_SAMPLE_32BIT,
.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT,
.communication_format = I2S_COMM_FORMAT_I2S,
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
.dma_buf_count = I2S_BUFFER_COUNT,
.dma_buf_len = I2S_BUFFER_SIZE,
.use_apll = false,
.tx_desc_auto_clear = false,
.fixed_mclk = 0
};
i2s_pin_config_t pin_config_MIC = {
.bck_io_num = I2S_PIN_CLK_MIC,
.ws_io_num = I2S_PIN_WS_MIC,
.data_out_num = I2S_PIN_DOUT_MIC,
.data_in_num = I2S_PIN_DIN_MIC,
};
i2s_driver_install(I2S_NUM_MIC, &i2s_config_MIC, 0, NULL);
i2s_set_pin(I2S_NUM_MIC, &pin_config_MIC);
}
void setup()
{
Serial.begin(115200);
i2s_MIC_Init();
i2s_AMP_Init();
}
void loop()
{
int32_t buf[128];
int a,b;
size_t transBytes;
int32_t* val;
i2s_read(I2S_NUM_MIC, (char*)recBuffer, I2S_BUFFER_SIZE, &transBytes, portMAX_DELAY);
b=0;
for(a=0; a< I2S_BUFFER_SIZE; a += 4)
{
val = (int32_t*)&recBuffer[a];
buf[b]= *val;
b ++;
}
i2s_write_bytes(I2S_NUM_AMP, buf, sizeof(buf), portMAX_DELAY);
}
- アンプの設定を、i2s_AMP_Init()で。マイクの設定を、void i2s_MIC_Init()で行っています。
- アンプ側のI2Sの番号を、I2S_NUM_0(0番)。マイク側を、I2S_NUM_1(1番)としています。
- 94行:i2s_read(I2S_NUM_MIC, (char*)recBuffer, I2S_BUFFER_SIZE, &transBytes, portMAX_DELAY);
- ここで、マイクの音を読み込み
- 104行:i2s_write_bytes(I2S_NUM_AMP, buf, sizeof(buf), portMAX_DELAY);
- ここで、マイクのデータをアンプに送っています
コンパイル後実行して下さい。サイン波を出しているESP32と同じ音がもう一方のESP32(このスケッチを実行しているESP32)のスピーカーから聞こえたと思います。これでアンプとマイクが動作しました。
前回理解出来なかった、”dma_buf_count/dma_buf_len”ですが、アンプと同じ値を使用して取り敢えず動いている様です。今回新たに保存用のバッファーが出てきました。疑問が深まりますが、次回はこの音声データをUDPで送信してみたいと思います。