Commit ab6a78c0 authored by 957dd's avatar 957dd

Merge branch 'feature/add_shot_ms' into 'master'

Feature/add shot ms See merge request !3
parents f81a86c8 59a60cb2
......@@ -7,6 +7,7 @@ AudioManager::AudioManager(int bckPin, int lrcPin, int dinPin, int sampleRate, i
: _bckPin(bckPin), _lrcPin(lrcPin), _dinPin(dinPin),
_sampleRate(sampleRate), _dmaBufCount(dmaBufCount), _dmaBufLen(dmaBufLen) {
// 构造函数体
}
// 初始化 I2S 实现
......@@ -53,8 +54,15 @@ bool AudioManager::begin() {
return true;
}
// 请求播放实现
// 请求爆炸声音播放实现
void AudioManager::requestPlay() {
_audioplay_index =false;
_playRequested = true;
}
// 请求滴滴声音声音播放实现
void AudioManager::requestPlay_Drip() {
_audioplay_index =true;
_playRequested = true;
}
......@@ -64,6 +72,55 @@ bool AudioManager::isPlaying() const {
return _playRequested; // 在play()函数结束时需要将此标志设回false
}
// 内部播放函数实现,暂定播放滴滴声
void AudioManager::play_Drip_sound() {
Serial.println("Start playing_Drip_sound audio...");
size_t bytes_written = 0;
int current_pos = 0;
// 创建一个RAM缓冲区,用于从PROGMEM拷贝数据块
uint8_t ram_buffer[_dmaBufLen];
// 循环直到所有音频数据都被发送
while (current_pos < pcm_sound_data_len_Drip) {
int bytes_to_copy = _dmaBufLen;
if (current_pos + bytes_to_copy > pcm_sound_data_len_Drip) {
bytes_to_copy = pcm_sound_data_len_Drip - current_pos;
}
// 从PROGMEM拷贝数据到RAM缓冲区
memcpy_P(ram_buffer, &pcm_sound_data_Drip[current_pos], bytes_to_copy);
// 将RAM缓冲区中的数据写入I2S总线
esp_err_t result = i2s_write(_i2sPort, ram_buffer, bytes_to_copy, &bytes_written, portMAX_DELAY);
if (result != ESP_OK) {
Serial.printf("I2S write error: %d\n", result);
break;
}
// if (bytes_written < bytes_to_copy) {
// Serial.printf("I2S write incomplete: Wrote %d / %d bytes\n", bytes_written, bytes_to_copy);
// }
current_pos += bytes_written; // 更新当前位置
}
// 播放完毕后,清空DMA缓冲区确保静音
i2s_zero_dma_buffer(_i2sPort);
Serial.println("Audio playback finished.");
_playRequested = false; // 重置播放请求标志
// 播放结束后,如果 MQTTManager 指针有效,发送播放结束消息
// if (_mqttManager) {
// Serial.println("Publishing audio play end message...");
// _mqttManager->publishJson("audioplayend", 1);
// }
}
// 内部播放函数实现
void AudioManager::play() {
......@@ -124,7 +181,8 @@ void audioPlayTask(void* pvParameters) {
for (;;) {
if (audioManager->_playRequested) {
audioManager->play();
if(audioManager->_audioplay_index == false) audioManager->play();
if(audioManager->_audioplay_index == true ) audioManager->play_Drip_sound();
}
vTaskDelay(pdMS_TO_TICKS(50)); // 短暂延时,避免忙等
}
......
......@@ -12,25 +12,30 @@ class AudioManager {
private:
// I2S 配置参数
i2s_port_t _i2sPort = I2S_NUM_0; // 使用I2S端口0 (可配置)
int _sampleRate = 16000; // 音频采样率 (Hz) (可配置)
int _sampleRate = 8000; // 音频采样率 (Hz) (可配置)
int _bitsPerSample = I2S_BITS_PER_SAMPLE_16BIT; // 每个采样点的位数
i2s_channel_fmt_t _channelFormat = I2S_CHANNEL_FMT_RIGHT_LEFT; // 通道格式
i2s_channel_fmt_t _channelFormat = I2S_CHANNEL_FMT_ONLY_LEFT;//I2S_CHANNEL_FMT_RIGHT_LEFT; // 通道格式
i2s_comm_format_t _commFormat = I2S_COMM_FORMAT_STAND_I2S; // 通信格式
int _dmaBufCount = 8; // DMA缓冲区数量 (可配置)
int _dmaBufLen = 1024; // 每个DMA缓冲区的长度 (字节) (可配置)
int _dmaBufLen = 2048; // 每个DMA缓冲区的长度 (字节) (可配置)
// I2S 引脚
int _bckPin;
int _lrcPin;
int _dinPin; // MAX98357A 不需要 DIN
bool _audioplay_index = false;
bool _playRequested = false; // 播放请求标志
MQTTManager* _mqttManager = nullptr; // MQTT 管理器指针,用于发送播放结束消息
// 内部播放函数(在任务中调用)
// 内部播放爆炸函数(在任务中调用)
void play();
// 内部播放滴滴函数(在任务中调用)
void play_Drip_sound();
public:
// 构造函数
AudioManager(int bckPin, int lrcPin, int dinPin, int sampleRate = 16000, int dmaBufCount = 8, int dmaBufLen = 1024);
......@@ -38,9 +43,12 @@ public:
// 初始化 I2S
bool begin();
// 请求播放音频(任务会检查此标志)
// 请求播放爆炸音频(任务会检查此标志)
void requestPlay();
// 请求播放滴滴音频(任务会检查此标志)
void requestPlay_Drip();
// 检查当前是否正在播放(如果play()是阻塞的,这个可能没意义,除非在play()内部设置标志)
bool isPlaying() const;
......
......@@ -118,47 +118,66 @@ void DeviceControl::handleMQTTCommand(int message_Type, JsonObject body) {
} else if (msgType == message_type::PWM_CONTROL) { // PWM控制
setPWMValue(pin, val);
}
Serial.printf("Processed command: pin=%d, val=%d (Type %d)\n", pin, val, msgType);
} else if (msgType == message_type::SERSOR_CONTROL) { // 动作控制
if (body.containsKey("action")) {
String action = body["action"].as<String>();
int val = 0;
if (body.containsKey("val")) {
val = body["val"]; // 获取值
}
if (action == "led") {
setLedBlood(val); // 设置 LED 血量Connected
Serial.printf("Processed action: led, val=%d\n", val);
} else if (action == "fog_ms") {
// 原代码中有 time_count=0,这里可以根据需要实现烟雾机控制逻辑
Serial.printf("Processed action: fog_ms, val=%d (Not fully implemented)\n", val);
} // audio 动作在 MQTT 回调中处理并请求 AudioManager 播放
else if (action == "audio") {
audioManager.requestPlay(); // 请求音频播放
_mqttManager->publishJson("audioplayend", 102);
// 原代码中有 time_count=0,这里可以根据需要实现烟雾机控制逻辑
Serial.printf("audio playend\n", val);
}else if (action == "smoke_ms") {
_smoke_count=0;
_smoke_ms=val;
_smoke_index=true;
// 原代码中有 time_count=0,这里可以根据需要实现烟雾机控制逻辑
Serial.printf("smoke set\n");
}else {
Serial.printf("Unknown action: %s\n", action.c_str());
}
Serial.printf("Processed command: pin=%d, val=%d (Type %d)\n", pin, val, msgType);
} else if (msgType == message_type::LED_CONTROL||msgType == message_type::AUDIO_CONTROL||msgType == message_type::SMOKE_CONTROL
||msgType == message_type::LED_INIT_CONTROL) { // 动作控制
if (body.containsKey("action")) {
String action = body["action"].as<String>();
int val = 0;
if (body.containsKey("val")) {
val = body["val"]; // 获取值
}
if (action == "led") {
setLedBlood(val); // 设置 LED 血量Connected
Serial.printf("Processed action: led, val=%d\n", val);
} else if (action == "audio") {// audio 动作在 MQTT 回调中处理并请求 AudioManager 播放
audioManager.requestPlay(); // 请求音频播放
_mqttManager->publishJson("audioplayend", 1);
// 原代码中有 time_count=0,这里可以根据需要实现烟雾机控制逻辑
Serial.printf("audio playend\n", val);
}else if (action == "smoke_ms") {
if(body.containsKey("open")) int open = body["open"];
_smoke_count=0;
_smoke_ms=val;
_smoke_index=true;
if(open == 0) {
setPinValue(4,0);
_smoke_index =false;
}
// 原代码中有 time_count=0,这里可以根据需要实现烟雾机控制逻辑
Serial.printf("smoke set\n",val);
}else if(action =="led_init"){
setLedBlood(val);
Serial.printf("smoke set\n",val);
}else {
Serial.printf("Unknown action: %s\n", action.c_str());
}
}
}
}
void DeviceControl::control_smoke() {
int smoke_i = _smoke_ms / 50;
if(_smoke_count < smoke_i){
setPinValue(4,1);
}else setPinValue(4,0);
void DeviceControl::updatecontrol_smoke() {
if(_smoke_index = true){
int smoke_i = _smoke_ms / 50;
if(_smoke_count < smoke_i){
setPinValue(4,1);
}else setPinValue(4,0);
_smoke_count++;
if(_smoke_count>60000) _smoke_count =60001;
}
}
void DeviceControl::updatebeartheat(){
_beat_count++;
if(_beat_count>60) {
_beat_count=0;
Serial.print("心跳发送");
_mqttManager->publishJson("heartbeat", 1);
}if(_beat_count>100) _beat_count=101;
}
// 更新方法,在周期性任务中调用
......@@ -166,35 +185,34 @@ void DeviceControl::update() {
// 更新 LED 状态
updateLedBlood();
if(_smoke_index = true){
control_smoke();
//更新烟雾模块
_smoke_count++;
if(_smoke_count>2000) _smoke_count =2001;
}
// 更新 烟雾 状态
updatecontrol_smoke();
//更新心跳
updatebeartheat();
// 读取传感器并发布数据 (可以设置一个频率,而不是每次 update() 都发布)
// 简单的实现:每隔一定时间或阈值触发时发布
// static unsigned long lastSensorPublishTime = 0;
// const unsigned long sensorPublishInterval = 50; // 每 1 秒发布一次传感器数据
if(_shot_count < 6){
return ;
}
// if (millis() - lastSensorPublishTime >= sensorPublishInterval) {
double sensorVoltage = readSensor();
// 原始代码中的阈值判断
if (sensorVoltage >= 1.25&& mqttManager.wifi_connected_is()==0) { // 传感器值超过阈值
Serial.println("Sensor threshold reached, publishing shoted message.");
if (_mqttManager) {
// 发布 "shoted" 消息
_mqttManager->publishJson("shoted", 101);
_mqttManager->publishJson("shoted", 1);
Serial.print(sensorVoltage);
} else {
Serial.println("MQTTManager not set, cannot publish sensor data.");
}
}
// lastSensorPublishTime = millis();
// }
_shot_count=0;
}
......
......@@ -17,6 +17,10 @@ private:
int _smoke_count = 0;
int _smoke_ms = 0;
bool _smoke_index =false;
int _beat_count=0;
int _shot_count=0;
MQTTManager* _mqttManager = nullptr; // MQTT 管理器指针,用于发布传感器数据
......@@ -39,6 +43,17 @@ public:
// 初始化引脚模式等
void begin();
int getLedBlood() const {
return _ledblood;
}
bool getsmoke() const {
return _smoke_index;
}
//心跳函数
void updatebeartheat();
// 设置 MQTTManager 指针
void setMQTTManager(MQTTManager* mqttManager) { _mqttManager = mqttManager; }
......@@ -51,21 +66,26 @@ public:
// 在周期性任务中调用,更新LED状态并读取/发布传感器数据
void update();
// FreeRTOS 任务函数(需要在主文件或单独的.cpp中实现,并友元声明)
// FreeRTOS 任务函数(需要 在主文件或单独的.cpp中实现,并友元声明)
friend void devicePeriodicTask(void* pvParameters);
//烟雾传感器
void control_smoke();
void updatecontrol_smoke();
// 枚举类作为 MqttClient 的成员
enum class message_type : int {
PIN_CONTROL = 3,
PWM_CONTROL = 4,
SERSOR_CONTROL = 5
SMOKE_CONTROL = 2101,
AUDIO_CONTROL=2102,
LED_CONTROL = 2103,
LED_INIT_CONTROL = 2104
};
};
// FreeRTOS 任务函数声明
void devicePeriodicTask(void* pvParameters);
#endif // DEVICE_CONTROL_H
\ No newline at end of file
......@@ -111,16 +111,35 @@ bool MQTTManager::publishJson(const char* action, int value) {
// 构建head部分
JsonObject head = doc.createNestedObject("head");
// 构建body部分
JsonObject body = doc.createNestedObject("body");
if(action == "audioplayend"){
head["message_type"] = 102; // 根据你的协议调整
head["message_type"] = 3102; // 根据你的协议调整
body["action"] = action; // 传入 action 字符串
body["val"] = value; // 传入 val 值
}else if(action == "shoted"){
head["message_type"] = 101; // 根据你的协议调整
head["message_type"] = 3101; // 根据你的协议调整
body["action"] = action; // 传入 action 字符串
body["val"] = value; // 传入 val 值
}else if(action =="heartbeat") {
head["message_type"] = 1;
body["val"] = value;
// 2. 添加空指针检查
if (_DeviceControl != nullptr) {
body["led"] = _DeviceControl->getLedBlood();
body["smokeindex"] = _DeviceControl->getsmoke();
} else {
// 处理指针为空的情况,避免崩溃
body["led"] = 0; // 默认值
body["smokeindex"] = false; // 默认值
Serial.println("Error: DeviceControl pointer is null!");
}
}
// 构建body部分
JsonObject body = doc.createNestedObject("body");
body["action"] = action; // 传入 action 字符串
body["val"] = value; // 传入 val 值
// 序列化JSON为字符串
String jsonString;
......
......@@ -7,10 +7,13 @@
#include <ArduinoJson.h> // 需要用于发布JSON
#include <functional> // For std::function
#include <string> // For std::string
#include "DeviceControl.h"
// 定义 MQTT 回调函数类型
using MqttCallbackType = std::function<void(char* topic, byte* payload, unsigned int length)>;
class DeviceControl;
class MQTTManager {
private:
WiFiClient _espClient;
......@@ -24,6 +27,9 @@ private:
std::string _subscribeTopic;
std::string _publishTopic;
DeviceControl *_DeviceControl = nullptr;
MqttCallbackType _callback; // 存储用户提供的回调函数
unsigned long lastReconnectAttempt = 0;
......@@ -44,6 +50,11 @@ public:
// 初始化方法
void begin();
// 设置 DeviceControl 指针
void setDeviceControl( DeviceControl *deviceControl) {
_DeviceControl = deviceControl;
}
// 设置消息回调函数
void setCallback(MqttCallbackType callback);
......
// #include <string> // For std::string
// #include "button_heartbeat.h"
// #ifndef BUTTON_HEARTBEAT_H
// #define BUTTON_HEARTBEAT_H
// class ButtonHeartbeat {
// public:
// // 按键检测常量
// const unsigned long LONG_PRESS_DURATION_MS = 5000; // 长按触发时间:5秒
// const unsigned long DEBOUNCE_DELAY_MS = 50; // 按键去抖动时间:50毫秒
// private:
// }
// #endif
\ No newline at end of file
......@@ -36,7 +36,7 @@ const int MQTT_PORT = 1883; // MQTT 端口
const char* MQTT_USERNAME = "admin"; // 你的 MQTT 用户名
const char* MQTT_PASSWORD = "admin"; // 你的 MQTT 密码
const char* MQTT_SUBSCRIBE_TOPIC = "ser2dev/13990100000001"; // 你要订阅的 MQTT 主题
const char* MQTT_RELEASE_TOPIC = "dev2ser/13990100000001"; // 你要发布的 MQTT 主题
const char* MQTT_RELEASE_TOPIC = "13990100000001"; // 你要发布的 MQTT 主题
// --- 创建类实例 ---
// 这些实例现在包含了之前分散的全局变量和相关函数
......@@ -120,6 +120,7 @@ void setup() {
audioManager.setMQTTManager(&mqttManager);
deviceControl.setMQTTManager(&mqttManager);
mqttManager.setCallback(handleMqttMessage); // 设置 MQTT 回调函数
mqttManager.setDeviceControl(&deviceControl);
// 初始化设备控制(引脚模式等)
deviceControl.begin();
......@@ -138,12 +139,12 @@ void setup() {
// 任务名称, 栈大小, 参数, 优先级, 任务句柄
xTaskCreate(buttonMonitorTask, "ButtonMonitor", 2048, (void*)&wifiManager, 1, NULL); // 将 wifiManager 实例地址传递给按键任务
xTaskCreate(mqttLoopTask, "MQTTLoop", 4096, (void*)&mqttManager, 1, NULL); // 将 mqttManager 实例地址传递给 MQTT 任务
xTaskCreate(audioPlayTask, "AudioPlay", 2048, (void*)&audioManager, 1, NULL); // 将 audioManager 实例地址传递给音频任务
xTaskCreate(audioPlayTask, "AudioPlay", 4096, (void*)&audioManager, 1, NULL); // 将 audioManager 实例地址传递给音频任务
xTaskCreate(devicePeriodicTask, "DevicePeriodic", 2048, (void*)&deviceControl, 1, NULL); // 将 deviceControl 实例地址传递给设备任务
// 启动时播放一次声音(指示启动完成或AP模式启动)
audioManager.requestPlay(); // 请求播放启动音
audioManager.requestPlay_Drip(); // 请求播放启动音
}
......@@ -194,7 +195,7 @@ void buttonMonitorTask(void* pvParameters) {
Serial.println("Long press detected on D10! Clearing WiFi config...");
// 听到声音表示长按成功
audioManager.requestPlay(); // 请求播放声音
audioManager.requestPlay_Drip(); // 请求播放声音
// 清除 WiFi 配置并重启
wifiManager->clearCredentials();
......
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -4,4 +4,9 @@
extern unsigned int pcm_sound_data_len;
extern unsigned char pcm_sound_data[];
extern unsigned int pcm_sound_data_len_Drip ;
extern unsigned char pcm_sound_data_Drip[];
#endif
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment