ESP32でマイクの音に反応させるために、最初は安価なMAX4466搭載のアナログマイクを使用した。

ESP32のAD入力を使用し、マイク入力を得ると静音時この様なグラフになる。

VCCは3.3Vに接続。問題は無線LANを有効にした時に発生した。無線LANを有効にした所、ノイズが乗り始めた。

ブレッドボード上で短いワイヤーを使っている間はノイズはほぼ出ないが、ジャンパワイヤーでマイクを伸ばすと、上記の様にノイズが乗る。

電源を分けたりコンデンサをかましてみたりしたが、どうも電源ラインからノイズが入る様で、別マイコンに分けない限りどうやってもノイズを消す事が出来なかった。
サンプルスケッチは次の通り。
#define USE_WIFI
#ifdef USE_WIFI
#include <WiFi.h>
char ssid[] = "YourNetworkName"; //WiFiのSSID
char pass[] = "YourPassword"; //WiFiのパスワード
#endif
//VCC - 3.3V
//GND - GND
//OUT - GPIO35
#define NUM_HISTORY 60 //マイク入力履歴保持数(直近で最大を取るため)
int mic_history[NUM_HISTORY]; //直近のマイク入力値、フィルタリング後
int mic_index = 0; //保存位置
#define MIC_PIN 35
void setup() {
Serial.begin(115200);
#ifdef USE_WIFI
WiFi.mode(WIFI_STA);
if (String(WiFi.SSID()) != String(ssid)) {
WiFi.begin(ssid, pass);
}
#endif
}
void loop() {
int micin;
unsigned long micavg = 0;
int micinr = analogRead(MIC_PIN);
mic_history[mic_index++] = micinr;
if (mic_index == NUM_HISTORY)
mic_index = 0;
micin = 0;
for (int i = 0; i < NUM_HISTORY; i++) //最近のマイク入力の平均値
micavg += (unsigned long)mic_history[i];
micavg = micavg / NUM_HISTORY;
for (int i = 0; i < NUM_HISTORY; i++) //最近のマイク入力の最大のふり幅
micin = max(micin, abs(mic_history[i] - (int)micavg));
Serial.printf("2500,%d,%d,0\n", micinr, micin);
delay(10);
}
他のマイクではどうなのかと思い、スイッチサイエンスから購入できるMEMSマイク4種類を試してみる事にした。
まずはSPW2430のマイク

https://www.switch-science.com/catalog/2450/
ピンが5つあるが、使うのはVIN、GNDとDCの3本でDCからアナログ出力。MAX4466と似たような感じである。
無線LAN無しの場合。

無線LANありの場合。

やはりアナログ系は無線LANの影響を受ける様だ。サンプルスケッチは以下の通り。
#define USE_WIFI
#ifdef USE_WIFI
#include <WiFi.h>
char ssid[] = YourNetworkName"; //WiFiのSSID
char pass[] = YourPassword"; //WiFiのパスワード
#endif
#define NUM_HISTORY 60 //マイク入力履歴保持数(直近で最大を取るため)
int mic_history[NUM_HISTORY]; //直近のマイク入力値、フィルタリング後
int mic_index = 0; //保存位置
#define MIC_PIN 35
void setup() {
Serial.begin(115200);
#ifdef USE_WIFI
WiFi.mode(WIFI_STA);
if (String(WiFi.SSID()) != String(ssid)) {
WiFi.begin(ssid, pass);
}
#endif
}
void loop() {
int micin;
unsigned long micavg = 0;
int micinr = analogRead(MIC_PIN) - 700;
mic_history[mic_index++] = micinr;
if (mic_index == NUM_HISTORY)
mic_index = 0;
micin = 0;
for (int i = 0; i < NUM_HISTORY; i++) //最近のマイク入力の平均値
micavg += (unsigned long)mic_history[i];
micavg = micavg / NUM_HISTORY;
for (int i = 0; i < NUM_HISTORY; i++) //最近のマイク入力の最大のふり幅
micin = max(micin, abs(mic_history[i] - (int)micavg));
Serial.printf("300,%d,%d,0\n", micinr, micin);
delay(10);
}
次はSPH0645のI2S通信タイプ。アナログと違い、ちょっと扱い(スケッチ)が難しい。チップ下部にマイクの口があり、基板裏側に穴が開いている。


https://www.switch-science.com/catalog/3207/
無線LAN無しの場合。

無線LANありの場合。

