48f208b2e6
2. 试着测试了一下LVGL_GIF渲染+音乐播放+语音识别的组合简单优化后,
发现lvgl渲染略显卡顿,语音识别有缓冲区空警告,不过无伤大雅,还需要进一步深度优化。
213 lines
7.3 KiB
C++
213 lines
7.3 KiB
C++
//
|
|
// Created by misaki on 2025/9/2.
|
|
//
|
|
|
|
#pragma once
|
|
|
|
#include <string>
|
|
#include <functional>
|
|
#include <mutex>
|
|
#include <queue>
|
|
#include <condition_variable>
|
|
#include "esp_websocket_client.h"
|
|
#include "cJSON.h"
|
|
#include "ThreadManager.h"
|
|
#include "WifiConnectors.h"
|
|
|
|
// WebSocket事件类型
|
|
enum class WebSocketEvent {
|
|
CONNECTED,
|
|
DISCONNECTED,
|
|
DATA_RECEIVED,
|
|
ERROR
|
|
};
|
|
|
|
// WebSocket配置
|
|
struct WebSocketConfig {
|
|
std::string uri; // WebSocket服务器URI
|
|
int reconnect_interval = 5000; // 重连间隔(ms)
|
|
int heartbeat_interval = 30000; // 心跳间隔(ms)
|
|
int max_reconnect_attempts = 10; // 最大重连次数
|
|
bool auto_reconnect = true; // 是否自动重连
|
|
};
|
|
|
|
// JSON数据回调
|
|
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) {
|
|
// 处理接收到的JSON数据
|
|
cJSON* type = cJSON_GetObjectItem(json, "type");
|
|
if (type && cJSON_IsString(type)) {
|
|
ESP_LOGI("App", "Received message type: %s", type->valuestring);
|
|
|
|
if (strcmp(type->valuestring, "sensor_data") == 0) {
|
|
cJSON* value = cJSON_GetObjectItem(json, "value");
|
|
if (value && cJSON_IsNumber(value)) {
|
|
ESP_LOGI("App", "Sensor value: %.2f", value->valuedouble);
|
|
}
|
|
}
|
|
}
|
|
|
|
cJSON_Delete(json);
|
|
}
|
|
// 事件回调示例
|
|
void onWebSocketEvent(WebSocketEvent event, const std::string& message) {
|
|
switch (event) {
|
|
case WebSocketEvent::CONNECTED:
|
|
ESP_LOGI("App", "WebSocket connected: %s", message.c_str());
|
|
break;
|
|
case WebSocketEvent::DISCONNECTED:
|
|
ESP_LOGI("App", "WebSocket disconnected: %s", message.c_str());
|
|
break;
|
|
case WebSocketEvent::DATA_RECEIVED:
|
|
ESP_LOGI("App", "Received raw data: %s", message.c_str());
|
|
break;
|
|
case WebSocketEvent::ERROR:
|
|
ESP_LOGE("App", "WebSocket error: %s", message.c_str());
|
|
break;
|
|
}
|
|
}
|
|
*/
|
|
|
|
|
|
|
|
class WebSocketManager {
|
|
public:
|
|
// 获取单例实例
|
|
static WebSocketManager* getInstance();
|
|
|
|
// 删除拷贝构造函数和赋值运算符
|
|
WebSocketManager(const WebSocketManager&) = delete;
|
|
WebSocketManager& operator=(const WebSocketManager&) = delete;
|
|
|
|
// 初始化WebSocket管理器
|
|
bool initialize(const WebSocketConfig& ws_config);
|
|
|
|
// 连接到WebSocket服务器
|
|
bool connect();
|
|
|
|
// 断开连接
|
|
void disconnect();
|
|
|
|
// 发送JSON数据,注意此成员函数会释放json数据,调用之后请不要再次释放json数据
|
|
bool sendJson(cJSON* json);
|
|
|
|
// 发送原始字符串数据
|
|
bool sendRaw(const std::string& data);
|
|
|
|
// 设置JSON数据回调
|
|
void setJsonCallback(const JsonDataCallback &callback);
|
|
|
|
// 设置事件回调
|
|
void setEventCallback(const EventCallback &callback);
|
|
|
|
// 获取连接状态
|
|
[[nodiscard]] bool isConnected() const;
|
|
|
|
// 获取配置信息
|
|
[[nodiscard]] WebSocketConfig getConfig() const;
|
|
|
|
// 更新配置
|
|
void updateConfig(const WebSocketConfig& ws_config);
|
|
|
|
// 获取统计信息
|
|
struct Stats {
|
|
uint32_t messages_sent; /// 发送消息数
|
|
uint32_t messages_received; /// 接收到的消息数
|
|
uint32_t connection_attempts; /// 连接尝试次数
|
|
uint32_t successful_connections; /// 成功连接次数
|
|
};
|
|
[[nodiscard]] Stats getStats() const;
|
|
|
|
private:
|
|
WebSocketManager(); // 私有构造函数
|
|
~WebSocketManager(); // 私有析构函数
|
|
|
|
/**
|
|
* WebSocket事件处理函数
|
|
* @param handler_args 处理参数
|
|
* @param base 事件基础
|
|
* @param event_id 事件ID
|
|
* @param event_data 事件数据
|
|
*/
|
|
static void websocketEventHandler(void* handler_args, esp_event_base_t base,
|
|
int32_t event_id, void* event_data);
|
|
|
|
/**
|
|
* 处理接收到的数据
|
|
* @param data 数据
|
|
* @param len 数据长度
|
|
*/
|
|
void handleReceivedData(const char* data, int len) const;
|
|
|
|
// 重连线程函数
|
|
void reconnectThread();
|
|
|
|
// 心跳线程函数
|
|
void heartbeatThread();
|
|
|
|
// 发送线程函数
|
|
void sendThread();
|
|
|
|
// 创建WebSocket客户端
|
|
bool createWebSocketClient();
|
|
|
|
// 销毁WebSocket客户端
|
|
void destroyWebSocketClient();
|
|
|
|
// 处理WiFi事件
|
|
static void wifiEventHandler(void* arg, esp_event_base_t event_base,
|
|
int32_t event_id, void* event_data);
|
|
|
|
private:
|
|
static WebSocketManager* instance;
|
|
static std::mutex instance_mutex;
|
|
|
|
esp_websocket_client_handle_t client;
|
|
WebSocketConfig config;
|
|
|
|
// 线程相关
|
|
std::thread reconnect_thread; /// 重连线程
|
|
std::thread heartbeat_thread; /// 心跳线程
|
|
std::thread send_thread; /// 发送线程
|
|
bool threads_running; /// 线程运行标志
|
|
|
|
// 回调函数
|
|
JsonDataCallback json_callback; /// JSON数据回调
|
|
EventCallback event_callback; /// 事件回调
|
|
|
|
// 状态变量
|
|
bool connected;
|
|
bool connecting;
|
|
uint32_t reconnect_attempts;
|
|
|
|
// 统计信息
|
|
Stats stats;
|
|
|
|
// 发送队列
|
|
std::queue<std::string> send_queue;
|
|
std::mutex queue_mutex;
|
|
std::condition_variable queue_cv;
|
|
|
|
// 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
|
|
}; |