项目简介
本项目是一套基于 C++ 和 Arduino 的噪声检测系统,通过采集声音数据,检测异常噪声,并触发 MP3 模块播放特定声音以干预。系统还具有数据记录、分析及远程通信等功能,支持未来的拓展。
功能
-
实时检测:通过麦克风采集声音数据,利用 C++ 进行信号处理
-
分贝判断:通过均方根(RMS)计算声音分贝,检测异常声音
-
Arduino 控制:检测到噪声后,通过COM8串口通信触发 Arduino 控制 MP3 模块播放声音
-
智能化检测时间:支持定时检测
-
数据延迟处理:避免连续误判,触发后暂停 3 分钟再检测
-
数据精确化:支持多 Max9814 模块协同检测,提高检测准确性
-
远程传输:可结合 ESP 模块进行长距离数据传输环境配置
硬件需求
-
Max9814 麦克风模块
-
MP3-TF-16P模块+8欧0.5w喇叭+TF卡
-
arduino nano
-
-
MP3模块的测试
1.首先格式化 TF 卡,放入要放的.mp3,命名成00001这样的格式。
2.用arduino来测试或直接短接 ADKEY 和 GND 这两个管脚,再对模块进行上电,这样模块会循环播放 TF 卡 或者 U 盘里面的所有文件
3.用arduino测试的接线
4.模块正常,板子的LED会闪一下,MP3模块只会在输出时才会亮
MP3模块 | arduino |
VCC | 5V |
GND | GND |
RX | 5 |
TX | 4 |
SPK1 | 扬声器正/负 |
SPK2 | 扬声器正/负 |
#include "DFRobotDFPlayerMini.h"
#include <SoftwareSerial.h>
SoftwareSerial mySerial(4, 5); // RX, TX
DFRobotDFPlayerMini myDFPlayer;
void setup() {
Serial.begin(115200);
mySerial.begin(9600);
if (!myDFPlayer.begin(mySerial)) {
Serial.println("DFPlayer Mini 初始化失败!");
while (1);
}
Serial.println("DFPlayer Mini 初始化成功!");
myDFPlayer.volume(20); // 设置音量 0~30
myDFPlayer.play(1); // 播放第一首歌曲
}
void loop() {
}
模块测试成功后,就可以进行下面的步骤
Max9814的接线
arduino | Max9814 |
VCC | 5V |
GND | GND |
A0 | OUT |
上传arduino的代码
#include "DFRobotDFPlayerMini.h"
#include <SoftwareSerial.h>
// 硬件定义
#define MIC_PIN A0 // 麦克风模拟输入(连接传感器模块)
#define SAMPLE_SIZE 128 // 每次采集128个样本
#define THRESHOLD 500 // 用于调试的声音强度阈值
#define LED_PIN 13 // LED 指示灯
// MP3 模块相关(DFPlayer Mini 使用 SoftwareSerial 通信)
SoftwareSerial mySerial(2, 3); // DFPlayer Mini 的 RX, TX 连接到数字2和3
DFRobotDFPlayerMini myDFPlayer;
void setup() {
Serial.begin(115200); // 与 PC 通信
mySerial.begin(9600); // 与 DFPlayer Mini 通信
pinMode(MIC_PIN, INPUT);
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, LOW);
// 初始化 DFPlayer Mini
if (!myDFPlayer.begin(mySerial)) {
Serial.println("DFPlayer Mini 初始化失败!");
while (1); // 初始化失败时停在这里
}
Serial.println("DFPlayer Mini 初始化成功!");
myDFPlayer.volume(20); // 设置音量,范围 0~30
// 去除自动播放,等待 PC 控制
delay(2000); // 等待 DFPlayer 完全初始化
}
void loop() {
// 采集声音数据,形成一行字符串(128 个采样点,逗号分隔)
String data = "";
for (int i = 0; i < SAMPLE_SIZE; i++) {
int reading = analogRead(MIC_PIN);
data += String(reading);
if (i < SAMPLE_SIZE - 1) {
data += ",";
}
delayMicroseconds(500); // 控制采样间隔
}
// 将采集到的数据发送给 PC
Serial.println(data);
// 检查是否收到来自 PC 的指令
if (Serial.available()) {
String command = Serial.readStringUntil('\n');
command.trim(); // 去掉空白字符
if (command == "play_sound") {
Serial.println("播放声音中...");
myDFPlayer.play(2); // 播放第二首歌曲
digitalWrite(LED_PIN, HIGH);
delay(500);
digitalWrite(LED_PIN, LOW);
}
}
delay(50); // 避免过于频繁的采样
}
VS的代码
#include <iostream>
#include <sstream>
#include <vector>
#include <cmath>
#include <string>
#include <windows.h>
#define SAMPLES 128
#define THRESHOLD 49 //设置所需要分贝阈值 可以一下面的为参考
/*疗养区、高级别墅区、高级宾馆区,昼间50dB(分贝)、夜间(指22时至次日6时,下同)40dB
以居住、文教机关为主的区域,昼间55dB、夜间45dB;
居住、商业、工业混杂区,昼间60dB、夜间50dB;
工业区,昼间65dB、夜间55dB;
城市中的道路交通干线道路、内河航道、铁路主次干线两侧区域,昼间70dB、夜间55dB*/
#define WAIT_TIME 180000 // 3 分钟等待时间(根据根据实际需要来设置)
#define PREHEAT_TIME 60000 // 预热时间,主要是扬声器启动会有噪声,可能会误判
using namespace std;
HANDLE hSerial;
// 打开串口并设置超时参数
bool openSerialPort(const string& portName) {
hSerial = CreateFileA(portName.c_str(),
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
0,
NULL);
if (hSerial == INVALID_HANDLE_VALUE) {
cerr << "无法打开串口 " << portName << ",错误代码:" << GetLastError() << endl;
return false;
}
// 设置超时参数,防止读取数据时出现粘包或乱码
COMMTIMEOUTS timeouts = { 0 };
timeouts.ReadIntervalTimeout = 50;
timeouts.ReadTotalTimeoutConstant = 50;
timeouts.ReadTotalTimeoutMultiplier = 10;
timeouts.WriteTotalTimeoutConstant = 50;
timeouts.WriteTotalTimeoutMultiplier = 10;
if (!SetCommTimeouts(hSerial, &timeouts)) {
cerr << "设置串口超时失败" << endl;
CloseHandle(hSerial);
return false;
}
DCB dcbSerialParams = { 0 };
dcbSerialParams.DCBlength = sizeof(dcbSerialParams);
if (!GetCommState(hSerial, &dcbSerialParams)) {
cerr << "获取串口状态失败" << endl;
CloseHandle(hSerial);
return false;
}
dcbSerialParams.BaudRate = CBR_115200;
dcbSerialParams.ByteSize = 8;
dcbSerialParams.StopBits = ONESTOPBIT;
dcbSerialParams.Parity = NOPARITY;
if (!SetCommState(hSerial, &dcbSerialParams)) {
cerr << "设置串口参数失败" << endl;
CloseHandle(hSerial);
return false;
}
cout << "串口打开成功: " << portName << endl;
return true;
}
// 逐字符读取直到遇到换行符 ('\n')
string readSerialLine() {
string line;
char ch;
DWORD bytesRead;
while (true) {
if (ReadFile(hSerial, &ch, 1, &bytesRead, NULL) && bytesRead == 1) {
if (ch == '\n')
break;
line.push_back(ch);
}
else {
break;
}
}
return line;
}
// 发送数据到串口
void writeSerialData(const string& data) {
DWORD bytesWritten;
WriteFile(hSerial, data.c_str(), data.size(), &bytesWritten, NULL);
}
// 解析由逗号分隔的字符串,将每个数转换为 double 类型
vector<double> parseSerialData(const string& data) {
vector<double> samples;
stringstream ss(data);
string token;
while (getline(ss, token, ',')) {
try {
samples.push_back(stod(token));
}
catch (...) {
cerr << "数据解析错误: " << token << endl;
}
}
return samples;
}
// 计算分贝值:先计算均方根,再用 20 * log10(rms)
// 这种方法更符合实际的声音强度计算
double calculateDecibels(const vector<double>& data) {
double sum = 0;
for (double sample : data) {
sum += sample * sample;
}
double mean = sum / data.size();
double rms = sqrt(mean);
double db = 20 * log10(rms + 1e-9); // 添加极小值以避免 log(0)
return db;
}
// 等待指定时间,期间持续清空串口数据,避免数据堆积
void waitWithFlush(ULONGLONG waitMs) {
ULONGLONG start = GetTickCount64();
while (GetTickCount64() - start < waitMs) {
// 清空接收到的数据
string dummy = readSerialLine();
Sleep(100);
}
}
int main() {
string portName = "COM8"; // 根据实际情况调整串口号
if (!openSerialPort(portName)) {
return -1;
}
cout << "预热 1 分钟,清空初始数据..." << endl;
waitWithFlush(PREHEAT_TIME);
cout << "预热结束,开始检测..." << endl;
while (true) {
string line = readSerialLine();
if (!line.empty()) {
cout << "Received: " << line << endl;
vector<double> samples = parseSerialData(line);
if (samples.size() >= SAMPLES) {
double dbLevel = calculateDecibels(samples);
cout << "Sound Level (dB): " << dbLevel << endl;
if (dbLevel > THRESHOLD) {
cout << "噪声 detected, sending signal to Arduino" << endl;
writeSerialData("play_sound\n");
// 检测到后进入等待循环3分钟,同时清空串口数据
waitWithFlush(WAIT_TIME);
}
else {
cout << "No snore detected" << endl;
}
}
}
Sleep(100);
}
CloseHandle(hSerial);
return 0;
}
运行的效果
未来拓展
1.多传感器布置:使用多个 MAX9814 传感器进行空间覆盖,提升检测精度
2.远程监控:结合 ESP 模块,实现远程数据上传和监控
3.智能算法优化:基于更多样本训练更精确的数学模型
4.噪声抵消:利用 MP3 模块播放与噪声相反的声波进行主动噪声消除