I2Sタイプのマイクであれば、無線LANの影響を受けない様だ。ただ、使い方によるのか小さい音は拾ってくれなかった。
スケッチは以下の通り。I2Sマイクは左右チャンネルの概念があり、LRピンをVCCに繋ぐと右、GNDに繋ぐと左の音としてデータが扱われる。
I2S通信は難しいので、I2S通信の情報を提供しているサイトの方に感謝。
参考:
http://k-hiura.cocolog-nifty.com/blog/2019/06/post-60619f.html
https://github.com/maspetsberger/esp32-i2s-mems/blob/master/examples/InputSerialPlotter/InputSerialPlotter.ino
//3V - 3.3V
//GND - GND
//BCLK - GPIO25
//DOUT - GPIO26
//LRCL - GPIO27
//SEL - GND
#define USE_WIFI
#ifdef USE_WIFI
#include <WiFi.h>
char ssid[] = YourNetworkName"; //WiFiのSSID
char pass[] = YourPassword"; //WiFiのパスワード
#endif
#define NUM_HISTORY 60 //マイク入力履歴保持数(直近で最大を取るため)
int mic_history[NUM_HISTORY]; //直近のマイク入力値、フィルタリング後
int mic_index = 0; //保存位置
#include <driver/i2s.h>
const i2s_port_t I2S_PORT = I2S_NUM_0;
const i2s_config_t i2s_config = {
.mode = i2s_mode_t(I2S_MODE_MASTER | I2S_MODE_RX),
.sample_rate = 16000, // サンプリング 16kHz
.bits_per_sample = I2S_BITS_PER_SAMPLE_32BIT, // 32bit以外不可
.channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT, //右チャンネル
.communication_format = i2s_comm_format_t(I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB),
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, // Interrupt level 1
.dma_buf_count = 4, // number of buffers
.dma_buf_len = 8 // 8 samples per buffer
};
const i2s_pin_config_t pin_config = {
.bck_io_num = 25, // BCLK
.ws_io_num = 27, // LRCL
.data_out_num = -1, // n/a
.data_in_num = 26 // DOUT
};
void setup() {
Serial.begin(115200);
#ifdef USE_WIFI
WiFi.mode(WIFI_STA);
if (String(WiFi.SSID()) != String(ssid)) {
WiFi.begin(ssid, pass);
}
#endif
esp_err_t _err;
_err = i2s_driver_install(I2S_PORT, &i2s_config, 0, NULL);
if (_err != ESP_OK) {
Serial.printf("Failed installing driver: %d\n", _err);
while (true);
}
_err = i2s_set_pin(I2S_PORT, &pin_config);
if (_err != ESP_OK) {
Serial.printf("Failed setting pin: %d\n", _err);
while (true);
}
Serial.println("I2S driver installed.");
delay(5000);
}
void loop() {
// Read a single sample and log it for the Serial Plotter.
int32_t micinr = 0;
int bytes_read = i2s_pop_sample(I2S_PORT, (char *)&micinr, portMAX_DELAY); // no timeout
if (bytes_read > 0) {
unsigned long micavg = 0;
int micin = 0;
//Serial.println(micinr);
micinr = (micinr >> 15) + 7700;
//Serial.println(micinr);
mic_history[mic_index++] = constrain(abs(micinr), 0, 32767);
if (mic_index == NUM_HISTORY)
mic_index = 0;
for (int i = 0; i < NUM_HISTORY; i++) //最近のマイク入力の最大のふり幅
micin = max(micin, mic_history[i]);
Serial.printf("-1500,1500,%d,%d\n", micinr, micin);
}
delay(10);
}
同じくI2Sタイプで MSM261S4030H0 のチップを使ったSipeed 1マイクモジュール。一つ374円と他のAdafruit MEMSマイクと比べても半額以下で安い(Adafruit系はそもそも高いが)。

https://www.switch-science.com/catalog/5709/
結論から言うと、このマイクがノイズ耐性も感度も一番良好だった。
無線LAN無しの場合

無線LANありの場合

小さい音でも反応が良かった。スケッチは以下の通り。
//VCC - 3.3V
//GND - GND
//WS - GPIO27
//LR - 3.3V (right)
//CLK - GPIO25
//DAT - GPIO26
#define USE_WIFI
#ifdef USE_WIFI
#include <WiFi.h>
char ssid[] = "YourNetworkName"; //WiFiのSSID
char pass[] = "YourPassword"; //WiFiのパスワード
#endif
#define NUM_HISTORY 60 //マイク入力履歴保持数(直近で最大を取るため)
int mic_history[NUM_HISTORY]; //直近のマイク入力値、フィルタリング後
int mic_index = 0; //保存位置
#include <driver/i2s.h>
const i2s_port_t I2S_PORT = I2S_NUM_0;
const i2s_config_t i2s_config = {
.mode = i2s_mode_t(I2S_MODE_MASTER | I2S_MODE_RX),
.sample_rate = 16000, // サンプリング 16kHz
.bits_per_sample = I2S_BITS_PER_SAMPLE_32BIT, // 32bit以外不可
.channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT, //右チャンネル
.communication_format = i2s_comm_format_t(I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB),
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, // Interrupt level 1
.dma_buf_count = 4, // number of buffers
.dma_buf_len = 8 // 8 samples per buffer
};
const i2s_pin_config_t pin_config = {
.bck_io_num = 25, // BCLK
.ws_io_num = 27, // LRCL
.data_out_num = -1, // n/a
.data_in_num = 26 // DOUT
};
void setup() {
Serial.begin(115200);
#ifdef USE_WIFI
WiFi.mode(WIFI_STA);
if (String(WiFi.SSID()) != String(ssid)) {
WiFi.begin(ssid, pass);
}
#endif
esp_err_t _err;
_err = i2s_driver_install(I2S_PORT, &i2s_config, 0, NULL);
if (_err != ESP_OK) {
Serial.printf("Failed installing driver: %d\n", _err);
while (true);
}
_err = i2s_set_pin(I2S_PORT, &pin_config);
if (_err != ESP_OK) {
Serial.printf("Failed setting pin: %d\n", _err);
while (true);
}
Serial.println("I2S driver installed.");
delay(5000);
}
void loop() {
// Read a single sample and log it for the Serial Plotter.
int32_t micinr = 0;
int bytes_read = i2s_pop_sample(I2S_PORT, (char *)&micinr, portMAX_DELAY); // no timeout
if (bytes_read > 0) {
unsigned long micavg = 0;
int micin = 0;
micinr = micinr >> 15;
mic_history[mic_index++] = constrain(abs(micinr), 0, 32767);
if (mic_index == NUM_HISTORY)
mic_index = 0;
for (int i = 0; i < NUM_HISTORY; i++) //最近のマイク入力の最大のふり幅
micin = max(micin, mic_history[i]);
Serial.printf("-1500,1500,%d,%d\n", micinr, micin);
}
delay(10);
}
最後にMP34DT01-Mのチップを搭載し、PDM方式と言うアナログでもI2Sでもない方式のマイク。

