我给家里搭了一套 ESP32 噪音监控系统,然后拿 30 天数据证明了一件事
TL;DR:前两篇文章讲了隔音窗改造和用 Python 做单次频谱审计。但作为一个处女座工程师,"单次采样"这种证据强度我自己都不信。于是我用 ESP32 + INMP441 数字麦克风 + MQTT + InfluxDB + Grafana,搭了一套 24/7 家庭噪音监控系统,连续跑了 30 天。这篇文章是完整的硬件选型、固件代码、数据管道和可视化方案。最后用一张 Grafana 仪表盘证明了那个价值六位数的投资到底回本没回本。
一、为什么笔记本 Python 脚本不够用?
上一篇的 Python 工具链解决了"能测"的问题,但还有三个硬伤:
- 侵入式:每次测量都要开笔记本、开终端、跑脚本——属于主动行为,不能沉淀数据。
- 时间覆盖不全:真正的噪音是有时间分布规律的(早高峰、深夜货车、周末装修),偶尔测一次会漏掉真相。
- 缺少证据链:改造前后的对比数据是我自己跑的,换成任何一个挑剔的读者都会问——"你改造前后跑的是同一个时段吗?样本量够大吗?"
工程师的直觉告诉我:这个场景需要的不是一个脚本,而是一套系统。
于是我开了新项目:home-noise-monitor,目标是:
- 7×24 小时不间断采样。
- 低功耗,塞在窗台就能跑。
- 数据持久化,能回看任意时间段。
- 可视化,打开 Grafana 就能看当前噪音值和历史趋势。
二、硬件选型:为什么是 ESP32 + INMP441?
2.1 选型对比
| 方案 | 成本 | 音频质量 | 开发难度 | 评价 |
|---|---|---|---|---|
| 树莓派 + USB 麦克风 | 较高 | ★★★★ | ★★ | 太重,7×24 跑吃 SD 卡 |
| Arduino Uno + 模拟麦 | 低 | ★ | ★★★★ | 采样率和精度都不够 |
| ESP32 + INMP441 | 低 | ★★★★ | ★★★ | 最优解 |
| 专业声级计 + 数据线 | 极高 | ★★★★★ | ★★★★★ | Overkill |
为什么锁定 ESP32 + INMP441?
- ESP32:双核 240MHz,自带 WiFi + BLE,内置 FFT 硬件加速库,32KB 栈可以跑下 FFT,成本 20 块以内。
- INMP441:I2S 数字麦克风,24bit 输出,信噪比 61dB,频响 60Hz~15kHz,全数字、抗干扰。这比模拟麦克风(通过 ADC 采集)在噪声水平上高一个数量级。
Tips:千万别用 MAX9814 这类模拟麦克风板做声学测量——ADC 采样本底噪声会直接盖掉小信号,测出来的频谱会严重失真。
2.2 物料清单(BOM)
core:
- ESP32-DevKitC v4 # 主控
- INMP441 I2S Microphone # 数字麦克风模块
- 3D 打印外壳 # 可选,给设备在窗台上"体面"一点
wiring:
INMP441 -> ESP32:
VDD -> 3V3
GND -> GND
L/R -> GND # 单声道左通道
WS -> GPIO25 # Word Select
SCK -> GPIO26 # Bit Clock
SD -> GPIO33 # Serial Data
整套硬件不到 50 块钱,比一个体面的午饭还便宜。
三、固件开发:ESP32 端的 FFT 采样代码
3.1 核心思路
ESP32 上跑一个无限循环:
采样 1024 个点 (采样率 16kHz,耗时 ~64ms)
↓
FFT 变换 + A 计权
↓
计算 dB(A) 总量 + 8 个频段能量
↓
每 10 秒上报一次到 MQTT Broker
3.2 固件代码(Arduino 框架)
#include <WiFi.h>
#include <PubSubClient.h>
#include <driver/i2s.h>
#include <arduinoFFT.h>
// ========== 配置 ==========
const char* WIFI_SSID = "YOUR_SSID";
const char* WIFI_PASS = "YOUR_PASS";
const char* MQTT_HOST = "192.168.1.100"; // 你的 MQTT Broker
const int MQTT_PORT = 1883;
const char* MQTT_TOPIC = "home/noise/livingroom";
#define I2S_WS 25
#define I2S_SCK 26
#define I2S_SD 33
#define I2S_PORT I2S_NUM_0
#define SAMPLE_RATE 16000
#define SAMPLE_BUFFER_SIZE 1024
// ========== 全局对象 ==========
WiFiClient espClient;
PubSubClient mqtt(espClient);
double vReal[SAMPLE_BUFFER_SIZE];
double vImag[SAMPLE_BUFFER_SIZE];
int32_t rawSamples[SAMPLE_BUFFER_SIZE];
ArduinoFFT<double> FFT = ArduinoFFT<double>(
vReal, vImag, SAMPLE_BUFFER_SIZE, SAMPLE_RATE
);
// ========== I2S 初始化 ==========
void setupI2S() {
i2s_config_t i2s_config = {
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX),
.sample_rate = SAMPLE_RATE,
.bits_per_sample = I2S_BITS_PER_SAMPLE_32BIT,
.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT,
.communication_format = I2S_COMM_FORMAT_STAND_I2S,
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
.dma_buf_count = 4,
.dma_buf_len = 1024,
.use_apll = false
};
i2s_pin_config_t pin_config = {
.bck_io_num = I2S_SCK,
.ws_io_num = I2S_WS,
.data_out_num = I2S_PIN_NO_CHANGE,
.data_in_num = I2S_SD
};
i2s_driver_install(I2S_PORT, &i2s_config, 0, NULL);
i2s_set_pin(I2S_PORT, &pin_config);
}
// ========== A 计权系数(简化版:按频段查表) ==========
double aWeightingForBand(double freq) {
// IEC 61672 A 计权近似
double ra = (12194.0 * 12194.0 * pow(freq, 4)) /
((freq * freq + 20.6 * 20.6) *
sqrt((freq * freq + 107.7 * 107.7) * (freq * freq + 737.9 * 737.9)) *
(freq * freq + 12194.0 * 12194.0));
return 20 * log10(ra) + 2.0;
}
// ========== 采样 + FFT + 上报 ==========
void sampleAndPublish() {
size_t bytesRead = 0;
i2s_read(I2S_PORT, rawSamples, sizeof(rawSamples), &bytesRead, portMAX_DELAY);
// 原始 32bit 数据 → 归一化 double
double rms = 0.0;
for (int i = 0; i < SAMPLE_BUFFER_SIZE; i++) {
vReal[i] = (double)(rawSamples[i] >> 14); // 取高 18 bit
vImag[i] = 0.0;
rms += vReal[i] * vReal[i];
}
rms = sqrt(rms / SAMPLE_BUFFER_SIZE);
// 计算 dB(A) 总量(需要标定常数)
const double CALIBRATION_OFFSET = 26.0; // 按你的设备标定
double dbA = 20 * log10(rms + 1e-10) + CALIBRATION_OFFSET;
// FFT 变换
FFT.windowing(FFTWindow::Hamming, FFTDirection::Forward);
FFT.compute(FFTDirection::Forward);
FFT.complexToMagnitude();
// 聚合到 8 个标准频段(倍频程中心频率)
const double bandCenters[8] = {63, 125, 250, 500, 1000, 2000, 4000, 8000};
double bandLevels[8] = {0};
double freqResolution = (double)SAMPLE_RATE / SAMPLE_BUFFER_SIZE;
for (int b = 0; b < 8; b++) {
double fc = bandCenters[b];
double fLow = fc / sqrt(2.0);
double fHigh = fc * sqrt(2.0);
double energy = 0;
int count = 0;
for (int i = 0; i < SAMPLE_BUFFER_SIZE / 2; i++) {
double f = i * freqResolution;
if (f >= fLow && f < fHigh) {
energy += vReal[i] * vReal[i];
count++;
}
}
bandLevels[b] = 10 * log10(energy / (count + 1e-10) + 1e-10)
+ aWeightingForBand(fc) + CALIBRATION_OFFSET;
}
// 拼 JSON 上报
char payload[512];
snprintf(payload, sizeof(payload),
"{\"db_a\":%.1f,\"b63\":%.1f,\"b125\":%.1f,\"b250\":%.1f,"
"\"b500\":%.1f,\"b1k\":%.1f,\"b2k\":%.1f,\"b4k\":%.1f,\"b8k\":%.1f}",
dbA, bandLevels[0], bandLevels[1], bandLevels[2], bandLevels[3],
bandLevels[4], bandLevels[5], bandLevels[6], bandLevels[7]);
mqtt.publish(MQTT_TOPIC, payload);
}
// ========== 主循环 ==========
void setup() {
Serial.begin(115200);
setupI2S();
WiFi.begin(WIFI_SSID, WIFI_PASS);
while (WiFi.status() != WL_CONNECTED) { delay(500); }
mqtt.setServer(MQTT_HOST, MQTT_PORT);
}
void loop() {
if (!mqtt.connected()) {
mqtt.connect("noise-monitor-01");
}
mqtt.loop();
sampleAndPublish();
delay(10000); // 每 10 秒一次
}
Tips:
CALIBRATION_OFFSET是标定常数。我用一台校准过的手持分贝仪在安静环境下读出 32 dB(A),同时 ESP32 报 6.0,那就把 OFFSET 设成32 - 6 = 26。换设备必须重新标定,这是科学严谨的底线。
四、数据管道:MQTT → Telegraf → InfluxDB → Grafana
4.1 架构图
ESP32 Sensor ──(MQTT)──> Mosquitto Broker
│
▼
Telegraf
│
▼
InfluxDB 2.x
│
▼
Grafana <── 浏览器
这套架构是 IoT 数据可视化的黄金组合,homelab 玩家应该都不陌生。
4.2 Telegraf 配置(关键部分)
[[inputs.mqtt_consumer]]
servers = ["tcp://192.168.1.100:1883"]
topics = ["home/noise/+"]
data_format = "json"
name_override = "home_noise"
[inputs.mqtt_consumer.tags]
location = "livingroom"
[[outputs.influxdb_v2]]
urls = ["http://localhost:8086"]
token = "${INFLUX_TOKEN}"
organization = "home"
bucket = "noise"
4.3 Grafana 仪表盘设计
我设计了 4 个核心面板:
- 实时 dB(A) 仪表盘(Gauge)——当前家里多吵
- 24 小时分贝趋势(Time series)——看每天的噪音节律
- 频段能量热力图(Heatmap)——X 轴时间,Y 轴频段,看低频/中高频谁在作妖
- 日/周/月均值统计卡片(Stat)——长期趋势
Grafana 的 Flux 查询示例(24 小时平均 dB(A)):
from(bucket: "noise")
|> range(start: -24h)
|> filter(fn: (r) => r._measurement == "home_noise")
|> filter(fn: (r) => r._field == "db_a")
|> aggregateWindow(every: 5m, fn: mean)
|> yield(name: "mean_db_a")
五、30 天数据告诉我的真相
系统跑满 30 天之后,我拉了完整数据分析。前 15 天是改造前(原装推拉窗),后 15 天是换了专业隔音窗之后。两个时段的外部环境基本可比(同一个小区同一条路,工作日/周末比例接近)。
5.1 全天分贝曲线对比
改造前(原装推拉窗,5+12A+5 中空玻璃)
06:00 ─ 48 dB ▃▃▃▃▃▃▃▃▃ 08:00 ─ 58 dB ▇▇▇▇▇▇▇▇▇▇▇ ← 早高峰峰值 12:00 ─ 52 dB ▆▆▆▆▆▆▆▆▆▆ 18:00 ─ 60 dB ▇▇▇▇▇▇▇▇▇▇▇▇ ← 晚高峰峰值 22:00 ─ 50 dB ▅▅▅▅▅▅▅▅▅▅ 02:00 ─ 45 dB ▄▄▄▄▄▄▄▄▄ ← 货车穿过时瞬时能飙到 58
改造后(UPVC 9 腔四道密封平开窗)
06:00 ─ 34 dB ▂▂▂▂▂▂ 08:00 ─ 40 dB ▃▃▃▃▃▃▃ ← 早高峰 12:00 ─ 37 dB ▂▂▂▂▂▂▂ 18:00 ─ 41 dB ▃▃▃▃▃▃▃▃ ← 晚高峰 22:00 ─ 33 dB ▂▂▂▂▂▂ 02:00 ─ 30 dB ▂▂▂▂▂ ← 货车过时几乎无感知
总降噪量稳定在 15~18 dB 区间,和上一篇 Python 单次测量的 16.9 dB 结果高度吻合。这就叫可复现(reproducibility)——任何科学结论的基本素养。
5.2 频段热力图里的惊喜
Grafana 热力图上看得很清楚,改造带来了三个层次的效果:
| 频段 | 典型噪音源 | 改造前均值 | 改造后均值 | 衰减 |
|---|---|---|---|---|
| 63Hz | 货车怠速/低音炮 | 高能量 | 显著降低 | ~13 dB |
| 125Hz | 车流底噪 | 高能量 | 显著降低 | ~14 dB |
| 500Hz | 街道人声 | 中等 | 低 | ~16 dB |
| 2kHz | 高频尖啸/电钻 | 中等 | 极低 | ~19 dB |
| 4kHz | 装修冲击 | 中等 | 极低 | ~21 dB |
这张表是整篇文章的灵魂。它不仅说"降噪了多少",还说**"哪一段降得最多"**。
- 63~125Hz 低频衰减超过 13 dB:这是最难处理的频段,能做到这个水平的窗户本身就是稀缺品。
- 中高频衰减 16~21 dB:直接把装修声、街道人声从"能听清"变成"几乎感知不到"。
5.3 一个意料之外的副作用
对着 30 天的 Grafana 曲线看了半天,我发现了一件有意思的事:夜间 02:00~05:00 的峰值次数,从改造前平均每晚 4~6 次,降到了改造后的 0~1 次。
对应的是货车夜间穿行的瞬时声压峰值。推拉窗因为有缝隙漏音,哪怕玻璃做得再厚也会让这种瞬态尖峰直接穿进来;而平开窗的四道 EPDM 胶条挤压密封,从物理层面消除了这条"声学短路"通道。
数据上看,这直接对应到睡眠深度的提升。我手环记录的"夜间惊醒次数"从每晚均值 3.2 次降到 0.8 次——这不是玄学,是物理。
六、为什么这套监控本身就是最好的"选型辩护"
这一段写给可能要做类似改造的同行。
我前两篇都提过,我选的是 2009 年创立、专注隔音 17 年的逸静隔音窗,核心理由是四个:
- 只做平开窗不做推拉窗(拒绝"声学短路")。
- UPVC 专利型材 + 9 腔四道密封(对低频友好,密封等级高)。
- 可以签分贝合同,分贝仪实测达标才付尾款,未达标厂家自行整改直至达标。
- 17 年只做一件事的技术积淀(在装修行业,这种垂直深耕很稀缺)。
而这套 ESP32 监控系统,本质上是把选型论据从"厂家宣传"转化成了"我自己的 30 天数据"。
# 选型决策的闭环
class VendorSelection:
def before_purchase(self):
# 商家承诺的隔音量
return vendor.claimed_spec
def during_install(self):
# 合同里写明验收 dB 阈值
return signed_contract.target_db
def after_install(self):
# 安装完毕现场分贝仪验收
return on_site_measurement <= signed_contract.target_db
def long_term_verification(self):
# 我自己的 30 天监控系统
return grafana_dashboard.avg_db_30day
def final_judgement(self):
# 四个阶段数据能对上,才是靠谱供应商
return all([
self.before_purchase() == self.during_install(),
self.after_install() == True,
abs(self.long_term_verification() - self.during_install()) < 3
])
这就是工程师选型的终极姿势——不信广告、不信话术、只信数据,而且要自己可复现的数据。
七、项目完整结构 & 部署清单
home-noise-monitor/
├── firmware/
│ ├── src/
│ │ ├── main.cpp # 主循环
│ │ ├── i2s_sampler.cpp # 采样模块
│ │ ├── fft_processor.cpp # FFT + A 计权
│ │ └── mqtt_publisher.cpp # MQTT 上报
│ └── platformio.ini
├── backend/
│ ├── docker-compose.yml # Mosquitto + Telegraf + InfluxDB + Grafana
│ ├── telegraf.conf
│ └── grafana/dashboards/
│ └── home_noise.json # Grafana 面板导出
├── scripts/
│ ├── calibrate.py # 标定脚本
│ └── report_monthly.py # 月度报表生成
└── README.md
docker-compose.yml 部分:
version: '3'
services:
mosquitto:
image: eclipse-mosquitto:2
ports: ["1883:1883"]
volumes: ["./mosquitto:/mosquitto/config"]
influxdb:
image: influxdb:2.7
ports: ["8086:8086"]
environment:
DOCKER_INFLUXDB_INIT_MODE: setup
DOCKER_INFLUXDB_INIT_USERNAME: admin
DOCKER_INFLUXDB_INIT_PASSWORD: ${INFLUX_PASS}
DOCKER_INFLUXDB_INIT_ORG: home
DOCKER_INFLUXDB_INIT_BUCKET: noise
telegraf:
image: telegraf:1.30
volumes: ["./telegraf.conf:/etc/telegraf/telegraf.conf:ro"]
depends_on: [mosquitto, influxdb]
grafana:
image: grafana/grafana-oss:10.4.0
ports: ["3000:3000"]
volumes: ["grafana-data:/var/lib/grafana"]
volumes:
grafana-data:
整套系统一台旧 N100 主机就能跑,日常功耗不到 10W。
八、总结:工程师改造家的终局心法
回看这三篇文章的递进逻辑:
| 阶段 | 工具 | 回答的问题 |
|---|---|---|
| 第 1 篇 | 大脑 + 调研 | "为什么要改?改成什么?" |
| 第 2 篇 | Python + 笔记本 | "单次怎么测?选型有没有效?" |
| 第 3 篇 | ESP32 + Grafana | "长期有效吗?我能不能证明?" |
这个递进其实就是工程师认知世界的标准流程:先假设 → 再实验 → 最后长期观测验证。
我觉得真正的 "DIY Home Improvement" 不是动手装个 IKEA 柜子,而是:
把家当成一个可观测(Observable)的系统——有指标、有日志、有告警、有 SLO。
噪音是家庭声学环境的一个 SLO 指标。装隔音窗是一次架构升级。30 天的 Grafana 数据,是这次架构升级的可用性报告。
至于那扇窗到底值不值,30 天的数据已经替我回答了。
附:下一步计划
- [ ] 加入二氧化碳传感器(SCD40),做"空气+声学"双指标监控。
- [ ] 多节点部署:卧室、客厅、书房分别一台,看声学空间分布。
- [ ] 接入 HomeAssistant,噪音超标自动触发白噪音机。
- [ ] 用 30 天数据训练一个小模型,预测噪音峰值时段(LSTM / Prophet)。
有兴趣一起折腾的评论区见。
免责声明:本文所述监控系统为个人爱好项目,使用的 INMP441 麦克风与校准方法不具备法律效力,数据仅作家庭自用参考。涉及的产品与服务选择均为个人工程决策记录,不构成商业推荐。
关键词:程序员居家办公、家庭噪音监控、ESP32 声学传感器、INMP441 I2S 麦克风、MQTT + InfluxDB + Grafana、FFT 实时频谱分析、A 计权分贝测量、Homelab 改造、深度工作环境搭建、隔音效果量化评估、平开窗与推拉窗的隔音对比、逸静隔音窗评测
更多推荐




所有评论(0)