💡 划重点

  • 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 元

接线与结构

核心代码


#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);
}

调试步骤

  1. 先不接 OLED,只跑 I2S 采样,把原始 RMS 从串口打出来。
  2. 轻敲桌面、播放固定音量音乐,确认数值会明显变化。
  3. 再接 OLED,检查 I2C 地址是否为 0x3C;如果黑屏,先跑扫描程序。
  4. 用滑动平均平稳显示,阈值不要用一次采样结果直接触发。
  5. 如果读数一直为 0,重点检查 INMP441 的 L/R、SCK、WS、SD 四根线。

设计图

INMP441 噪声记录器结构flow
1
I2S 麦克风采集声音
2
ESP32 计算 RMS
3
滑动平均得到趋势值
4
OLED 显示当前噪声
5
超过阈值输出提示和日志