https://www.switch-science.com/catalog/3667/
AdafruitのサイトにあるライブラリはESP32ではビルド出来ずどう扱うのか悩んだが、 https://esp32.com/viewtopic.php?t=6062 の情報を元にI2S通信と同様の方法で入力を得る事が出来た。
無線LAN無しの場合

無線LANありの場合

こちらもノイズの影響は無く、感度も悪くは無かった。
サンプルスケッチは以下の通り。
//3V - 3V
//GND - GND
//SEL - no connection
//CLK - 25
//DAT - 26
#define USE_WIFI
#ifdef USE_WIFI
#include <WiFi.h>
char ssid[] = "YourNetworkName"; //WiFiのSSID
char pass[] = "YourPassword"; //WiFiのパスワード
#endif
#define NUM_HISTORY 60 //マイク入力履歴保持数(直近で最大を取るため)
int mic_history[NUM_HISTORY]; //直近のマイク入力値、フィルタリング後
int mic_index = 0; //保存位置
#include <driver/i2s.h>
const i2s_port_t I2S_PORT = I2S_NUM_0;
const i2s_config_t i2s_config = {
.mode = i2s_mode_t(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_PDM),
.sample_rate = 16000, // サンプリング 16kHz
.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
.channel_format = I2S_CHANNEL_FMT_ALL_RIGHT,
.communication_format = i2s_comm_format_t(I2S_COMM_FORMAT_I2S),
.intr_alloc_flags = 0, // Interrupt level
.dma_buf_count = 2, // number of buffers
.dma_buf_len = 128 // samples per buffer
};
const i2s_pin_config_t pin_config = {
.bck_io_num = -1, // BCLK
.ws_io_num = 25, // LRCL
.data_out_num = -1, // n/a
.data_in_num = 26 // DOUT
};
void setup() {
Serial.begin(115200);
#ifdef USE_WIFI
WiFi.mode(WIFI_STA);
if (String(WiFi.SSID()) != String(ssid)) {
WiFi.begin(ssid, pass);
}
#endif
esp_err_t _err;
_err = i2s_driver_install(I2S_PORT, &i2s_config, 0, NULL);
if (_err != ESP_OK) {
Serial.printf("Failed installing driver: %d\n", _err);
while (true);
}
_err = i2s_set_pin(I2S_PORT, &pin_config);
if (_err != ESP_OK) {
Serial.printf("Failed setting pin: %d\n", _err);
while (true);
}
i2s_set_clk(I2S_NUM_0, 44100, I2S_BITS_PER_SAMPLE_16BIT, I2S_CHANNEL_MONO);
Serial.println("I2S driver installed.");
delay(5000);
}
void loop() {
// Read a single sample and log it for the Serial Plotter.
int16_t micinr = 0;
int bytes_read = i2s_pop_sample(I2S_PORT, (char *)&micinr, portMAX_DELAY); // no timeout
if (bytes_read > 0) {
//Serial.println(micinr);
unsigned long micavg = 0;
int micin = 0;
mic_history[mic_index++] = micinr;
if (mic_index == NUM_HISTORY)
mic_index = 0;
for (int i = 0; i < NUM_HISTORY; i++) //最近のマイク入力の平均値
micavg += (unsigned long)mic_history[i];
micavg = micavg / NUM_HISTORY;
for (int i = 0; i < NUM_HISTORY; i++) //最近のマイク入力の最大のふり幅
micin = max(micin, abs(mic_history[i] - (int)micavg));
Serial.printf("1500,%d,%d,0\n", micinr, micin);
}
delay(10);
}
マイクの使い方スケッチ含め、参考になれば幸いである。