From 97fe13da26103e454aa1c0982225b75253a87788 Mon Sep 17 00:00:00 2001 From: Misaki Date: Fri, 12 Sep 2025 02:11:50 +0800 Subject: [PATCH] =?UTF-8?q?1.=20=E5=AE=8C=E6=88=90=E4=BA=86=E5=AF=B9?= =?UTF-8?q?=E9=9F=B3=E9=A2=91=E6=92=AD=E6=94=BE=E7=B1=BB=E7=9A=84=E5=AE=8C?= =?UTF-8?q?=E6=95=B4C++=E5=B0=81=E8=A3=85=EF=BC=8C=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E9=80=9A=E8=BF=87=202.=20=E4=BF=AE=E5=A4=8D=E4=BA=86LVGL?= =?UTF-8?q?=E6=B8=B2=E6=9F=93=E7=B1=BB=E5=BD=93=E4=B8=AD=E7=9A=84=E4=B8=80?= =?UTF-8?q?=E4=BA=9B=E5=B0=8Fbug=203.=20=E5=A2=9E=E5=8A=A0=E4=BA=86?= =?UTF-8?q?=E4=B8=80=E4=BA=9BCPU=E8=B5=84=E6=BA=90=E5=8D=A0=E7=94=A8?= =?UTF-8?q?=E7=9A=84=E6=97=A5=E5=BF=97=E6=89=93=E5=8D=B0=E5=87=BD=E6=95=B0?= =?UTF-8?q?=EF=BC=8C=E8=BF=90=E8=A1=8C=E5=9C=A8=E4=B8=BB=E7=BA=BF=E7=A8=8B?= =?UTF-8?q?=E5=BD=93=E4=B8=AD=204.=20=E5=AE=8C=E5=96=84=E4=BA=86=E5=BA=95?= =?UTF-8?q?=E5=B1=82=E9=80=9A=E4=BF=A1=E7=B1=BB=E7=9A=84=E5=B0=81=E8=A3=85?= =?UTF-8?q?=EF=BC=8C=E5=9F=BA=E4=BA=8Ewebsocket=EF=BC=8C=E5=B0=9A=E6=9C=AA?= =?UTF-8?q?=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Bionic_Core/CommClass/CommClass.cpp | 384 +++++++++++++++++- Bionic_Core/CommClass/CommClass.h | 194 +++++++++ Bionic_Core/OTAClass/OTAClass.cpp | 97 ++--- .../ToolsClass/AudioOutput/AudioOutput.cpp | 248 +++++++++++ .../ToolsClass/AudioOutput/AudioOutput.h | 202 +++++++++ .../ToolsClass/LVGL_Render/LVGLRender.cpp | 42 +- .../ToolsClass/LVGL_Render/LVGLRender.h | 6 + .../SDFileManager/SDFileManager.cpp | 5 + .../ToolsClass/SDFileManager/SDFileManager.h | 4 + .../ToolsClass/ThreadManager/ThreadManager.h | 28 ++ Lib/Audio_Driver/PCM5101.c | 17 +- Temp.md | 109 +++++ main/CMakeLists.txt | 2 + sdkconfig | 13 +- sdkconfig.old | 20 +- 项目开发日志.md | 11 + 16 files changed, 1297 insertions(+), 85 deletions(-) create mode 100644 Bionic_Core/ToolsClass/AudioOutput/AudioOutput.cpp create mode 100644 Bionic_Core/ToolsClass/AudioOutput/AudioOutput.h diff --git a/Bionic_Core/CommClass/CommClass.cpp b/Bionic_Core/CommClass/CommClass.cpp index 8b3fdee..e18f849 100644 --- a/Bionic_Core/CommClass/CommClass.cpp +++ b/Bionic_Core/CommClass/CommClass.cpp @@ -1,4 +1,386 @@ // // Created by misaki on 2025/9/2. // -#include "CommClass.h" \ No newline at end of file +#include "CommClass.h" +#include // 引入字符串处理库头文件 +#include // 时间库头文件 +#include +#include +#include +#include + +// 静态成员初始化 +WebSocketManager* WebSocketManager::instance = nullptr; +std::mutex WebSocketManager::instance_mutex; + +// 标签用于日志 +static const char* TAG = "WebSocketManager"; + + +// 构造函数 +WebSocketManager::WebSocketManager() + : client(nullptr), threads_running(false), + connected(false), connecting(false), reconnect_attempts(0), + stats{0, 0, 0, 0}, + wifi(WifiConnectors::getInstance()) + +{ + + // 初始化统计信息 + stats = {0, 0, 0, 0}; +} + + +// 析构函数 +WebSocketManager::~WebSocketManager() { + disconnect(); + + // 停止线程 + threads_running = false; + queue_cv.notify_all(); + + if (reconnect_thread.joinable()) { + reconnect_thread.join(); + } + + if (heartbeat_thread.joinable()) { + heartbeat_thread.join(); + } + + if (send_thread.joinable()) { + send_thread.join(); + } +} + +WebSocketManager* WebSocketManager::getInstance() { + std::lock_guard lock(instance_mutex); + if (instance == nullptr) { + instance = new WebSocketManager(); + } + return instance; +} + +bool WebSocketManager::initialize(const WebSocketConfig& config) { + this->config = config; + + // 注册WiFi事件处理 + esp_event_handler_instance_t instance; + esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, + &WebSocketManager::wifiEventHandler, + this, &instance); + + // 启动发送线程 + threads_running = true; + + ThreadConfig thread_config; + thread_config.name = "WS_Send"; + thread_config.stack_size = 4096; + + send_thread = ThreadManager::createMemberThread(thread_config, this, + &WebSocketManager::sendThread); + + return true; +} + +bool WebSocketManager::connect() { + if (connecting || connected) { + ESP_LOGI(TAG, "Already connected or connecting"); + return true; + } + + // 检查WiFi连接 + if (!wifi->isWifiConnect()) { + ESP_LOGE(TAG, "WiFi not connected, cannot establish WebSocket connection"); + if (event_callback) { + event_callback(WebSocketEvent::ERROR, "WiFi not connected"); + } + return false; + } + + connecting = true; + stats.connection_attempts++; + + // 创建WebSocket客户端 + if (!createWebSocketClient()) { + connecting = false; + return false; + } + + // 启动WebSocket客户端 + esp_err_t err = esp_websocket_client_start(client); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to start WebSocket client: %d", err); + destroyWebSocketClient(); + connecting = false; + return false; + } + + // 启动重连和心跳线程 + ThreadConfig thread_config; + thread_config.name = "WS_Reconnect"; + thread_config.stack_size = 3072; + + reconnect_thread = ThreadManager::createMemberThread(thread_config, this, + &WebSocketManager::reconnectThread); + + thread_config.name = "WS_Heartbeat"; + heartbeat_thread = ThreadManager::createMemberThread(thread_config, this, + &WebSocketManager::heartbeatThread); + + return true; +} + +void WebSocketManager::disconnect() { + if (client) { + esp_websocket_client_stop(client); + destroyWebSocketClient(); + } + + connected = false; + connecting = false; + + // 通知事件回调 + if (event_callback) { + event_callback(WebSocketEvent::DISCONNECTED, "Disconnected by user"); + } +} + +bool WebSocketManager::sendJson(cJSON* json) { + if (!connected) { // 检查连接状态 + ESP_LOGE(TAG, "Not connected, cannot send data"); + cJSON_Delete(json); + return false; + } + + char* json_str = cJSON_PrintUnformatted(json); // 将JSON对象转换为字符串 + cJSON_Delete(json); // 释放JSON对象 + + if (!json_str) { // 检查转换结果 + ESP_LOGE(TAG, "Failed to stringify JSON"); + return false; + } + + std::lock_guard lock(queue_mutex); // 锁定队列 + send_queue.push(json_str); // 添加到队列 + free(json_str); // 释放内存 + + queue_cv.notify_one(); // 通知发送线程 + return true; +} + +bool WebSocketManager::sendRaw(const std::string& data) { + if (!connected) { // 检查连接状态 + ESP_LOGE(TAG, "Not connected, cannot send data"); + return false; + } + + std::lock_guard lock(queue_mutex); // 锁定队列 + send_queue.push(data); // 添加到队列 + queue_cv.notify_one(); // 通知发送线程 + return true; +} + +void WebSocketManager::setJsonCallback(JsonDataCallback callback) { + json_callback = callback; +} + +void WebSocketManager::setEventCallback(EventCallback callback) { + event_callback = callback; +} + +bool WebSocketManager::isConnected() const { + return connected; +} + +WebSocketConfig WebSocketManager::getConfig() const { + return config; +} + +void WebSocketManager::updateConfig(const WebSocketConfig& config) { + this->config = config; +} + +WebSocketManager::Stats WebSocketManager::getStats() const { + return stats; +} + +void WebSocketManager::websocketEventHandler(void* handler_args, esp_event_base_t base, + int32_t event_id, void* event_data) { + WebSocketManager* instance = static_cast(handler_args); + esp_websocket_event_data_t* data = (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++; + + ESP_LOGI(TAG, "WebSocket connected"); + if (instance->event_callback) { + instance->event_callback(WebSocketEvent::CONNECTED, "Connected successfully"); + } + break; + + case WEBSOCKET_EVENT_DISCONNECTED: + instance->connected = false; + instance->connecting = false; + + ESP_LOGI(TAG, "WebSocket disconnected"); + if (instance->event_callback) { + 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); + } + break; + + case WEBSOCKET_EVENT_ERROR: + instance->connected = false; + instance->connecting = false; + + ESP_LOGE(TAG, "WebSocket error"); + if (instance->event_callback) { + instance->event_callback(WebSocketEvent::ERROR, "WebSocket error occurred"); + } + break; + } +} + +void WebSocketManager::handleReceivedData(const char* data, int len) { + // 尝试解析JSON + cJSON* json = cJSON_ParseWithLength(data, len); + if (json) { + // 成功解析为JSON + if (json_callback) { + json_callback(json); + } else { + cJSON_Delete(json); + } + } else { + // 不是JSON格式,作为原始数据处理 + std::string message(data, len); + if (event_callback) { + event_callback(WebSocketEvent::DATA_RECEIVED, message); + } + } +} + +void WebSocketManager::reconnectThread() { + while (threads_running) { + if (!connected && config.auto_reconnect && + (config.max_reconnect_attempts == 0 || + reconnect_attempts < config.max_reconnect_attempts)) { + + // 检查WiFi连接 + if (!wifi->isWifiConnect()) { + ESP_LOGI(TAG, "WiFi not connected, waiting before WebSocket reconnect"); + std::this_thread::sleep_for(std::chrono::milliseconds(config.reconnect_interval)); + continue; + } + + reconnect_attempts++; + ESP_LOGI(TAG, "Attempting to reconnect (%ld/%d)", + reconnect_attempts, config.max_reconnect_attempts); + + if (connect()) { + ESP_LOGI(TAG, "Reconnection attempt initiated"); + } else { + ESP_LOGE(TAG, "Reconnection attempt failed"); + } + } + + std::this_thread::sleep_for(std::chrono::milliseconds(config.reconnect_interval)); + } +} + +void WebSocketManager::heartbeatThread() { + while (threads_running) { + if (connected && config.heartbeat_interval > 0) { + // 发送心跳消息 + cJSON* heartbeat = cJSON_CreateObject(); + cJSON_AddStringToObject(heartbeat, "type", "heartbeat"); + cJSON_AddNumberToObject(heartbeat, "timestamp", esp_log_timestamp()); + + sendJson(heartbeat); + + std::this_thread::sleep_for(std::chrono::milliseconds(config.heartbeat_interval)); + } else { + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + } + } +} + +void WebSocketManager::sendThread() { + while (threads_running) { + std::unique_lock lock(queue_mutex); + queue_cv.wait(lock, [this]() { + return !send_queue.empty() || !threads_running; + }); + + if (!threads_running) { + break; + } + + if (!send_queue.empty() && connected) { + std::string data = std::move(send_queue.front()); + send_queue.pop(); + lock.unlock(); + + // 发送数据 + int sent = esp_websocket_client_send_text(client, data.c_str(), static_cast(data.length()), portMAX_DELAY); + if (sent >= 0) { + stats.messages_sent++; + } else { + ESP_LOGE(TAG, "Failed to send data"); + // 将数据重新放回队列 + lock.lock(); + send_queue.push(std::move(data)); + lock.unlock(); + } + } + } +} + +bool WebSocketManager::createWebSocketClient() { + esp_websocket_client_config_t ws_config = {}; + ws_config.uri = config.uri.c_str(); + ws_config.user_context = this; + + client = esp_websocket_client_init(&ws_config); + if (!client) { + ESP_LOGE(TAG, "Failed to initialize WebSocket client"); + return false; + } + + // 注册事件处理 + esp_websocket_register_events(client, WEBSOCKET_EVENT_ANY, + &WebSocketManager::websocketEventHandler, this); + + return true; +} + +void WebSocketManager::destroyWebSocketClient() { + if (client) { + esp_websocket_client_stop(client); + esp_websocket_client_destroy(client); + client = nullptr; + } +} + +void WebSocketManager::wifiEventHandler(void* arg, esp_event_base_t event_base, + int32_t event_id, void* event_data) { + WebSocketManager* instance = static_cast(arg); + + if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { + // WiFi已连接,尝试重新连接WebSocket + if (instance->config.auto_reconnect && !instance->connected) { + ESP_LOGI(TAG, "WiFi connected, attempting to reconnect WebSocket"); + instance->connect(); + } + } +} \ No newline at end of file diff --git a/Bionic_Core/CommClass/CommClass.h b/Bionic_Core/CommClass/CommClass.h index af4b41e..a38826d 100644 --- a/Bionic_Core/CommClass/CommClass.h +++ b/Bionic_Core/CommClass/CommClass.h @@ -3,3 +3,197 @@ // #pragma once + +#include +#include + +#include +#include +#include +#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; +// 事件回调 +using EventCallback = std::function; + +/* 回调函数示例: + // 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& config); + + // 连接到WebSocket服务器 + bool connect(); + + // 断开连接 + void disconnect(); + + // 发送JSON数据 + bool sendJson(cJSON* json); + + // 发送原始字符串数据 + bool sendRaw(const std::string& data); + + // 设置JSON数据回调 + void setJsonCallback(JsonDataCallback callback); + + // 设置事件回调 + void setEventCallback(EventCallback callback); + + // 获取连接状态 + bool isConnected() const; + + // 获取配置信息 + WebSocketConfig getConfig() const; + + // 更新配置 + void updateConfig(const WebSocketConfig& config); + + // 获取统计信息 + struct Stats { + uint32_t messages_sent; /// 发送消息数 + uint32_t messages_received; /// 接收到的消息数 + uint32_t connection_attempts; /// 连接尝试次数 + uint32_t successful_connections; /// 成功连接次数 + }; + 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); + + // 重连线程函数 + 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 send_queue; + std::mutex queue_mutex; + std::condition_variable queue_cv; + + // WiFi实例 + WifiConnectors* wifi; +}; \ No newline at end of file diff --git a/Bionic_Core/OTAClass/OTAClass.cpp b/Bionic_Core/OTAClass/OTAClass.cpp index 2bd532f..add5e50 100644 --- a/Bionic_Core/OTAClass/OTAClass.cpp +++ b/Bionic_Core/OTAClass/OTAClass.cpp @@ -20,35 +20,6 @@ const auto sleep_time = seconds{ 5 }; -// 下面的这个函数可以放在任何线程中,自动打印出对应线程的信息 -void print_thread_info(const char *extra = nullptr) -{ - std::stringstream ss; - if (extra) { - ss << extra; - } - ss << "Core id: " << xPortGetCoreID() - << ", prio: " << uxTaskPriorityGet(nullptr) - << ", minimum free stack: " << uxTaskGetStackHighWaterMark(nullptr) << " bytes."; - ESP_LOGI(pcTaskGetName(nullptr), "%s", ss.str().c_str()); -} - -void thread_func_inherited() -{ - while (true) { - print_thread_info("我是 普普通通的inherited 线程"); - std::this_thread::sleep_for(sleep_time); - } -} - -void thread_func_any_core() -{ - while (true) { - print_thread_info("我是一个会跑在任意一个核的任务~"); - std::this_thread::sleep_for(sleep_time); - } -} - esp_pthread_cfg_t create_config(const char *name, int core_id, int stack, int prio) { @@ -66,6 +37,8 @@ esp_pthread_cfg_t create_config(const char *name, int core_id, int stack, int pr #include "LVGLRender.h" #include "SDFileManager.h" +#include "AudioOutput.h" + void OTAClass::Init() { ESP_LOGI("OTA", "Init"); @@ -75,22 +48,28 @@ void OTAClass::Init() { std::string listing = SDFileManager::getInstance()->lsCommand(".", false, true); ESP_LOGI("SD", "%s", listing.c_str()); - LVGLRender::getInstance()->RenderGif("sequence01.gif"); + // 切换到music目录 + SDFileManager::getInstance()->cdCommand("music"); + std::string pwdPath = SDFileManager::getInstance()->pwdCommand(); + ESP_LOGI("SD", "%s", pwdPath.c_str()); - // 1. 创建普通函数一个可以运行在任意核上的线程 - ThreadConfig config1; - config1.name = "NormalThread"; - // config1.core_id = 0; // 不指定运行在哪个核,使其自动选择 - config1.stack_size = 3072; - config1.priority = 5; // 优先级 - std::thread normal_thread = ThreadManager::createThread(config1, thread_func_any_core); + // 列出当前目录内容 + listing = SDFileManager::getInstance()->lsCommand(".", false, true); + ESP_LOGI("SD", "%s", listing.c_str()); - ThreadConfig config2; - config2.name = "Thread2"; - config2.core_id = 1; // 指定运行在核1 - config2.stack_size = 3072; - config2.priority = 5; - std::thread thread2 = ThreadManager::createThread(config2, thread_func_inherited); + LVGLRender::getInstance()->RenderGif("sequence01m.gif"); + + // 设置音量 + AudioOutput::getInstance()->setVolume(5); + + // 同步播放 + AudioOutput::getInstance()->playSync("/sdcard/music", "Old_Memory.mp3"); + + // 等待5秒 + // vTaskDelay(pdMS_TO_TICKS(10000)); + + // 暂停 + // AudioOutput::getInstance()->pause(); // 配置Wifi连接线程参数 @@ -108,25 +87,23 @@ void OTAClass::Init() { 5 // 最大重试次数 ); - ThreadConfig ota_config; - ota_config.name = "OTA"; - ota_config.stack_size = 4096; - ota_config.priority = 6; - ota_config.core_id = 0; - std::thread ota_thread = ThreadManager::createMemberThread( - ota_config, - this, - &OTAClass::Update - ); + // ThreadConfig ota_config; + // ota_config.name = "OTA"; + // ota_config.stack_size = 4096; + // ota_config.priority = 6; + // ota_config.core_id = 0; + // std::thread ota_thread = ThreadManager::createMemberThread( + // ota_config, + // this, + // &OTAClass::Update + // ); - while (true) { - std::stringstream ss; - ss << "core id: " << xPortGetCoreID() - << ", prio: " << uxTaskPriorityGet(nullptr) - << ", minimum free stack: " << uxTaskGetStackHighWaterMark(nullptr) << " bytes."; - ESP_LOGI(pcTaskGetName(nullptr), "%s", ss.str().c_str()); - std::this_thread::sleep_for(sleep_time); + while (true) { // 主线程线程循环 + ThreadManager::print_sys_memory(); // 打印系统内存使用情况 + ThreadManager::stats_task(); // 打印任务统计信息 + + std::this_thread::sleep_for(sleep_time); // 休眠5秒 } } diff --git a/Bionic_Core/ToolsClass/AudioOutput/AudioOutput.cpp b/Bionic_Core/ToolsClass/AudioOutput/AudioOutput.cpp new file mode 100644 index 0000000..25884d3 --- /dev/null +++ b/Bionic_Core/ToolsClass/AudioOutput/AudioOutput.cpp @@ -0,0 +1,248 @@ +// +// Created by misaki on 2025/9/9. +// + +#include "AudioOutput.h" +#include "esp_log.h" +#include +#include "PCM5101.h" + + +// 初始化静态成员 +AudioOutput* AudioOutput::instance = nullptr; +std::mutex AudioOutput::instanceMutex; + +AudioOutput* AudioOutput::getInstance() { + std::lock_guard lock(instanceMutex); + if (instance == nullptr) { + instance = new AudioOutput(); + } + return instance; +} + +AudioOutput::AudioOutput() + : currentState(AudioState::IDLE), + currentVolume(Volume_MAX - 2), + hardwareInitialized(false) { + // 初始化硬件 + init(); +} + +AudioOutput::~AudioOutput() { + // 停止播放并清理资源 + stop(); +} + +bool AudioOutput::init() { + std::lock_guard lock(stateMutex); + + if (hardwareInitialized) { + ESP_LOGI("AudioOutput", "Audio hardware already initialized"); + return true; + } + + // 初始化SD卡 + SDFileManager::getInstance()->tryInitSDCard(); + + // 初始化音频硬件 + Audio_Init(); + hardwareInitialized = true; + ESP_LOGI("AudioOutput", "Audio hardware initialized successfully"); + + return hardwareInitialized; +} + +void AudioOutput::tryInitAudioOutput() { + ESP_LOGI("AudioOutput", "Trying to initialize audio output......"); +} + + +bool AudioOutput::playSync(const char* directory, const char* fileName) { + std::lock_guard lock(stateMutex); + + if (!hardwareInitialized) { + ESP_LOGE("AudioOutput", "Audio hardware not initialized"); + return false; + } + + // 停止当前播放 TODO:有bug,需要fix + // stop(); + + // 播放新文件 + Play_Music(directory, fileName); + + if (audio_player_get_state() == AUDIO_PLAYER_STATE_PLAYING) { + currentState = AudioState::PLAYING; + currentFilePath = std::string(directory) + "/" + fileName; + return true; + } + + currentState = AudioState::ERROR; + return false; +} + +void AudioOutput::playAsync(const char* directory, const char* fileName, AudioCallback callback) { + ThreadConfig config = getThreadConfig("play_async"); + + ThreadManager::createThread(config, [this, directory = std::string(directory), + fileName = std::string(fileName), callback]() { + this->playInternal(directory.c_str(), fileName.c_str(), callback); + }).detach(); +} + +void AudioOutput::playInternal(const char* directory, const char* fileName, AudioCallback callback) { + bool success = false; + AudioState finalState = AudioState::ERROR; + + { + std::lock_guard lock(stateMutex); + + if (!hardwareInitialized) { + ESP_LOGE("AudioOutput", "Audio hardware not initialized"); + finalState = AudioState::ERROR; + } else { + // 停止当前播放 + // stop(); + + // 播放新文件 + Play_Music(directory, fileName); + + if (audio_player_get_state() == AUDIO_PLAYER_STATE_PLAYING) { + currentState = AudioState::PLAYING; + currentFilePath = std::string(directory) + "/" + fileName; + success = true; + finalState = AudioState::PLAYING; + } else { + currentState = AudioState::ERROR; + finalState = AudioState::ERROR; + } + } + } + + if (callback) { + callback(finalState, success ? currentFilePath.c_str() : nullptr); + } +} + +bool AudioOutput::pause() { + std::lock_guard lock(stateMutex); + + if (currentState != AudioState::PLAYING) { + return false; + } + + Music_pause(); + + if (audio_player_get_state() == AUDIO_PLAYER_STATE_PAUSE) { + currentState = AudioState::PAUSED; + return true; + } + + return false; +} + +bool AudioOutput::resume() { + std::lock_guard lock(stateMutex); + + if (currentState != AudioState::PAUSED) { + return false; + } + + Music_resume(); + + if (audio_player_get_state() == AUDIO_PLAYER_STATE_PLAYING) { + currentState = AudioState::PLAYING; + return true; + } + + return false; +} + +bool AudioOutput::stop() { + std::lock_guard lock(stateMutex); + + if (currentState == AudioState::IDLE || currentState == AudioState::STOPPED) { // 如果当前状态为IDLE或STOPPED,则直接返回成功 + return true; + } + + // 暂停播放 + Music_pause(); // 内部已经完成关闭文件的操作了 + + currentState = AudioState::STOPPED; + currentFilePath.clear(); + + return true; +} + +bool AudioOutput::setVolume(uint8_t volume) { + std::lock_guard lock(stateMutex); + + if (volume > Volume_MAX) { + ESP_LOGE("AudioOutput", "Volume value %d is out of range (0-%d)", volume, Volume_MAX); + return false; + } + + Volume_adjustment(volume); + currentVolume = volume; + return true; +} + +uint8_t AudioOutput::getVolume() const { + std::lock_guard lock(stateMutex); + return currentVolume; +} + +uint8_t AudioOutput::getMaxVolume() const { + return Volume_MAX; +} + +uint32_t AudioOutput::getDuration() const { + return Music_Duration(); +} + +uint32_t AudioOutput::getElapsed() const { + return Music_Elapsed(); +} + +uint16_t AudioOutput::getEnergy() const { + return Music_Energy(); +} + +AudioState AudioOutput::getState() const { + std::lock_guard lock(stateMutex); + return currentState; +} + +bool AudioOutput::isPlaying() const { + std::lock_guard lock(stateMutex); + return currentState == AudioState::PLAYING; +} + +bool AudioOutput::isPaused() const { + std::lock_guard lock(stateMutex); + return currentState == AudioState::PAUSED; +} + +bool AudioOutput::isStopped() const { + std::lock_guard lock(stateMutex); + return currentState == AudioState::STOPPED || currentState == AudioState::IDLE; +} + +bool AudioOutput::isFinished() const { + return Music_Next_Flag; +} + +ThreadConfig AudioOutput::getThreadConfig(const char* operation) { + ThreadConfig config; + config.name = "audio_" + std::string(operation); + config.core_id = -1; // 不绑定核心 + config.stack_size = 4096; + config.priority = 5; + config.inherit_cfg = false; + return config; +} + +void AudioOutput::setState(AudioState newState) { + std::lock_guard lock(stateMutex); + currentState = newState; +} \ No newline at end of file diff --git a/Bionic_Core/ToolsClass/AudioOutput/AudioOutput.h b/Bionic_Core/ToolsClass/AudioOutput/AudioOutput.h new file mode 100644 index 0000000..3a4c24a --- /dev/null +++ b/Bionic_Core/ToolsClass/AudioOutput/AudioOutput.h @@ -0,0 +1,202 @@ +// +// Created by misaki on 2025/9/9. +// + + + +#pragma once + +#include +#include +#include +#include "ThreadManager.h" +#include "SDFileManager.h" + +// 音频播放状态枚举 +enum class AudioState { + IDLE, // 音频未播放 + PLAYING, // 音频播放中 + PAUSED, // 音频暂停中 + STOPPED, // 音频已停止 + ERROR // 音频播放错误 +}; + +// 音频播放回调函数类型 +using AudioCallback = std::function; + +/* + * 回调函数示例: + void audioCallback(AudioState state, const char* filePath) { + switch (state) { + case AudioState::PLAYING: + ESP_LOGI("Example", "Started playing: %s", filePath); + break; + case AudioState::PAUSED: + ESP_LOGI("Example", "Paused: %s", filePath); + break; + case AudioState::STOPPED: + ESP_LOGI("Example", "Stopped: %s", filePath); + break; + case AudioState::ERROR: + ESP_LOGE("Example", "Error playing: %s", filePath); + break; + default: + break; + } + } + */ + + +/** + * 本模块为音频输出模块 + * 支持同步,异步音频输出 + * + * 注意:底层C驱动任务运行在核0, 请不要把例如lvgl这种高CPU占比的任务放在核0中,避免资源抢占导致播放卡顿 + */ +class AudioOutput { +public: + // 删除拷贝构造函数和赋值运算符 + AudioOutput(AudioOutput const&) = delete; + AudioOutput& operator=(AudioOutput const&) = delete; + + /** + * 获取单例实例 + * @return AudioOutput实例 + */ + static AudioOutput* getInstance(); + + /** + * 初始化音频输出 + * @return 是否成功 + */ + bool init(); + + // try to init AudioOutput + void tryInitAudioOutput(); + + /** + * 同步播放音频文件 + * @param directory 目录路径 + * @param fileName 文件名 + * @return 是否成功 + */ + bool playSync(const char* directory, const char* fileName); + + /** + * 异步播放音频文件 + * @param directory 目录路径 + * @param fileName 文件名 + * @param callback 回调函数 + */ + void playAsync(const char* directory, const char* fileName, AudioCallback callback = nullptr); + + /** + * 暂停播放 + * @return 是否成功 + */ + bool pause(); + + /** + * 恢复播放 + * @return 是否成功 + */ + bool resume(); + + /** + * 停止播放 + * @return 是否成功 + */ + bool stop(); + + /** + * 设置音量 + * @param volume 音量值 (0-100) + * @return 是否成功 + */ + bool setVolume(uint8_t volume); + + /** + * 获取当前音量 + * @return 音量值 + */ + uint8_t getVolume() const; + + /** + * 获取最大音量 + * @return 最大音量值 + */ + uint8_t getMaxVolume() const; + + /** + * 获取音频总时长 + * @return 总时长(毫秒) + */ + uint32_t getDuration() const; + + /** + * 获取已播放时长 + * @return 已播放时长(毫秒) + */ + uint32_t getElapsed() const; + + /** + * 获取音频能量值 + * @return 能量值 + */ + uint16_t getEnergy() const; + + /** + * 获取当前播放状态 + * @return 播放状态 + */ + AudioState getState() const; + + /** + * 检查是否正在播放 + * @return 是否正在播放 + */ + bool isPlaying() const; + + /** + * 检查是否暂停 + * @return 是否暂停 + */ + bool isPaused() const; + + /** + * 检查是否停止 + * @return 是否停止 + */ + bool isStopped() const; + + /** + * 检查播放是否完成 + * @return 是否完成 + */ + bool isFinished() const; + +private: + // 私有构造函数 + AudioOutput(); + ~AudioOutput(); + + // 初始化线程配置 + ThreadConfig getThreadConfig(const char* operation); + + // 内部播放实现 + void playInternal(const char* directory, const char* fileName, AudioCallback callback); + + // 状态转换辅助方法 + void setState(AudioState newState); + + // 单例实例指针 + static AudioOutput* instance; + static std::mutex instanceMutex; + + // 成员变量 + mutable std::mutex stateMutex; + AudioState currentState; + std::string currentFilePath; + uint8_t currentVolume; + bool hardwareInitialized; +}; \ No newline at end of file diff --git a/Bionic_Core/ToolsClass/LVGL_Render/LVGLRender.cpp b/Bionic_Core/ToolsClass/LVGL_Render/LVGLRender.cpp index 61d4636..8c3da5a 100644 --- a/Bionic_Core/ToolsClass/LVGL_Render/LVGLRender.cpp +++ b/Bionic_Core/ToolsClass/LVGL_Render/LVGLRender.cpp @@ -54,7 +54,7 @@ LVGLRender::LVGLRender() { std::thread tick_thread = ThreadManager::createMemberThread(trickConfig, this, &LVGLRender::LVGL_Update); - tick_thread.detach(); // 线程分离 生命周期跟随主线程结束,线程结束后自动销毁 + tick_thread.detach(); // 线程分离 生命周期跟随主线程结束,线程结束后自动销毁(核心渲染线程) ESP_LOGI("LVGL_Render", "LVGL_Render构造函数...创建LVGL心跳成功..."); } @@ -104,36 +104,50 @@ bool LVGLRender::getGifWH(const uint8_t* raw, uint32_t& w, uint32_t& h) void LVGLRender::renderGifInternal(const std::vector& data, uint32_t w, uint32_t h) { - // 构造 lv_img_dsc_t —— 数据指针直接指向 vector 内部 - static lv_img_dsc_t gif_desc; + // 删除旧对象 + if (current_gif_obj != nullptr) { + lv_obj_del(current_gif_obj); + current_gif_obj = nullptr; + } + + // 保存数据,防止被释放 + current_gif_data = data; + + // 构造新的描述符(不要 static) + lv_img_dsc_t gif_desc = {}; gif_desc.header.cf = LV_IMG_CF_RAW_CHROMA_KEYED; gif_desc.header.always_zero = 0; gif_desc.header.reserved = 0; gif_desc.header.w = (lv_coord_t)w; gif_desc.header.h = (lv_coord_t)h; - gif_desc.data_size = data.size(); - gif_desc.data = data.data(); // 指向 vector 内部 + gif_desc.data_size = current_gif_data.size(); + gif_desc.data = current_gif_data.data(); - // 创建 lv_gif 对象 - lv_obj_t* gif = lv_gif_create(lv_scr_act()); - lv_gif_set_src(gif, &gif_desc); - lv_obj_center(gif); + // 创建新的 GIF 对象 + current_gif_obj = lv_gif_create(lv_scr_act()); + lv_gif_set_src(current_gif_obj, &gif_desc); + lv_obj_center(current_gif_obj); - ESP_LOGI("LVGLRender", "GIF 已渲染到屏幕"); + ESP_LOGI("LVGLRender", "GIF 已渲染并循环播放"); } void LVGLRender::RenderGif(const std::string &filename) { - std::string fullPath = makeFullPath(filename); + if (filename == last_gif_filename) { + ESP_LOGW("LVGLRender", "重复加载同一 GIF,忽略"); + return; + } + last_gif_filename = filename; - // 读文件 + lv_obj_set_style_bg_color(lv_scr_act(), lv_color_black(), 0); // 背景黑色 + lv_obj_set_style_bg_opa(lv_scr_act(), LV_OPA_COVER, 0); // 透明度 + + std::string fullPath = makeFullPath(filename); std::vector gifBin = readWholeFile(fullPath); if (gifBin.empty()) return; - // 校验并解析宽高 uint32_t w = 0, h = 0; if (!getGifWH(gifBin.data(), w, h)) return; - // LVGL 渲染函数 renderGifInternal(gifBin, w, h); } diff --git a/Bionic_Core/ToolsClass/LVGL_Render/LVGLRender.h b/Bionic_Core/ToolsClass/LVGL_Render/LVGLRender.h index 5e4f683..db7e724 100644 --- a/Bionic_Core/ToolsClass/LVGL_Render/LVGLRender.h +++ b/Bionic_Core/ToolsClass/LVGL_Render/LVGLRender.h @@ -12,6 +12,8 @@ #include #include +#include +#include class LVGLRender { public: @@ -54,6 +56,10 @@ private: static LVGLRender* LVGLRenderInstance; /// 单例实例 static std::mutex instance_mutex; /// 单例锁 static uint16_t fps; /// 帧率 + + lv_obj_t* current_gif_obj = nullptr; /// 当前GIF对象 + std::vector current_gif_data; /// 当前GIF数据 + std::string last_gif_filename; /// 最后一次渲染的GIF文件名 }; diff --git a/Bionic_Core/ToolsClass/SDFileManager/SDFileManager.cpp b/Bionic_Core/ToolsClass/SDFileManager/SDFileManager.cpp index 48f26bd..1a352ed 100644 --- a/Bionic_Core/ToolsClass/SDFileManager/SDFileManager.cpp +++ b/Bionic_Core/ToolsClass/SDFileManager/SDFileManager.cpp @@ -42,6 +42,11 @@ void SDFileManager::init() { is_initialized = true; } +void SDFileManager::tryInitSDCard() { + ESP_LOGI("SDFileManager", "Trying to initialize SD card..."); +} + + bool SDFileManager::writeFileSync(const char* path, const char* data) { std::lock_guard lock(file_operation_mutex); diff --git a/Bionic_Core/ToolsClass/SDFileManager/SDFileManager.h b/Bionic_Core/ToolsClass/SDFileManager/SDFileManager.h index bef25a7..693c5f2 100644 --- a/Bionic_Core/ToolsClass/SDFileManager/SDFileManager.h +++ b/Bionic_Core/ToolsClass/SDFileManager/SDFileManager.h @@ -25,6 +25,10 @@ public: static SDFileManager* getInstance(); + // try to init sdcard 对外提供的自动初始化,如果已经初始化了,只会打印一个日志,如果没有初始化,会自动初始化,这得益于单例模式的特性 + // 与一般的初始化函数不同的是,该函数可以被重复调用,不会重复初始化sd卡 + void tryInitSDCard(); + // 同步文件操作 /** * 同步写入文件 diff --git a/Bionic_Core/ToolsClass/ThreadManager/ThreadManager.h b/Bionic_Core/ToolsClass/ThreadManager/ThreadManager.h index 1f3bf02..56f1564 100644 --- a/Bionic_Core/ToolsClass/ThreadManager/ThreadManager.h +++ b/Bionic_Core/ToolsClass/ThreadManager/ThreadManager.h @@ -132,6 +132,34 @@ public: ESP_LOGI(pcTaskGetName(nullptr), "%s", ss.str().c_str()); } + /** + * @brief 打印系统内存信息 + */ + static void print_sys_memory(void) + { + size_t internal = heap_caps_get_free_size(MALLOC_CAP_INTERNAL); + size_t spiram = heap_caps_get_free_size(MALLOC_CAP_SPIRAM); + printf("Internal(内部): %zu kB, SPIRAM(外部): %zu kB\n", internal / 1024, spiram / 1024); + } + + + + static void stats_task(void) + { + char stats_buf[2*1024]; + /* 任务列表 + 绑核信息 */ + printf("\n-------- vTaskList --------\n"); + vTaskList(stats_buf); + printf("Name State Prio HWM Num Core\n"); + printf("%s", stats_buf); + + /* 各任务 CPU 使用率(已按核分开统计) */ + printf("-------- vTaskGetRunTimeStats --------\n"); + vTaskGetRunTimeStats(stats_buf); + printf("Task AbsTime %%Time\n"); + printf("%s", stats_buf); + } + private: /** * @brief 创建ESP32线程配置 diff --git a/Lib/Audio_Driver/PCM5101.c b/Lib/Audio_Driver/PCM5101.c index dab1cc7..00e1cfb 100644 --- a/Lib/Audio_Driver/PCM5101.c +++ b/Lib/Audio_Driver/PCM5101.c @@ -91,8 +91,8 @@ void Audio_Init(void) .mute_fn = audio_mute_function, .write_fn = bsp_i2s_write, .clk_set_fn = bsp_i2s_reconfig_clk, - .priority = 3, - .coreID = 1 + .priority = 5, + .coreID = 0 // 运行在0号核,避免与lvgl抢占资源 }; ret = audio_player_new(config); if (ret != ESP_OK) { @@ -124,12 +124,25 @@ void Play_Music(const char* directory, const char* fileName) } else { snprintf(filePath, maxPathLength, "%s/%s", directory, fileName); } + ESP_LOGD(TAG, "Playing MP3 file: %s", filePath); Music_File = Open_File(filePath); if (!Music_File) { ESP_LOGE(TAG, "Failed to open MP3 file: %s", filePath); return; } + // 跳过非音频数据 + uint8_t buf[4]; + while (fread(buf, 1, 4, Music_File) == 4) { + uint32_t head = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; + if ((head & 0xFFE00000) == 0xFFE00000) { // 找到 MP3 帧头 + fseek(Music_File, -4, SEEK_CUR); // 回退 4 字节 + break; + } + fseek(Music_File, -3, SEEK_CUR); // 逐字节滑动窗口 + } + ESP_LOGI(TAG, "Skipped non-audio data, now at offset %ld", ftell(Music_File)); + expected_event = AUDIO_PLAYER_CALLBACK_EVENT_PLAYING; esp_err_t ret = audio_player_play(Music_File); if (ret != ESP_OK) { diff --git a/Temp.md b/Temp.md index 3daf2f0..1efff8e 100644 --- a/Temp.md +++ b/Temp.md @@ -197,4 +197,113 @@ extern "C" void app_main(void) std::this_thread::sleep_for(sleep_time); } } +``` + + + +```c++ +#include "WebSocketManager.h" +#include "cJSON.h" + +// 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; + } +} + +extern "C" void app_main() { + // 初始化WiFi + WifiConnectors* wifi = WifiConnectors::getInstance(); + + // 连接WiFi(在实际应用中应该在单独线程中进行) + ThreadConfig wifi_config; + wifi_config.name = "WiFi_Connector"; + + auto wifi_thread = ThreadManager::createSingletonThread( + wifi_config, &WifiConnectors::connectWifi, "Your_SSID", "Your_Password"); + + wifi_thread.join(); + + // 获取WebSocket管理器实例 + WebSocketManager* ws = WebSocketManager::getInstance(); + + // 配置WebSocket + WebSocketConfig config; + config.uri = "ws://your-websocket-server.com/ws"; + config.auto_reconnect = true; + config.reconnect_interval = 5000; + config.heartbeat_interval = 30000; + config.max_reconnect_attempts = 10; + + // 初始化WebSocket管理器 + if (!ws->initialize(config)) { + ESP_LOGE("App", "Failed to initialize WebSocket manager"); + return; + } + + // 设置回调 + ws->setJsonCallback(onJsonData); + ws->setEventCallback(onWebSocketEvent); + + // 连接WebSocket + if (!ws->connect()) { + ESP_LOGE("App", "Failed to connect WebSocket"); + return; + } + + // 等待连接建立 + vTaskDelay(2000 / portTICK_PERIOD_MS); + + // 发送JSON数据 + cJSON* json = cJSON_CreateObject(); + cJSON_AddStringToObject(json, "type", "greeting"); + cJSON_AddStringToObject(json, "message", "Hello from ESP32!"); + cJSON_AddNumberToObject(json, "timestamp", esp_log_timestamp()); + + ws->sendJson(json); + + // 保持运行 + while (true) { + vTaskDelay(1000 / portTICK_PERIOD_MS); + + // 可以在这里发送其他数据或处理业务逻辑 + if (ws->isConnected()) { + cJSON* status = cJSON_CreateObject(); + cJSON_AddStringToObject(status, "type", "status"); + cJSON_AddNumberToObject(status, "free_heap", esp_get_free_heap_size()); + + ws->sendJson(status); + } + } +} ``` \ No newline at end of file diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 726fe09..1affd53 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -33,6 +33,7 @@ idf_component_register(SRCS "Bionic_sphere.c" "../Bionic_Core/OTAClass/OTAClass.cpp" # OTA类库 "../Bionic_Core/CommClass/CommClass.cpp" # 通信类库 "../Bionic_Core/ToolsClass/ToolsClass.cpp" # 工具类库 + "../Bionic_Core/ToolsClass/AudioOutput/AudioOutput.cpp" # 音频输出类库 "../Bionic_Core/ToolsClass/LVGL_Render/LVGLRender.cpp" # LVGL渲染类库 "../Bionic_Core/ToolsClass/SDFileManager/SDFileManager.cpp" # SD文件管理类库 "../Bionic_Core/ToolsClass/WifiConnectors/WifiConnectors.cpp" # WIFI连接类库 @@ -67,6 +68,7 @@ idf_component_register(SRCS "Bionic_sphere.c" "../Bionic_Core/OTAClass" "../Bionic_Core/CommClass" "../Bionic_Core/ToolsClass" + "../Bionic_Core/ToolsClass/AudioOutput" "../Bionic_Core/ToolsClass/LVGL_Render" "../Bionic_Core/ToolsClass/SDFileManager" "../Bionic_Core/ToolsClass/WifiConnectors" diff --git a/sdkconfig b/sdkconfig index e63ec21..3bfea24 100644 --- a/sdkconfig +++ b/sdkconfig @@ -1679,7 +1679,7 @@ CONFIG_ESP_SYSTEM_MEMPROT_FEATURE_LOCK=y CONFIG_ESP_SYSTEM_EVENT_QUEUE_SIZE=32 CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=2304 -CONFIG_ESP_MAIN_TASK_STACK_SIZE=3584 +CONFIG_ESP_MAIN_TASK_STACK_SIZE=8192 CONFIG_ESP_MAIN_TASK_AFFINITY_CPU0=y # CONFIG_ESP_MAIN_TASK_AFFINITY_CPU1 is not set # CONFIG_ESP_MAIN_TASK_AFFINITY_NO_AFFINITY is not set @@ -1902,9 +1902,13 @@ CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=2048 CONFIG_FREERTOS_TIMER_QUEUE_LENGTH=10 CONFIG_FREERTOS_QUEUE_REGISTRY_SIZE=0 CONFIG_FREERTOS_TASK_NOTIFICATION_ARRAY_ENTRIES=1 -# CONFIG_FREERTOS_USE_TRACE_FACILITY is not set +CONFIG_FREERTOS_USE_TRACE_FACILITY=y +CONFIG_FREERTOS_USE_STATS_FORMATTING_FUNCTIONS=y # CONFIG_FREERTOS_USE_LIST_DATA_INTEGRITY_CHECK_BYTES is not set -# CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS is not set +CONFIG_FREERTOS_VTASKLIST_INCLUDE_COREID=y +CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS=y +CONFIG_FREERTOS_RUN_TIME_COUNTER_TYPE_U32=y +# CONFIG_FREERTOS_RUN_TIME_COUNTER_TYPE_U64 is not set # CONFIG_FREERTOS_USE_APPLICATION_TASK_TAG is not set # end of Kernel @@ -1923,6 +1927,7 @@ CONFIG_FREERTOS_TICK_SUPPORT_SYSTIMER=y CONFIG_FREERTOS_CORETIMER_SYSTIMER_LVL1=y # CONFIG_FREERTOS_CORETIMER_SYSTIMER_LVL3 is not set CONFIG_FREERTOS_SYSTICK_USES_SYSTIMER=y +CONFIG_FREERTOS_RUN_TIME_STATS_USING_ESP_TIMER=y # CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH is not set # CONFIG_FREERTOS_CHECK_PORT_CRITICAL_COMPLIANCE is not set # end of Port @@ -3216,7 +3221,7 @@ CONFIG_ESP32S3_DEFAULT_CPU_FREQ_240=y CONFIG_ESP32S3_DEFAULT_CPU_FREQ_MHZ=240 CONFIG_SYSTEM_EVENT_QUEUE_SIZE=32 CONFIG_SYSTEM_EVENT_TASK_STACK_SIZE=2304 -CONFIG_MAIN_TASK_STACK_SIZE=3584 +CONFIG_MAIN_TASK_STACK_SIZE=8192 CONFIG_CONSOLE_UART_DEFAULT=y # CONFIG_CONSOLE_UART_CUSTOM is not set # CONFIG_CONSOLE_UART_NONE is not set diff --git a/sdkconfig.old b/sdkconfig.old index 6d8b7b5..c9f49df 100644 --- a/sdkconfig.old +++ b/sdkconfig.old @@ -1413,7 +1413,7 @@ CONFIG_HTTPD_PURGE_BUF_LEN=32 # ESP HTTPS OTA # # CONFIG_ESP_HTTPS_OTA_DECRYPT_CB is not set -# CONFIG_ESP_HTTPS_OTA_ALLOW_HTTP is not set +CONFIG_ESP_HTTPS_OTA_ALLOW_HTTP=y # end of ESP HTTPS OTA # @@ -1902,9 +1902,13 @@ CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=2048 CONFIG_FREERTOS_TIMER_QUEUE_LENGTH=10 CONFIG_FREERTOS_QUEUE_REGISTRY_SIZE=0 CONFIG_FREERTOS_TASK_NOTIFICATION_ARRAY_ENTRIES=1 -# CONFIG_FREERTOS_USE_TRACE_FACILITY is not set +CONFIG_FREERTOS_USE_TRACE_FACILITY=y +CONFIG_FREERTOS_USE_STATS_FORMATTING_FUNCTIONS=y # CONFIG_FREERTOS_USE_LIST_DATA_INTEGRITY_CHECK_BYTES is not set -# CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS is not set +CONFIG_FREERTOS_VTASKLIST_INCLUDE_COREID=y +CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS=y +CONFIG_FREERTOS_RUN_TIME_COUNTER_TYPE_U32=y +# CONFIG_FREERTOS_RUN_TIME_COUNTER_TYPE_U64 is not set # CONFIG_FREERTOS_USE_APPLICATION_TASK_TAG is not set # end of Kernel @@ -1923,6 +1927,7 @@ CONFIG_FREERTOS_TICK_SUPPORT_SYSTIMER=y CONFIG_FREERTOS_CORETIMER_SYSTIMER_LVL1=y # CONFIG_FREERTOS_CORETIMER_SYSTIMER_LVL3 is not set CONFIG_FREERTOS_SYSTICK_USES_SYSTIMER=y +CONFIG_FREERTOS_RUN_TIME_STATS_USING_ESP_TIMER=y # CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH is not set # CONFIG_FREERTOS_CHECK_PORT_CRITICAL_COMPLIANCE is not set # end of Port @@ -2635,6 +2640,13 @@ CONFIG_DSP_MAX_FFT_SIZE_4096=y CONFIG_DSP_MAX_FFT_SIZE=4096 # end of DSP Library +# +# ESP WebSocket client +# +# CONFIG_ESP_WS_CLIENT_ENABLE_DYNAMIC_BUFFER is not set +# CONFIG_ESP_WS_CLIENT_SEPARATE_TX_LOCK is not set +# end of ESP WebSocket client + # # LVGL configuration # @@ -3184,7 +3196,7 @@ CONFIG_POST_EVENTS_FROM_ISR=y CONFIG_POST_EVENTS_FROM_IRAM_ISR=y CONFIG_GDBSTUB_SUPPORT_TASKS=y CONFIG_GDBSTUB_MAX_TASKS=32 -# CONFIG_OTA_ALLOW_HTTP is not set +CONFIG_OTA_ALLOW_HTTP=y CONFIG_ESP32S3_DEEP_SLEEP_WAKEUP_DELAY=2000 CONFIG_ESP_SLEEP_DEEP_SLEEP_WAKEUP_DELAY=2000 CONFIG_ESP32S3_RTC_CLK_SRC_INT_RC=y diff --git a/项目开发日志.md b/项目开发日志.md index 39c06b3..c033bb8 100644 --- a/项目开发日志.md +++ b/项目开发日志.md @@ -206,3 +206,14 @@ - [x] 3. 修复了硬件厂商提供的驱动的Bug - [x] 4. 初步定义了宠物基类的抽象信息 + +#### Day13 2025.9.12(前两天在忙考研复习) +##### 主要目标:完成具体业务开发&各种优化 +实际完成任务: +- [x] 1. 完成了对音频播放类的完整C++封装,测试通过 + +- [x] 2. 修复了LVGL渲染类当中的一些小bug + +- [x] 3. 增加了一些CPU资源占用的日志打印函数,运行在主线程当中 + +- [x] 4. 完善了底层通信类的封装,基于websocket,尚未测试 \ No newline at end of file