💡 划重点
- INMP441 是数字 I2S 麦克风,和普通模拟声音传感器不同,它直接输出采样数据,抗干扰更好。
- 本项目用 ESP32 每秒采样一段音频,计算 RMS 并做近似分贝趋势显示,不把它伪装成专业声级计。
- 练习重点是 I2S 配置、滑动平均、阈值判断和 OLED 可视化。
需求洞察
办公室、教室、机房和工作室里,“吵不吵”往往只靠主观感受。真正有用的是趋势:什么时候噪声升高、持续多久、是否超过自定义阈值。手机 App 可以测,但不能长期固定在一个位置记录。ESP32 加 INMP441 可以做成一个低成本环境噪声记录器,用来观察空间使用习惯。
需要说清楚边界:没有校准声源时,它只能做相对趋势和粗略分贝估算,不能替代专业计量设备。这个诚实边界反而让项目更可靠。
方案设计
感知层使用 INMP441,通过 I2S 总线向 ESP32 输出 24bit 数字音频。控制层每秒读取一批样本,去掉直流偏置后计算 RMS,并做指数滑动平均,避免单次尖峰让屏幕乱跳。执行层使用 OLED 显示当前值和最近趋势,蜂鸣器只在超过阈值并持续数秒后触发。日志层通过串口输出 CSV,后续可接入 MQTT 或保存到 SD 卡。
物料清单
| 元件 | 数量 | 用途 | 估算价 |
|---|---|---|---|
| ESP32 DevKitC | 1 | 主控与数据处理 | 25 元 |
| INMP441 I2S 麦克风 | 1 | 采集环境声音 | 15 元 |
| 0.96 寸 SSD1306 OLED | 1 | 显示实时值和趋势 | 12 元 |
| 有源蜂鸣器 | 1 | 超阈值提示 | 2 元 |
| 面包板与杜邦线 | 1 套 | 原型连接 | 10 元 |
接线与结构
- INMP441 SCK 接 GPIO14,WS 接 GPIO15,SD 接 GPIO32,L/R 接 GND。
- OLED SDA 接 GPIO21,SCL 接 GPIO22,VCC 接 3V3,GND 共地。
- 蜂鸣器正极接 GPIO27,负极接 GND;如果用无源蜂鸣器,需要 PWM 驱动。
- 麦克风开孔不要被外壳挡住,远离蜂鸣器和 USB 供电线,减少机械振动和电源噪声。
核心代码
#include <Arduino.h>
#include <driver/i2s.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define I2S_WS 15
#define I2S_SCK 14
#define I2S_SD 32
#define BUZZER_PIN 27
Adafruit_SSD1306 display(128, 64, &Wire, -1);
float smoothDb = 0;
void setupI2S() {
i2s_config_t cfg = {
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX),
.sample_rate = 16000,
.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 = 4,
.dma_buf_len = 256,
.use_apll = false
};
i2s_pin_config_t pins = {
.bck_io_num = I2S_SCK,
.ws_io_num = I2S_WS,
.data_out_num = -1,
.data_in_num = I2S_SD
};
i2s_driver_install(I2S_NUM_0, &cfg, 0, NULL);
i2s_set_pin(I2S_NUM_0, &pins);
}
float readNoiseDb() {
int32_t samples[512];
size_t bytesRead = 0;
i2s_read(I2S_NUM_0, samples, sizeof(samples), &bytesRead, portMAX_DELAY);
int n = bytesRead / sizeof(int32_t);
double sum = 0;
for (int i = 0; i < n; i++) {
int32_t v = samples[i] >> 14;
sum += (double)v * (double)v;
}
double rms = sqrt(sum / max(n, 1));
return 20.0 * log10(rms + 1.0);
}
void setup() {
Serial.begin(115200);
pinMode(BUZZER_PIN, OUTPUT);
Wire.begin(21, 22);
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
setupI2S();
}
void loop() {
float db = readNoiseDb();
smoothDb = smoothDb == 0 ? db : smoothDb * 0.85 + db * 0.15;
bool high = smoothDb > 68;
digitalWrite(BUZZER_PIN, high ? HIGH : LOW);
display.clearDisplay();
display.setTextColor(SSD1306_WHITE);
display.setTextSize(2);
display.setCursor(0, 0);
display.printf("%.1f", smoothDb);
display.setTextSize(1);
display.setCursor(78, 12);
display.print("dB est");
display.setCursor(0, 42);
display.print(high ? "over threshold" : "normal");
display.display();
Serial.printf("%lu,%.2f\n", millis(), smoothDb);
delay(1000);
}
调试步骤
- 先不接 OLED,只跑 I2S 采样,把原始 RMS 从串口打出来。
- 轻敲桌面、播放固定音量音乐,确认数值会明显变化。
- 再接 OLED,检查 I2C 地址是否为 0x3C;如果黑屏,先跑扫描程序。
- 用滑动平均平稳显示,阈值不要用一次采样结果直接触发。
- 如果读数一直为 0,重点检查 INMP441 的 L/R、SCK、WS、SD 四根线。
设计图
INMP441 噪声记录器结构flow
1
I2S 麦克风采集声音
→
2
ESP32 计算 RMS
→
3
滑动平均得到趋势值
→
4
OLED 显示当前噪声
→
5
超过阈值输出提示和日志