基于 C++ 和 Arduino实现实时噪音检测(附源码)

项目简介

本项目是一套基于 C++ 和 Arduino 的噪声检测系统,通过采集声音数据,检测异常噪声,并触发 MP3 模块播放特定声音以干预。系统还具有数据记录、分析及远程通信等功能,支持未来的拓展。

功能

  1. 实时检测:通过麦克风采集声音数据,利用 C++ 进行信号处理

  2. 分贝判断:通过均方根(RMS)计算声音分贝,检测异常声音

  3. Arduino 控制:检测到噪声后,通过COM8串口通信触发 Arduino 控制 MP3 模块播放声音

  4. 智能化检测时间:支持定时检测

  5. 数据延迟处理:避免连续误判,触发后暂停 3 分钟再检测

  6. 数据精确化:支持多 Max9814 模块协同检测,提高检测准确性

  7. 远程传输:可结合 ESP 模块进行长距离数据传输环境配置

硬件需求

  1. Max9814 麦克风模块

  2. MP3-TF-16P模块+8欧0.5w喇叭+TF卡

  3. arduino nano

MP3模块的测试

1.首先格式化 TF 卡,放入要放的.mp3,命名成00001这样的格式。

2.用arduino来测试或直接短接 ADKEY 和 GND 这两个管脚,再对模块进行上电,这样模块会循环播放 TF 卡 或者 U 盘里面的所有文件

3.用arduino测试的接线

4.模块正常,板子的LED会闪一下,MP3模块只会在输出时才会亮

MP3模块arduino
VCC5V
GNDGND
RX5
TX4
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的接线

arduinoMax9814
VCC5V
GNDGND
A0OUT

上传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 模块播放与噪声相反的声波进行主动噪声消除

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值