1. 完成了语音识别的C++业务层封装,测试通过

2. 试着测试了一下LVGL_GIF渲染+音乐播放+语音识别的组合简单优化后,
          发现lvgl渲染略显卡顿,语音识别有缓冲区空警告,不过无伤大雅,还需要进一步深度优化。
This commit is contained in:
Misaki
2025-09-23 03:17:23 +08:00
parent 4cc761aab3
commit 48f208b2e6
19 changed files with 587 additions and 94 deletions
+65 -45
View File
@@ -9,9 +9,13 @@
#include <esp_log.h>
#include <esp_netif_types.h>
#include "ToolsClass.h"
#include "sys_conf_singleton.h"
// 静态成员初始化
WebSocketManager* WebSocketManager::instance = nullptr;
std::mutex WebSocketManager::instance_mutex;
std::string WebSocketManager::sn = SYS_CONF_JSON().loadSN(); // 获取SN(同时也能初始化文件系统,如果还没有初始化这个文件系统的话)
// 标签用于日志
static const char* TAG = "WebSocketManager";
@@ -51,21 +55,21 @@ WebSocketManager::~WebSocketManager() {
}
WebSocketManager* WebSocketManager::getInstance() {
std::lock_guard<std::mutex> lock(instance_mutex);
std::lock_guard<std::mutex> lock(instance_mutex); // 加锁
if (instance == nullptr) {
instance = new WebSocketManager();
}
return instance;
}
bool WebSocketManager::initialize(const WebSocketConfig& config) {
this->config = config;
bool WebSocketManager::initialize(const WebSocketConfig& ws_config) {
this->config = ws_config;
// 注册WiFi事件处理
esp_event_handler_instance_t instance;
esp_event_handler_instance_t wifi_instance;
esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP,
&WebSocketManager::wifiEventHandler,
this, &instance);
this, &wifi_instance);
// 启动发送线程
threads_running = true;
@@ -159,8 +163,8 @@ bool WebSocketManager::sendJson(cJSON* json) {
}
std::lock_guard<std::mutex> lock(queue_mutex); // 锁定队列
send_queue.push(json_str); // 添加到队列
free(json_str); // 释放内存
send_queue.emplace(json_str); // 添加到队列
free(json_str); // 释放json_str的内存
queue_cv.notify_one(); // 通知发送线程
return true;
@@ -178,11 +182,11 @@ bool WebSocketManager::sendRaw(const std::string& data) {
return true;
}
void WebSocketManager::setJsonCallback(JsonDataCallback callback) {
void WebSocketManager::setJsonCallback(const JsonDataCallback &callback) {
json_callback = callback;
}
void WebSocketManager::setEventCallback(EventCallback callback) {
void WebSocketManager::setEventCallback(const EventCallback &callback) {
event_callback = callback;
}
@@ -194,8 +198,8 @@ WebSocketConfig WebSocketManager::getConfig() const {
return config;
}
void WebSocketManager::updateConfig(const WebSocketConfig& config) {
this->config = config;
void WebSocketManager::updateConfig(const WebSocketConfig& ws_config) {
this->config = ws_config;
}
WebSocketManager::Stats WebSocketManager::getStats() const {
@@ -204,55 +208,57 @@ WebSocketManager::Stats WebSocketManager::getStats() const {
void WebSocketManager::websocketEventHandler(void* handler_args, esp_event_base_t base,
int32_t event_id, void* event_data) {
WebSocketManager* instance = static_cast<WebSocketManager*>(handler_args);
esp_websocket_event_data_t* data = (esp_websocket_event_data_t*)event_data;
auto* ws_instance = static_cast<WebSocketManager*>(handler_args);
auto* data = static_cast<esp_websocket_event_data_t *>(event_data);
switch(event_id) {
case WEBSOCKET_EVENT_CONNECTED:
instance->connected = true;
instance->connecting = false;
instance->reconnect_attempts = 0;
instance->stats.successful_connections++;
ws_instance->connected = true;
ws_instance->connecting = false;
ws_instance->reconnect_attempts = 0;
ws_instance->stats.successful_connections++;
ESP_LOGI(TAG, "WebSocket connected");
if (instance->event_callback) {
instance->event_callback(WebSocketEvent::CONNECTED, "Connected successfully");
if (ws_instance->event_callback) {
ws_instance->event_callback(WebSocketEvent::CONNECTED, "Connected successfully");
}
break;
case WEBSOCKET_EVENT_DISCONNECTED:
instance->connected = false;
instance->connecting = false;
ws_instance->connected = false;
ws_instance->connecting = false;
ESP_LOGI(TAG, "WebSocket disconnected");
if (instance->event_callback) {
instance->event_callback(WebSocketEvent::DISCONNECTED, "Disconnected");
if (ws_instance->event_callback) {
ws_instance->event_callback(WebSocketEvent::DISCONNECTED, "Disconnected");
}
break;
case WEBSOCKET_EVENT_DATA:
if (data->data_len > 0) {
instance->stats.messages_received++;
instance->handleReceivedData((const char*)data->data_ptr, data->data_len);
ws_instance->stats.messages_received++;
ws_instance->handleReceivedData((const char*)data->data_ptr, data->data_len);
}
break;
case WEBSOCKET_EVENT_ERROR:
instance->connected = false;
instance->connecting = false;
ws_instance->connected = false;
ws_instance->connecting = false;
ESP_LOGE(TAG, "WebSocket error");
if (instance->event_callback) {
instance->event_callback(WebSocketEvent::ERROR, "WebSocket error occurred");
if (ws_instance->event_callback) {
ws_instance->event_callback(WebSocketEvent::ERROR, "WebSocket error occurred");
}
break;
default:
ESP_LOGW(TAG, "Unhandled WebSocket event: %ld", event_id);
break;
}
}
void WebSocketManager::handleReceivedData(const char* data, int len) {
void WebSocketManager::handleReceivedData(const char* data, const int len) const {
// 尝试解析JSON
cJSON* json = cJSON_ParseWithLength(data, len);
if (json) {
if (cJSON* json = cJSON_ParseWithLength(data, len)) {
// 成功解析为JSON
if (json_callback) {
json_callback(json);
@@ -298,16 +304,16 @@ void WebSocketManager::reconnectThread() {
void WebSocketManager::heartbeatThread() {
while (threads_running) {
if (connected && config.heartbeat_interval > 0) {
if (connected && config.heartbeat_interval > 0) { // 如果处于连接状态且心跳间隔大于0
// 发送心跳消息
cJSON* heartbeat = cJSON_CreateObject();
cJSON_AddStringToObject(heartbeat, "type", "heartbeat");
cJSON_AddNumberToObject(heartbeat, "timestamp", esp_log_timestamp());
cJSON_AddNumberToObject(heartbeat, "timestamp", static_cast<double>(esp_log_timestamp()));
sendJson(heartbeat);
std::this_thread::sleep_for(std::chrono::milliseconds(config.heartbeat_interval));
} else {
} else { // 否则就让出CPU
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}
}
@@ -316,23 +322,24 @@ void WebSocketManager::heartbeatThread() {
void WebSocketManager::sendThread() {
while (threads_running) {
std::unique_lock<std::mutex> lock(queue_mutex);
queue_cv.wait(lock, [this]() {
queue_cv.wait(lock, // 传入已持有的 unique_lock
[this]() { // 等待队列非空或者线程停止
return !send_queue.empty() || !threads_running;
});
if (!threads_running) {
if (!threads_running) { // 其实永远不会进入这里,写上只是保险起见
break;
}
if (!send_queue.empty() && connected) {
if (!send_queue.empty() && connected) { // 如果队列非空且已连接
std::string data = std::move(send_queue.front());
send_queue.pop();
lock.unlock();
lock.unlock(); // 释放锁
// 发送数据
int sent = esp_websocket_client_send_text(client, data.c_str(), static_cast<int>(data.length()), portMAX_DELAY);
if (sent >= 0) {
stats.messages_sent++;
stats.messages_sent++; // 发送成功
} else {
ESP_LOGE(TAG, "Failed to send data");
// 将数据重新放回队列
@@ -355,6 +362,19 @@ bool WebSocketManager::createWebSocketClient() {
return false;
}
// 处理sn码逻辑
// 一般情况下sn码会在开机的时候从flash当中读出,因此此处判断是否为空
if (!sn.empty()) { // 如果sn码不为空
// 则在头部加入sn码提交
esp_websocket_client_append_header(client, "sn", sn.c_str());
}
else {
// 否则在头部加入mac码和芯片序列号
esp_websocket_client_append_header(client, "mac", ToolsClass::getChipMAC().c_str());
esp_websocket_client_append_header(client, "chip_id", ToolsClass::getChipSerialNumber().c_str());
}
// 注册事件处理
esp_websocket_register_events(client, WEBSOCKET_EVENT_ANY,
&WebSocketManager::websocketEventHandler, this);
@@ -371,14 +391,14 @@ void WebSocketManager::destroyWebSocketClient() {
}
void WebSocketManager::wifiEventHandler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data) {
WebSocketManager* instance = static_cast<WebSocketManager*>(arg);
int32_t event_Id, void* event_data) {
auto* ws_instance = static_cast<WebSocketManager*>(arg);
if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
if (event_base == IP_EVENT && event_Id == IP_EVENT_STA_GOT_IP) {
// WiFi已连接,尝试重新连接WebSocket
if (instance->config.auto_reconnect && !instance->connected) {
if (ws_instance->config.auto_reconnect && !ws_instance->connected) {
ESP_LOGI(TAG, "WiFi connected, attempting to reconnect WebSocket");
instance->connect();
ws_instance->connect();
}
}
}
+24 -10
View File
@@ -6,7 +6,6 @@
#include <string>
#include <functional>
#include <mutex>
#include <queue>
#include <condition_variable>
@@ -37,6 +36,11 @@ using JsonDataCallback = std::function<void(cJSON* json)>;
// 事件回调
using EventCallback = std::function<void(WebSocketEvent event, const std::string& message)>;
// 在此说明一下上面的两个回调函数的作用,主要目的还是为了解耦,在handleReceivedData有对这两个回调的调用
// 对于JSON数据回调,如果来自客户端的数据被解析为了数据,那么就会调用JSON数据回调,并将解析后的json数据传给JSON数据回调,回调函数内部只需要访问并释放json数据
// 对于WebSocket事件回调,会有所不同,重点需要关注DATA_RECEIVED事件,这是非json数据的情况,其他的见下面的示例
// 实际上对于本项目,几乎99%的情况都是只使用JSON数据回调
/* 回调函数示例:
// JSON数据回调示例
void onJsonData(cJSON* json) {
@@ -86,7 +90,7 @@ public:
WebSocketManager& operator=(const WebSocketManager&) = delete;
// 初始化WebSocket管理器
bool initialize(const WebSocketConfig& config);
bool initialize(const WebSocketConfig& ws_config);
// 连接到WebSocket服务器
bool connect();
@@ -94,26 +98,26 @@ public:
// 断开连接
void disconnect();
// 发送JSON数据
// 发送JSON数据,注意此成员函数会释放json数据,调用之后请不要再次释放json数据
bool sendJson(cJSON* json);
// 发送原始字符串数据
bool sendRaw(const std::string& data);
// 设置JSON数据回调
void setJsonCallback(JsonDataCallback callback);
void setJsonCallback(const JsonDataCallback &callback);
// 设置事件回调
void setEventCallback(EventCallback callback);
void setEventCallback(const EventCallback &callback);
// 获取连接状态
bool isConnected() const;
[[nodiscard]] bool isConnected() const;
// 获取配置信息
WebSocketConfig getConfig() const;
[[nodiscard]] WebSocketConfig getConfig() const;
// 更新配置
void updateConfig(const WebSocketConfig& config);
void updateConfig(const WebSocketConfig& ws_config);
// 获取统计信息
struct Stats {
@@ -122,7 +126,7 @@ public:
uint32_t connection_attempts; /// 连接尝试次数
uint32_t successful_connections; /// 成功连接次数
};
Stats getStats() const;
[[nodiscard]] Stats getStats() const;
private:
WebSocketManager(); // 私有构造函数
@@ -143,7 +147,7 @@ private:
* @param data 数据
* @param len 数据长度
*/
void handleReceivedData(const char* data, int len);
void handleReceivedData(const char* data, int len) const;
// 重连线程函数
void reconnectThread();
@@ -196,4 +200,14 @@ private:
// WiFi实例
WifiConnectors* wifi;
// sn码和设备信息
static std::string sn; /// sn码会在设备首次连网后连接服务器后经过服务器计算后返回唯一的sn码
/// 在首次连接服务器后会提交设备的芯片序列号和mac地址,以用于计算唯一sn码
/// 在计算后,服务器首先会把计算好的sn码返回给设备,并保存起来
/// 客户端会将服务器返回的sn码存在设备flash当中
/// 这样客户端和服务端之间就都有sn码了
/// 当客户端再次连接服务器的时候,会先从flash中读取sn码,如果sn码存在,则将sn码发送给服务器以建立连接
/// 如此下来服务器也方便找到设备保存的信息
std::string device_info = "esp32s3"; /// 可以根据不同的设备进行修改,在本项目中使用的是esp32s3
};