Compare commits

..

10 Commits

Author SHA1 Message Date
Misaki ba5e47bc77 这是一次长久的提交:
1. 应用界面增加了返回主页的按钮
2. 修复了gif渲染内存泄漏的严重bug
3. 将PetDao当中的cJSON API替换为cpp_json,完美通过测试
4. 整合已经实现的各种上层建筑,实现了一个宠物对话基本业务应用,用于样品测试展示用
5. 重构了音频播放类,使其更modern,更加便于移植和拓展
2025-10-16 11:36:45 +08:00
Misaki 801138631e 1. 花了几天时间基于lvgl8.3封装了lvgl_cpp,写了一些基本需要的控件,支持链式调用
还存在一点点bug,不难fix
2. 增加了中文字库,支持中文显示
3. 修复和优化了一些地方
2025-09-27 05:43:43 +08:00
Misaki a47e20cb64 1. 优化了cpp_json的内容,使其更modern
2. 稍微优化了一下系统配置类
3. 增加了系统版本号,便于区分系统版本,方便OTA
4. 重写OTA的逻辑,完成了Cpp的OTA封装,测试通过
2025-09-24 04:01:23 +08:00
Misaki 6c4749ba0c 1. 基于cJSON进行了上层现代C++封装,作为脚手架使用
2. 在esp32s3剩余的flash当中开辟了一个文件系统,用于保存设备重要配置信息
2025-09-23 03:18:25 +08:00
Misaki 48f208b2e6 1. 完成了语音识别的C++业务层封装,测试通过
2. 试着测试了一下LVGL_GIF渲染+音乐播放+语音识别的组合简单优化后,
          发现lvgl渲染略显卡顿,语音识别有缓冲区空警告,不过无伤大雅,还需要进一步深度优化。
2025-09-23 03:17:23 +08:00
Misaki 4cc761aab3 1. 完成了语音识别的C++业务层封装,测试通过
2. 试着测试了一下LVGL_GIF渲染+音乐播放+语音识别的组合简单优化后,
          发现lvgl渲染略显卡顿,语音识别有缓冲区空警告,不过无伤大雅,还需要进一步深度优化。
2025-09-16 01:29:17 +08:00
Misaki dc420c3b7a 1. 历时两天,完整且完美的设计了宠物类,使用到了多种设计模式,完成了低耦合,高内聚的完美代码,测试也完美通过。
2. 顺便完善了底层通信类的封装,基于websocket,基准测试通过,但存在一点很小的线程bug,似乎是来自于esp32 idf底层的问题,待解决
2025-09-15 01:49:09 +08:00
Misaki 97fe13da26 1. 完成了对音频播放类的完整C++封装,测试通过
2. 修复了LVGL渲染类当中的一些小bug
3. 增加了一些CPU资源占用的日志打印函数,运行在主线程当中
4. 完善了底层通信类的封装,基于websocket,尚未测试
2025-09-12 02:11:50 +08:00
Misaki 4985fee7c2 1. 完整封装并拓展了SD卡文件管理类,支持基本文件管理功能
2. 简单封装了LVGL渲染类,已经封装好了gif渲染功能
3. 修复了硬件厂商提供的驱动的Bug
4. 初步定义了宠物基类的抽象信息
2025-09-09 03:40:24 +08:00
Misaki 28ceb0caf5 1. 完成了ota功能的基本测试,测试通过
2. 封装了一个模板线程类,支持创建来自单例类的成员函数线程,普通类的线程,普通函数线程
3. 封装了一个Wifi模块类,支持Wifi的各种基本配置
2025-09-05 01:09:12 +08:00
85 changed files with 209134 additions and 1295 deletions
+2
View File
@@ -4,3 +4,5 @@ log
# 组件库,配置好idf环境后,运行idf.py reconfigure就会自动生成出来 # 组件库,配置好idf环境后,运行idf.py reconfigure就会自动生成出来
managed_components managed_components
.idea .idea
.venv
.vscode
+4
View File
@@ -0,0 +1,4 @@
//
// Created by misaki on 2025/9/24.
//
#include "CommBean.h"
+7
View File
@@ -0,0 +1,7 @@
//
// Created by misaki on 2025/9/24.
//
#pragma once
+399
View File
@@ -2,3 +2,402 @@
// Created by misaki on 2025/9/2. // Created by misaki on 2025/9/2.
// //
#include "CommClass.h" #include "CommClass.h"
#include <cstring> // 引入字符串处理库头文件
#include <chrono> // 时间库头文件
#include <memory>
#include <esp_event.h>
#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 = SysConfJson::getInstance()->loadSN(); // 获取SN(同时也能初始化文件系统,如果还没有初始化这个文件系统的话)
// 标签用于日志
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())
{
}
// 析构函数
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<std::mutex> lock(instance_mutex); // 加锁
if (instance == nullptr) {
instance = new WebSocketManager();
}
return instance;
}
bool WebSocketManager::initialize(const WebSocketConfig& ws_config) {
this->config = ws_config;
// 注册WiFi事件处理
esp_event_handler_instance_t wifi_instance;
esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP,
&WebSocketManager::wifiEventHandler,
this, &wifi_instance);
// 启动发送线程
// 只在线程未运行时启动发送线程
if (!send_thread.joinable()) {
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;
}
// 启动重连和心跳线程
// 只有在线程未运行时才启动新线程
if (!reconnect_thread.joinable()) {
ThreadConfig thread_config;
thread_config.name = "WS_Reconnect";
thread_config.stack_size = 3072;
reconnect_thread = ThreadManager::createMemberThread(thread_config, this,
&WebSocketManager::reconnectThread);
}
if (!heartbeat_thread.joinable()) {
ThreadConfig thread_config;
thread_config.name = "WS_Heartbeat";
thread_config.stack_size = 3072;
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(const cppjson::Json& json) {
if (!connected) { // 检查连接状态
ESP_LOGE(TAG, "Not connected, cannot send data");
return false;
}
std::string json_str = json.dump(); // 序列化 JSON
if (json_str.empty() || json_str == "null") { // 检查序列化结果
ESP_LOGE(TAG, "Failed to stringify JSON");
return false;
}
{ // 临界区
std::lock_guard<std::mutex> lock(queue_mutex);
send_queue.emplace(std::move(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<std::mutex> lock(queue_mutex); // 锁定队列
send_queue.push(data); // 添加到队列
queue_cv.notify_one(); // 通知发送线程
return true;
}
void WebSocketManager::setJsonCallback(const JsonDataCallback &callback) {
json_callback = callback;
}
void WebSocketManager::setEventCallback(const EventCallback &callback) {
event_callback = callback;
}
bool WebSocketManager::isConnected() const {
return connected;
}
WebSocketConfig WebSocketManager::getConfig() const {
return config;
}
void WebSocketManager::updateConfig(const WebSocketConfig& ws_config) {
this->config = ws_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) {
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:
ws_instance->connected = true;
ws_instance->connecting = false;
ws_instance->reconnect_attempts = 0;
ws_instance->stats.successful_connections++;
ESP_LOGI(TAG, "WebSocket connected");
if (ws_instance->event_callback) {
ws_instance->event_callback(WebSocketEvent::CONNECTED, "Connected successfully");
}
break;
case WEBSOCKET_EVENT_DISCONNECTED:
ws_instance->connected = false;
ws_instance->connecting = false;
ESP_LOGI(TAG, "WebSocket disconnected");
if (ws_instance->event_callback) {
ws_instance->event_callback(WebSocketEvent::DISCONNECTED, "Disconnected");
}
break;
case WEBSOCKET_EVENT_DATA:
if (data->data_len > 0) {
ws_instance->stats.messages_received++;
ws_instance->handleReceivedData((const char*)data->data_ptr, data->data_len);
}
break;
case WEBSOCKET_EVENT_ERROR: // 错误事件
ws_instance->connected = false;
ws_instance->connecting = false;
ESP_LOGE(TAG, "WebSocket error");
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, const int len) const {
// 解析JSON
cppjson::Json json = cppjson::Json::parse(std::string(data, len)); // 失败会得到空 Json
if (!json.isNull()) { // 解析成功
if (json_callback) json_callback(json);
} else { // 解析失败,当原始文本处理
if (event_callback)
event_callback(WebSocketEvent::DATA_RECEIVED, std::string(data, len));
}
}
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");
}
}
// sleep
std::this_thread::sleep_for(std::chrono::milliseconds(config.reconnect_interval));
}
}
void WebSocketManager::heartbeatThread() {
while (threads_running) {
if (connected && config.heartbeat_interval > 0) { // 如果处于连接状态且心跳间隔大于0
// 发送心跳消息
cppjson::Json hb = cppjson::Json::object();
hb.set("type", cppjson::Json("ping"))
.set("timestamp", cppjson::Json(esp_log_timestamp()));
sendJson(hb); // 已经重载好了,直接塞
std::this_thread::sleep_for(std::chrono::milliseconds(config.heartbeat_interval));
} else { // 否则就让出CPU
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}
}
}
void WebSocketManager::sendThread() {
while (threads_running) {
std::unique_lock<std::mutex> lock(queue_mutex);
queue_cv.wait(lock, // 传入已持有的 unique_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<int>(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;
}
// 处理sn码逻辑
// 一般情况下sn码会在开机的时候从flash当中读出,因此此处判断是否为空
sn = SysConfJson::getInstance()->loadSN(); // 再读一次sn码
if (!sn.empty()) { // 如果sn码不为空
// 则在头部加入sn码提交
esp_websocket_client_append_header(client, "X-SN", sn.c_str());
// 同时在头部加入mac码和芯片序列号,方便服务端做验证
esp_websocket_client_append_header(client, "X-MAC", ToolsClass::getChipMAC().c_str());
esp_websocket_client_append_header(client, "X-CHIP-ID", ToolsClass::getChipSerialNumber().c_str());
}
else {
// 如果sn码为空,设备可能存在问题
ESP_LOGE(TAG, "SN is empty, please check your device");
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) {
auto* ws_instance = static_cast<WebSocketManager*>(arg);
if (event_base == IP_EVENT && event_Id == IP_EVENT_STA_GOT_IP) {
// WiFi已连接,尝试重新连接WebSocket
if (ws_instance->config.auto_reconnect && !ws_instance->connected) {
ESP_LOGI(TAG, "WiFi connected, attempting to reconnect WebSocket");
ws_instance->connect();
}
}
}
+200 -5
View File
@@ -3,15 +3,210 @@
// //
#pragma once #pragma once
#include <string>
#include <functional>
#include <mutex>
#include <queue>
#include <condition_variable>
#include "esp_websocket_client.h"
#include "cpp_json.h"
#include "ThreadManager.h"
#include "WifiConnectors.h"
#ifdef __cplusplus // WebSocket事件类型
extern "C" { enum class WebSocketEvent {
#endif 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(cppjson::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);
#ifdef __cplusplus 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);
} }
#endif }
}
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数据
bool sendJson(const cppjson::Json& 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
};
+451 -1
View File
@@ -4,7 +4,457 @@
#include "CppHandle.h" #include "CppHandle.h"
#include "OTAClass.h" #include "OTAClass.h"
#include <iostream>
#include <optional>
#include <mbedtls/base64.h>
#include "ToolsClass.h"
#include "WifiConnectors.h"
#include "CommClass.h"
#include "sys_conf_singleton.h"
#include "HttpOtaUpdater.h"
#include "AudioOutput.h"
// OTA相关
HttpOtaUpdater otaUpdater;
void setupOtaCallbacks() {
// 设置进度回调
otaUpdater.setProgressCallback([](int progress, int total) {
ESP_LOGI("OTA", "Progress: %d%%", progress);
});
// 设置状态回调
otaUpdater.setStateCallback([](HttpOtaUpdater::OtaState state, const std::string &message) {
const char *stateNames[] = {
"IDLE", "CONNECTING", "DOWNLOADING", "VERIFYING", "SUCCESS", "FAILED"
};
ESP_LOGI("OTA", "State: %s - %s", stateNames[static_cast<int>(state)], message.c_str());
});
// 设置完成回调
otaUpdater.setFinishCallback([](bool success, const std::string &message) {
if (success) {
ESP_LOGI("OTA", "Completed successfully: %s", message.c_str());
} else {
ESP_LOGE("OTA", "Failed: %s", message.c_str());
}
});
// 如果需要HTTPS,可以在这里设置证书(保留供后期使用)
// otaUpdater.setCACert(my_ca_cert_pem);
// otaUpdater.skipCertCommonNameCheck(true); // 仅用于测试
}
// 把 HTTP 文件完整下载到 PSRAM,返回首地址和长度
uint8_t *download_to_psram(const char *url, size_t *out_len)
{
uint8_t *buf = nullptr;
size_t total = 0;
esp_http_client_config_t cfg = {
.url = url,
.timeout_ms = 10000,
.keep_alive_enable = true,
};
esp_http_client_handle_t client = esp_http_client_init(&cfg);
if (esp_http_client_open(client, 0) != ESP_OK) {
ESP_LOGE("HTTP", "Failed to open HTTP connection");
}
int content_len = esp_http_client_fetch_headers(client);
if (content_len <= 0) {
ESP_LOGE("HTTP", "Content-Length invalid");
goto err;
}
buf = (uint8_t *)heap_caps_malloc(content_len, MALLOC_CAP_SPIRAM);
if (!buf) {
ESP_LOGE("HTTP", "PSRAM malloc %d bytes failed", content_len);
goto err;
}
int read;
do {
read = esp_http_client_read(client, (char *)buf + total, content_len - total);
if (read > 0) total += read;
} while (read > 0 && total < content_len);
if (total != content_len) {
ESP_LOGE("HTTP", "Download incomplete %d/%d", total, content_len);
heap_caps_free(buf);
buf = nullptr;
goto err;
}
*out_len = total;
ESP_LOGI("HTTP", "Download done, %d bytes in PSRAM", total);
err:
esp_http_client_close(client);
esp_http_client_cleanup(client);
return buf;
}
// JSON 数据回调函数
/** 交互所使用的json内容
{
"type": "xxx", // 消息类型
"xxx":"xxx", // 其他数据
......
}
此回调函数为核心业务处理函数!!!
*/
void onJsonData(const cppjson::Json &json) {
// 打印收到的 JSON
ESP_LOGI("JSON_CALLBACK", "收到JSON数据: %s", json.dump().c_str());
// 解析消息类型
const cppjson::Json &type = json["type"];
if (!type.isString()) return;
std::string typeStr = type.asString();
if (typeStr == "greeting") {
ESP_LOGI("JSON_CALLBACK", "收到服务器问候消息");
} else if (typeStr == "heartbeat") {
ESP_LOGI("JSON_CALLBACK", "收到心跳响应");
} else if (typeStr == "response") {
ESP_LOGI("JSON_CALLBACK", "收到服务器响应");
} else if (typeStr == "echo") {
ESP_LOGI("JSON_CALLBACK", "收到回显消息");
} else if (typeStr == "broadcast") {
ESP_LOGI("JSON_CALLBACK", "收到广播消息");
} else if (typeStr == "ota") {
ESP_LOGI("JSON_CALLBACK", "收到OTA消息");
// 进一步处理OTA消息
// 获取OTA中的版本信息
const cppjson::Json &version = json["version"];
if (!version.isString()) return;
std::string versionStr = version.asString();
// 获取OTA中的HTTP URL
const cppjson::Json &url = json["url"];
if (!url.isString()) return;
std::string urlStr = url.asString();
// 告诉服务端,升级开始
cppjson::Json response = cppjson::Json::object();
response.set("type", cppjson::Json("ota_start"));
WebSocketManager::getInstance()->sendJson(response);
otaUpdater.start(urlStr);
} else if (typeStr == "audio") {
ESP_LOGI("JSON_CALLBACK", "收到音频消息");
// 进一步解析音频消息内容
// 获取音频信息
cppjson::Json audio_url = json["audio_url"]; // 音频信息
cppjson::Json audio_size = json["audio_size"]; // 音频信息
if (!audio_url.isObject()) return;
if (!audio_size.isObject()) return;
std::cout << "base64: " << audio_url.asString() << std::endl;
std::cout << "size: " << audio_size.asInt() << std::endl;
size_t size;
const uint8_t *pcm_buf = download_to_psram(audio_url.asString().c_str(), &size);
// 播放音频
AudioOutput::getInstance()->playPcmStream(pcm_buf, size);
}
}
// WebSocket事件回调函数
void onWebSocketEvent(WebSocketEvent event, const std::string &message) {
switch (event) {
case WebSocketEvent::CONNECTED:
ESP_LOGI("EVENT_CALLBACK", "WebSocket已连接: %s", message.c_str());
break;
case WebSocketEvent::DISCONNECTED:
ESP_LOGI("EVENT_CALLBACK", "WebSocket已断开: %s", message.c_str());
break;
case WebSocketEvent::DATA_RECEIVED:
ESP_LOGI("EVENT_CALLBACK", "收到原始数据: %s", message.c_str());
break;
case WebSocketEvent::ERROR:
ESP_LOGE("EVENT_CALLBACK", "WebSocket错误: %s", message.c_str());
break;
}
}
// 发送状态信息函数
void sendStatus() {
cppjson::Json status = cppjson::Json::object();
status.set("type", cppjson::Json("status"));
cppjson::Json data = cppjson::Json::object();
data.set("free_heap", cppjson::Json(esp_get_free_heap_size()))
.set("uptime", cppjson::Json(xTaskGetTickCount() * portTICK_PERIOD_MS / 1000));
status.set("data", data); // 嵌套对象
if (WebSocketManager::getInstance()->sendJson(status)) {
ESP_LOGI("SEND", "已发送状态信息");
} else {
ESP_LOGE("SEND", "发送状态信息失败");
}
}
// 发送问候消息函数
void sendGreeting() {
cppjson::Json greeting = cppjson::Json::object();
greeting.set("type", cppjson::Json("greeting"))
.set("message", cppjson::Json("Hello from ESP32-S3"))
.set("timestamp", cppjson::Json(xTaskGetTickCount() * portTICK_PERIOD_MS / 1000));
if (WebSocketManager::getInstance()->sendJson(greeting)) {
ESP_LOGI("SEND", "已发送问候消息");
} else {
ESP_LOGE("SEND", "发送问候消息失败");
}
}
void websocket_task() {
TickType_t lastStatusTime = 0;
TickType_t lastHeartbeatTime = 0;
TickType_t lastGreetingTime = 0;
while (true) {
TickType_t currentTime = xTaskGetTickCount();
// 检查连接状态
if (!WebSocketManager::getInstance()->isConnected()) {
ESP_LOGI("APP_TASK", "WebSocket未连接,尝试重新连接...");
// 确保WiFi已连接
if (!WifiConnectors::getInstance()->isWifiConnect()) {
ESP_LOGI("APP_TASK", "WiFi未连接,等待WiFi连接...");
vTaskDelay(5000 / portTICK_PERIOD_MS);
continue;
}
if (WebSocketManager::getInstance()->connect()) {
ESP_LOGI("APP_TASK", "重新连接成功");
} else {
ESP_LOGI("APP_TASK", "重新连接失败");
}
vTaskDelay(5000 / portTICK_PERIOD_MS);
continue;
}
// 每10秒发送状态信息
if (currentTime - lastStatusTime > (10000 / portTICK_PERIOD_MS)) {
sendStatus();
lastStatusTime = currentTime;
}
// 每60秒发送问候
if (currentTime - lastGreetingTime > (60000 / portTICK_PERIOD_MS)) {
sendGreeting();
lastGreetingTime = currentTime;
}
vTaskDelay(100 / portTICK_PERIOD_MS);
}
}
void createWebSocket() {
// 等待WiFi连接成功后再连接WebSocket
ESP_LOGI("APP_TASK", "等待WiFi连接...");
while (!WifiConnectors::getInstance()->isWifiConnect()) {
ESP_LOGI("APP_TASK", "WiFi未连接,等待中...");
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
// 保存SN
SysConfJson::getInstance()->saveSN(
ToolsClass::GenerateSN(ToolsClass::getChipMAC(), ToolsClass::getChipSerialNumber()));
// 读取SN
std::string sn = SysConfJson::getInstance()->loadSN();
ESP_LOGI("conf", "loaded sn = %s", sn.c_str());
// 配置WebSocket
WebSocketConfig config;
config.uri = "ws://" + std::string("192.168.1.11") + ":" + std::to_string(8080) + "/ws";
config.auto_reconnect = true; // 自动重连
config.reconnect_interval = 5000; // 重连间隔(毫秒)
config.heartbeat_interval = 30000; // 心跳间隔(毫秒)
config.max_reconnect_attempts = 10; // 最大重连尝试次数
// TODO: 此处通信类存在线程重复创建bug,似乎是来自esp-idf的bug,待查证
// 初始化WebSocket管理器
if (!WebSocketManager::getInstance()->initialize(config)) {
ESP_LOGE("APP_TASK", "WebSocket管理器初始化失败");
vTaskDelete(nullptr);
return;
}
// 设置回调函数
WebSocketManager::getInstance()->setJsonCallback(onJsonData);
WebSocketManager::getInstance()->setEventCallback(onWebSocketEvent);
// 连接WebSocket服务器
ESP_LOGI("APP_TASK", "正在连接WebSocket服务器: %s", config.uri.c_str());
if (!WebSocketManager::getInstance()->connect()) {
ESP_LOGE("APP_TASK", "WebSocket连接失败");
}
// 创建WebSocket任务
ThreadConfig websocket_config;
websocket_config.core_id = 0;
websocket_config.inherit_cfg = true;
websocket_config.name = "websocket_task";
websocket_config.priority = 5;
websocket_config.stack_size = 4096;
std::thread websocket_thread = ThreadManager::createThread(websocket_config, websocket_task);
websocket_thread.detach();
}
#include "LVGLRender.h"
#include "SimpleI2SForwarder.h"
#include "lvpp.h"
#include "BaseApp.h"
LV_FONT_DECLARE(SiYuanHeiTiGoogleBan);
// 使用全局智能指针管理主要对象
static std::shared_ptr<lvgl_cpp::Screen> g_screen;
static std::shared_ptr<lvgl_cpp::HomePage> g_home;
static std::shared_ptr<lvgl_cpp::AppMenu> g_menu;
int pet_Test() {
/*
// 存档
PetDAO dao(SDFileManager::getInstance());
const string filename = "cheese_snow_leopard.json";
cout << "\n===== 存档 =====" << endl;
if (dao.savePet(pet, filename))
cout << "✅ 已保存到 " << filename << endl;
else
cout << "❌ 保存失败" << endl;
// 5. 读档
cout << "\n===== 读档 =====" << endl;
auto loaded = dao.loadPet(filename);
if (!loaded) {
cout << "❌ 读档失败" << endl;
return 0;
}
cout << "✅ 读档成功,继续互动:" << endl;
printPet(*loaded);
// 6. 对新对象继续互动
loaded->neglect(); printPet(*loaded);
loaded->feed(); printPet(*loaded);
*/
return 1;
}
void Cpp_Hand() { void Cpp_Hand() {
OTAClass::Init(); // 打印设备信息
ESP_LOGI("CppHandle::Cpp_Hand", "当前固件版本 %s:", ToolsClass::getDeviceVersion().c_str());
ESP_LOGI("CppHandle::Cpp_Hand", "当前设备MAC地址 %s:", ToolsClass::getChipMAC().c_str());
ESP_LOGI("CppHandle::Cpp_Hand", "当前设备固件序列号 %s:", ToolsClass::getChipSerialNumber().c_str());
SDFileManager::getInstance()->tryInitSDCard(); // 初始化SD卡
LVGLRender::getInstance()->tryToInitRenderGif(); // 初始化lvgl驱动(包括任务循环)
LVGLRender::setFps(60); // 设置lvgl刷新频率
// 创建屏幕
g_screen = std::make_shared<lvgl_cpp::Screen>();
lv_scr_load(g_screen->raw());
// 延迟创建界面组件
auto init_timer = std::make_unique<lvgl_cpp::Timer>([&]() {
ESP_LOGI("MAIN", "Initializing UI components...");
// 创建主页
g_home = std::make_shared<lvgl_cpp::HomePage>(*g_screen);
g_home->bg("pic360.bin", 360, 360)
.onBattery([]() -> uint8_t {
return static_cast<uint8_t>(ToolsClass::getInstance()->getBatteryPer());
})
.onDateTime([](char *buf, const size_t len) {
snprintf(buf, len, "06/25 Tue 14:30");
});
// 创建菜单
g_menu = std::make_shared<lvgl_cpp::AppMenu>(*g_screen);
g_menu->addItem("Calcu", 100, 50)
.addItem("Gif测试", 100, 50)
.addItem("Button", 100, 50)
.addItem("AI测试", 100, 50)
.addItem("长按录音", 100, 50)
.addItem("宠物样例", 100, 50)
.onClick([](const char *name) {
ESP_LOGI("APP", "Launching: %s", name);
// 让工厂创建子应用
auto app = lvgl_cpp::AppFactory::create(name, *g_screen);
if (!app) return;
// 隐藏菜单,显示应用
lv_obj_add_flag(g_menu->raw(), LV_OBJ_FLAG_HIDDEN);
app->onShow();
// 应用退出时回到菜单
app->onExit([app_ptr = app.release()]() {
// 转移所有权到 lambda
lv_obj_clear_flag(g_menu->raw(), LV_OBJ_FLAG_HIDDEN);
lv_obj_del(app_ptr->raw()); // 自毁
});
});
g_menu->onBack([]() {
ESP_LOGI("AppMenu", "返回主页回调执行");
// 添加调试信息
if (g_menu && g_menu->raw()) {
ESP_LOGI("AppMenu", "隐藏菜单");
lv_obj_add_flag(g_menu->raw(), LV_OBJ_FLAG_HIDDEN);
} else {
ESP_LOGE("AppMenu", "菜单对象无效");
}
if (g_home && g_home->raw()) {
ESP_LOGI("AppMenu", "显示主页");
lv_obj_clear_flag(g_home->raw(), LV_OBJ_FLAG_HIDDEN);
} else {
ESP_LOGE("AppMenu", "主页对象无效");
}
// 强制刷新显示
lv_refr_now(nullptr);
});
lv_obj_add_flag(g_menu->raw(), LV_OBJ_FLAG_HIDDEN);
// 安全的事件连接
g_home->onOpenMenu([]() {
if (g_home && g_menu) {
lv_obj_add_flag(g_home->raw(), LV_OBJ_FLAG_HIDDEN);
lv_obj_clear_flag(g_menu->raw(), LV_OBJ_FLAG_HIDDEN);
}
});
ESP_LOGI("MAIN", "UI initialization complete");
}, 1000, true); // 1秒后执行一次
// 注册应用
lvgl_cpp::AppFactory::registerApp("Calcu", [](lvgl_cpp::Obj &p) {
return std::make_unique<AppCalc>(p);
});
lvgl_cpp::AppFactory::registerApp("Gif测试", [](lvgl_cpp::Obj &p) {
return std::make_unique<AppMusic>(p);
});
lvgl_cpp::AppFactory::registerApp("Button", [](lvgl_cpp::Obj &p) {
return std::make_unique<ButtonApp>(p);
});
lvgl_cpp::AppFactory::registerApp("AI测试", [](lvgl_cpp::Obj &p) {
return std::make_unique<WebSocketVoice>(p);
});
lvgl_cpp::AppFactory::registerApp("长按录音", [](lvgl_cpp::Obj &p) {
return std::make_unique<LongPressButtonAnim>(p);
});
lvgl_cpp::AppFactory::registerApp("宠物样例", [](lvgl_cpp::Obj &p) {
return std::make_unique<PetApp>(p);
});
// 连接wifi
WifiConnectors::getInstance()->connectWifi("Misaki-2.4G", "88888888", 5);
// 创建WebSocket
createWebSocket();
// 设置OTA回调
// setupOtaCallbacks();
while (true) {
// 主线程线程循环
ThreadManager::print_sys_memory(); // 打印系统内存使用情况
// ThreadManager::stats_task(); // 打印任务统计信息
// ESP_LOGI("APP_TASK", "Battery is:%ld", ToolsClass::getInstance()->getBatteryPer());
// ESP_LOGI("APP_TASK", "Battery is:%f", ToolsClass::getInstance()->getBatteryVolts());
std::this_thread::sleep_for(std::chrono::seconds(1)); // 休眠 1 秒
}
} }
+248
View File
@@ -0,0 +1,248 @@
//
// Created by misaki on 2025/9/24.
//
#include "HttpOtaUpdater.h"
#include <esp_http_client.h>
#include <cstring>
HttpOtaUpdater::HttpOtaUpdater()
: m_currentState(OtaState::IDLE)
, m_isUpdating(false)
, m_cert_pem(nullptr)
, m_skip_cert_common_name_check(false)
, m_totalSize(0)
, m_downloadedSize(0) {
}
HttpOtaUpdater::~HttpOtaUpdater() {
stop();
}
bool HttpOtaUpdater::start(const std::string& url) {
if (m_isUpdating) {
ESP_LOGE(TAG, "OTA update is already in progress");
return false;
}
if (url.empty()) {
ESP_LOGE(TAG, "URL is empty");
return false;
}
m_url = url;
m_errorMessage.clear();
m_totalSize = 0;
m_downloadedSize = 0;
// 配置线程参数
ThreadConfig config;
config.name = "ota_task";
config.stack_size = 8192;
config.priority = 6;
config.core_id = -1; // 不绑定特定核心
// 使用ThreadManager创建线程
m_otaThread = ThreadManager::createMemberThread(config, this, &HttpOtaUpdater::otaTask);
if (!m_otaThread.joinable()) {
ESP_LOGE(TAG, "Failed to create OTA thread");
updateState(OtaState::FAILED, "Failed to create thread");
return false;
}
m_isUpdating = true;
updateState(OtaState::CONNECTING, "Starting OTA update");
return true;
}
void HttpOtaUpdater::stop() {
if (m_isUpdating && m_otaThread.joinable()) {
// 注意:实际OTA过程很难安全中断,这里只是标记状态并分离线程
m_isUpdating = false;
m_otaThread.detach(); // 分离线程,让系统自动回收资源
}
}
bool HttpOtaUpdater::isUpdating() const {
return m_isUpdating;
}
void HttpOtaUpdater::setProgressCallback(const ProgressCallback &callback) {
m_progressCallback = callback;
}
void HttpOtaUpdater::setStateCallback(const StateCallback &callback) {
m_stateCallback = callback;
}
void HttpOtaUpdater::setFinishCallback(const FinishCallback &callback) {
m_finishCallback = callback;
}
void HttpOtaUpdater::setCACert(const char* cert_pem) {
m_cert_pem = cert_pem;
}
void HttpOtaUpdater::skipCertCommonNameCheck(bool skip) {
m_skip_cert_common_name_check = skip;
}
HttpOtaUpdater::OtaState HttpOtaUpdater::getCurrentState() const {
return m_currentState;
}
std::string HttpOtaUpdater::getErrorMessage() const {
return m_errorMessage;
}
void HttpOtaUpdater::otaTask() {
ThreadManager::printThreadInfo("OTA任务启动");
performOta(m_url);
}
void HttpOtaUpdater::performOta(const std::string& url) {
ESP_LOGI(TAG, "Starting OTA update from: %s", url.c_str());
// 配置HTTP客户端
esp_http_client_config_t http_config = {};
http_config.url = url.c_str();
http_config.event_handler = httpEventHandle;
http_config.user_data = this;
http_config.keep_alive_enable = true;
http_config.timeout_ms = 30000;
http_config.buffer_size = 2048;
// 设置证书(如果提供)- 保留供后期使用
if (m_cert_pem) {
http_config.cert_pem = m_cert_pem;
ESP_LOGI(TAG, "Using custom CA certificate for HTTPS");
}
// 设置证书检查 - 保留供后期使用
http_config.skip_cert_common_name_check = m_skip_cert_common_name_check;
if (m_skip_cert_common_name_check) {
ESP_LOGW(TAG, "Skipping certificate common name check");
}
// 配置HTTPS OTA
esp_https_ota_config_t ota_config = {};
ota_config.http_config = &http_config;
updateState(OtaState::DOWNLOADING, "Connecting to server");
esp_err_t ret = esp_https_ota(&ota_config);
if (ret == ESP_OK) {
ESP_LOGI(TAG, "OTA update successful");
updateState(OtaState::SUCCESS, "OTA update completed successfully");
if (m_finishCallback) {
m_finishCallback(true, "OTA update completed successfully");
}
// 等待一段时间后重启
ESP_LOGI(TAG, "Preparing to restart system...");
vTaskDelay(pdMS_TO_TICKS(2000));
esp_restart();
} else {
m_errorMessage = "OTA failed with error: " + std::string(esp_err_to_name(ret));
ESP_LOGE(TAG, "%s", m_errorMessage.c_str());
updateState(OtaState::FAILED, m_errorMessage);
if (m_finishCallback) {
m_finishCallback(false, m_errorMessage);
}
}
m_isUpdating = false;
}
void HttpOtaUpdater::updateState(OtaState state, const std::string& message) {
m_currentState = state;
if (m_stateCallback) {
m_stateCallback(state, message);
}
switch (state) {
case OtaState::CONNECTING:
ESP_LOGI(TAG, "State: CONNECTING - %s", message.c_str());
break;
case OtaState::DOWNLOADING:
ESP_LOGI(TAG, "State: DOWNLOADING - %s", message.c_str());
break;
case OtaState::VERIFYING:
ESP_LOGI(TAG, "State: VERIFYING - %s", message.c_str());
break;
case OtaState::SUCCESS:
ESP_LOGI(TAG, "State: SUCCESS - %s", message.c_str());
break;
case OtaState::FAILED:
ESP_LOGE(TAG, "State: FAILED - %s", message.c_str());
break;
default:
break;
}
}
esp_err_t HttpOtaUpdater::httpEventHandle(esp_http_client_event_t* event) {
auto* updater = static_cast<HttpOtaUpdater*>(event->user_data);
if (!updater) {
return ESP_FAIL;
}
switch (event->event_id) {
case HTTP_EVENT_ON_CONNECTED:
ESP_LOGI(TAG, "HTTP connected");
updater->updateState(OtaState::DOWNLOADING, "Connected to server");
break;
case HTTP_EVENT_ON_HEADER: {
// 获取文件总大小
if (strcasecmp(event->header_key, "Content-Length") == 0) {
updater->m_totalSize = atoi(event->header_value);
ESP_LOGI(TAG, "Total file size: %d bytes", updater->m_totalSize);
}
break;
}
case HTTP_EVENT_ON_DATA: {
if (event->data_len > 0) {
updater->m_downloadedSize += event->data_len;
// 更新进度
if (updater->m_totalSize > 0 && updater->m_progressCallback) {
int progress = (updater->m_downloadedSize * 100) / updater->m_totalSize;
updater->m_progressCallback(progress, 100);
}
// 定期打印下载进度
if (updater->m_downloadedSize % (100 * 1024) < event->data_len) { // 每100KB打印一次
ESP_LOGI(TAG, "Downloaded: %d/%d bytes (%.1f%%)",
updater->m_downloadedSize, updater->m_totalSize,
(updater->m_downloadedSize * 100.0) / updater->m_totalSize);
}
}
break;
}
case HTTP_EVENT_ON_FINISH:
ESP_LOGI(TAG, "HTTP download finished");
updater->updateState(OtaState::VERIFYING, "Download completed, verifying firmware");
break;
case HTTP_EVENT_DISCONNECTED:
ESP_LOGI(TAG, "HTTP disconnected");
break;
case HTTP_EVENT_ERROR:
ESP_LOGE(TAG, "HTTP error occurred");
updater->m_errorMessage = "HTTP error occurred";
break;
default:
break;
}
return ESP_OK;
}
+217
View File
@@ -0,0 +1,217 @@
//
// Created by misaki on 2025/9/24.
//
#pragma once
#include <esp_https_ota.h>
#include <esp_ota_ops.h>
#include <esp_log.h>
#include <string>
#include <functional>
#include "ThreadManager.h"
class HttpOtaUpdater {
public:
// OTA状态枚举
enum class OtaState {
IDLE, // 空闲状态
CONNECTING, // 连接中
DOWNLOADING, // 下载中
VERIFYING, // 验证中
SUCCESS, // 成功
FAILED // 失败
};
// 进度回调函数类型
using ProgressCallback = std::function<void(int progress, int total)>;
// 状态回调函数类型
using StateCallback = std::function<void(OtaState state, const std::string& message)>;
// 完成回调函数类型
using FinishCallback = std::function<void(bool success, const std::string& message)>;
/**
* @brief 构造函数
*/
HttpOtaUpdater();
/**
* @brief 析构函数
*/
~HttpOtaUpdater();
/**
* @brief 开始OTA升级
* @param url HTTP下载链接
* @return true-成功开始升级,false-失败
*/
bool start(const std::string& url);
/**
* @brief 停止OTA升级
*/
void stop();
/**
* @brief 检查是否正在升级
* @return true-正在升级,false-空闲
*/
[[nodiscard]] bool isUpdating() const;
/**
* @brief 设置进度回调
* @param callback 回调函数
*/
void setProgressCallback(const ProgressCallback &callback);
/**
* @brief 设置状态回调
* @param callback 回调函数
*/
void setStateCallback(const StateCallback &callback);
/**
* @brief 设置完成回调
* @param callback 回调函数
*/
void setFinishCallback(const FinishCallback &callback);
/**
* @brief 设置CA证书(用于HTTPS,保留供后期使用)
* @param cert_pem PEM格式的证书内容
*/
void setCACert(const char* cert_pem);
/**
* @brief 跳过证书通用名检查(仅用于测试,保留供后期使用)
* @param skip true-跳过检查,false-不跳过
*/
void skipCertCommonNameCheck(bool skip);
/**
* @brief 获取当前状态
* @return 当前OTA状态
*/
[[nodiscard]] OtaState getCurrentState() const;
/**
* @brief 获取错误信息
* @return 错误信息字符串
*/
[[nodiscard]] std::string getErrorMessage() const;
// 禁止拷贝和赋值
HttpOtaUpdater(const HttpOtaUpdater&) = delete;
HttpOtaUpdater& operator=(const HttpOtaUpdater&) = delete;
private:
/**
* @brief OTA任务主函数
*/
void otaTask();
/**
* @brief 执行OTA升级
* @param url 下载链接
*/
void performOta(const std::string& url);
/**
* @brief 更新状态
* @param state 新状态
* @param message 状态信息
*/
void updateState(OtaState state, const std::string& message = "");
/**
* @brief HTTPS OTA事件处理函数
* @param event 事件
*/
static esp_err_t httpEventHandle(esp_http_client_event_t* event);
// 成员变量
std::string m_url; ///<! 下载链接
OtaState m_currentState; ///<! 当前状态
std::string m_errorMessage; ///<! 错误信息
bool m_isUpdating; ///<! 是否正在升级
std::thread m_otaThread; ///<! OTA线程
// HTTP配置(保留供后期使用)
const char* m_cert_pem; ///<! CA证书
bool m_skip_cert_common_name_check; ///<! 跳过证书通用名检查
// 回调函数
ProgressCallback m_progressCallback;///<! 进度回调
StateCallback m_stateCallback; ///<! 状态回调
FinishCallback m_finishCallback; ///<! 完成回调
// 进度相关变量
int m_totalSize; ///<! 总大小
int m_downloadedSize; ///<! 已下载大小
// 日志标签
static constexpr auto TAG = "HttpOtaUpdater";
};
/** 使用示例
// 创建全局OTA更新器实例
HttpOtaUpdater otaUpdater;
void setupOtaCallbacks() {
// 设置进度回调
otaUpdater.setProgressCallback([](int progress, int total) {
ESP_LOGI("OTA", "Progress: %d%%", progress);
});
// 设置状态回调
otaUpdater.setStateCallback([](HttpOtaUpdater::OtaState state, const std::string& message) {
const char* stateNames[] = {
"IDLE", "CONNECTING", "DOWNLOADING", "VERIFYING", "SUCCESS", "FAILED"
};
ESP_LOGI("OTA", "State: %s - %s", stateNames[static_cast<int>(state)], message.c_str());
});
// 设置完成回调
otaUpdater.setFinishCallback([](bool success, const std::string& message) {
if (success) {
ESP_LOGI("OTA", "Completed successfully: %s", message.c_str());
} else {
ESP_LOGE("OTA", "Failed: %s", message.c_str());
}
});
// 如果需要HTTPS,可以在这里设置证书(保留供后期使用)
// otaUpdater.setCACert(my_ca_cert_pem);
// otaUpdater.skipCertCommonNameCheck(true); // 仅用于测试
}
// 在websocket消息处理函数中调用
void onOtaUrlReceived(const std::string& otaUrl) {
ESP_LOGI("OTA", "Received OTA URL: %s", otaUrl.c_str());
if (otaUpdater.isUpdating()) {
ESP_LOGW("OTA", "OTA update is already in progress");
return;
}
if (otaUpdater.start(otaUrl)) {
ESP_LOGI("OTA", "OTA update started successfully");
} else {
ESP_LOGE("OTA", "Failed to start OTA update");
}
}
// 检查OTA状态
void checkOtaStatus() {
if (otaUpdater.isUpdating()) {
ESP_LOGI("OTA", "OTA update in progress, state: %d",
static_cast<int>(otaUpdater.getCurrentState()));
} else {
ESP_LOGI("OTA", "No OTA update in progress");
}
std::string error = otaUpdater.getErrorMessage();
if (!error.empty()) {
ESP_LOGE("OTA", "Last error: %s", error.c_str());
}
}
*/
+50
View File
@@ -3,7 +3,57 @@
// //
#include "OTAClass.h" #include "OTAClass.h"
#include "esp_log.h" #include "esp_log.h"
#include <freertos/FreeRTOS.h>
#include <string>
#include "SDFileManager.h"
#include "CommClass.h"
void OTAClass::Init() { void OTAClass::Init() {
ESP_LOGI("OTA", "Init"); ESP_LOGI("OTA", "Init");
// 列出当前目录内容
std::string listing = SDFileManager::getInstance()->lsCommand(".", false, true);
ESP_LOGI("SD", "%s", listing.c_str());
// 切换到music目录
SDFileManager::getInstance()->cdCommand("music");
std::string pwdPath = SDFileManager::getInstance()->pwdCommand();
ESP_LOGI("SD", "%s", pwdPath.c_str());
// 列出当前目录内容
listing = SDFileManager::getInstance()->lsCommand(".", false, true);
ESP_LOGI("SD", "%s", listing.c_str());
// LVGLRender::getInstance()->RenderGif("gzl_m.gif");
// 设置音量
// AudioOutput::getInstance()->setVolume(5);
// 同步播放
// AudioOutput::getInstance()->playSync("/sdcard/music", "kokoronashi_8k.mp3");
// // 配置Wifi连接线程参数
// ThreadConfig wifi_config;
// wifi_config.name = "WifiConnector"; // 线程名称
// wifi_config.core_id = 1; // 绑定到核心1(避免与主线程冲突)
// wifi_config.stack_size = 4096; // 设置稍大的栈空间(Wifi连接可能需要)
// wifi_config.priority = 6; // 设置较高优先级(确保连接及时)
// // 使用单例方式创建线程,调用connectWifi成员函数
// std::thread wifi_thread = ThreadManager::createSingletonThread<WifiConnectors>(
// wifi_config,
// &WifiConnectors::connectWifi,
// "Misaki-2.4G", // SSID
// "88888888", // 密码
// 5 // 最大重试次数
// );
// wifi_thread.detach();
} }
// 启动OTA更新线程,前提是已经连接WiFi
void OTAClass::Update() {
}
+1 -9
View File
@@ -4,18 +4,10 @@
#pragma once #pragma once
#ifdef __cplusplus
extern "C" {
#endif
class OTAClass { class OTAClass {
public: public:
static void Init(void); void Init(void);
void Update(void); void Update(void);
}; };
#ifdef __cplusplus
}
#endif
+210 -11
View File
@@ -2,15 +2,214 @@
// Created by misaki on 2025/9/2. // Created by misaki on 2025/9/2.
// //
#pragma once #pragma once
#include "PetInterface.h"
#ifdef __cplusplus #include "PetObserver.h"
extern "C" { // 宠物基类
#endif // 宠物基本信息
struct PetBaseInfo {
///<! 宠物名称
std::string pet_name;
///<! 宠物生命值(1~100)
int pet_hp;
#ifdef __cplusplus ///<! 宠物亲密度(1~150) 每过50为一个阶段
int pet_density;
///<! 宠物身份
std::string pet_identity;
};
// 宠物阶段策略,继承自主题
class PetBase : public PetSubject, public std::enable_shared_from_this<PetBase> {
public:
// 构造函数
PetBase() = default;
// 带参数的构造函数
PetBase(PetBaseInfo info,
std::shared_ptr<PetStageStrategy> stageStrategy,
std::shared_ptr<PetActionStrategy> actionStrategy)
: pet_info(std::move(info)),
pet_stage_strategy(std::move(stageStrategy)),
pet_action_strategy(std::move(actionStrategy)) {}
virtual ~PetBase() = default;
// 实现PetSubject接口
/**
* 添加观察者
* @param observer 观察者
*/
void addObserver(std::shared_ptr<PetObserver> observer) override {
observers.push_back(observer);
} }
#endif /**
* 移除观察者
* @param observer 观察者
*/
void removeObserver(std::shared_ptr<PetObserver> observer) override {
observers.remove(observer);
}
/**
* 通知动作
* @param action 动作
*/
void notifyAction(PetActionType action) override {
for (auto& observer : observers) {
observer->onPetAction(action);
}
}
/**
* 通知阶段
* @param oldStage 旧阶段
* @param newStage 新阶段
*/
void notifyStageChange(PetStageType oldStage, PetStageType newStage) override {
for (auto& observer : observers) {
observer->onPetStageChange(oldStage, newStage);
}
}
// 宠物行为方法
// 喂食
virtual void feed() {
// 喂食会增加生命值和亲密度
pet_info.pet_hp = std::min(100, pet_info.pet_hp + 10);
pet_info.pet_density = std::min(150, pet_info.pet_density + 15);
// 执行喂食动作并通知观察者
if (pet_action_strategy->performAction(PetActionType::PET_ACTION_EAT)) {
notifyAction(PetActionType::PET_ACTION_EAT);
}
// 检查是否可以进化
checkEvolution();
}
// 玩耍
virtual void play() {
// 玩耍会增加亲密度
pet_info.pet_density = std::min(150, pet_info.pet_density + 10);
// 执行开心动作并通知观察者
if (pet_action_strategy->performAction(PetActionType::PET_ACTION_HAPPY)) {
notifyAction(PetActionType::PET_ACTION_HAPPY);
}
// 检查是否可以进化
checkEvolution();
}
// 责骂
virtual void scold() {
// 责骂会减少亲密度
pet_info.pet_density = std::max(0, pet_info.pet_density - 10);
// 执行生气动作并通知观察者
if (pet_action_strategy->performAction(PetActionType::PET_ACTION_ANGRY)) {
notifyAction(PetActionType::PET_ACTION_ANGRY);
}
}
// 忽视
virtual void neglect() {
// 忽视会减少生命值和亲密度
pet_info.pet_hp = std::max(0, pet_info.pet_hp - 5);
pet_info.pet_density = std::max(0, pet_info.pet_density - 8);
// 执行沮丧动作并通知观察者
if (pet_action_strategy->performAction(PetActionType::PET_ACTION_SAD)) {
notifyAction(PetActionType::PET_ACTION_SAD);
}
}
// 触摸
virtual void touch() {
// 触摸会增加亲密度
pet_info.pet_density = std::min(150, pet_info.pet_density + 5);
// 执行被触摸动作并通知观察者
if (pet_action_strategy->performAction(PetActionType::PET_ACTION_TOUCH)) {
notifyAction(PetActionType::PET_ACTION_TOUCH);
}
// 检查是否可以进化
checkEvolution();
}
// 进化检查
virtual bool checkEvolution() {
// 获取当前阶段
auto currentStage = pet_stage_strategy->getCurrentStageType();
// 根据亲密度决定是否可以进化
if (pet_info.pet_density >= 100 && currentStage == PetStageType::PET_STAGE_YOUNG) {
// 从幼年进化到青年
return evolveToStage(PetStageType::PET_STAGE_ADULT);
} else if (pet_info.pet_density >= 150 && currentStage == PetStageType::PET_STAGE_ADULT) {
// 从青年进化到成年
return evolveToStage(PetStageType::PET_STAGE_OLD);
}
return false;
}
// 进化到指定阶段
virtual bool evolveToStage(PetStageType newStage) {
auto oldStage = pet_stage_strategy->getCurrentStageType(); // 获取当前阶段
if (pet_stage_strategy->switchToStage(newStage)) {
// 执行进化动作并通知观察者
if (pet_action_strategy->performAction(PetActionType::PET_ACTION_EVOLVE)) {
notifyAction(PetActionType::PET_ACTION_EVOLVE);
}
// 通知阶段变化
notifyStageChange(oldStage, newStage);
return true;
}
return false;
}
/**
* 获取宠物信息
* @return 宠物信息
*/
virtual const PetBaseInfo& getPetInfo() const {
return pet_info;
}
/**
* 设置宠物信息
* @param info 宠物信息
*/
virtual void setPetInfo(const PetBaseInfo& info) {
pet_info = info;
}
/**
* 获取阶段策略
* @return 阶段策略
*/
virtual std::shared_ptr<PetStageStrategy> getStageStrategy() const {
return pet_stage_strategy;
}
/**
* 设置阶段策略
* @param strategy 阶段策略
*/
virtual void setStageStrategy(std::unique_ptr<PetStageStrategy> strategy) {
pet_stage_strategy = std::move(strategy);
}
/**
* 获取动作策略
* @return 动作策略
*/
virtual std::shared_ptr<PetActionStrategy> getActionStrategy() const {
return pet_action_strategy;
}
/**
* 设置动作策略
* @param strategy 动作策略
*/
virtual void setActionStrategy(std::unique_ptr<PetActionStrategy> strategy) {
pet_action_strategy = std::move(strategy);
}
/**
* 获取当前阶段
* @return 当前阶段
*/
virtual PetStageType getCurrentStage() const {
return pet_stage_strategy->getCurrentStageType();
}
/**
* 获取当前动作
* @return 当前动作
*/
virtual PetActionType getCurrentAction() const {
return pet_action_strategy->getCurrentActionType();
}
public:
///<! 宠物信息
PetBaseInfo pet_info;
///<! 宠物阶段策略
std::shared_ptr<PetStageStrategy> pet_stage_strategy;
///<! 宠物动作策略
std::shared_ptr<PetActionStrategy> pet_action_strategy;
///<! 观察者列表
std::list<std::shared_ptr<PetObserver>> observers;
};
+274
View File
@@ -0,0 +1,274 @@
//
// Created by misaki on 2025/9/14.
//
#include "PetDao.h"
#include <iostream>
#include <sstream>
using namespace PetEnumConverter;
// PetEnumConverter 实现
std::string PetEnumConverter::stageTypeToString(PetStageType stage) {
switch (stage) {
#define X(name, desc) case PetStageType::name: return #name;
PET_STAGE_TYPES
#undef X
default: return "UNKNOWN";
}
}
PetStageType PetEnumConverter::stringToStageType(const std::string& str) {
#define X(name, desc) if (str == #name) return PetStageType::name;
PET_STAGE_TYPES
#undef X
return PetStageType::PET_STAGE_YOUNG; // 默认值
}
std::string PetEnumConverter::actionTypeToString(PetActionType action) {
switch (action) {
#define X(name, desc) case PetActionType::name: return #name;
PET_ACTION_TYPES
#undef X
default: return "UNKNOWN";
}
}
PetActionType PetEnumConverter::stringToActionType(const std::string& str) {
#define X(name, desc) if (str == #name) return PetActionType::name;
PET_ACTION_TYPES
#undef X
return PetActionType::PET_ACTION_SLEEP; // 默认值
}
// PetDAO 实现
PetDAO::PetDAO(SDFileManager* fileManager) : fileManager(fileManager) {
// 确保宠物数据目录存在
fileManager->mkdirCommand( PET_DATA_DIR);
}
bool PetDAO::savePet(const std::shared_ptr<PetBase>& pet, const std::string& filename) {
try {
// 转换为JSON
cppjson::Json json = petToJson(pet);
// 序列化为字符串
std::string jsonStr = json.dump();
std::string fullPath = std::string(PET_DATA_DIR) + "/" + filename;
// 保存到文件
const bool success = fileManager->writeFileSync(fullPath.c_str(), jsonStr.c_str(), jsonStr.size(), "w");
return success;
} catch (const std::exception& e) {
std::cerr << "Save pet failed: " << e.what() << std::endl;
return false;
}
}
std::shared_ptr<PetBase> PetDAO::loadPet(const std::string& filename) {
try {
std::string fullPath = std::string(PET_DATA_DIR) + "/" + filename;
// 读取文件内容
std::string content = fileManager->readFileSync(fullPath.c_str());
if (content.empty()) {
return nullptr;
}
// 解析JSON
cppjson::Json json = cppjson::Json::parse(content);
// 创建宠物对象
return petFromJson(json);
} catch (const std::exception& e) {
std::cerr << "Load pet failed: " << e.what() << std::endl;
return nullptr;
}
}
std::vector<std::string> PetDAO::listPetFiles() {
return fileManager->listFilesSync(PET_DATA_DIR, ".json");
}
bool PetDAO::deletePetFile(const std::string& filename) {
std::string fullPath = std::string(PET_DATA_DIR) + "/" + filename;
return fileManager->rmCommand(fullPath.c_str(), false);
}
cppjson::Json PetDAO::petToJson(const std::shared_ptr<PetBase>& pet) {
auto json = cppjson::Json::object();
// 添加基本信息
json.set("name", cppjson::Json(pet->pet_info.pet_name))
.set("hp", cppjson::Json(pet->pet_info.pet_hp))
.set("density", cppjson::Json(pet->pet_info.pet_density))
.set("identity", cppjson::Json(pet->pet_info.pet_identity));
// 添加阶段策略
auto stageJson = stageStrategyToJson(pet->pet_stage_strategy);
if (!stageJson.isNull()) {
json.set("stage_strategy", stageJson);
}
// 添加动作策略
auto actionJson = actionStrategyToJson(pet->pet_action_strategy);
if (!actionJson.isNull()) {
json.set("action_strategy", actionJson);
}
return json;
}
std::shared_ptr<PetBase> PetDAO::petFromJson(const cppjson::Json& json) {
if (json.isNull()) return nullptr;
// 创建宠物基本信息
PetBaseInfo info;
if (json["name"].isString()) {
info.pet_name = json["name"].asString();
}
if (json["hp"].isNumber()) {
info.pet_hp = json["hp"].asInt();
}
if (json["density"].isNumber()) {
info.pet_density = json["density"].asInt();
}
if (json["identity"].isString()) {
info.pet_identity = json["identity"].asString();
}
// 创建阶段策略
auto stageStrategy = stageStrategyFromJson(json["stage_strategy"]);
// 创建动作策略
auto actionStrategy = actionStrategyFromJson(json["action_strategy"]);
// 创建宠物对象
return std::make_shared<PetBase>(info, stageStrategy, actionStrategy);
}
cppjson::Json PetDAO::stageStrategyToJson(const std::shared_ptr<PetStageStrategy>& strategy) {
if (!strategy) return {};
auto json = cppjson::Json::object();
// 添加当前阶段
json.set("current_stage", cppjson::Json(stageTypeToString(strategy->getCurrentStageType())));
// 添加阶段模型映射
auto modelMap = cppjson::Json::object();
for (const auto& pair : strategy->getStageModelMap()) {
modelMap.set(stageTypeToString(pair.first), cppjson::Json(pair.second));
}
json.set("stage_model_map", modelMap);
// 添加阶段音频映射
auto audioMap = cppjson::Json::object();
for (const auto& pair : strategy->getStageAudioMap()) {
audioMap.set(stageTypeToString(pair.first), cppjson::Json(pair.second));
}
json.set("stage_audio_map", audioMap);
return json;
}
std::shared_ptr<PetStageStrategy> PetDAO::stageStrategyFromJson(const cppjson::Json& json) {
if (json.isNull()) return nullptr;
auto strategy = std::make_shared<PetStageStrategy>();
// 获取当前阶段
if (json["current_stage"].isString()) {
strategy->current_stage = stringToStageType(json["current_stage"].asString());
}
// 获取阶段模型映射
auto modelMapJson = json["stage_model_map"];
if (modelMapJson.isObject()) {
for (const auto& item : modelMapJson.items()) {
if (item.second.isString()) {
PetStageType stage = stringToStageType(item.first);
strategy->stage_model_map[stage] = item.second.asString();
}
}
}
// 获取阶段音频映射
auto audioMapJson = json["stage_audio_map"];
if (audioMapJson.isObject()) {
for (const auto& item : audioMapJson.items()) {
if (item.second.isString()) {
PetStageType stage = stringToStageType(item.first);
strategy->stage_audio_map[stage] = item.second.asString();
}
}
}
return strategy;
}
cppjson::Json PetDAO::actionStrategyToJson(const std::shared_ptr<PetActionStrategy>& strategy) {
if (!strategy) return {};
auto json = cppjson::Json::object();
// 添加当前动作
json.set("current_action", cppjson::Json(actionTypeToString(strategy->getCurrentActionType())));
// 添加动作模型映射
auto modelMap = cppjson::Json::object();
for (const auto& pair : strategy->getActionModelMap()) {
modelMap.set(actionTypeToString(pair.first), cppjson::Json(pair.second));
}
json.set("action_model_map", modelMap);
// 添加动作音频映射
auto audioMap = cppjson::Json::object();
for (const auto& pair : strategy->getActionAudioMap()) {
audioMap.set(actionTypeToString(pair.first), cppjson::Json(pair.second));
}
json.set("action_audio_map", audioMap);
return json;
}
std::shared_ptr<PetActionStrategy> PetDAO::actionStrategyFromJson(const cppjson::Json& json) {
if (json.isNull()) return nullptr;
auto strategy = std::make_shared<PetActionStrategy>();
// 获取当前动作
if (json["current_action"].isString()) {
strategy->current_action = stringToActionType(json["current_action"].asString());
}
// 获取动作模型映射
auto modelMapJson = json["action_model_map"];
if (modelMapJson.isObject()) {
for (const auto& item : modelMapJson.items()) {
if (item.second.isString()) {
PetActionType action = stringToActionType(item.first);
strategy->action_model_map[action] = item.second.asString();
}
}
}
// 获取动作音频映射
auto audioMapJson = json["action_audio_map"];
if (audioMapJson.isObject()) {
for (const auto& item : audioMapJson.items()) {
if (item.second.isString()) {
PetActionType action = stringToActionType(item.first);
strategy->action_audio_map[action] = item.second.asString();
}
}
}
return strategy;
}
+107
View File
@@ -0,0 +1,107 @@
//
// Created by misaki on 2025/9/14.
//
#pragma once
#include "PetBaseClass.h"
#include "SDFileManager.h"
#include "cpp_json.h"
#include <string>
// 辅助函数:枚举类型与字符串的转换
namespace PetEnumConverter {
// PetStageType 转换
std::string stageTypeToString(PetStageType stage);
PetStageType stringToStageType(const std::string &str);
// PetActionType 转换
std::string actionTypeToString(PetActionType action);
PetActionType stringToActionType(const std::string &str);
}
// PetDAO 类 - 负责宠物的数据持久化
class PetDAO {
public:
// 构造函数,需要SDFileManager实例
explicit PetDAO(SDFileManager *fileManager);
// 保存宠物数据到文件
bool savePet(const std::shared_ptr<PetBase> &pet, const std::string &filename);
// 从文件加载宠物数据
std::shared_ptr<PetBase> loadPet(const std::string &filename);
// 获取所有保存的宠物文件列表
std::vector<std::string> listPetFiles();
// 删除宠物文件
bool deletePetFile(const std::string &filename);
private:
// 将宠物数据转换为JSON对象
cppjson::Json petToJson(const std::shared_ptr<PetBase> &pet); // 返回 cppjson::Json
// 从JSON对象创建宠物
std::shared_ptr<PetBase> petFromJson(const cppjson::Json &json); // 参数改为 cppjson::Json
// 将阶段策略转换为JSON对象
cppjson::Json stageStrategyToJson(const std::shared_ptr<PetStageStrategy> &strategy);
// 从JSON对象创建阶段策略
std::shared_ptr<PetStageStrategy> stageStrategyFromJson(const cppjson::Json &json);
// 将动作策略转换为JSON对象
cppjson::Json actionStrategyToJson(const std::shared_ptr<PetActionStrategy> &strategy);
// 从JSON对象创建动作策略
std::shared_ptr<PetActionStrategy> actionStrategyFromJson(const cppjson::Json &json);
// 文件管理器实例
SDFileManager *fileManager;
// 宠物数据存储目录
static constexpr const char *PET_DATA_DIR = "/sdcard/pet_data";
};
/**
* 宠物数据结构(JSON)
{
"name": "芝士雪豹",
"hp": 85,
"density": 120,
"identity": "我是顶真,是妈妈省的",
"stage_strategy": {
"current_stage": "PET_STAGE_ADULT",
"stage_model_map": {
"PET_STAGE_YOUNG": "/models/snow_leopard_young.gif",
"PET_STAGE_ADULT": "/models/snow_leopard_adult.gif",
"PET_STAGE_OLD": "/models/snow_leopard_old.gif"
},
"stage_audio_map": {
"PET_STAGE_YOUNG": "/audio/snow_leopard_young.mp3",
"PET_STAGE_ADULT": "/audio/snow_leopard_adult.mp3",
"PET_STAGE_OLD": "/audio/snow_leopard_old.mp3"
}
},
"action_strategy": {
"current_action": "PET_ACTION_SLEEP",
"action_model_map": {
"PET_ACTION_SLEEP": "/models/actions/sleep.gif",
"PET_ACTION_EAT": "/models/actions/eat.gif",
"PET_ACTION_HAPPY": "/models/actions/happy.gif",
"PET_ACTION_ANGRY": "/models/actions/angry.gif",
"PET_ACTION_SAD": "/models/actions/sad.gif",
"PET_ACTION_EVOLVE": "/models/actions/evolve.gif",
"PET_ACTION_TOUCH": "/models/actions/touch.gif"
},
"action_audio_map": {
"PET_ACTION_SLEEP": "/audio/actions/sleep.mp3",
"PET_ACTION_EAT": "/audio/actions/eat.mp3",
"PET_ACTION_HAPPY": "/audio/actions/happy.mp3",
"PET_ACTION_ANGRY": "/audio/actions/angry.mp3",
"PET_ACTION_SAD": "/audio/actions/sad.mp3",
"PET_ACTION_EVOLVE": "/audio/actions/evolve.mp3",
"PET_ACTION_TOUCH": "/audio/actions/touch.mp3"
}
}
}
*/
@@ -0,0 +1,5 @@
//
// Created by misaki on 2025/9/8.
//
#include "PetInterface.h"
+389
View File
@@ -0,0 +1,389 @@
//
// Created by misaki on 2025/9/8.
//
/**
* 需要实现的最终效果
1. 可以选择多种宠物,如雪豹,卡皮巴拉,守宫,每种宠物有独特音频和动作,通过联网更新切换。
2. 每种宠物有可以编辑的身份设定,类似小智AI的身份
3. 宠物三个阶段,幼年,青年,成年,阶段切换载入新模型即可。
4. 语音互动可以实现喂食,开心,生气,沮丧几种动作
5. 宠物具有生命值hp和亲密度,当发生喂食等动作会对生命值和亲密度产生影响,亲密度达到一定程度宠物可以进化到新的阶段。
6. 宠物的各种信息是可以保存和读取的。
分析一下具体设计思路:
1. 首先抽象出所有的宠物的共同点为一个基类,包括:名称,生命值,亲密度,身份设定
对于阶段,动作,不同的宠物之间存在不同的实现差异,可以为其抽象出不同的接口,组合到抽象出来的宠物基类当中
这样当创建新的宠物时,只需要实现对应的接口即可(在此使用策略模式),而在创建不同的宠物的过程中,又使用到了抽象工厂模式
对于音频,由于阶段变化或是产生动作,都会触发响应的音频,因此音频需要作为一个观察者,当宠物产生阶段变化或是产生动作,就会触发相应的音频播放
2. 接着要考虑到与宠物相关的一些事件,例如外部通知网络更新,语音输入互动,喂食这些事件,需要及时做出响应,在此使用依赖注入来解决这一类事件处理问题
向外部类当中组合一个依赖注入类,将宠物类当中的回调函数注入到其中,当发生某些事件时,就会触发相应的回调函数,并执行相应的操作
这样在增加新的事件时,就不需要频繁修改外部事件类,只需要新增新的依赖注入项(如果C++17支持反射的话还有更好的实现方法)
3. 再者是对于宠物的信息保存与读取,在此处增加一个DAO层,专门用于数据持久化,信息以json的方式存储与读取
既然存在了宠物的数据信息,那么就需要在DAO层之上增加一个可以从json数据中得到新的宠物对象的方法或者是类,以此组合出所需的宠物对象
4. 还需要考虑到一些极端事件的情况,例如设备断电,用户强制关机,网络中断等,最好的处理方式就是及时做好数据持久化和数据备份
设置bak备份防止在写文件的时候断电导致写文件错误
*/
#pragma once
#include <string>
#include <memory>
#include <utility>
#include <vector>
#include <list>
#include <algorithm>
#include <functional>
// 前向声明主题
class PetSubject;
// 某个宠物不同阶段的抽象接口 宠物的不同阶段对应于不同的模型文件路径
// 如果需要添加新的阶段,要在PetStageType当中增加新的阶段,并将新的类继承自PetStage类
// 由于使用的是C++17,并不支持反射,只能通过宏定义统一管理,以此实现松耦合和开闭原则
#define PET_STAGE_TYPES \
X(PET_STAGE_YOUNG, "幼年") \
X(PET_STAGE_ADULT, "青年") \
X(PET_STAGE_OLD, "成年")
enum class PetStageType {
#define X(name, desc) name,
PET_STAGE_TYPES
#undef X
};
// 某个宠物所有阶段的接口集合,该接口集合会被组合到抽象出来的宠物基类当中,该接口还需要抽象出宠物不同阶段可能具备的行为
class PetStageStrategy {
public:
virtual ~PetStageStrategy() = default;
// 定义阶段顺序的静态常量
static const std::vector<PetStageType>& getStageOrder() {
static const std::vector<PetStageType> stageOrder = []() {
std::vector<PetStageType> order;
#define X(name, desc) order.push_back(PetStageType::name);
PET_STAGE_TYPES
#undef X
return order;
}();
return stageOrder;
}
/**
* 添加阶段及其模型路径
* @param stage 阶段类型
* @param model_path 模型路径
*/
virtual void addStage(PetStageType stage, const std::string& model_path) {
stage_model_map[stage] = model_path;
// 如果这是第一个阶段,设置为当前阶段
if (stage_model_map.size() == 1) {
current_stage = stage;
}
}
/**
* 添加阶段音频映射
* @param stage 阶段类型
* @param audio_path 音频文件路径
*/
virtual void addStageAudio(PetStageType stage, const std::string& audio_path) {
stage_audio_map[stage] = audio_path;
}
/**
* 获取阶段模型映射表
* @return 阶段到模型路径的映射表
*/
virtual const std::unordered_map<PetStageType, std::string>& getStageModelMap() const {
return stage_model_map;
}
/**
* 获取阶段音频映射表
* @return 阶段到音频路径的映射表
*/
virtual const std::unordered_map<PetStageType, std::string>& getStageAudioMap() const {
return stage_audio_map;
}
/**
* 获取特定阶段的模型路径
* @param stage 阶段类型
* @return 模型路径,如果不存在则返回空字符串
*/
virtual std::string getStageModelPath(PetStageType stage) const {
auto it = stage_model_map.find(stage);
if (it != stage_model_map.end()) {
return it->second;
}
return "";
}
/**
* 获取特定阶段的音频路径
* @param stage 阶段类型
* @return 音频路径,如果不存在则返回空字符串
*/
virtual std::string getStageAudioPath(PetStageType stage) const {
auto it = stage_audio_map.find(stage);
if (it != stage_audio_map.end()) {
return it->second;
}
return "";
}
/**
* 切换到指定阶段
* @param newStage 要切换到的阶段类型
* @return 是否成功切换
*/
virtual bool switchToStage(PetStageType newStage) {
if (stage_model_map.find(newStage) != stage_model_map.end()) {
current_stage = newStage;
return true;
}
return false;
}
/**
* 进化到下一阶段(基于当前阶段)
* @return 是否成功进化
*/
virtual bool evolveToNextStage() {
const auto& stageOrder = getStageOrder();
auto it = std::find(stageOrder.begin(), stageOrder.end(), current_stage);
if (it == stageOrder.end() || it == stageOrder.end() - 1) {
return false;
}
// 查找下一个可用阶段
for (auto iter = it + 1; iter != stageOrder.end(); ++iter) {
if (stage_model_map.find(*iter) != stage_model_map.end()) {
current_stage = *iter;
return true;
}
}
return false;
}
/**
* 获取当前阶段
* @return 当前阶段类型
*/
virtual PetStageType getCurrentStageType() const {
return current_stage;
}
/**
* 获取当前阶段的模型路径
* @return 当前阶段模型路径
*/
virtual std::string getCurrentStageModelPath() const {
return getStageModelPath(current_stage);
}
/**
* 获取所有可用阶段
* @return 所有阶段的列表
*/
virtual std::list<PetStageType> getAllStages() const {
std::list<PetStageType> stages;
for (const auto& stage : stage_model_map) {
stages.push_back(stage.first);
}
return stages;
}
/**
* 移除阶段
* @param stage 要移除的阶段类型
*/
virtual void removeStage(PetStageType stage) {
if (current_stage == stage && stage_model_map.size() > 1) {
// 找到另一个阶段作为当前阶段
for (const auto& s : stage_model_map) {
if (s.first != stage) {
current_stage = s.first;
break;
}
}
}
stage_model_map.erase(stage);
stage_audio_map.erase(stage);
}
public:
///<! 阶段到模型路径的映射表
std::unordered_map<PetStageType, std::string> stage_model_map;
///<! 阶段到音频路径的映射表
std::unordered_map<PetStageType, std::string> stage_audio_map;
///<! 当前阶段
PetStageType current_stage;
};
// 宠物动作接口
// 一般而言,对于所有的动物,其动作行为基本相同,不同的地方在与发生动作时,所触发的模型不同
// 因此,宠物的动作设计,和阶段设计基本类似
#define PET_ACTION_TYPES \
X(PET_ACTION_SLEEP, "睡觉") \
X(PET_ACTION_EAT, "喂食") \
X(PET_ACTION_HAPPY, "开心") \
X(PET_ACTION_ANGRY, "生气") \
X(PET_ACTION_SAD, "沮丧") \
X(PET_ACTION_EVOLVE, "进化") \
X(PET_ACTION_TOUCH, "被触摸")
enum class PetActionType {
#define X(name, desc) name,
PET_ACTION_TYPES
#undef X
};
// 宠物动作策略类
class PetActionStrategy {
public:
virtual ~PetActionStrategy() = default;
// 定义动作顺序的静态常量
static const std::vector<PetActionType>& getActionOrder() {
static const std::vector<PetActionType> actionOrder = []() {
std::vector<PetActionType> order;
#define X(name, desc) order.push_back(PetActionType::name);
PET_ACTION_TYPES
#undef X
return order;
}();
return actionOrder;
}
/**
* 添加动作及其模型路径
* @param action 动作类型
* @param model_path 模型路径
*/
virtual void addAction(PetActionType action, const std::string& model_path) {
action_model_map[action] = model_path;
}
/**
* 添加动作音频映射
* @param action 动作类型
* @param audio_path 音频文件路径
*/
virtual void addActionAudio(PetActionType action, const std::string& audio_path) {
action_audio_map[action] = audio_path;
}
/**
* 获取动作模型映射表
* @return 动作到模型路径的映射表
*/
virtual const std::unordered_map<PetActionType, std::string>& getActionModelMap() const {
return action_model_map;
}
/**
* 获取动作音频映射表
* @return 动作到音频路径的映射表
*/
virtual const std::unordered_map<PetActionType, std::string>& getActionAudioMap() const {
return action_audio_map;
}
/**
* 获取特定动作的模型路径
* @param action 动作类型
* @return 模型路径,如果不存在则返回空字符串
*/
virtual std::string getActionModelPath(PetActionType action) const {
auto it = action_model_map.find(action);
if (it != action_model_map.end()) {
return it->second;
}
return "";
}
/**
* 获取特定动作的音频路径
* @param action 动作类型
* @return 音频路径,如果不存在则返回空字符串
*/
virtual std::string getActionAudioPath(PetActionType action) const {
auto it = action_audio_map.find(action);
if (it != action_audio_map.end()) {
return it->second;
}
return "";
}
/**
* 执行指定动作
* @param action 要执行的动作类型
* @return 是否成功执行
*/
virtual bool performAction(PetActionType action) {
if (action_model_map.find(action) != action_model_map.end()) {
current_action = action;
return true;
}
return false;
}
/**
* 获取当前动作
* @return 当前动作类型
*/
virtual PetActionType getCurrentActionType() const {
return current_action;
}
/**
* 获取当前动作的模型路径
* @return 当前动作模型路径
*/
virtual std::string getCurrentActionModelPath() const {
return getActionModelPath(current_action);
}
/**
* 获取所有可用动作
* @return 所有动作的列表
*/
virtual std::list<PetActionType> getAllActions() const {
std::list<PetActionType> actions;
for (const auto& action : action_model_map) {
actions.push_back(action.first);
}
return actions;
}
/**
* 获取动作的描述信息
* @param action 动作类型
* @return 动作描述
*/
virtual std::string getActionDescription(PetActionType action) const {
switch (action) {
#define X(name, desc) case PetActionType::name: return desc;
PET_ACTION_TYPES
#undef X
default: return "未知动作";
}
}
/**
* 获取当前动作的描述信息
* @return 当前动作描述
*/
virtual std::string getCurrentActionDescription() const {
return getActionDescription(current_action);
}
/**
* 移除动作
* @param action 要移除的动作类型
*/
virtual void removeAction(PetActionType action) {
action_model_map.erase(action);
action_audio_map.erase(action);
if (current_action == action) {
current_action = PetActionType::PET_ACTION_SLEEP; // 默认动作
}
}
public:
///<! 动作到模型路径的映射表
std::unordered_map<PetActionType, std::string> action_model_map;
///<! 动作到音频路径的映射表
std::unordered_map<PetActionType, std::string> action_audio_map;
///<! 当前动作
PetActionType current_action = PetActionType::PET_ACTION_SLEEP; // 默认动作
};
// 观察者接口
class PetObserver {
public:
virtual ~PetObserver() = default;
// 宠物动作
virtual void onPetAction(PetActionType action) = 0;
// 宠物阶段
virtual void onPetStageChange(PetStageType oldStage, PetStageType newStage) = 0;
};
// 主题接口
class PetSubject {
public:
virtual ~PetSubject() = default;
// 添加观察者
virtual void addObserver(std::shared_ptr<PetObserver> observer) = 0;
// 移除观察者
virtual void removeObserver(std::shared_ptr<PetObserver> observer) = 0;
// 通知动作
virtual void notifyAction(PetActionType action) = 0;
// 通知阶段
virtual void notifyStageChange(PetStageType oldStage, PetStageType newStage) = 0;
};
+4
View File
@@ -0,0 +1,4 @@
//
// Created by misaki on 2025/9/14.
//
#include "PetObserver.h"
+212
View File
@@ -0,0 +1,212 @@
//
// Created by misaki on 2025/9/14.
//
#pragma once
#include "PetInterface.h"
// 用于回调函数传递回调类型,以区分回调的是宠物动作还是宠物阶段
enum class PetType : uint8_t {Action, Stage};
// 宠物音频播放观察者类,继承自宠物观察者
class PetAudioStrategy : public PetObserver, public std::enable_shared_from_this<PetAudioStrategy> {
public:
using AudioCallback = std::function<void(PetType petType, const std::string&)>;
/**
* 构造时候就将“事件→音频”两张表填好
* @param actionAudios 动作→音频
* @param stageAudios 阶段→音频
*/
explicit PetAudioStrategy(
std::unordered_map<PetActionType, std::string> actionAudios = {},
std::unordered_map<PetStageType, std::string> stageAudios = {})
: action_audio(std::move(actionAudios)),
stage_audio(std::move(stageAudios))
{}
/**
* 构造函数,需要传入阶段策略和动作策略,从其中获取所需的类型→模型路径映射
* @param stageStrategy 阶段策略
* @param actionStrategy 动作策略
*/
explicit PetAudioStrategy(
const std::shared_ptr<PetStageStrategy>& stageStrategy,
const std::shared_ptr<PetActionStrategy>& actionStrategy) {
stage_audio = stageStrategy->getStageAudioMap(); // 获取阶段→音频map
action_audio = actionStrategy->getActionAudioMap(); // 获取动作→音频map
}
~PetAudioStrategy() override {
// 自动取消注册
if (auto subject = pet_subject.lock()) {
subject->removeObserver(shared_from_this());
}
}
/**
* 设置音频播放回调函数,注入真正的“播放函数”,由外部(主程序 / 平台层)提供
* @param callback 音频播放回调,接受音频文件路径
*/
virtual void setAudioCallback(AudioCallback callback) {
audio_callback = std::move(callback);
}
/**
* 注册到 Subject(一般由 Pet 基类实现)
* @param subject 主题
*/
void subscribe(std::shared_ptr<PetSubject> subject)
{
if (auto old = pet_subject.lock()) {
old->removeObserver(shared_from_this());
}
pet_subject = subject;
if (subject) {
subject->addObserver(shared_from_this());
}
}
/**
* 宠物动作时触发[Observer 接口实现]
* @param action 动作
*/
void onPetAction(const PetActionType action) override
{
if (!audio_callback) return;
auto it = action_audio.find(action);
if (it != action_audio.end()) {
audio_callback(PetType::Action, it->second); // 播放对应音频
}
}
/**
* 宠物阶段时触发[Observer 接口实现]
* @param oldStage 旧阶段
* @param newStage 新阶段
*/
void onPetStageChange(PetStageType oldStage, const PetStageType newStage) override
{
if (!audio_callback) return;
auto it = stage_audio.find(newStage); // 注意:播“新阶段”的音频
if (it != stage_audio.end()) {
audio_callback(PetType::Stage, it->second);
}
}
private:
///<! 宠物音频播放回调
AudioCallback audio_callback;
///<! 宠物主题
std::weak_ptr<PetSubject> pet_subject;
///<! 两张映射表,保证“同一事件只对应一种音频”
std::unordered_map<PetActionType, std::string> action_audio;
std::unordered_map<PetStageType, std::string> stage_audio;
};
// 渲染器观察者类,继承自PetObserver
class PetRendererStrategy : public PetObserver, public std::enable_shared_from_this<PetRendererStrategy> {
public:
using RenderCallback = std::function<void(PetType petType, const std::string&)>;
/**
* 构造函数,可以传入动作到模型路径和阶段到模型路径的映射表
* @param actionModels 动作→模型路径映射
* @param stageModels 阶段→模型路径映射
*/
explicit PetRendererStrategy(
std::unordered_map<PetActionType, std::string> actionModels = {},
std::unordered_map<PetStageType, std::string> stageModels = {})
: action_models(std::move(actionModels)),
stage_models(std::move(stageModels))
{}
/**
* 构造函数,需要传入阶段策略和动作策略,从其中获取所需的类型→模型路径映射
* @param stageStrategy 阶段策略
* @param actionStrategy 动作策略
*/
explicit PetRendererStrategy(
const std::shared_ptr<PetStageStrategy>& stageStrategy,
const std::shared_ptr<PetActionStrategy>& actionStrategy) {
stage_models = stageStrategy->getStageModelMap();
action_models = actionStrategy->getActionModelMap();
}
~PetRendererStrategy() override {
// 自动取消注册
if (const auto subject = pet_subject.lock()) {
subject->removeObserver(shared_from_this());
}
}
/**
* 设置渲染回调函数,注入真正的"渲染函数",由外部(主程序/平台层)提供
* @param callback 渲染回调,接受模型文件路径
*/
virtual void setRenderCallback(RenderCallback callback) {
render_callback = std::move(callback);
}
/**
* 注册到Subject(一般由Pet基类实现)
* @param subject 主题
*/
void subscribe(std::shared_ptr<PetSubject> subject) {
if (auto old = pet_subject.lock()) {
old->removeObserver(shared_from_this());
}
pet_subject = subject;
if (subject) {
subject->addObserver(shared_from_this());
}
}
/**
* 宠物动作时触发[Observer接口实现]
* @param action 动作类型
*/
void onPetAction(const PetActionType action) override {
if (!render_callback) return;
const auto it = action_models.find(action);
if (it != action_models.end()) {
render_callback(PetType::Action, it->second); // 渲染对应动作模型
}
}
/**
* 宠物阶段变化时触发[Observer接口实现]
* @param oldStage 旧阶段
* @param newStage 新阶段
*/
void onPetStageChange(PetStageType oldStage, const PetStageType newStage) override {
if (!render_callback) return;
const auto it = stage_models.find(newStage); // 注意:渲染"新阶段"的模型,实际上并没有使用到旧阶段,但还是保留,留一手
if (it != stage_models.end()) {
render_callback(PetType::Stage, it->second);
}
}
/**
* 添加动作到模型的映射
* @param action 动作类型
* @param modelPath 模型路径
*/
virtual void addActionModel(PetActionType action, const std::string& modelPath) {
action_models[action] = modelPath;
}
/**
* 添加阶段到模型的映射
* @param stage 阶段类型
* @param modelPath 模型路径
*/
virtual void addStageModel(PetStageType stage, const std::string& modelPath) {
stage_models[stage] = modelPath;
}
/**
* 移除动作模型映射
* @param action 动作类型
*/
virtual void removeActionModel(PetActionType action) {
action_models.erase(action);
}
/**
* 移除阶段模型映射
* @param stage 阶段类型
*/
virtual void removeStageModel(PetStageType stage) {
stage_models.erase(stage);
}
private:
///<! 渲染回调函数
RenderCallback render_callback;
///<! 宠物主题
std::weak_ptr<PetSubject> pet_subject;
///<! 动作到模型路径的映射表
std::unordered_map<PetActionType, std::string> action_models;
///<! 阶段到模型路径的映射表
std::unordered_map<PetStageType, std::string> stage_models;
};
@@ -0,0 +1,365 @@
//
// Created by misaki on 2025/9/9.
//
#include "AudioOutput.h"
#include "esp_log.h"
#include <memory>
#include "PCM5101.h"
// 初始化静态成员
AudioOutput* AudioOutput::instance = nullptr;
std::mutex AudioOutput::instanceMutex;
AudioOutput* AudioOutput::getInstance() {
std::lock_guard<std::mutex> 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<std::mutex> 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<std::mutex> 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, const AudioCallback& callback) {
const 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, const AudioCallback& callback) {
bool success = false;
auto finalState = AudioState::ERROR;
{
std::lock_guard<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> lock(stateMutex);
return currentState;
}
bool AudioOutput::isPlaying() const {
std::lock_guard<std::mutex> lock(stateMutex);
return currentState == AudioState::PLAYING;
}
bool AudioOutput::isPaused() const {
std::lock_guard<std::mutex> lock(stateMutex);
return currentState == AudioState::PAUSED;
}
bool AudioOutput::isStopped() const {
std::lock_guard<std::mutex> 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<std::mutex> lock(stateMutex);
currentState = newState;
}
bool AudioOutput::playPcmFile(const char* filePath,
uint32_t sampleRate,
i2s_data_bit_width_t bits,
i2s_slot_mode_t ch)
{
// 简单文件尺寸获取
FILE* f = fopen(filePath, "rb");
if (!f) return false;
fseek(f, 0, SEEK_END);
const size_t bytes = ftell(f);
fclose(f);
playPcmCommon(filePath, bytes, true, sampleRate, bits, ch, nullptr);
return getState() == AudioState::PLAYING;
}
bool AudioOutput::playPcmStream(const uint8_t* pcmData,
size_t dataBytes,
uint32_t sampleRate,
i2s_data_bit_width_t bits,
i2s_slot_mode_t ch)
{
playPcmCommon(pcmData, dataBytes, false, sampleRate, bits, ch, nullptr);
return getState() == AudioState::PLAYING;
}
void AudioOutput::playPcmFileAsync(const char* filePath,
uint32_t sampleRate,
i2s_data_bit_width_t bits,
i2s_slot_mode_t ch,
const AudioCallback& cb)
{
ThreadConfig cfg = getThreadConfig("pcm_file");
std::thread([=](){
playPcmCommon(filePath, 0, true, sampleRate, bits, ch, cb);
}).detach();
}
void AudioOutput::playPcmStreamAsync(const uint8_t* pcmData,
size_t dataBytes,
uint32_t sampleRate,
i2s_data_bit_width_t bits,
i2s_slot_mode_t ch,
const AudioCallback& cb)
{
ThreadConfig cfg = getThreadConfig("pcm_stream");
std::thread([=](){
playPcmCommon(pcmData, dataBytes, false, sampleRate, bits, ch, cb);
}).detach();
}
void AudioOutput::playPcmCommon(const void* source,
size_t bytes,
bool isFile,
uint32_t sampleRate,
i2s_data_bit_width_t bits,
i2s_slot_mode_t ch,
const AudioCallback& cb)
{
// 停止旧播放
stop();
// 重新配置 I2S 时钟/位宽/声道
bsp_i2s_reconfig_clk(sampleRate, bits, ch);
// 打开“文件”或“内存”数据源
FILE* f = nullptr;
const uint8_t* mem = nullptr;
size_t memLeft = 0;
if (isFile) {
f = fopen(static_cast<const char *>(source), "rb");
if (!f) {
if (cb) cb(AudioState::ERROR, static_cast<const char *>(source));
return;
}
} else {
mem = static_cast<const uint8_t *>(source);
memLeft = bytes;
}
// 状态置为 PLAYING
{
std::lock_guard<std::mutex> lk(stateMutex);
currentState = AudioState::PLAYING;
currentFilePath = isFile ? static_cast<const char *>(source) : "<stream>";
}
if (cb) cb(AudioState::PLAYING, currentFilePath.c_str());
// 循环送 PCM 数据到 I2S
constexpr size_t CHUNK = 512; // 任意 2 的幂
int16_t buf[CHUNK];
size_t bw;
while (true) {
size_t rd = 0;
if (isFile) {
rd = fread(buf, 1, sizeof(buf), f);
} else {
rd = memLeft > sizeof(buf) ? sizeof(buf) : memLeft;
memcpy(buf, mem, rd);
mem += rd;
memLeft -= rd;
}
if (rd == 0) break;
// 音量实时缩放(复用已有逻辑)
const float vf = currentVolume / 100.0f;
for (size_t i = 0; i < rd / 2; ++i) buf[i] = static_cast<int16_t>(buf[i] * vf);
// 写 I2S
i2s_channel_write(i2s_tx_chan, buf, rd, &bw, portMAX_DELAY);
}
// 播放结束
if (f) fclose(f);
{
std::lock_guard<std::mutex> lk(stateMutex);
currentState = AudioState::STOPPED;
}
if (cb) cb(AudioState::STOPPED, currentFilePath.c_str());
}
@@ -0,0 +1,252 @@
//
// Created by misaki on 2025/9/9.
//
#pragma once
#include <mutex>
#include <functional>
#include <string>
#include <hal/i2s_types.h>
#include "ThreadManager.h"
#include "SDFileManager.h"
// 音频播放状态枚举
enum class AudioState {
IDLE, // 音频未播放
PLAYING, // 音频播放中
PAUSED, // 音频暂停中
STOPPED, // 音频已停止
ERROR // 音频播放错误
};
// 音频播放回调函数类型
using AudioCallback = std::function<void(AudioState state, const char* filePath)>;
/*
* 回调函数示例:
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, const 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;
/**
* 播放 PCM 文件(阻塞)
* @param filePath PCM 文件路径
* @param sampleRate 采样率
* @param bits 数据位宽
* @param ch 插槽模式
* @return 是否成功
*/
bool playPcmFile(const char* filePath,
uint32_t sampleRate = 16000,
i2s_data_bit_width_t bits = I2S_DATA_BIT_WIDTH_16BIT,
i2s_slot_mode_t ch = I2S_SLOT_MODE_MONO);
// 异步播放 PCM 文件
void playPcmFileAsync(const char* filePath,
uint32_t sampleRate = 16000,
i2s_data_bit_width_t bits = I2S_DATA_BIT_WIDTH_16BIT,
i2s_slot_mode_t ch = I2S_SLOT_MODE_MONO,
const AudioCallback& cb = nullptr);
/**
* 播放内存 PCM 流(阻塞)
* @param pcmData PCM 数据
* @param dataBytes 数据字节数
* @param sampleRate 采样率
* @param bits 数据位宽
* @param ch 插槽模式
* @return 是否成功
*/
bool playPcmStream(const uint8_t* pcmData,
size_t dataBytes,
uint32_t sampleRate = 16000,
i2s_data_bit_width_t bits = I2S_DATA_BIT_WIDTH_16BIT,
i2s_slot_mode_t ch = I2S_SLOT_MODE_MONO);
// 异步播放 PCM 流
void playPcmStreamAsync(const uint8_t* pcmData,
size_t dataBytes,
uint32_t sampleRate = 16000,
i2s_data_bit_width_t bits = I2S_DATA_BIT_WIDTH_16BIT,
i2s_slot_mode_t ch = I2S_SLOT_MODE_MONO,
const AudioCallback& cb = nullptr);
private:
// 通用 PCM 播放实现
void playPcmCommon(const void* source,
size_t bytes,
bool isFile,
uint32_t sampleRate,
i2s_data_bit_width_t bits,
i2s_slot_mode_t ch,
const AudioCallback& cb);
private:
// 私有构造函数
AudioOutput();
~AudioOutput();
// 初始化线程配置
ThreadConfig getThreadConfig(const char* operation);
// 内部播放实现
void playInternal(const char* directory, const char* fileName, const 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;
};
@@ -0,0 +1,4 @@
//
// Created by misaki on 2025/9/22.
//
#include "cpp_json.h"
+348
View File
@@ -0,0 +1,348 @@
//
// Created by misaki on 2025/9/22.
//
/**
* CppJson.h
* 对cJSON的Cpp封装
* Code By Misaki
* 2025.9.22
*/
/**
* 如何使用:\n
std::string text = R"({"name":"Misaki","age":18,"skills":["C++","RISC-V"]})"; \n
auto j = cppjson::Json::parse(text);\n
std::cout << "name = " << j["name"].asString() << '\n';\n
j["skills"].append(cppjson::Json("Go"));\n
std::cout << j.dumpPretty() << '\n';\n
or:\n
Json j = Json::object();\n
j.set("name", Json("misaki"))\n
.set("age", Json(24))\n
.set("vip", Json(true))\n
.set("list", Json::array()\n
.append(Json(1))\n
.append(Json("hello")));\n
std::cout << j.dumpPretty() << "\n";\n
Json j2 = j["list"];\n
for (auto& v : j2) std::cout << v.dump() << " ";\n
std::cout << "\n";\n
*/
// cpp_json.h
#pragma once
#include <cJSON.h>
#include <memory>
#include <string>
#include <vector>
#include <map>
#include <stdexcept>
#include <utility>
namespace cppjson {
//前向声明 + 别名
class Json;
using Array = std::vector<Json>; // 这里只是别名,不会实例化
using Object = std::map<std::string, Json>;
class Json {
public:
enum Type { Null, Bool, Number, String, ArrayType, ObjectType };
// 构造
Json() noexcept : ptr_(nullptr), owner_(false) {}
// 支持所有整型(包括 int32_t / uint64_t 等 typedef
template <typename T,
std::enable_if_t<std::is_integral_v<T>, int> = 0>
explicit Json(T v) noexcept
: ptr_(cJSON_CreateNumber(static_cast<double>(v))), owner_(true) {}
explicit Json(std::nullptr_t) noexcept : Json() {}
explicit Json(bool v) : ptr_(cJSON_CreateBool(v)), owner_(true) {}
explicit Json(int v) : ptr_(cJSON_CreateNumber(v)), owner_(true) {}
explicit Json(double v) : ptr_(cJSON_CreateNumber(v)), owner_(true) {}
explicit Json(const char* v) : ptr_(cJSON_CreateString(v)), owner_(true) {}
explicit Json(const std::string& v):ptr_(cJSON_CreateString(v.c_str())), owner_(true) {}
explicit Json(const Array& arr); // 类外实现
explicit Json(const Object& obj); // 类外实现
// 接管原始指针,owner=true 会负责 delete,切记不要接管后再在外部cJSON_Delete
explicit Json(cJSON* raw, bool owner = true) noexcept
: ptr_(raw), owner_(owner) {}
// 拷贝/移动
Json(const Json& rhs)
: ptr_(rhs.ptr_ ? cJSON_Duplicate(rhs.ptr_, 1) : nullptr), owner_(true) {}
Json(Json&& rhs) noexcept
: ptr_(rhs.ptr_), owner_(rhs.owner_) { rhs.ptr_ = nullptr; rhs.owner_ = false; }
Json& operator=(Json rhs) noexcept { swap(rhs); return *this; }
~Json() { reset(); }
// 工厂
static Json parse(const std::string& text) {
cJSON* p = cJSON_Parse(text.c_str());
if (!p) throw std::runtime_error("cppjson::parse failed");
return Json(p, true);
}
static Json array() { return Json(cJSON_CreateArray(), true); }
static Json object() { return Json(cJSON_CreateObject(), true); }
// 序列化
[[nodiscard]] std::string dump() const {
if (!ptr_) return "null";
char* s = cJSON_PrintUnformatted(ptr_);
std::string ret(s ? s : "null");
if (s) free(s);
return ret;
}
[[nodiscard]] std::string dumpPretty() const {
if (!ptr_) return "null";
char* s = cJSON_Print(ptr_);
std::string ret(s ? s : "null");
if (s) free(s);
return ret;
}
// 类型查询
[[nodiscard]] Type type() const noexcept {
if (!ptr_) return Null;
switch (ptr_->type & 0xFF) {
case cJSON_False:
case cJSON_True: return Bool;
case cJSON_Number:return Number;
case cJSON_String:return String;
case cJSON_Array: return ArrayType;
case cJSON_Object:return ObjectType;
default: return Null;
}
}
[[nodiscard]] bool isNull() const noexcept { return type() == Null; }
[[nodiscard]] bool isBool() const noexcept { return type() == Bool; }
[[nodiscard]] bool isNumber() const noexcept { return type() == Number; }
[[nodiscard]] bool isString() const noexcept { return type() == String; }
[[nodiscard]] bool isArray() const noexcept { return type() == ArrayType; }
[[nodiscard]] bool isObject() const noexcept { return type() == ObjectType; }
// 取值
[[nodiscard]] bool asBool() const { if (!isBool()) throw std::runtime_error("not bool"); return cJSON_IsTrue(ptr_); }
[[nodiscard]] int asInt() const { if (!isNumber()) throw std::runtime_error("not number"); return static_cast<int>(ptr_->valueint); }
[[nodiscard]] double asDouble() const { if (!isNumber()) throw std::runtime_error("not number"); return ptr_->valuedouble; }
[[nodiscard]] std::string asString() const { if (!isString()) throw std::runtime_error("not string"); return ptr_->valuestring; }
// 数组/对象接口
[[nodiscard]] size_t size() const {
if (isArray() || isObject()) return cJSON_GetArraySize(ptr_);
return 0;
}
Json operator[](size_t idx) const {
if (!isArray()) throw std::runtime_error("not array");
cJSON* it = cJSON_GetArrayItem(ptr_, static_cast<int>(idx));
return it ? Json(it, false) : Json();
}
Json operator[](const std::string& key) const {
if (!isObject()) throw std::runtime_error("not object");
cJSON* it = cJSON_GetObjectItem(ptr_, key.c_str());
return it ? Json(it, false) : Json();
}
Json& append(const Json& v) {
if (!isArray()) throw std::runtime_error("not array");
cJSON_AddItemToArray(ptr_, cJSON_Duplicate(v.ptr_, 1));
return *this;
}
// 便捷的 append 方法重载
Json& append(int value) {
return append(Json(value));
}
Json& append(double value) {
return append(Json(value));
}
Json& append(bool value) {
return append(Json(value));
}
Json& append(const char* value) {
return append(Json(value));
}
Json& append(const std::string& value) {
return append(Json(value));
}
Json& set(const std::string& key, const Json& v) {
if (!isObject()) throw std::runtime_error("not object");
cJSON_AddItemToObject(ptr_, key.c_str(), cJSON_Duplicate(v.ptr_, 1));
return *this;
}
// 便捷的 set 方法重载
Json& set(const std::string& key, int value) {
return set(key, Json(value));
}
Json& set(const std::string& key, double value) {
return set(key, Json(value));
}
Json& set(const std::string& key, bool value) {
return set(key, Json(value));
}
Json& set(const std::string& key, const char* value) {
return set(key, Json(value));
}
Json& set(const std::string& key, const std::string& value) {
return set(key, Json(value));
}
// 获取对象的所有键
[[nodiscard]] std::vector<std::string> keys() const {
if (!isObject()) throw std::runtime_error("not object");
std::vector<std::string> result;
cJSON* child = ptr_->child;
while (child) {
if (child->string) {
result.emplace_back(child->string);
}
child = child->next;
}
return result;
}
// 数组迭代器(只读)
template <bool IsConst>
struct array_iterator_impl {
using iterator_category = std::forward_iterator_tag;
using value_type = Json;
using difference_type = std::ptrdiff_t;
using pointer = typename std::conditional<IsConst, const Json, Json>::type*;
using reference = typename std::conditional<IsConst, const Json, Json>::type&;
array_iterator_impl(cJSON* head, cJSON* c) : head_(head), cur_(c) {}
reference operator*() {
tmp = std::make_unique<Json>(cur_, false);
return *tmp;
}
pointer operator->() { return &(operator*()); }
array_iterator_impl& operator++() { cur_ = cur_ ? cur_->next : nullptr; return *this; }
friend bool operator==(const array_iterator_impl& a, const array_iterator_impl& b) { return a.cur_ == b.cur_; }
friend bool operator!=(const array_iterator_impl& a, const array_iterator_impl& b) { return !(a == b); }
private:
cJSON *head_, *cur_;
std::unique_ptr<Json> tmp;
};
using array_iterator = array_iterator_impl<false>;
using const_array_iterator = array_iterator_impl<true>;
// 对象键值对迭代器
template <bool IsConst>
struct object_iterator_impl {
using iterator_category = std::forward_iterator_tag;
using value_type = std::pair<std::string, Json>;
using difference_type = std::ptrdiff_t;
using pointer = typename std::conditional<IsConst, const value_type, value_type>::type*;
using reference = typename std::conditional<IsConst, const value_type, value_type>::type&;
object_iterator_impl(cJSON* c) : cur_(c) {
if (cur_ && cur_->string) {
updateValue();
}
}
reference operator*() {
tmp = std::make_unique<value_type>(cur_->string ? cur_->string : "", Json(cur_, false));
return *tmp;
}
pointer operator->() { return &(operator*()); }
object_iterator_impl& operator++() {
cur_ = cur_ ? cur_->next : nullptr;
if (cur_) {
updateValue();
}
return *this;
}
friend bool operator==(const object_iterator_impl& a, const object_iterator_impl& b) { return a.cur_ == b.cur_; }
friend bool operator!=(const object_iterator_impl& a, const object_iterator_impl& b) { return !(a == b); }
private:
void updateValue() {
if (cur_ && cur_->string) {
current_key = cur_->string;
}
}
cJSON* cur_ = nullptr;
std::string current_key;
std::unique_ptr<value_type> tmp;
};
using object_iterator = object_iterator_impl<false>;
using const_object_iterator = object_iterator_impl<true>;
// 统一的迭代器接口(用于数组)
array_iterator begin() {
if (!isArray()) throw std::runtime_error("not array");
return {ptr_, ptr_ ? ptr_->child : nullptr};
}
array_iterator end() {
if (!isArray()) throw std::runtime_error("not array");
return {ptr_, nullptr};
}
[[nodiscard]] const_array_iterator begin() const { return cbegin(); }
[[nodiscard]] const_array_iterator end() const { return cend(); }
[[nodiscard]] const_array_iterator cbegin() const {
if (!isArray()) throw std::runtime_error("not array");
return {ptr_, ptr_ ? ptr_->child : nullptr};
}
[[nodiscard]] const_array_iterator cend() const {
if (!isArray()) throw std::runtime_error("not array");
return {ptr_, nullptr};
}
// 对象项迭代器
object_iterator begin_object() {
if (!isObject()) throw std::runtime_error("not object");
return {ptr_ ? ptr_->child : nullptr};
}
object_iterator end_object() {
if (!isObject()) throw std::runtime_error("not object");
return {nullptr};
}
[[nodiscard]] const_object_iterator begin_object() const { return cbegin_object(); }
[[nodiscard]] const_object_iterator end_object() const { return cend_object(); }
[[nodiscard]] const_object_iterator cbegin_object() const {
if (!isObject()) throw std::runtime_error("not object");
return {ptr_ ? ptr_->child : nullptr};
}
[[nodiscard]] const_object_iterator cend_object() const {
if (!isObject()) throw std::runtime_error("not object");
return {nullptr};
}
// 便利的 items() 方法,用于结构化绑定
class ItemsProxy {
public:
explicit ItemsProxy(Json* json) : json_(json) {}
object_iterator begin() { return json_->begin_object(); }
object_iterator end() { return json_->end_object(); }
[[nodiscard]] const_object_iterator begin() const { return json_->cbegin_object(); }
[[nodiscard]] const_object_iterator end() const { return json_->cend_object(); }
private:
Json* json_;
};
ItemsProxy items() { return ItemsProxy(this); }
[[nodiscard]] ItemsProxy items() const { return ItemsProxy(const_cast<Json*>(this)); }
/*-------- 工具 ------------------------------------------------*/
void swap(Json& rhs) noexcept { std::swap(ptr_, rhs.ptr_); std::swap(owner_, rhs.owner_); }
private:
cJSON* ptr_ = nullptr;
bool owner_ = false;
void reset() { if (owner_ && ptr_) cJSON_Delete(ptr_); ptr_ = nullptr; owner_ = true; }
};
// 类外实现:Array / Object 构造
inline Json::Json(const Array& arr)
: ptr_(cJSON_CreateArray()), owner_(true) {
for (const auto& v : arr) append(v);
}
inline Json::Json(const Object& obj)
: ptr_(cJSON_CreateObject()), owner_(true) {
for (const auto& kv : obj) set(kv.first, kv.second);
}
} // namespace cppjson
@@ -0,0 +1,82 @@
//
// Created by misaki on 2025/9/8.
//
#include "ThreadManager.h"
#include "LVGLRender.h"
#include "I2C_Driver.h"
#include "ST77916.h"
#include "LVGL_Driver.h"
#include <esp_log.h>
#include <ToolsClass.h>
LVGLRender* LVGLRender::LVGLRenderInstance = nullptr;
std::mutex LVGLRender::instance_mutex;
uint16_t LVGLRender::fps = 30;
LVGLRender* LVGLRender::getInstance() {
// 双检锁(DCLP),C++11 起 atomic+mutex 组合保证线程安全
LVGLRender* tmp = LVGLRenderInstance;
if (tmp == nullptr) {
std::lock_guard<std::mutex> lock(instance_mutex);
tmp = LVGLRenderInstance;
if (tmp == nullptr) {
tmp = new LVGLRender();
LVGLRenderInstance = tmp;
}
}
return tmp;
}
void LVGLRender::LVGL_Update() {
while (true) {
{
const std::lock_guard<std::mutex> lock(mtx); // lvgl并非线程安全,需要加锁
lv_timer_handler();
}
std::this_thread::sleep_for(std::chrono::milliseconds(fps));
}
}
// 构造函数
LVGLRender::LVGLRender() {
ESP_LOGI("LVGL_Render", "LVGL_Render构造函数...初始化媒体驱动...");
I2C_Init();
LCD_Init();
LVGL_Init();
// std::this_thread::sleep_for(std::chrono::milliseconds(2000)); // 延时2秒 确保所有 LVGL 对象已创建完成
ESP_LOGI("LVGL_Render", "LVGL_Render构造函数...初始化媒体驱动成功...");
ESP_LOGI("LVGL_Render", "LVGL_Render构造函数...创建LVGL心跳...");
ThreadConfig trickConfig;
trickConfig.core_id = 1; // 渲染分配给核0
trickConfig.name = "LVGL_Render";
trickConfig.priority = 5; //
trickConfig.stack_size = 8192; // 给LVGL一个较大的堆栈,避免栈溢出
std::thread tick_thread = ThreadManager::createMemberThread(trickConfig, this, &LVGLRender::LVGL_Update);
tick_thread.detach(); // 线程分离 生命周期跟随主线程结束,线程结束后自动销毁(核心渲染线程)
ESP_LOGI("LVGL_Render", "LVGL_Render构造函数...创建LVGL心跳成功...");
}
LVGLRender::~LVGLRender() {
}
void LVGLRender::setFps(uint16_t fps_) {
fps = fps_;
}
uint16_t LVGLRender::getFps() {
return fps;
}
void LVGLRender::tryToInitRenderGif() {
ESP_LOGI("LVGL_Render", "尝试初始化 LVGL 底层驱动...");
}
@@ -0,0 +1,47 @@
//
// Created by misaki on 2025/9/8.
//
/**
* 本类为单例类,用于实现LVGL的渲染
* 封装了一整套的LVGL渲染流
* 同时兼顾了底层的显示驱动初始化
*/
#pragma once
#define LVGL_DELAY_FROM_FPS(fps) pdMS_TO_TICKS(1000 / (fps))
#include <mutex>
#include <string>
#include <vector>
#include <core/lv_obj.h>
class LVGLRender {
public:
LVGLRender(LVGLRender const&) = delete; // 禁止拷贝构造函数
LVGLRender& operator=(LVGLRender const&) = delete;// 禁止赋值运算符
public:
static LVGLRender* getInstance();
static void setFps(uint16_t fps_);
static uint16_t getFps();
void tryToInitRenderGif();
private:
explicit LVGLRender(); // 构造函数私有化
~LVGLRender();
void LVGL_Update(); // 渲染lvgl上下文(持久性线程)
private:
static LVGLRender* LVGLRenderInstance; /// 单例实例
static std::mutex instance_mutex; /// 单例锁
static uint16_t fps; /// 帧率
std::mutex mtx;
};
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,5 @@
//
// Created by misaki on 2025/9/26.
//
#include "lvpp.h"
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,5 @@
//
// Created by misaki on 2025/9/28.
//
#include "BaseApp.h"
@@ -0,0 +1,415 @@
//
// Created by misaki on 2025/9/28.
//
#pragma once
#include <optional>
#include <iostream>
#include "lvpp.h"
#include "cpp_json.h"
#include "CommClass.h"
LV_FONT_DECLARE(SiYuanHeiTiGoogleBan); // 引入字体
struct AppCalc : lvgl_cpp::BaseApp {
using BaseApp::BaseApp;
void onShow() override {
lvgl_cpp::Label(*this).text("Calculator").center();
}
};
struct AppMusic : lvgl_cpp::BaseApp {
using BaseApp::BaseApp;
void onShow() override {
btn_.emplace(*this);
gif.emplace(*this);
btn_->size(180, 60)
.align(LV_ALIGN_CENTER, nullptr, 0, 80)
.on(LV_EVENT_CLICKED, [&](lv_event_t*) {
// 居中 GIF,背景黑
lv_obj_set_style_bg_color(this->raw(), lv_color_black(), 0);
lv_obj_clear_flag(this->raw(), LV_OBJ_FLAG_SCROLLABLE); // 禁止滚动
lv_obj_set_scroll_dir(this->raw(), LV_DIR_NONE);
lv_obj_set_style_bg_opa(this->raw(), LV_OPA_COVER, 0);
gif->src("sequence02mmm.gif")
.center();
});
// 按钮文字
lv_obj_t* label = lv_label_create(btn_->raw());
lv_obj_set_style_text_font(label, &SiYuanHeiTiGoogleBan, LV_PART_MAIN);
lv_label_set_text(label, "Gif测试");
lv_obj_center(label);
}
void onCleanup() override {
if (gif) gif->drop(); // 对象还活着,立即释放
}
~AppMusic() override{
gif.reset();
btn_.reset();
}
private:
std::optional<lvgl_cpp::Button> btn_; // c++17 轻量 RAII
std::optional<lvgl_cpp::Gif> gif;
};
class ButtonApp : public lvgl_cpp::BaseApp {
public:
using BaseApp::BaseApp; // 继承 exit 按钮机制
void onShow() override {
/* 居中按钮:180×60 px,圆角,现代蓝 */
btn_.emplace(*this);
btn_->size(180, 60)
.align(LV_ALIGN_CENTER, nullptr, 0, 80)
.on(LV_EVENT_CLICKED, [](lv_event_t*) {
lvgl_cpp::Toast::show("咕咕嘎嘎!",
lvgl_cpp::Toast::Type::INFO,
1000);
});
/* 按钮文字 */
lv_obj_t* label = lv_label_create(btn_->raw());
lv_obj_set_style_text_font(label, &SiYuanHeiTiGoogleBan, LV_PART_MAIN);
lv_label_set_text(label, "按我");
lv_obj_center(label);
}
private:
std::optional<lvgl_cpp::Button> btn_; // c++17 轻量 RAII
};
class WebSocketVoice : public lvgl_cpp::BaseApp {
public:
using BaseApp::BaseApp; // 继承 exit 按钮机制
void onShow() override {
/* 居中按钮:180×60 px,圆角,现代蓝 */
btn_.emplace(*this);
btn_->size(180, 60)
.align(LV_ALIGN_CENTER, nullptr, 0, 80)
.on(LV_EVENT_CLICKED, [](lv_event_t*) {
lvgl_cpp::Toast::show("测试!",
lvgl_cpp::Toast::Type::ERROR,
1000);
});
/* 按钮文字 */
lv_obj_t* label = lv_label_create(btn_->raw());
lv_obj_set_style_text_font(label, &SiYuanHeiTiGoogleBan, LV_PART_MAIN);
lv_label_set_text(label, "按我");
lv_obj_center(label);
}
private:
std::optional<lvgl_cpp::Button> btn_; // c++17 轻量 RAII
};
#include "SDFileManager.h"
#include "SimpleI2SForwarder.h"
#include <mbedtls/base64.h> // base64 编解码
class LongPressButtonAnim : public lvgl_cpp::BaseApp {
public:
using BaseApp::BaseApp; // 继承 exit 按钮机制
void onShow() override {
/* 居中按钮:180×60 px,圆角,现代蓝 */
btn_.emplace(*this);
btn_->size(180, 60)
.align(LV_ALIGN_CENTER, nullptr, 0, 80);
btn_->onLong([](const std::atomic<bool>& stop) { // 长按循环按钮
std::vector<int16_t> audio_data;
std::vector<int16_t> audio_data_sum;
constexpr int SHIFT_AVE = 14; // 你喂 AFE 时右移的位数
constexpr int GAIN_SHIFT = 14 - 9; // 想拉回 0 dB 需要左移多少
ESP_LOGI("LP","开始录音");
while(!stop){
// 获取所有可用的最新数据,并累加到 audio_data_sum 当中
if (LatestDataForwarder::getInstance()->retrieveLatestData(audio_data, 100)) {
// 放大到正常幅度再累加(放大声音)
for (auto &s : audio_data) {
int32_t tmp = static_cast<int32_t>(s) << GAIN_SHIFT; // 能量拉回 0 dB
if (tmp > 32767) tmp = 32767;
if (tmp < -32768) tmp = -32768;
audio_data_sum.push_back(static_cast<int16_t>(tmp));
}
// ESP_LOGI("LP", "got %zu samples, total now %zu",
// audio_data.size(), audio_data_sum.size());
} else {
ESP_LOGW("LP", "no data within 100 ms");
}
// vTaskDelay(pdMS_TO_TICKS(5));
}
ESP_LOGI("LP","结束录音");
// 在SD卡根目录创建一个pcm文件,向其中写入数据
// ESP_LOGI("LP","开始写入文件");
// SDFileManager::getInstance()->writeFileSync(
// ToolsClass::makeFullPath("temp_little_endian_16bit_16khz.pcm").c_str(),
// reinterpret_cast<const char *>(audio_data_sum.data()),
// audio_data_sum.size() * sizeof(int16_t),
// "wb");
// ESP_LOGI("LP","写入文件成功");
// 将数据封装成 json 后通过 websocket 发送给服务器
ESP_LOGI("LP","开始发送数据");
size_t pcm_bytes = audio_data_sum.size() * sizeof(int16_t);
size_t b64_len = 0;
mbedtls_base64_encode(nullptr, 0, &b64_len,
reinterpret_cast<const unsigned char*>(audio_data_sum.data()),
pcm_bytes);
/* 再真正编码 */
std::vector<unsigned char> b64_buf(b64_len);
mbedtls_base64_encode(b64_buf.data(), b64_len, &b64_len,
reinterpret_cast<const unsigned char*>(audio_data_sum.data()),
pcm_bytes);
/* 转成 std::string(不含 \0 */
std::string b64_str(reinterpret_cast<char*>(b64_buf.data()), b64_len);
cppjson::Json audio_json = cppjson::Json::object(); // 创建 json 对象
cppjson::Json audio_json_body = cppjson::Json::object();
audio_json.set("type", cppjson::Json("audio"));
audio_json_body
.set("data", cppjson::Json(b64_str))
.set("size", cppjson::Json(audio_data_sum.size()));
audio_json.set("body", audio_json_body);
WebSocketManager::getInstance()->sendJson(audio_json);
ESP_LOGI("LP","发送数据 OVER!");
ESP_LOGI("LP","long exit");
});
btn_->onShort([]() { // 短按(点一下)
ESP_LOGI("Btn", "short click");
});
/* 按钮文字 */
lv_obj_t* label = lv_label_create(btn_->raw());
lv_obj_set_style_text_font(label, &SiYuanHeiTiGoogleBan, LV_PART_MAIN);
lv_label_set_text(label, "长按录音");
lv_obj_center(label);
}
private:
std::optional<lvgl_cpp::LongPressButton> btn_; // c++17 轻量 RAII
};
#include "SpeechRecognizer.h"
#include "AudioOutput.h"
#include "PetBaseClass.h"
#include "PetDao.h"
class PetApp : public lvgl_cpp::BaseApp {
public:
using BaseApp::BaseApp; // 继承 exit 按钮机制
// 打印宠物当前状态
static void printPet(const PetBase& pet)
{
const auto& info = pet.getPetInfo();
std::cout << "【状态】"
<< " 名称:" << info.pet_name
<< " HP:" << info.pet_hp
<< " 亲密度:" << info.pet_density
<< " 身份:" << info.pet_identity
<< " 阶段:" << static_cast<int>(pet.getCurrentStage())
<< " 动作:" << static_cast<int>(pet.getCurrentAction())
<< std::endl;
}
// 初始化测试宠物信息
void initPet() {
// 创建雪豹
PetBaseInfo info{
.pet_name = "芝士雪豹",
.pet_hp = 100,
.pet_density = 45, // 故意设低,方便看进化
.pet_identity = "我是顶真,来自妈妈省"
};
const auto stageStrategy = std::make_shared<PetStageStrategy>();
const auto actionStrategy = std::make_shared<PetActionStrategy>();
// 填阶段资源(模型/音频)
stageStrategy->addStage(PetStageType::PET_STAGE_YOUNG, "pet_data/cheese_snow_leopard/models/setdown30.gif");
stageStrategy->addStage(PetStageType::PET_STAGE_ADULT, "pet_data/cheese_snow_leopard/models/setdown10.gif");
stageStrategy->addStage(PetStageType::PET_STAGE_OLD, "pet_data/cheese_snow_leopard/models/setdown20.gif");
stageStrategy->addStageAudio(PetStageType::PET_STAGE_YOUNG, "pet_data/cheese_snow_leopard/audio/dog.pcm");
stageStrategy->addStageAudio(PetStageType::PET_STAGE_ADULT, "/audio/snow_adult.mp3");
stageStrategy->addStageAudio(PetStageType::PET_STAGE_OLD, "/audio/snow_old.mp3");
// 填动作资源
actionStrategy->addAction(PetActionType::PET_ACTION_EAT, "pet_data/cheese_snow_leopard/models/eat1.gif");
actionStrategy->addActionAudio(PetActionType::PET_ACTION_EAT, "pet_data/cheese_snow_leopard/audio/cat.pcm");
// 创建宠物
pet = std::make_shared<PetBase>(info, stageStrategy, actionStrategy);
// 4. 构造观察者
audioObserver = std::make_shared<PetAudioStrategy>(stageStrategy, actionStrategy);
audioObserver->setAudioCallback([&](PetType petType, const std::string& path){
std::cout << "🔊 播放音频: " << path << std::endl;
if (petType == PetType::Action) { // 如果播放的是动作
AudioOutput::getInstance()->playPcmFile(ToolsClass::makeFullPath(path).c_str());
}
if (petType == PetType::Stage) { // 如果播放的是阶段
// 暂时nothing to do
}
});
renderObserver = std::make_shared<PetRendererStrategy>(stageStrategy, actionStrategy);
renderObserver->setRenderCallback([&](PetType petType, const std::string& path){
std::cout << "🖼️ 渲染模型: " << path << std::endl;
if (petType == PetType::Action) { // 如果播放的是动作,则突发播放
gif_->burst(path.c_str(), 7000);
}
if (petType == PetType::Stage) { // 如果播放的是阶段,则持久循环播放
std::cout << "渲染模型路径: " << path << std::endl;
gif_->loop(path.c_str());
}
});
audioObserver->subscribe(pet);
renderObserver->subscribe(pet);
// 触发初始渲染
pet->notifyStageChange(PetStageType::PET_STAGE_YOUNG, pet->getCurrentStage());
printPet(*pet);
}
// 初始化语音识别
void initSpeech() {
// 获取 SpeechRecognizer 全局单例实例
recognizer = SpeechRecognizer::getInstance();
// 初始化
recognizer->init(false, VAD_MODE_3, "/sdcard/srmodels");
// 添加自定义命令
std::vector<std::pair<int, std::string> > commands = {
{0, "mo tou"}, // 摸头
{1, "wei shi"}, // 喂食
{2, "jiao liang sheng"}, // 叫两声
};
// 添加命令
recognizer->addCommands(commands);
// 注册回调函数
recognizer->registerCommandCallback([this](int command_id, const std::string& phrase, float probability) {
this->commandCallback(command_id, phrase, probability);
});
recognizer->registerStateCallback([this](const std::string& state) {
this->stateCallback(state);
});
recognizer->start(); // 启动识别
}
// 状态回调函数
void stateCallback(const std::string &state) {
ESP_LOGI("Example", "状态改变到: %s", state.c_str());
}
// 命令回调函数
void commandCallback(int command_id, const std::string &phrase, float probability) {
ESP_LOGI("Example", "Received command: ID=%d, Phrase='%s', Probability=%.2f",
command_id, phrase.c_str(), probability);
// 根据命令执行相应操作
switch (command_id) {
case 0:
pet->play();
info_->text_fmt("HP: %d \n Dens: %d", pet->getPetInfo().pet_hp, pet->getPetInfo().pet_density);
printPet(*pet);
break;
case 1:
pet->feed();
info_->text_fmt("HP: %d \n Dens: %d", pet->getPetInfo().pet_hp, pet->getPetInfo().pet_density);
printPet(*pet);
break;
case 2:
pet->play();
info_->text_fmt("HP: %d \n Dens: %d", pet->getPetInfo().pet_hp, pet->getPetInfo().pet_density);
printPet(*pet);
break;
default:
ESP_LOGI("Example", "未知的命令ID: %d", command_id);
break;
}
}
void onShow() override {
lv_obj_set_style_bg_color(this->raw(), lv_color_black(), 0); // 背景
lv_obj_clear_flag(this->raw(), LV_OBJ_FLAG_SCROLLABLE); // 禁止滚动
gif_ = std::make_unique<lvgl_cpp::GifPlayer>(*this); // 注意初始化顺序
gif_->size(360, 360);
initPet(); // 初始化测试宠物信息
initSpeech(); // 初始化语音识别
// 居中按钮:180×60 px,圆角,现代蓝
btn_.emplace(*this);
btn_->size(120, 40)
.align(LV_ALIGN_CENTER, nullptr, 0, 80);
// 按钮文字
lv_obj_t* label = lv_label_create(btn_->raw());
lv_obj_set_style_text_font(label, &SiYuanHeiTiGoogleBan, LV_PART_MAIN);
lv_label_set_text(label, "长按交流");
lv_obj_center(label);
btn_->onShort([]() { // 短按事件(点一下)
ESP_LOGI("Btn", "short click");
});
btn_->onLong([](const std::atomic<bool>& stop) { // 长按循环按钮
std::vector<int16_t> audio_data;
std::vector<int16_t> audio_data_sum;
constexpr int SHIFT_AVE = 14; // 喂 AFE 时右移的位数
constexpr int GAIN_SHIFT = 14 - 9; // 想拉回 0 dB 需要左移多少
ESP_LOGI("LP","开始录音");
while(!stop){
// 获取所有可用的最新数据,并累加到 audio_data_sum 当中
if (LatestDataForwarder::getInstance()->retrieveLatestData(audio_data, 100)) {
// 放大到正常幅度再累加(放大声音)
for (auto &s : audio_data) {
int32_t tmp = static_cast<int32_t>(s) << GAIN_SHIFT; // 能量拉回 0 dB
if (tmp > 32767) tmp = 32767;
if (tmp < -32768) tmp = -32768;
audio_data_sum.push_back(static_cast<int16_t>(tmp));
}
// ESP_LOGI("LP", "got %zu samples, total now %zu",
// audio_data.size(), audio_data_sum.size());
} else {
ESP_LOGW("LP", "no data within 100 ms");
}
}
ESP_LOGI("LP","结束录音");
// 将数据封装成 json 后通过 websocket 发送给服务器
ESP_LOGI("LP","开始发送数据");
size_t pcm_bytes = audio_data_sum.size() * sizeof(int16_t);
size_t b64_len = 0;
mbedtls_base64_encode(nullptr, 0, &b64_len,
reinterpret_cast<const unsigned char*>(audio_data_sum.data()),
pcm_bytes);
// 再真正编码
std::vector<unsigned char> b64_buf(b64_len);
mbedtls_base64_encode(b64_buf.data(), b64_len, &b64_len,
reinterpret_cast<const unsigned char*>(audio_data_sum.data()),
pcm_bytes);
// 转成 std::string(不含 \0
std::string b64_str(reinterpret_cast<char*>(b64_buf.data()), b64_len);
cppjson::Json audio_json = cppjson::Json::object(); // 创建 json 对象
cppjson::Json audio_json_body = cppjson::Json::object();
audio_json.set("type", "audio");
audio_json_body
.set("data", b64_str)
.set("size", cppjson::Json(audio_data_sum.size()));
audio_json.set("body", audio_json_body);
WebSocketManager::getInstance()->sendJson(audio_json);
ESP_LOGI("LP","发送数据 OVER!");
ESP_LOGI("LP","long exit");
});
// 透明文字浮层(初始提示)
info_.emplace(*this);
info_->text_fmt("HP: %d \n Dens: %d", pet->getPetInfo().pet_hp, pet->getPetInfo().pet_density)
.textColor(lv_color_black())
.font(&SiYuanHeiTiGoogleBan)
.alignText(LV_TEXT_ALIGN_CENTER)
.place(LV_ALIGN_CENTER, nullptr, 0, -120); // 屏幕正中央
}
void onCleanup() override {
if (gif_) gif_->stop(); // 对象还活着,立即释放
}
~PetApp() override = default; // unique_ptr 自动析构
private:
std::shared_ptr<PetBase> pet;
std::shared_ptr<PetAudioStrategy> audioObserver;
std::shared_ptr<PetRendererStrategy> renderObserver;
private:
SpeechRecognizer *recognizer; // 语音识别
std::unique_ptr<lvgl_cpp::GifPlayer> gif_; // gif 播放器
std::optional<lvgl_cpp::TextOverlay> info_; // 透明文字
std::optional<lvgl_cpp::LongPressButton> btn_; // 长按按钮
};
@@ -0,0 +1,555 @@
//
// Created by misaki on 2025/9/9.
//
#include "SDFileManager.h"
#include "SD_MMC.h"
#include <cstring>
#include <vector>
#include <string>
#include <iomanip>
SDFileManager* SDFileManager::SDFileInstance = nullptr;
std::mutex SDFileManager::instance_mutex;
SDFileManager *SDFileManager::getInstance() {
// 双检锁(DCLP),C++11 起 atomic+mutex 组合保证线程安全
SDFileManager* tmp = SDFileInstance;
if (tmp == nullptr) {
std::lock_guard<std::mutex> lock(instance_mutex);
tmp = SDFileInstance;
if (tmp == nullptr) {
tmp = new SDFileManager();
SDFileInstance = tmp;
}
}
return tmp;
}
SDFileManager::SDFileManager() : is_initialized(false), current_directory(MOUNT_POINT) {
// 初始化SD卡
init();
// 获取Flash大小
Flash_Searching();
}
SDFileManager::~SDFileManager() {
// 清理资源
}
void SDFileManager::init() {
SD_Init();
is_initialized = true;
}
void SDFileManager::tryInitSDCard() {
ESP_LOGI("SDFileManager", "Trying to initialize SD card...");
}
bool SDFileManager::writeFileSync(const char* path, const char* data, const size_t len, const char* type) {
std::lock_guard<std::mutex> lock(file_operation_mutex);
if (!is_initialized) {
ESP_LOGE("SDFileManager", "SD card not initialized");
return false;
}
ESP_LOGI("SDFileManager", "Opening file %s", path);
FILE *f = fopen(path, type);
if (f == nullptr) {
ESP_LOGE("SDFileManager", "Failed to open file for writing");
return ESP_FAIL;
}
const size_t written = fwrite(data, 1, len, f);
fclose(f);
if (written != len) {
ESP_LOGE("SDFileManager", "Only %zu/%zu bytes written", written, len);
return ESP_FAIL;
}
ESP_LOGI("SDFileManager", "File written (%zu bytes)", written);
return true;
}
std::string SDFileManager::readFileSync(const char* path) {
std::lock_guard<std::mutex> lock(file_operation_mutex);
if (!is_initialized) {
ESP_LOGE("SDFileManager", "SD card not initialized");
return "";
}
FILE* file = fopen(path, "r");
if (file == nullptr) {
ESP_LOGE("SDFileManager", "Failed to open file: %s", path);
return "";
}
// 获取文件大小
fseek(file, 0, SEEK_END);
long size = ftell(file);
fseek(file, 0, SEEK_SET);
// 读取文件内容
std::string content;
content.resize(size);
size_t bytes_read = fread(&content[0], 1, size, file);
fclose(file);
if (bytes_read != static_cast<size_t>(size)) {
ESP_LOGE("SDFileManager", "Failed to read entire file: %s", path);
return "";
}
return content;
}
std::string SDFileManager::readFileSync(const std::string& path) {
std::lock_guard<std::mutex> lock(file_operation_mutex);
if (!is_initialized) {
ESP_LOGE("SDFileManager", "SD card not initialized");
return "";
}
FILE* file = fopen(path.c_str(), "r");
if (file == nullptr) {
ESP_LOGE("SDFileManager", "Failed to open file: %s", path.c_str());
return "";
}
// 获取文件大小
fseek(file, 0, SEEK_END);
long size = ftell(file);
fseek(file, 0, SEEK_SET);
// 读取文件内容
std::string content;
content.resize(size);
size_t bytes_read = fread(&content[0], 1, size, file);
fclose(file);
if (bytes_read != static_cast<size_t>(size)) {
ESP_LOGE("SDFileManager", "Failed to read entire file: %s", path.c_str());
return "";
}
return content;
}
std::vector<std::string> SDFileManager::listFilesSync(const char* directory, const char* extension) {
std::lock_guard<std::mutex> lock(file_operation_mutex);
if (!is_initialized) {
ESP_LOGE("SDFileManager", "SD card not initialized");
return {};
}
constexpr int max_files = 50;
char file_names[max_files][100];
const uint16_t file_count = Folder_retrieval(directory, extension, file_names, max_files);
std::vector<std::string> files;
for (uint16_t i = 0; i < file_count; ++i) {
files.emplace_back(file_names[i]);
}
return files;
}
FILE* SDFileManager::openFileSync(const char* path, const char* mode) {
std::lock_guard<std::mutex> lock(file_operation_mutex);
if (!is_initialized) {
ESP_LOGE("SDFileManager", "SD card not initialized");
return nullptr;
}
return Open_File(path);
}
bool SDFileManager::closeFileSync(FILE* file) {
if (file != nullptr) {
return fclose(file) == 0;
}
return false;
}
void SDFileManager::asyncWriteFile(const char* path, const char* data, const size_t len, const char* type, WriteCallback callback) {
ThreadConfig config = getThreadConfig("write_file");
ThreadManager::createThread(config, [this, path = std::string(path),
data = std::string(data), callback, type, len]() {
const bool success = this->writeFileSync(path.c_str(), data.c_str(), len, type);
if (callback) {
callback(success, path.c_str());
}
}).detach(); // 分离线程,使其独立运行
}
void SDFileManager::asyncReadFile(const char* path, ReadCallback callback) {
if (!callback) {
return; // 没有回调函数,异步读取无意义
}
ThreadConfig config = getThreadConfig("read_file");
ThreadManager::createThread(config, [this, path = std::string(path), callback]() {
std::string content = this->readFileSync(path.c_str());
bool success = !content.empty();
callback(success, path.c_str(), content);
}).detach();
}
void SDFileManager::asyncListFiles(const char* directory, const char* extension, ListCallback callback) {
if (!callback) {
return; // 没有回调函数,异步列出文件无意义
}
ThreadConfig config = getThreadConfig("list_files");
ThreadManager::createThread(config, [this, directory = std::string(directory),
extension = std::string(extension ? extension : ""),
callback]() {
std::vector<std::string> files = this->listFilesSync(
directory.c_str(),
extension.empty() ? nullptr : extension.c_str()
);
bool success = !files.empty();
callback(success, files);
}).detach();
}
void SDFileManager::asyncOpenFile(const char* path, const char* mode, OpenCallback callback) {
if (!callback) {
return; // 没有回调函数,异步打开文件无意义
}
ThreadConfig config = getThreadConfig("open_file");
ThreadManager::createThread(config, [this, path = std::string(path),
mode = std::string(mode), callback]() {
FILE* file = this->openFileSync(path.c_str(), mode.c_str());
bool success = (file != nullptr);
callback(success, file);
}).detach();
}
ThreadConfig SDFileManager::getThreadConfig(const char* operation) {
ThreadConfig config;
config.name = "sd_" + std::string(operation);
config.core_id = -1; // 不绑定核心
config.stack_size = 4096;
config.priority = 5;
config.inherit_cfg = false;
return config;
}
// 实现ls命令
std::string SDFileManager::lsCommand(const char* path, bool recursive, bool showDetails) {
std::lock_guard<std::mutex> lock(file_operation_mutex);
if (!is_initialized) {
ESP_LOGE("SDFileManager", "SD card not initialized");
return "Error: SD card not initialized";
}
std::string fullPath = path;
if (fullPath.empty() || fullPath == ".") {
fullPath = current_directory;
} else if (fullPath[0] != '/') {
// 相对路径
fullPath = current_directory + "/" + fullPath;
}
DIR* dir = opendir(fullPath.c_str());
if (dir == nullptr) {
return "Error: Cannot open directory " + fullPath;
}
std::stringstream result;
struct dirent* entry;
struct stat st;
result << "Directory listing for: " << fullPath << "\n";
result << "----------------------------------------\n";
while ((entry = readdir(dir)) != nullptr) {
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
continue;
}
std::string entryPath = fullPath + "/" + entry->d_name;
if (stat(entryPath.c_str(), &st) != 0) {
continue;
}
if (showDetails) {
result << getFileInfoString(entry->d_name, &st);
} else {
result << entry->d_name;
}
if (S_ISDIR(st.st_mode)) {
result << "/";
}
result << "\n";
// 递归列出子目录
if (recursive && S_ISDIR(st.st_mode)) {
std::string subDirResult = lsCommand(entryPath.c_str(), true, showDetails);
result << subDirResult;
}
}
closedir(dir);
return result.str();
}
// 实现cat命令
std::string SDFileManager::catCommand(const char* path, bool lineNumbers) {
std::lock_guard<std::mutex> lock(file_operation_mutex);
if (!is_initialized) {
ESP_LOGE("SDFileManager", "SD card not initialized");
return "Error: SD card not initialized";
}
std::string fullPath = path;
if (fullPath[0] != '/') {
// 相对路径
fullPath = current_directory + "/" + fullPath;
}
FILE* file = fopen(fullPath.c_str(), "r");
if (file == nullptr) {
return "Error: Cannot open file " + fullPath;
}
std::stringstream result;
char buffer[256];
int lineNumber = 1;
while (fgets(buffer, sizeof(buffer), file) != nullptr) {
if (lineNumbers) {
result << std::setw(4) << lineNumber << ": ";
}
result << buffer;
lineNumber++;
}
fclose(file);
return result.str();
}
// 实现pwd命令
std::string SDFileManager::pwdCommand() {
return current_directory;
}
// 实现cd命令
bool SDFileManager::cdCommand(const char* path) {
std::lock_guard<std::mutex> lock(file_operation_mutex);
if (!is_initialized) {
ESP_LOGE("SDFileManager", "SD card not initialized");
return false;
}
std::string newPath;
if (path[0] == '/') {
// 绝对路径
newPath = path;
} else if (strcmp(path, "..") == 0) {
// 上级目录
size_t pos = current_directory.find_last_of('/');
if (pos != std::string::npos && pos > 0) {
newPath = current_directory.substr(0, pos);
} else {
newPath = "/";
}
} else {
// 相对路径
newPath = current_directory + "/" + path;
}
// 检查路径是否存在且是目录
struct stat st;
if (stat(newPath.c_str(), &st) != 0 || !S_ISDIR(st.st_mode)) {
return false;
}
current_directory = newPath;
return true;
}
// 实现mkdir命令
bool SDFileManager::mkdirCommand(const char* path) {
std::lock_guard<std::mutex> lock(file_operation_mutex);
if (!is_initialized) {
ESP_LOGE("SDFileManager", "SD card not initialized");
return false;
}
std::string fullPath = path;
if (fullPath[0] != '/') {
// 相对路径
fullPath = current_directory + "/" + fullPath;
}
// 创建目录
int result = mkdir(fullPath.c_str(), 0777);
return result == 0;
}
// 实现rm命令
bool SDFileManager::rmCommand(const char* path, bool recursive) {
std::lock_guard<std::mutex> lock(file_operation_mutex);
if (!is_initialized) {
ESP_LOGE("SDFileManager", "SD card not initialized");
return false;
}
std::string fullPath = path;
if (fullPath[0] != '/') {
// 相对路径
fullPath = current_directory + "/" + fullPath;
}
struct stat st;
if (stat(fullPath.c_str(), &st) != 0) {
return false;
}
if (S_ISDIR(st.st_mode)) {
if (recursive) {
return removeDirectoryRecursive(fullPath.c_str());
} else {
// 非递归删除空目录
return rmdir(fullPath.c_str()) == 0;
}
} else {
// 删除文件
return remove(fullPath.c_str()) == 0;
}
}
// 实现stat命令
std::string SDFileManager::statCommand(const char* path) {
std::lock_guard<std::mutex> lock(file_operation_mutex);
if (!is_initialized) {
ESP_LOGE("SDFileManager", "SD card not initialized");
return "Error: SD card not initialized";
}
std::string fullPath = path;
if (fullPath[0] != '/') {
// 相对路径
fullPath = current_directory + "/" + fullPath;
}
struct stat st;
if (stat(fullPath.c_str(), &st) != 0) {
return "Error: Cannot get file status for " + fullPath;
}
return getFileInfoString(fullPath.c_str(), &st);
}
// 辅助方法:递归删除目录
bool SDFileManager::removeDirectoryRecursive(const char* path) {
DIR* dir = opendir(path);
if (dir == nullptr) {
return false;
}
struct dirent* entry;
while ((entry = readdir(dir)) != nullptr) {
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
continue;
}
std::string entryPath = std::string(path) + "/" + entry->d_name;
struct stat st;
if (stat(entryPath.c_str(), &st) != 0) {
continue;
}
if (S_ISDIR(st.st_mode)) {
if (!removeDirectoryRecursive(entryPath.c_str())) {
closedir(dir);
return false;
}
} else {
if (remove(entryPath.c_str()) != 0) {
closedir(dir);
return false;
}
}
}
closedir(dir);
return rmdir(path) == 0;
}
// 辅助方法:获取文件信息字符串
std::string SDFileManager::getFileInfoString(const char* path, const struct stat* st) {
std::stringstream info;
// 文件类型和权限
info << getFileTypeString(st->st_mode);
info << ((st->st_mode & S_IRUSR) ? "r" : "-");
info << ((st->st_mode & S_IWUSR) ? "w" : "-");
info << ((st->st_mode & S_IXUSR) ? "x" : "-");
info << ((st->st_mode & S_IRGRP) ? "r" : "-");
info << ((st->st_mode & S_IWGRP) ? "w" : "-");
info << ((st->st_mode & S_IXGRP) ? "x" : "-");
info << ((st->st_mode & S_IROTH) ? "r" : "-");
info << ((st->st_mode & S_IWOTH) ? "w" : "-");
info << ((st->st_mode & S_IXOTH) ? "x" : "-");
// 文件大小
info << " " << std::setw(10) << formatFileSize(st->st_size);
// 最后修改时间
struct tm* timeinfo = localtime(&st->st_mtime);
char timeBuf[20];
strftime(timeBuf, sizeof(timeBuf), "%Y-%m-%d %H:%M", timeinfo);
info << " " << timeBuf;
// 文件名
info << " " << path;
return info.str();
}
// 辅助方法:获取文件类型字符串
std::string SDFileManager::getFileTypeString(mode_t mode) {
if (S_ISREG(mode)) return "-"; // 普通文件
if (S_ISDIR(mode)) return "d"; // 目录
if (S_ISCHR(mode)) return "c"; // 字符设备
if (S_ISBLK(mode)) return "b"; // 块设备
if (S_ISFIFO(mode)) return "p"; // 管道
if (S_ISLNK(mode)) return "l"; // 符号链接
if (S_ISSOCK(mode)) return "s"; // 套接字
return "?"; // 未知类型
}
// 辅助方法:格式化文件大小
std::string SDFileManager::formatFileSize(size_t size) {
std::stringstream ss;
if (size < 1024) {
ss << size << " B";
} else if (size < 1024 * 1024) {
ss << std::fixed << std::setprecision(1) << (size / 1024.0) << " KB";
} else if (size < 1024 * 1024 * 1024) {
ss << std::fixed << std::setprecision(1) << (size / (1024.0 * 1024.0)) << " MB";
} else {
ss << std::fixed << std::setprecision(1) << (size / (1024.0 * 1024.0 * 1024.0)) << " GB";
}
return ss.str();
}
@@ -0,0 +1,193 @@
//
// Created by misaki on 2025/9/9.
//
/**
* 本模块用于管理SD卡文件
* 同样为单例类
* 支持异步读取和写入
*/
#pragma once
#include <mutex>
#include <functional>
#include <vector>
#include <string>
#include "ThreadManager.h"
#include "SD_MMC.h"
class SDFileManager {
public:
// 文件操作回调函数类型定义
using WriteCallback = std::function<void(bool success, const char* path)>;
using ReadCallback = std::function<void(bool success, const char* path, const std::string& content)>;
using ListCallback = std::function<void(bool success, const std::vector<std::string>& files)>;
using OpenCallback = std::function<void(bool success, FILE* file)>;
static SDFileManager* getInstance();
// try to init sdcard 对外提供的自动初始化,如果已经初始化了,只会打印一个日志,如果没有初始化,会自动初始化,这得益于单例模式的特性
// 与一般的初始化函数不同的是,该函数可以被重复调用,不会重复初始化sd卡
void tryInitSDCard();
// 同步文件操作
/**
* 同步写入文件
* @param path 文件路径
* @param data 数据
* @param len 数据长度
* @param type 文件类型
* @return 是否成功
*/
bool writeFileSync(const char* path, const char* data, const size_t len, const char* type);
/**
* 同步读取文件
* @param path 文件路径
* @return 文件内容
*/
std::string readFileSync(const char* path);
/**
* 同步读取文件(std::string)
* @param path 文件路径
* @return 文件内容
*/
std::string readFileSync(const std::string& path);
/**
* 同步列出目录下的文件
* @param directory 目录路径
* @param extension 文件扩展名
* @return 文件列表
*/
std::vector<std::string> listFilesSync(const char* directory, const char* extension = nullptr);
/**
* 同步打开文件
* @param path 文件路径
* @param mode 模式
* @return 文件指针
*/
FILE* openFileSync(const char* path, const char* mode = "rb");
/**
* 同步关闭文件
* @param file 文件指针
* @return 是否成功
*/
bool closeFileSync(FILE* file);
// 异步文件操作
/**
* 异步写入文件
* @param path 文件路径
* @param data 数据
* @param len 数据长度
* @param type 文件类型
* @param callback 回调函数
*/
void asyncWriteFile(const char* path, const char* data, const size_t len, const char* type, WriteCallback callback = nullptr);
/**
* 异步读取文件
* @param path 文件路径
* @param callback 回调函数
*/
void asyncReadFile(const char* path, ReadCallback callback = nullptr);
/**
* 异步列出目录下的文件
* @param directory 目录路径
* @param extension 文件扩展名
* @param callback 回调函数
*/
void asyncListFiles(const char* directory, const char* extension, ListCallback callback = nullptr);
/**
* 异步打开文件
* @param path 文件路径
* @param mode 模式
* @param callback 回调函数
*/
void asyncOpenFile(const char* path, const char* mode, OpenCallback callback = nullptr);
/**
* 类似Linux的ls命令,列出目录内容
* @param path 目录路径
* @param recursive 是否递归列出子目录
* @param showDetails 是否显示详细信息(大小、类型等)
* @return 格式化后的目录列表字符串
*/
std::string lsCommand(const char* path, bool recursive = false, bool showDetails = false);
/**
* 类似Linux的cat命令,显示文件内容
* @param path 文件路径
* @param lineNumbers 是否显示行号
* @return 文件内容字符串
*/
std::string catCommand(const char* path, bool lineNumbers = false);
/**
* 类似Linux的pwd命令,获取当前工作目录
* @return 当前工作目录
*/
std::string pwdCommand();
/**
* 类似Linux的cd命令,改变当前工作目录
* @param path 目标目录路径
* @return 是否成功
*/
bool cdCommand(const char* path);
/**
* 类似Linux的mkdir命令,创建目录
* @param path 目录路径
* @return 是否成功
*/
bool mkdirCommand(const char* path);
/**
* 类似Linux的rm命令,删除文件或目录
* @param path 文件或目录路径
* @param recursive 是否递归删除目录
* @return 是否成功
*/
bool rmCommand(const char* path, bool recursive = false);
/**
* 类似Linux的stat命令,获取文件/目录信息
* @param path 文件或目录路径
* @return 文件信息字符串
*/
std::string statCommand(const char* path);
// 获取SD卡和Flash信息
uint32_t getSDCardSize() const { return SDCard_Size; }
uint32_t getFlashSize() const { return Flash_Size; }
private:
explicit SDFileManager(); // 私有构造函数,确保单例
~SDFileManager();
SDFileManager(SDFileManager const&) = delete; // 禁止拷贝构造
SDFileManager& operator=(SDFileManager const&) = delete; // 禁止赋值构造
// 初始化SD卡
void init();
// 辅助方法
std::string getFileInfoString(const char* path, const struct stat* st);
std::string getFileTypeString(mode_t mode);
std::string formatFileSize(size_t size);
bool removeDirectoryRecursive(const char* path);
// 线程配置
ThreadConfig getThreadConfig(const char* operation);
private:
static SDFileManager* SDFileInstance; // 单例实例
static std::mutex instance_mutex; // 实例互斥锁
std::mutex file_operation_mutex; // 文件操作互斥锁
bool is_initialized; // SD卡初始化状态
std::string current_directory; // 当前工作目录
};
@@ -0,0 +1,9 @@
//
// Created by misaki on 2025/9/29.
//
#include "SimpleI2SForwarder.h"
// 静态成员初始化
LatestDataForwarder* LatestDataForwarder::instance = nullptr;
std::mutex LatestDataForwarder::instance_mutex;
@@ -0,0 +1,124 @@
//
// Created by misaki on 2025/9/29.
//
/**
* 音频数据转发器 单生产者单消费者模型
* 单纯只作为数据转发器,不进行任何处理
*/
#pragma once
#include <atomic>
#include <vector>
#include <mutex>
#include <condition_variable>
#include <deque>
class LatestDataForwarder {
private:
static LatestDataForwarder* instance;
static std::mutex instance_mutex;
std::deque<std::vector<int16_t>> queue_;
size_t max_size_;
std::mutex queue_mutex_;
std::condition_variable data_available_;
std::atomic<bool> is_running_;
public:
static LatestDataForwarder* getInstance() {
std::lock_guard<std::mutex> lock(instance_mutex);
if (!instance) {
instance = new LatestDataForwarder();
}
return instance;
}
void initialize(const size_t max_size = 500) {
std::lock_guard<std::mutex> lock(queue_mutex_);
max_size_ = max_size;
is_running_ = true;
}
// 生产者:总是注入最新数据,队列满时丢弃最旧数据
void injectData(const int16_t* data, const size_t length) {
if (!is_running_ || !data || length == 0) return;
std::lock_guard<std::mutex> lock(queue_mutex_);
// 创建新数据
std::vector<int16_t> new_data(data, data + length);
// 如果队列已满,移除最旧的数据
if (queue_.size() >= max_size_) {
queue_.pop_front();
}
// 添加最新数据
queue_.push_back(std::move(new_data));
data_available_.notify_one();
}
// 消费者:获取所有可用的最新数据
bool retrieveLatestData(std::vector<int16_t>& output, const int timeout_ms = 0) {
if (!is_running_) return false;
std::unique_lock<std::mutex> lock(queue_mutex_);
// 如果队列当中数据不足,则等待数据可用
if (queue_.empty()) {
if (timeout_ms <= 0) return false;
if (data_available_.wait_for(lock,
std::chrono::milliseconds(timeout_ms)) == std::cv_status::timeout) {
return false;
}
if (queue_.empty()) return false;
}
if (!is_running_) return false;
// 合并队列中的所有数据(最新的数据)
output.clear();
for (const auto& chunk : queue_) {
output.insert(output.end(), chunk.begin(), chunk.end());
}
// 清空队列,准备接收新的实时数据
queue_.clear();
return true;
}
// 消费者:只获取最新的N帧数据
bool retrieveRecentData(std::vector<int16_t>& output, const size_t recent_frames = 10, const int timeout_ms = 0) {
if (!is_running_) return false;
std::unique_lock<std::mutex> lock(queue_mutex_);
// 等待数据可用
if (queue_.empty()) {
if (timeout_ms <= 0) return false;
if (data_available_.wait_for(lock,
std::chrono::milliseconds(timeout_ms)) == std::cv_status::timeout) {
return false;
}
if (queue_.empty()) return false;
}
if (!is_running_) return false;
// 只取最新的recent_frames帧数据
output.clear();
const size_t start_index = queue_.size() > recent_frames ? queue_.size() - recent_frames : 0;
for (size_t i = start_index; i < queue_.size(); ++i) {
output.insert(output.end(), queue_[i].begin(), queue_[i].end());
}
// 移除已取出的旧数据,保留最新的数据
if (start_index > 0) {
std::deque<std::vector<int16_t>> new_queue(queue_.begin() + start_index, queue_.end());
queue_.swap(new_queue);
}
return true;
}
size_t getQueueSize() {
std::lock_guard<std::mutex> lock(queue_mutex_);
return queue_.size();
}
void clear() {
std::lock_guard<std::mutex> lock(queue_mutex_);
queue_.clear();
}
void stop() {
is_running_ = false;
data_available_.notify_all();
}
void start() {
is_running_ = true;
}
private:
LatestDataForwarder() : max_size_(500), is_running_(false) {}
};
@@ -0,0 +1,519 @@
//
// Created by misaki on 2025/9/15.
//
#include "SpeechRecognizer.h"
#include "VadSlidingWindow.h"
#include "SimpleI2SForwarder.h"
#include <esp_afe_sr_models.h>
#include <esp_mn_models.h>
#include <esp_wn_iface.h>
#include <esp_mn_speech_commands.h>
#include <model_path.h>
#include <driver/gpio.h>
#include <soc/soc_caps.h>
#include <esp_err.h>
#include <nvs_flash.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <atomic>
#include <cstring>
#include <utility>
// 初始化静态成员变量
SpeechRecognizer* SpeechRecognizer::instance = nullptr;
std::mutex SpeechRecognizer::instanceMutex;
SpeechRecognizer* SpeechRecognizer::getInstance() {
std::lock_guard<std::mutex> lock(instanceMutex);
if (!instance) {
instance = new SpeechRecognizer();
}
return instance;
}
SpeechRecognizer::SpeechRecognizer()
: initialized(false),
running(false),
enabled(true),
rx_handle(nullptr),
afe_handle(nullptr),
afe_data(nullptr),
models(nullptr),
multinet(nullptr),
model_data(nullptr),
vad_state_(AFE_VAD_SILENCE),
tasksRunning(false){}
SpeechRecognizer::~SpeechRecognizer() {
deinit();
}
bool SpeechRecognizer::init(const SpeechRecognizerConfig& config) {
if (initialized) {
ESP_LOGI("SpeechRecognizer", "Already initialized");
return true;
}
this->config = config;
// 初始化I2S
if (!initI2S()) {
ESP_LOGE("SpeechRecognizer", "I2S initialization failed");
return false;
}
// 初始化ESP-SR
if (!initESP_SR()) {
ESP_LOGE("SpeechRecognizer", "ESP-SR initialization failed");
return false;
}
initialized = true;
ESP_LOGI("SpeechRecognizer", "Initialization completed successfully");
return true;
}
bool SpeechRecognizer::init(const bool enable_vad, const vad_mode_t vad_mode, std::string model_path) {
if (initialized) {
ESP_LOGI("SpeechRecognizer", "Already initialized");
return true;
}
this->config.enable_vad = enable_vad;
this->config.vad_mode = vad_mode;
this->config.model_path = std::move(model_path);
// 初始化I2S
if (!initI2S()) {
ESP_LOGE("SpeechRecognizer", "I2S initialization failed");
return false;
}
// 初始化ESP-SR
if (!initESP_SR()) {
ESP_LOGE("SpeechRecognizer", "ESP-SR initialization failed");
return false;
}
initialized = true;
ESP_LOGI("SpeechRecognizer", "Initialization completed successfully");
return true;
}
void SpeechRecognizer::deinit() {
if (!initialized) {
return;
}
stop();
// 释放ESP-SR资源
if (model_data && multinet) {
multinet->destroy(model_data);
model_data = nullptr;
}
if (afe_data && afe_handle) {
afe_handle->destroy(afe_data);
afe_data = nullptr;
}
if (models) {
// 注意:esp_srmodel_init分配的资源可能需要特殊清理
// 根据ESP-SR文档进行适当清理
}
// 释放I2S资源
if (rx_handle) {
i2s_channel_disable(rx_handle); // 删除通道之前必须先禁用通道
i2s_del_channel(rx_handle); // 删除该句柄以释放通道资源
rx_handle = nullptr;
}
initialized = false;
ESP_LOGI("SpeechRecognizer", "Deinitialized");
}
bool SpeechRecognizer::initI2S() {
esp_err_t ret = ESP_OK;
// 通过辅助宏获取默认的通道配置, 它可以帮助指定 I2S 角色和端口 ID
constexpr i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_1, I2S_ROLE_MASTER);
// 分配新的 TX 通道并获取该通道的句柄
ret = i2s_new_channel(&chan_cfg, nullptr, &rx_handle);
if (ret != ESP_OK) {
ESP_LOGE("SpeechRecognizer", "Failed to create I2S channel: %s", esp_err_to_name(ret));
return false;
}
// 进行配置,通过宏生成声道配置和时钟配置, 这两个辅助宏在 'i2s_std.h' 中定义,只能用于 STD 模式
i2s_std_config_t std_cfg = {
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(16000), // 16KHz采样率
.slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_32BIT, I2S_SLOT_MODE_MONO), // 32位单声道
.gpio_cfg = {
.mclk = GPIO_NUM_NC, // 不使用MCLK
.bclk = config.bclk_pin, // BCLK引脚
.ws = config.ws_pin, // WS引脚
.dout = GPIO_NUM_NC, // 不使用DOUT
.din = config.din_pin, // DIN引脚
.invert_flags = { // 不使用倒置
.mclk_inv = false,
.bclk_inv = false,
.ws_inv = false,
},
},
};
std_cfg.slot_cfg.slot_mask = I2S_STD_SLOT_RIGHT; // 右声道
ret = i2s_channel_init_std_mode(rx_handle, &std_cfg); // 初始化STD标准模式
if (ret != ESP_OK) {
ESP_LOGE("SpeechRecognizer", "Failed to init I2S standard mode: %s", esp_err_to_name(ret));
return false;
}
ESP_LOGI("SpeechRecognizer", "I2S initialized successfully");
return true;
}
bool SpeechRecognizer::initESP_SR() {
// 获取AFE句柄
afe_handle = &ESP_AFE_SR_HANDLE;
if (!afe_handle) {
ESP_LOGE("SpeechRecognizer", "Failed to get AFE handle");
return false;
}
// 初始化模型
models = esp_srmodel_init(config.model_path.c_str());
if (!models) {
ESP_LOGE("SpeechRecognizer", "Failed to initialize models from path: %s", config.model_path.c_str());
return false;
}
// 配置AFE
afe_config_t afe_config = {
.aec_init = config.enable_aec,
.se_init = config.enable_se,
.vad_init = config.enable_vad,
.wakenet_init = false, // 禁用唤醒词
.voice_communication_init = false,
.voice_communication_agc_init = false,
.voice_communication_agc_gain = 15,
.vad_mode = config.vad_mode,
.wakenet_model_name = nullptr,
.wakenet_model_name_2 = nullptr,
.wakenet_mode = DET_MODE_2CH_90,
.afe_mode = SR_MODE_LOW_COST,
.afe_perferred_core = 0,
.afe_perferred_priority = 5,
.afe_ringbuf_size = 10,
.memory_alloc_mode = AFE_MEMORY_ALLOC_MORE_PSRAM,
.afe_linear_gain = 1.0,
.agc_mode = AFE_MN_PEAK_AGC_MODE_2,
.pcm_config = {
.total_ch_num = 2,
.mic_num = 1,
.ref_num = 1,
.sample_rate = 16000,
},
.debug_init = false,
.debug_hook = {{AFE_DEBUG_HOOK_MASE_TASK_IN, nullptr}, {AFE_DEBUG_HOOK_FETCH_TASK_IN, nullptr}},
};
afe_data = afe_handle->create_from_config(&afe_config);
if (!afe_data) {
ESP_LOGE("SpeechRecognizer", "Failed to create AFE data from config");
return false;
}
// 加载MultiNet模型 (采用esp-sr提供的宏来处理不同语种的模型的处理问题)
#if defined(CONFIG_SR_MN_CN_MULTINET5_RECOGNITION_QUANT8) || defined(CONFIG_SR_MN_CN_MULTINET6_QUANT) || defined(CONFIG_SR_MN_CN_MULTINET6_AC_QUANT)
char *mn_name = esp_srmodel_filter(models, ESP_MN_PREFIX, ESP_MN_CHINESE);
#else
char *mn_name = esp_srmodel_filter(models, ESP_MN_PREFIX, ESP_MN_ENGLISH);
#endif
if (!mn_name) {
ESP_LOGE("SpeechRecognizer", "No MultiNet model found");
return false;
}
multinet = esp_mn_handle_from_name(mn_name);
if (!multinet) {
ESP_LOGE("SpeechRecognizer", "Failed to get MultiNet handle");
return false;
}
model_data = multinet->create(mn_name, config.detection_timeout);
if (!model_data) {
ESP_LOGE("SpeechRecognizer", "Failed to create MultiNet model data");
return false;
}
ESP_LOGI("SpeechRecognizer", "ESP-SR initialized successfully with model: %s", mn_name);
return true;
}
bool SpeechRecognizer::start() {
if (!initialized) {
ESP_LOGE("SpeechRecognizer", "Not initialized");
return false;
}
if (running) {
ESP_LOGI("SpeechRecognizer", "Already running");
return true;
}
// 启用I2S通道
esp_err_t ret = i2s_channel_enable(rx_handle); // 在读取数据之前,先启动 RX 通道
if (ret != ESP_OK) {
ESP_LOGE("SpeechRecognizer", "Failed to enable I2S channel: %s", esp_err_to_name(ret));
return false;
}
// 启动任务
tasksRunning = true;
// 使用ThreadManager创建任务
feedThread = ThreadManager::createMemberThread(
config.feed_thread_config, this, &SpeechRecognizer::feedTask);
detectThread = ThreadManager::createMemberThread(
config.detect_thread_config, this, &SpeechRecognizer::detectTask);
running = true;
updateState("started");
ESP_LOGI("SpeechRecognizer", "Speech recognition started");
return true;
}
void SpeechRecognizer::stop() {
if (!running) {
return;
}
tasksRunning = false;
// 等待任务结束
if (feedThread.joinable()) {
feedThread.join();
}
if (detectThread.joinable()) {
detectThread.join();
}
// 禁用I2S通道
if (rx_handle) {
i2s_channel_disable(rx_handle);
}
running = false;
updateState("stopped");
ESP_LOGI("SpeechRecognizer", "Speech recognition stopped");
}
void SpeechRecognizer::feedTask() {
ThreadManager::printThreadInfo("Feed task started");
const int audio_chunksize = afe_handle->get_feed_chunksize(afe_data);
ESP_LOGW("SpeechRecognizer", "Feed task: audio_chunksize=%d", audio_chunksize);
int nch = afe_handle->get_channel_num(afe_data); // 获取I2S通道的声道数, 此处为1, 因此并没有被下面所使用
const size_t samp_len = audio_chunksize;
const size_t samp_len_bytes = samp_len * sizeof(int32_t); // 单声道32位
// 分配I2S缓冲区 放在PSRAM堆内存中
auto *i2s_buff = static_cast<int32_t *>(heap_caps_malloc((samp_len_bytes), MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM));
// 创建I2S转发副本 放在PSRAM堆内存中
auto *raw_pcm16 = static_cast<int16_t *>(heap_caps_malloc(samp_len * sizeof(int16_t), MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM));
if (!i2s_buff || !raw_pcm16) {
ESP_LOGE("SpeechRecognizer", "Failed to allocate memory for buffers");
if (i2s_buff) free(i2s_buff);
if (raw_pcm16) free(raw_pcm16);
return;
}
LatestDataForwarder::getInstance()->initialize(200); // 初始化转发队列,最多缓存 200 帧 I2S 数据
size_t bytes_read; // 读取的字节数
while (tasksRunning) {
// 读取I2S数据
const esp_err_t ret = i2s_channel_read(rx_handle, i2s_buff, samp_len_bytes, &bytes_read, portMAX_DELAY);
if (ret != ESP_OK) {
ESP_LOGE("SpeechRecognizer", "I2S read error: %s", esp_err_to_name(ret));
std::this_thread::sleep_for(std::chrono::milliseconds(10));
continue;
}
// 一次性处理所有数据转换
for (int i = 0; i < samp_len; ++i) {
// 转发数据转换:32位转16位(取高16位)
raw_pcm16[i] = static_cast<int16_t>(i2s_buff[i] >> 16);
// AFE数据转换:右移14位(在原始数据上操作)
i2s_buff[i] = i2s_buff[i] >> 14; // 32:8 是有效位,8:0 是低 8 位,全部为 0,AFE 输入是 16 位语音数据,29:13 位用于放大语音信号。
}
// 转发原始数据
LatestDataForwarder::getInstance()->injectData(raw_pcm16, samp_len);
// 喂数据给AFE
afe_handle->feed(afe_data, reinterpret_cast<int16_t *>(i2s_buff));
// 复制一份到滑动窗口人声检测区 (给网络) 注入音频帧到管理器
// AudioBufferManager::getInstance()->injectAudioFrame(
// reinterpret_cast<int16_t*>(i2s_buff),
// samp_len,
// this->vad_state_
// );
// std::this_thread::sleep_for(std::chrono::milliseconds(5)); // 休眠5ms
}
free(i2s_buff);
ESP_LOGI("SpeechRecognizer", "Feed task exited");
}
void SpeechRecognizer::detectTask() {
ThreadManager::printThreadInfo("Detect task started");
int afe_chunksize = afe_handle->get_fetch_chunksize(afe_data);
int mu_chunksize = multinet->get_samp_chunksize(model_data);
if (mu_chunksize != afe_chunksize) {
ESP_LOGE("SpeechRecognizer", "Chunk size mismatch: AFE=%d, MultiNet=%d", afe_chunksize, mu_chunksize);
return;
}
updateState("ready");
ESP_LOGI("SpeechRecognizer", "Ready for speech recognition");
while (tasksRunning) {
afe_fetch_result_t* res = afe_handle->fetch(afe_data);
// 从res中取出 vad 状态
this->vad_state_ = res->vad_state;
if (!res || res->ret_value == ESP_FAIL) {
ESP_LOGE("SpeechRecognizer", "AFE fetch error");
vTaskDelay(pdMS_TO_TICKS(10));
continue;
}
if (!enabled) {
vTaskDelay(pdMS_TO_TICKS(100));
continue;
}
// 使用MultiNet进行语音检测
esp_mn_state_t mn_state = multinet->detect(model_data, res->data);
if (mn_state == ESP_MN_STATE_DETECTING) {
// 检测中,不做处理
continue;
} else if (mn_state == ESP_MN_STATE_DETECTED) {
// 检测到语音命令
esp_mn_results_t *mn_result = multinet->get_results(model_data);
if (mn_result && mn_result->num > 0) {
SpeechRecognitionResult result;
result.command_id = mn_result->command_id[0];
result.phrase = mn_result->string;
result.probability = mn_result->prob[0];
result.phrase_id = mn_result->phrase_id[0];
handleRecognitionResult(result);
}
} else if (mn_state == ESP_MN_STATE_TIMEOUT) {
// 识别超时
updateState("timeout");
esp_mn_results_t *mn_result = multinet->get_results(model_data);
ESP_LOGI("SpeechRecognizer", "Detection timeout: %s",
mn_result && mn_result->string ? mn_result->string : "");
// 重置检测状态
multinet->clean(model_data);
updateState("ready");
}
}
ESP_LOGI("SpeechRecognizer", "Detect task exited");
}
bool SpeechRecognizer::addCommand(int command_id, const std::string& phrase) {
// 加强检查,确保所有相关指针都有效
if (!multinet) {
ESP_LOGE("SpeechRecognizer", "MultiNet handle is null");
return false;
}
if (!model_data) {
ESP_LOGE("SpeechRecognizer", "Model data is null");
return false;
}
esp_err_t ret = esp_mn_commands_add(command_id, phrase.c_str());
if (ret != ESP_OK) {
ESP_LOGE("SpeechRecognizer", "Failed to add command: %s", esp_err_to_name(ret));
return false;
}
// 更新命令列表
esp_mn_error_t *ret_mn = esp_mn_commands_update();
if (ret_mn) {
if (ret_mn->num >= 1) {
ESP_LOGE("SpeechRecognizer", "无法更新的指令数量: %d", ret_mn->num);
for (int i = 0; i < ret_mn->num; i++) {
ESP_LOGE("SpeechRecognizer", "无法更新的指令名称: %s,对应的id=%d", ret_mn->phrases[i]->string, ret_mn->phrases[i]->command_id);
}
return false;
}
else {
ESP_LOGE("SpeechRecognizer", "无法更新的指令短语数组为空");
}
}
// 打印缓存的指令
ESP_LOGI("SpeechRecognizer", "Added command: ID=%d, Phrase=%s", command_id, phrase.c_str());
return true;
}
bool SpeechRecognizer::addCommands(const std::vector<std::pair<int, std::string>>& commands) {
bool success = true;
for (const auto& cmd : commands) {
if (!addCommand(cmd.first, cmd.second)) {
success = false;
ESP_LOGE("main", "Failed to add some commands");
}
}
ESP_LOGI("SpeechRecognizer", "下面是当前已经缓存的指令:");
esp_mn_commands_print();
ESP_LOGI("SpeechRecognizer", "下面是当前已经应用的指令:");
esp_mn_active_commands_print();
return success;
}
void SpeechRecognizer::clearCommands() {
if (multinet && model_data) {
esp_mn_commands_clear();
ESP_LOGI("SpeechRecognizer", "All commands cleared");
}
}
void SpeechRecognizer::registerCommandCallback(SpeechCommandCallback callback) {
commandCallback = std::move(callback);
}
void SpeechRecognizer::registerStateCallback(SpeechStateCallback callback) {
stateCallback = std::move(callback);
}
void SpeechRecognizer::handleRecognitionResult(const SpeechRecognitionResult& result) {
// 保存到历史记录
{
std::lock_guard<std::mutex> lock(historyMutex);
lastResult = result;
history.push_back(result);
// 限制历史记录大小
if (history.size() > 100) {
history.erase(history.begin());
}
}
// 记录日志
ESP_LOGI("SpeechRecognizer", "Command detected: ID=%d, Phrase='%s', Probability=%.2f",
result.command_id, result.phrase.c_str(), result.probability);
updateState("command_detected");
// 调用回调函数
if (commandCallback) {
commandCallback(result.command_id, result.phrase, result.probability);
}
// 重置检测状态,准备下一次识别
if (multinet && model_data) {
multinet->clean(model_data);
}
updateState("ready");
}
void SpeechRecognizer::updateState(const std::string& state) {
{
std::lock_guard<std::mutex> lock(stateMutex);
currentState = state;
}
ESP_LOGI("SpeechRecognizer", "State changed: %s", state.c_str());
if (stateCallback) {
stateCallback(state);
}
}
SpeechRecognitionResult SpeechRecognizer::getLastResult() const {
std::lock_guard<std::mutex> lock(historyMutex);
return lastResult;
}
std::vector<SpeechRecognitionResult> SpeechRecognizer::getHistory() const {
std::lock_guard<std::mutex> lock(historyMutex);
return history;
}
void SpeechRecognizer::setEnabled(bool enabled) {
this->enabled = enabled;
updateState(enabled ? "enabled" : "disabled");
ESP_LOGI("SpeechRecognizer", "Speech recognition %s", enabled ? "enabled" : "disabled");
}
std::string SpeechRecognizer::getCurrentState() const {
std::lock_guard<std::mutex> lock(stateMutex);
return currentState;
}
afe_vad_state_t SpeechRecognizer::getVadState() const {
return vad_state_;
}
bool SpeechRecognizer::isRunning() const {
return running;
}
@@ -0,0 +1,175 @@
//
// Created by misaki on 2025/9/15.
//
#pragma once
#include <functional>
#include <vector>
#include <string>
#include <mutex>
#include <atomic>
#include "esp_afe_sr_iface.h"
#include "model_path.h"
#include "esp_mn_iface.h"
#include "driver/i2s_std.h"
#include "esp_log.h"
#include "ThreadManager.h"
// 前向声明
struct model_iface_data_t;
// 语音命令回调函数类型
using SpeechCommandCallback = std::function<void(int command_id, const std::string& phrase, float probability)>;
// 识别状态回调函数类型
using SpeechStateCallback = std::function<void(const std::string& state)>;
// 识别结果结构体
struct SpeechRecognitionResult {
int command_id;
std::string phrase;
float probability;
int phrase_id;
};
// 配置结构体
struct SpeechRecognizerConfig {
// I2S配置
gpio_num_t bclk_pin = GPIO_NUM_15;
gpio_num_t ws_pin = GPIO_NUM_2;
gpio_num_t din_pin = GPIO_NUM_39;
// 音频处理配置
bool enable_aec = false; // 回声消除
bool enable_se = false; // 降噪
bool enable_vad = false; // 语音活动检测
vad_mode_t vad_mode = VAD_MODE_0; // VAD灵敏度
// 模型路径
std::string model_path = "/sdcard/srmodels";
// 线程配置
ThreadConfig feed_thread_config = {"SR_Feed", 1, 4096, 3, false};
ThreadConfig detect_thread_config = {"SR_Detect", 0, 6 * 1024, 5, false};
// 识别超时时间(ms)
int detection_timeout = 6000;
};
class SpeechRecognizer {
public:
// 获取单例实例
static SpeechRecognizer* getInstance();
// 删除拷贝构造函数和赋值运算符
SpeechRecognizer(const SpeechRecognizer&) = delete;
SpeechRecognizer& operator=(const SpeechRecognizer&) = delete;
// 初始化语音识别系统
bool init(const SpeechRecognizerConfig& config = SpeechRecognizerConfig());
// 重载版本
bool init(const bool enable_vad, const vad_mode_t vad_mode, std::string model_path);
// 反初始化
void deinit();
// 添加自定义语音命令
bool addCommand(int command_id, const std::string& phrase);
// 批量添加语音命令
bool addCommands(const std::vector<std::pair<int, std::string>>& commands);
// 清除所有语音命令
void clearCommands();
// 开始语音识别
bool start();
// 停止语音识别
void stop();
// 是否正在运行
bool isRunning() const;
// 注册命令回调函数
void registerCommandCallback(SpeechCommandCallback callback);
// 注册状态回调函数
void registerStateCallback(SpeechStateCallback callback);
// 获取最后一次识别结果
SpeechRecognitionResult getLastResult() const;
// 获取识别历史
std::vector<SpeechRecognitionResult> getHistory() const;
// 设置VAD灵敏度
// void setVadSensitivity(vad_mode_t mode);
// 启用/禁用语音识别
void setEnabled(bool enabled);
// 获取当前识别状态
std::string getCurrentState() const;
// 获取当前 VAD 识别状态(噪音、静音 or 人声) 需要开启 VAD
afe_vad_state_t getVadState() const;
private:
SpeechRecognizer();
~SpeechRecognizer();
// I2S初始化
bool initI2S();
// ESP-SR初始化
bool initESP_SR();
// 喂数据任务(音频采集)
void feedTask();
// 检测任务(语音识别)
void detectTask();
// 处理识别结果
void handleRecognitionResult(const SpeechRecognitionResult& result);
// 更新识别状态
void updateState(const std::string& state);
// 静态成员函数用于C接口兼容
static void feedTaskWrapper(void* arg);
static void detectTaskWrapper(void* arg);
private:
static SpeechRecognizer* instance;
static std::mutex instanceMutex;
SpeechRecognizerConfig config;
bool initialized;
bool running;
bool enabled;
// I2S相关
i2s_chan_handle_t rx_handle;
// ESP-SR相关
const esp_afe_sr_iface_t* afe_handle;
esp_afe_sr_data_t* afe_data;
srmodel_list_t* models;
esp_mn_iface_t* multinet;
model_iface_data_t* model_data; /// 模型数据
afe_vad_state_t vad_state_; /// 语音活动检测状态
// 命令回调
SpeechCommandCallback commandCallback;
SpeechStateCallback stateCallback;
// 识别结果
SpeechRecognitionResult lastResult;
std::vector<SpeechRecognitionResult> history;
mutable std::mutex historyMutex;
// 任务句柄
std::thread feedThread;
std::thread detectThread;
std::atomic<bool> tasksRunning;
// 当前状态
std::string currentState;
mutable std::mutex stateMutex;
};
@@ -0,0 +1,9 @@
//
// Created by misaki on 2025/9/29.
//
#include "VadSlidingWindow.h"
// 静态成员初始化
AudioBufferManager* AudioBufferManager::instance = nullptr;
std::mutex AudioBufferManager::instance_mutex;
@@ -0,0 +1,377 @@
//
// Created by misaki on 2025/9/29.
//
/**
* 本模块用于处理音频数据,使用滑动窗口和VAD算法进行人声语音段过滤
* 依赖于VAD人声检测数据,并通过滑动窗口管理区间数据,以精确过滤出人声音频数据
* @author Misaki
* @date 2025/9/29
*/
#pragma once
#include <vector>
#include <queue>
#include <memory>
#include <mutex>
#include <condition_variable>
#include <atomic>
// PCM帧数据结构
struct PCMFrame {
std::vector<int16_t> audio_data; // PCM音频数据
size_t data_length; // 数据长度(样本数)
bool vad_status; // VAD检测状态
uint64_t timestamp; // 时间戳(可选,用于调试)
PCMFrame() : data_length(0), vad_status(false), timestamp(0) {}
PCMFrame(const int16_t* data, const size_t len, const bool vad, const uint64_t ts = 0)
: data_length(len), vad_status(vad), timestamp(ts) {
audio_data.assign(data, data + len);
}
};
// 滑动窗口结构
struct SlidingWindow {
std::vector<PCMFrame> frames; // 窗口内的所有帧
size_t total_frames; // 总帧数
size_t voice_frames; // 人声帧数
double voice_ratio; // 人声占比
bool is_active; // 窗口是否处于活跃状态
SlidingWindow() : total_frames(0), voice_frames(0), voice_ratio(0.0), is_active(false) {}
// 计算人声占比
void calculateVoiceRatio() {
if (total_frames > 0) {
voice_ratio = static_cast<double>(voice_frames) / total_frames;
} else {
voice_ratio = 0.0;
}
}
};
class AudioBufferManager {
private:
// 单例实例
static AudioBufferManager* instance;
static std::mutex instance_mutex;
// 配置参数
struct Config {
size_t max_window_frames; // 单个窗口最大帧数
size_t max_queue_size; // 队列最大长度
size_t silence_threshold_frames; // 静音阈值帧数
double voice_ratio_threshold; // 人声占比阈值
size_t pre_voice_frames; // 人声开始前预保留帧数
size_t post_voice_frames; // 人声结束后保留帧数
} config;
// 内部状态
std::unique_ptr<SlidingWindow> current_window;
std::queue<std::unique_ptr<SlidingWindow>> completed_windows;
std::vector<PCMFrame> pre_voice_buffer; // 人声开始前的预缓存
// 状态跟踪
std::atomic<bool> in_voice_segment;
size_t consecutive_silence_frames;
size_t current_frame_count;
// 线程同步
std::mutex data_mutex;
std::condition_variable data_condition;
// 内存使用跟踪
size_t estimated_memory_usage;
const size_t MAX_MEMORY_BYTES = 512 * 1024; // 512KB
private:
AudioBufferManager() {
initializeDefaultConfig();
resetState();
}
void initializeDefaultConfig() {
// 默认配置:基于16kHz采样率,每帧20ms(320样本)
config.max_window_frames = 500; // 10秒音频(500 * 20ms
config.max_queue_size = 8; // 队列最多8个窗口
config.silence_threshold_frames = 15; // 300ms静音判定结束(15 * 20ms
config.voice_ratio_threshold = 0.3; // 30%人声占比阈值
config.pre_voice_frames = 5; // 人声开始前保留100ms
config.post_voice_frames = 10; // 人声结束后保留200ms
estimated_memory_usage = 0;
}
void resetState() {
current_window = std::make_unique<SlidingWindow>();
in_voice_segment = false;
consecutive_silence_frames = 0;
current_frame_count = 0;
pre_voice_buffer.clear();
pre_voice_buffer.reserve(config.pre_voice_frames);
}
// 估算单个帧的内存使用
size_t estimateFrameMemory(const PCMFrame& frame) const {
return sizeof(PCMFrame) + (frame.audio_data.capacity() * sizeof(int16_t));
}
// 估算窗口内存使用
size_t estimateWindowMemory(const SlidingWindow& window) const {
size_t memory = sizeof(SlidingWindow);
for (const auto& frame : window.frames) {
memory += estimateFrameMemory(frame);
}
return memory;
}
// 检查内存限制
bool checkMemoryConstraints() const {
return estimated_memory_usage < MAX_MEMORY_BYTES;
}
public:
// 删除拷贝构造函数和赋值运算符
AudioBufferManager(const AudioBufferManager&) = delete;
AudioBufferManager& operator=(const AudioBufferManager&) = delete;
// 获取单例实例
static AudioBufferManager* getInstance() {
std::lock_guard<std::mutex> lock(instance_mutex);
if (!instance) {
instance = new AudioBufferManager();
}
return instance;
}
/**
* @brief 配置管理器参数
* @param max_window_frames 单个窗口最大帧数
* @param max_queue_size 队列最大长度
* @param silence_threshold 静音阈值帧数
* @param voice_ratio_threshold 人声占比阈值
* @param pre_voice_frames 人声开始前预保留帧数
* @param post_voice_frames 人声结束后保留帧数
*/
void configure(const size_t max_window_frames = 500,
const size_t max_queue_size = 8,
const size_t silence_threshold = 15,
const double voice_ratio_threshold = 0.2,
const size_t pre_voice_frames = 5,
const size_t post_voice_frames = 10) {
std::lock_guard<std::mutex> lock(data_mutex);
config.max_window_frames = max_window_frames;
config.max_queue_size = max_queue_size;
config.silence_threshold_frames = silence_threshold;
config.voice_ratio_threshold = voice_ratio_threshold;
config.pre_voice_frames = pre_voice_frames;
config.post_voice_frames = post_voice_frames;
// 重新初始化状态
resetState();
}
/**
* @brief 注入新的音频帧数据
* @param audio_data PCM音频数据指针
* @param data_length 数据长度(样本数)
* @param vad_status 当前帧的VAD状态
*/
void injectAudioFrame(const int16_t* audio_data, size_t data_length, bool vad_status) {
std::lock_guard<std::mutex> lock(data_mutex);
// 创建新帧
PCMFrame new_frame(audio_data, data_length, vad_status, current_frame_count++);
size_t frame_memory = estimateFrameMemory(new_frame);
// 检查内存限制
if (!checkMemoryConstraints()) {
// 内存不足,采取清理策略
if (!completed_windows.empty()) {
auto old_window = std::move(completed_windows.front());
completed_windows.pop();
estimated_memory_usage -= estimateWindowMemory(*old_window);
}
}
// 更新预缓存
updatePreVoiceBuffer(new_frame);
// 状态机处理
if (!in_voice_segment) {
handleNonVoiceState(new_frame, frame_memory);
} else {
handleVoiceState(new_frame, frame_memory);
}
estimated_memory_usage += frame_memory;
}
/**
* @brief 获取可用的音频窗口数据
* @param timeout_ms 超时时间(毫秒)
* @return 滑动窗口指针,如果没有可用数据则返回nullptr
*/
std::unique_ptr<SlidingWindow> getAudioWindow(int timeout_ms = 0) {
std::unique_lock<std::mutex> lock(data_mutex);
if (completed_windows.empty()) {
if (timeout_ms <= 0) {
return nullptr;
}
// 等待数据可用
if (data_condition.wait_for(lock,
std::chrono::milliseconds(timeout_ms)) == std::cv_status::timeout) {
return nullptr;
}
if (completed_windows.empty()) {
return nullptr;
}
}
auto window = std::move(completed_windows.front());
completed_windows.pop();
estimated_memory_usage -= estimateWindowMemory(*window);
return window;
}
/**
* @brief 检查是否有可用的音频数据
*/
bool hasAvailableData() {
std::lock_guard<std::mutex> lock(data_mutex);
return !completed_windows.empty();
}
/**
* @brief 获取当前队列大小
*/
size_t getQueueSize() {
std::lock_guard<std::mutex> lock(data_mutex);
return completed_windows.size();
}
/**
* @brief 获取估计的内存使用量
*/
size_t getEstimatedMemoryUsage() const {
return estimated_memory_usage;
}
/**
* @brief 强制结束当前语音段(如果有)
*/
void forceEndCurrentSegment() {
std::lock_guard<std::mutex> lock(data_mutex);
if (in_voice_segment && current_window->total_frames > 0) {
finalizeCurrentWindow();
}
}
/**
* @brief 清空所有缓存数据
*/
void clearAllData() {
std::lock_guard<std::mutex> lock(data_mutex);
resetState();
std::queue<std::unique_ptr<SlidingWindow>> empty_queue;
std::swap(completed_windows, empty_queue);
estimated_memory_usage = 0;
}
private:
// 更新人声开始前的预缓存
void updatePreVoiceBuffer(const PCMFrame& frame) {
pre_voice_buffer.push_back(frame);
// 保持预缓存大小不超过配置值
if (pre_voice_buffer.size() > config.pre_voice_frames) {
pre_voice_buffer.erase(pre_voice_buffer.begin());
}
}
// 处理非人声状态
void handleNonVoiceState(const PCMFrame& frame, size_t frame_memory) {
if (frame.vad_status) {
// 检测到人声开始
in_voice_segment = true;
consecutive_silence_frames = 0;
// 将预缓存数据加入当前窗口
for (const auto& pre_frame : pre_voice_buffer) {
current_window->frames.push_back(pre_frame);
current_window->total_frames++;
if (pre_frame.vad_status) {
current_window->voice_frames++;
}
}
// 添加当前帧
addFrameToCurrentWindow(frame);
}
// 非人声状态下,不进行其他处理
}
// 处理人声状态
void handleVoiceState(const PCMFrame& frame, size_t frame_memory) {
if (frame.vad_status) {
// 仍然是人声,重置静音计数
consecutive_silence_frames = 0;
} else {
// 静音帧
consecutive_silence_frames++;
}
// 添加当前帧到窗口
addFrameToCurrentWindow(frame);
// 检查是否需要结束当前语音段
if (consecutive_silence_frames >= config.silence_threshold_frames ||
current_window->frames.size() >= config.max_window_frames) {
// 添加人声结束后的保留帧
addPostVoiceFrames();
// 完成当前窗口
finalizeCurrentWindow();
}
}
// 添加帧到当前窗口
void addFrameToCurrentWindow(const PCMFrame& frame) {
current_window->frames.push_back(frame);
current_window->total_frames++;
if (frame.vad_status) {
current_window->voice_frames++;
}
current_window->calculateVoiceRatio();
}
// 添加人声结束后的保留帧
void addPostVoiceFrames() {
// 这个函数在实际实现中需要缓存后续的帧
// 简化实现:在当前设计中,我们依赖静音阈值来自然包含结束后的帧
}
// 完成当前窗口的处理
void finalizeCurrentWindow() {
// 计算最终的人声占比
current_window->calculateVoiceRatio();
// 检查人声占比是否达到阈值
if (current_window->voice_ratio >= config.voice_ratio_threshold) {
// 窗口有效,加入队列
if (completed_windows.size() >= config.max_queue_size) {
// 队列已满,移除最旧的数据
auto old_window = std::move(completed_windows.front());
completed_windows.pop();
estimated_memory_usage -= estimateWindowMemory(*old_window);
}
completed_windows.push(std::move(current_window));
data_condition.notify_one(); // 通知等待的消费者
}
// 重置状态,开始新的窗口
resetState();
}
};
@@ -0,0 +1,9 @@
//
// Created by misaki on 2025/9/22.
//
#include "sys_conf_singleton.h"
// 静态成员变量定义
SysConfJson* SysConfJson::instance = nullptr;
std::mutex SysConfJson::instanceMutex;
@@ -0,0 +1,141 @@
//
// Created by misaki on 2025/9/22.
//
#pragma once
#include "cpp_json.h"
#include <esp_vfs_fat.h>
#include <wear_levelling.h>
#include <esp_log.h>
#include <dirent.h>
#include <unistd.h>
#include <string>
#include <vector>
#include <fstream>
#include <mutex>
class SysConfJson {
public:
static SysConfJson* getInstance() {
std::lock_guard<std::mutex> lock(instanceMutex);
if (instance == nullptr) {
instance = new SysConfJson();
return instance;
}
return instance;
}
/* 把 sn 持久化到 <mount>/sn.json,成功返回 true */
bool saveSN(const std::string& sn)
{
cppjson::Json doc = cppjson::Json::object();
doc.set("sn", cppjson::Json(sn)); // {"sn":"xxxxxx"}
return write("sn", doc); // 实际文件 = <mount>/sn.json
}
/* 读取 sn,如果文件/字段不存在返回空串(绝不抛异常) */
std::string loadSN()
{
cppjson::Json j = read("sn"); // 读 <mount>/sn.json
if (!j.isObject()) return ""; // 文件不存在或解析失败
cppjson::Json snNode = j["sn"];
return snNode.isString() ? snNode.asString() : "";
}
/* 直接覆盖写,无 tmp */
bool write(const char* key, const cppjson::Json& j) {
const std::string path = buildPath(key);
const std::string txt = j.dump();
return writeRaw(path, txt);
}
/* 读 */
cppjson::Json read(const char* key) const {
std::string content;
if (!readRaw(key, content)) return {};
try {
return cppjson::Json::parse(content);
} catch (...) {
ESP_LOGE(TAG, "parse %s fail", key);
return {};
}
}
/* 删文件 */
bool remove(const char* key) const {
const std::string path = buildPath(key);
return ::unlink(path.c_str()) == 0;
}
/* 列文件 */
[[nodiscard]] std::vector<std::string> ls() const {
std::vector<std::string> names;
DIR* dir = ::opendir(kMount);
if (!dir) return names;
struct dirent* entry;
while ((entry = ::readdir(dir)) != nullptr) {
if (entry->d_name[0] == '.') continue;
names.emplace_back(entry->d_name);
}
::closedir(dir);
return names;
}
/* 格式化 */
[[nodiscard]] bool format() const {
ESP_LOGW(TAG, "format <%s>", kSysConfPartName);
return esp_vfs_fat_spiflash_format_rw_wl(kMount, kSysConfPartName) == ESP_OK;
}
/* 禁止拷贝 */
SysConfJson(const SysConfJson&) = delete;
SysConfJson& operator=(const SysConfJson&) = delete;
private:
SysConfJson() { mount(); }
~SysConfJson() { unmount(); }
void mount() {
esp_vfs_fat_mount_config_t cfg = {
.format_if_mount_failed = true, // 如果 FAT 分区无法挂载,并且此参数为 true,则创建分区表并格式化文件系统。
.max_files = 8,
.allocation_unit_size = CONFIG_WL_SECTOR_SIZE,
};
esp_err_t err = esp_vfs_fat_spiflash_mount_rw_wl(kMount, kSysConfPartName, &cfg, &wl_);
if (err != ESP_OK) {
ESP_LOGE(TAG, "mount fail <%s>", esp_err_to_name(err));
return;
}
mounted_ = true;
ESP_LOGI(TAG, "FAT mounted at %s", kMount);
}
void unmount() {
if (!mounted_) return;
esp_vfs_fat_spiflash_unmount_rw_wl(kMount, wl_);
wl_ = WL_INVALID_HANDLE;
mounted_ = false;
}
bool writeRaw(const std::string& path, const std::string& txt) {
FILE* f = std::fopen(path.c_str(), "wb");
if (!f) return false;
bool ok = std::fwrite(txt.data(), 1, txt.size(), f) == txt.size();
std::fclose(f);
return ok;
}
bool readRaw(const char* key, std::string& out) const {
const std::string path = buildPath(key);
FILE* f = std::fopen(path.c_str(), "rb");
if (!f) return false;
std::fseek(f, 0, SEEK_END);
size_t sz = std::ftell(f);
std::fseek(f, 0, SEEK_SET);
out.resize(sz);
bool ok = std::fread(&out[0], 1, sz, f) == sz;
std::fclose(f);
return ok;
}
std::string buildPath(const char* key) const {
return std::string(kMount) + "/" + key;
}
private:
constexpr static const char* TAG = "SysConfJson";
constexpr static const char* kMount = "/sys_conf";
static constexpr char kSysConfPartName[] = "sys_conf";
wl_handle_t wl_ = WL_INVALID_HANDLE;
bool mounted_ = false;
static constexpr const char* kFileName = "sys_conf.json";
static SysConfJson* instance; // 单例实例
static std::mutex instanceMutex; // 单例锁
};
@@ -0,0 +1,4 @@
//
// Created by misaki on 2025/9/4.
//
#include "ThreadManager.h"
@@ -0,0 +1,177 @@
//
// Created by misaki on 2025/9/4.
//
#pragma once
#include <thread>
#include <functional>
#include <memory>
#include <string>
#include <esp_pthread.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <esp_log.h>
#include <sstream>
/**
* @brief 线程配置结构体
*
* 用于配置线程的各种属性,如名称、核心绑定、栈大小和优先级
*/
struct ThreadConfig {
std::string name = "thread"; // 线程名称
int core_id = -1; // 绑定核心ID (-1表示不绑定)
int stack_size = 3072; // 栈大小 (字节)
int priority = 5; // 优先级
bool inherit_cfg = false; // 是否允许子线程继承此配置
};
/**
* @brief 线程管理类
*
* 封装了ESP32上的线程创建和管理功能,支持普通函数、类成员函数和单例类成员函数
*/
class ThreadManager {
public:
/**
* @brief 创建并启动一个线程
*
* @tparam Function 函数类型
* @tparam Args 参数类型
* @param config 线程配置
* @param func 要执行的函数
* @param args 函数参数
* @return std::thread 创建的线程对象
*/
template<typename Function, typename... Args>
static std::thread createThread(const ThreadConfig& config, Function&& func, Args&&... args) {
// 创建ESP32线程配置
auto esp_cfg = create_esp_config(config);
// 设置线程配置
esp_pthread_set_cfg(&esp_cfg);
// 创建并启动线程
return std::thread(std::forward<Function>(func), std::forward<Args>(args)...);
}
/**
* @brief 创建并启动一个执行类成员函数的线程
*
* @tparam T 类类型
* @tparam Method 成员函数类型
* @tparam Args 参数类型
* @param config 线程配置
* @param obj 类对象指针
* @param method 成员函数指针
* @param args 函数参数
* @return std::thread 创建的线程对象
*/
template<typename T, typename Method, typename... Args>
static std::thread createMemberThread(const ThreadConfig& config, T* obj, Method&& method, Args&&... args) {
// 使用lambda表达式包装成员函数调用
auto task = [obj, method, args...]() {
(obj->*method)(args...);
};
// 创建ESP32线程配置
auto esp_cfg = create_esp_config(config);
// 设置线程配置
esp_pthread_set_cfg(&esp_cfg);
// 创建并启动线程
return std::thread(task);
}
/**
* @brief 创建并启动一个执行单例类成员函数的线程
*
* @tparam T 单例类类型
* @tparam Method 成员函数类型
* @tparam Args 参数类型
* @param config 线程配置
* @param method 成员函数指针
* @param args 函数参数
* @return std::thread 创建的线程对象
*/
template<typename T, typename Method, typename... Args>
static std::thread createSingletonThread(const ThreadConfig& config, Method&& method, Args&&... args) {
// 获取单例实例
T* instance = T::getInstance(); // 获取单例实例,注意这里就要求每个单例都要实现这个同名的静态函数
// 使用lambda表达式包装成员函数调用
auto task = [instance, method, args...]() {
(instance->*method)(args...);
};
// 创建ESP32线程配置
auto esp_cfg = create_esp_config(config);
// 设置线程配置
esp_pthread_set_cfg(&esp_cfg);
// 创建并启动线程
return std::thread(task);
}
/**
* @brief 打印当前线程信息
*
* @param extra 额外信息
*/
static void printThreadInfo(const char *extra = nullptr) {
std::stringstream ss;
if (extra) {
ss << extra;
}
ss
<< ", 任务名: " << pcTaskGetName(nullptr)
<< "核心 id: " << xPortGetCoreID()
<< ", 优先级: " << uxTaskPriorityGet(nullptr)
<< ", 栈剩余空间: " << uxTaskGetStackHighWaterMark(nullptr) << " bytes.";
ESP_LOGI(pcTaskGetName(nullptr), "%s", ss.str().c_str());
}
/**
* @brief 打印系统内存信息
*/
static void print_sys_memory()
{
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()
{
char stats_buf[2 * 1024]; // 存储任务列表和绑核信息,占用 2KB 栈空间,调用时需注意
/* 任务列表 + 绑核信息 */
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线程配置
*
* @param config 线程配置
* @return esp_pthread_cfg_t ESP32线程配置
*/
static esp_pthread_cfg_t create_esp_config(const ThreadConfig& config) {
auto cfg = esp_pthread_get_default_config();
cfg.thread_name = config.name.c_str();
cfg.pin_to_core = config.core_id;
cfg.stack_size = config.stack_size;
cfg.prio = config.priority;
cfg.inherit_cfg = config.inherit_cfg;
return cfg;
}
};
+103 -1
View File
@@ -1,5 +1,107 @@
// //
// Created by misaki on 2025/9/2. // Created by misaki on 2025/9/2.
// //
#include "ToolsClass.h" #include "ToolsClass.h"
#include <esp_mac.h>
#include <esp_efuse.h>
#include <esp_log.h>
#include <array>
#include <esp_efuse_table.h>
#include <sstream>
#include <iomanip>
#include <algorithm>
#include "BAT_Driver.h"
#include "PWR_Key.h"
static const char *TAG = "ToolsClass";
ToolsClass* ToolsClass::instance;
std::mutex ToolsClass::instance_mutex;
ToolsClass *ToolsClass::getInstance() {
std::lock_guard<std::mutex> lock(instance_mutex);
if (!instance) {
instance = new ToolsClass();
}
return instance;
}
ToolsClass::ToolsClass() {
ESP_LOGI(TAG, "ToolsClass init");
PWR_Init();
BAT_Init(); // 电池驱动初始化
}
float ToolsClass::getBatteryVolts() {
return BAT_Get_Volts();
}
int32_t ToolsClass::getBatteryPer() {
return BAT_Get_Percent();
}
// 静态:拼出完整路径
std::string ToolsClass::makeFullPath(const std::string& filename)
{
return "/sdcard/" + filename;
}
std::string ToolsClass::getChipSerialNumber() {
// 获取芯片序列号(17 字节长的 "Chip ID" (乐鑫官方唯一序列号))
std::array<uint8_t, 17> id{};
/* 下面这个宏在 ESP32-S3 上展开为 17 byte 序列号字段,直接可用 */
esp_err_t err = esp_efuse_read_field_blob(ESP_EFUSE_OPTIONAL_UNIQUE_ID, id.data(), id.size() * 8);
if (err != ESP_OK) {
ESP_LOGE(TAG, "read Chip ID failed: %s", esp_err_to_name(err));
return "";
}
std::stringstream ss;
for (uint8_t v : id) ss << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(v);
return ss.str();
}
std::string ToolsClass::getChipMAC() {
std::array<uint8_t, 6> mac{};
esp_efuse_mac_get_default(mac.data()); // 读出厂 MAC
std::stringstream ss;
for (size_t i = 0; i < mac.size(); ++i) {
ss << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(mac[i]);
if (i + 1 != mac.size()) ss << ':';
}
return ss.str();
}
// 1. 去掉 mac 中的冒号
// 2. 拼接 chipID
// 3. 取前 24 字符并转大写
std::string ToolsClass::GenerateSN(const std::string& mac, const std::string& chipID)
{
// 1. 去掉 MAC 里的冒号
std::string plainMac;
plainMac.reserve(mac.size());
for (char ch : mac)
if (ch != ':') plainMac.push_back(ch);
// 2. 拼接
std::string raw = plainMac + chipID;
// 3. 取前 24 位并转大写
if (raw.size() > 24) raw.resize(24);
std::transform(raw.begin(), raw.end(), raw.begin(),
[](unsigned char c){ return std::toupper(c); });
return raw;
}
std::string ToolsClass::device_version = "Beta0.3";
std::string ToolsClass::getDeviceVersion() {
return device_version;
}
+69 -6
View File
@@ -2,15 +2,78 @@
// Created by misaki on 2025/9/2. // Created by misaki on 2025/9/2.
// //
#pragma once #pragma once
#include <string>
#include <mutex>
/**
* 本模块提供各种杂项工具类,基本都来源于对底层驱动的封装
*
*
*/
class ToolsClass {
public:
static ToolsClass* getInstance();
#ifdef __cplusplus private:
extern "C" { ToolsClass();
#endif ~ToolsClass() = default;
private:
static ToolsClass* instance;
static std::mutex instance_mutex;
public:
/**
* 获取当前时间
* @return 当前时间
*/
static std::string getCurrentTime();
/**
* 获取esp32s3的芯片序列号
* @return 芯片序列号
*/
static std::string getChipSerialNumber();
/**
* 获取esp32s3的MAC地址
* @return MAC地址
*/
static std::string getChipMAC();
/**
* 生成SN码
* @param mac mac地址
* @param chipID 芯片ID
* @return 生成的SN码
*/
static std::string GenerateSN(const std::string& mac, const std::string& chipID);
/**
* 获取电池电压
* @return 电池电压
*/
float getBatteryVolts();
/**
* 获取电池电量
* @return 电池电量百分比(0 ~ 100)
*/
int32_t getBatteryPer();
/**
* 拼接文件路径(for sd card)
* @param filename 文件名
* @return 拼接后的文件路径
*/
static std::string makeFullPath(const std::string& filename);
/**
* 获取设备版本
* @return 设备版本
*/
static std::string getDeviceVersion();
static std::string device_version; // 设备版本
};
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,72 @@
//
// Created by misaki on 2025/9/4.
//
#include "WifiConnectors.h"
#include "Wireless.h"
WifiConnectors* WifiConnectors::WifiConnectorsInstance = nullptr; /// 单例实例
std::mutex WifiConnectors::m_mutex;
WifiConnectors *WifiConnectors::getInstance() {
// 双检锁(DCLP),C++11 起 atomic+mutex 组合保证线程安全
WifiConnectors* tmp = WifiConnectorsInstance;
if (tmp == nullptr) {
std::lock_guard<std::mutex> lock(m_mutex);
tmp = WifiConnectorsInstance;
if (tmp == nullptr) {
tmp = new WifiConnectors();
WifiConnectorsInstance = tmp;
}
}
return tmp;
}
WifiConnectors::WifiConnectors() {
// 在此处调用底层的Wifi初始化驱动,这样Wifi就只会被初始化一次
Wireless_Init();
WIFI_Init(nullptr);
ESP_LOGI("WifiConnectors", "WifiConnectors getInstance");// 在此处调用底层的Wifi初始化驱动,这样Wifi就只会被初始化一次
}
// 析构函数
WifiConnectors::~WifiConnectors() {
}
// 连接Wifi
bool WifiConnectors::connectWifi(const std::string &ssid, const std::string &password, uint8_t max_retry) {
ESP_LOGI("WifiConnectors", "WifiConnectors connectWifi");
ESP_LOGI("WifiConnectors", "Now Try to connect %s", ssid.c_str());
if (WiFi_AutoConnect(ssid.c_str(), password.c_str(), max_retry)) {
ESP_LOGI("WifiConnectors", "WifiConnectors 连接WIFI %s 成功!!!", ssid.c_str());
this->isConnected = true; // 设置已连接
return true;
}
ESP_LOGI("WifiConnectors", "WifiConnectors 连接Wifi失败!!!");
return false;
}
// 断开Wifi
bool WifiConnectors::disconnectWifi() {
if (WiFi_Disconnect() == ESP_OK) {
ESP_LOGI("WifiConnectors", "WifiConnectors disconnectWifi");
this->isConnected = false; // 设置未连接
return true;
}
return false;
}
bool WifiConnectors::isWifiConnect() {
return this->isConnected;
}
void WifiConnectors::log() {
ESP_LOGI("WifiConnectors", "WifiConnectors log");
}
@@ -0,0 +1,50 @@
//
// Created by misaki on 2025/9/4.
//
#pragma once
#include <string>
#include <mutex>
class WifiConnectors{
// 显然,Wifi连接必须是单例的,否则必然出现冲突
public:
// 获取单例的静态方法
static WifiConnectors* getInstance();
public:
void log();
/**
* 连接Wifi
* 注意:此处只是对连接wifi接口做了C++封装,实际调用的时候需要为连接Wifi创建一个线程,不要让其阻塞主线程
* @param ssid Wifi名称
* @param password Wifi密码
* @param max_retry 最大重连次数
* @return 最终是否连接成功
*/
bool connectWifi(const std::string& ssid, const std::string& password, uint8_t max_retry = 3);
/**
*
* @return 是否成功断开连接
*/
bool disconnectWifi();
bool isWifiConnect();
private:
explicit WifiConnectors(); // 构造函数私有化
~WifiConnectors(); // 析构函数私有化
WifiConnectors(const WifiConnectors&) = delete; // 禁止拷贝
WifiConnectors& operator=(const WifiConnectors&) = delete; // 禁止赋值
private:
static WifiConnectors *WifiConnectorsInstance; /// 单例实例指针
static std::mutex m_mutex; /// 互斥锁以确保线程安全
bool isConnected = false; /// 当前Wifi是否连接成功
};
Binary file not shown.
@@ -0,0 +1,146 @@
# pip3 install g2p_en
from g2p_en import G2p
import argparse
# python3 gen_sr_commands.py "Turn on the light,Switch on the light;Turn off the light,Switch off the light,Go dark;\
# Start fan;Stop fan;Volume down,Turn down;Mute sound;Next song;Pause playback"
# enum {
# SR_CMD_TURN_ON_THE_LIGHT,
# SR_CMD_TURN_OFF_THE_LIGHT,
# SR_CMD_START_FAN,
# SR_CMD_STOP_FAN,
# SR_CMD_VOLUME_DOWN,
# SR_CMD_MUTE_SOUND,
# SR_CMD_NEXT_SONG,
# SR_CMD_PAUSE_PLAYBACK,
# };
# static const sr_cmd_t sr_commands[] = {
# { 0, "Turn on the light", "TkN nN jc LiT"},
# { 0, "Switch on the light", "SWgp nN jc LiT"},
# { 1, "Turn off the light", "TkN eF jc LiT"},
# { 1, "Switch off the light", "SWgp eF jc LiT"},
# { 1, "Go dark", "Gb DnRK"},
# { 2, "Start fan", "STnRT FaN"},
# { 3, "Stop fan", "STnP FaN"},
# { 4, "Volume down", "VnLYoM DtN"},
# { 4, "Turn down", "TkN DtN"},
# { 5, "Mute sound", "MYoT StND"},
# { 6, "Next song", "NfKST Sel"},
# { 7, "Pause playback", "PeZ PLdBaK"},
# };
def english_g2p(text):
g2p = G2p()
out = "static const sr_cmd_t sr_commands[] = {\n"
enum = "enum {\n"
alphabet = {
"AE1": "a",
"N": "N",
" ": " ",
"OW1": "b",
"V": "V",
"AH0": "c",
"L": "L",
"F": "F",
"EY1": "d",
"S": "S",
"B": "B",
"R": "R",
"AO1": "e",
"D": "D",
"AH1": "c",
"EH1": "f",
"OW0": "b",
"IH0": "g",
"G": "G",
"HH": "h",
"K": "K",
"IH1": "g",
"W": "W",
"AY1": "i",
"T": "T",
"M": "M",
"Z": "Z",
"DH": "j",
"ER0": "k",
"P": "P",
"NG": "l",
"IY1": "m",
"AA1": "n",
"Y": "Y",
"UW1": "o",
"IY0": "m",
"EH2": "f",
"CH": "p",
"AE0": "a",
"JH": "q",
"ZH": "r",
"AA2": "n",
"SH": "s",
"AW1": "t",
"OY1": "u",
"AW2": "t",
"IH2": "g",
"AE2": "a",
"EY2": "d",
"ER1": "k",
"TH": "v",
"UH1": "w",
"UW2": "o",
"OW2": "b",
"AY2": "i",
"UW0": "o",
"AH2": "c",
"EH0": "f",
"AW0": "t",
"AO2": "e",
"AO0": "e",
"UH0": "w",
"UH2": "w",
"AA0": "n",
"AY0": "i",
"IY2": "m",
"EY0": "d",
"ER2": "k",
"OY2": "u",
"OY0": "u",
}
cmd_id = 0
phrase_id = 0
text_list = text.split(";")
for item in text_list:
item = item.split(",")
phrase_id = 0
for phrase in item:
labels = g2p(phrase)
phoneme = ""
for char in labels:
if char not in alphabet:
print("skip %s, not found in alphabet")
continue
else:
phoneme += alphabet[char]
out += " { " + str(cmd_id) + ', "' + phrase + '", "' + phoneme + '"},\n'
if phrase_id == 0:
enum += " SR_CMD_" + phrase.upper().replace(" ", "_") + ",\n"
phrase_id += 1
cmd_id += 1
out += "};"
enum += "};"
# print(text)
print(enum)
print(out)
return out
if __name__ == "__main__":
parser = argparse.ArgumentParser(prog="English Speech Commands G2P")
parser.add_argument("text", type=str, default=None, help="input text")
args = parser.parse_args()
if args.text is not None:
english_g2p(args.text)
@@ -0,0 +1,2 @@
import nltk
nltk.download('averaged_perceptron_tagger')
Binary file not shown.
@@ -0,0 +1,76 @@
The Carnegie Mellon Pronouncing Dictionary [cmudict.0.7a]
ftp://ftp.cs.cmu.edu/project/speech/dict/
https://cmusphinx.svn.sourceforge.net/svnroot/cmusphinx/trunk/cmudict/cmudict.0.7a
Copyright (C) 1993-2008 Carnegie Mellon University. All rights reserved.
File Format: Each line consists of an uppercased word,
a counter (for alternative pronunciations), and a transcription.
Vowels are marked for stress (1=primary, 2=secondary, 0=no stress).
E.g.: NATURAL 1 N AE1 CH ER0 AH0 L
The dictionary contains 127069 entries. Of these, 119400 words are assigned
a unique pronunciation, 6830 words have two pronunciations, and 839 words have
three or more pronunciations. Many of these are fast-speech variants.
Phonemes: There are 39 phonemes, as shown below:
Phoneme Example Translation Phoneme Example Translation
------- ------- ----------- ------- ------- -----------
AA odd AA D AE at AE T
AH hut HH AH T AO ought AO T
AW cow K AW AY hide HH AY D
B be B IY CH cheese CH IY Z
D dee D IY DH thee DH IY
EH Ed EH D ER hurt HH ER T
EY ate EY T F fee F IY
G green G R IY N HH he HH IY
IH it IH T IY eat IY T
JH gee JH IY K key K IY
L lee L IY M me M IY
N knee N IY NG ping P IH NG
OW oat OW T OY toy T OY
P pee P IY R read R IY D
S sea S IY SH she SH IY
T tea T IY TH theta TH EY T AH
UH hood HH UH D UW two T UW
V vee V IY W we W IY
Y yield Y IY L D Z zee Z IY
ZH seizure S IY ZH ER
(For NLTK, entries have been sorted so that, e.g. FIRE 1 and FIRE 2
are contiguous, and not separated by FIRE'S 1.)
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
The contents of this file are deemed to be source code.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
This work was supported in part by funding from the Defense Advanced
Research Projects Agency, the Office of Naval Research and the National
Science Foundation of the United States of America, and by member
companies of the Carnegie Mellon Sphinx Speech Consortium. We acknowledge
the contributions of many volunteers to the expansion and improvement of
this dictionary.
THIS SOFTWARE IS PROVIDED BY CARNEGIE MELLON UNIVERSITY ``AS IS'' AND
ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY
NOR ITS EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
File diff suppressed because it is too large Load Diff
@@ -0,0 +1 @@
[".", "(", ")", ":", "''", "EX", "JJS", "WRB", "VBG", "VBP", "NN", "SYM", "VB", "UH", "NNPS", "NNP", "``", "$", "NNS", "JJR", "MD", "RP", "VBD", "DT", "POS", "RBR", ",", "VBZ", "PDT", "VBN", "WP$", "WDT", "WP", "PRP$", "CD", "IN", "#", "CC", "RB", "FW", "RBS", "PRP", "LS", "JJ", "TO"]
+21 -6
View File
@@ -1,8 +1,7 @@
#include "PCM5101.h" #include "PCM5101.h"
static const char *TAG = "AUDIO PCM5101"; static const char *TAG = "AUDIO PCM5101";
static i2s_chan_handle_t i2s_tx_chan; i2s_chan_handle_t i2s_tx_chan = NULL;
static i2s_chan_handle_t i2s_rx_chan; static i2s_chan_handle_t i2s_rx_chan;
uint8_t Volume = Volume_MAX - 2; uint8_t Volume = Volume_MAX - 2;
@@ -24,7 +23,7 @@ static esp_err_t bsp_i2s_write(void *audio_buffer, size_t len, size_t *bytes_wri
return i2s_channel_write(i2s_tx_chan, (char *)audio_buffer, len, bytes_written, timeout_ms); return i2s_channel_write(i2s_tx_chan, (char *)audio_buffer, len, bytes_written, timeout_ms);
} }
static esp_err_t bsp_i2s_reconfig_clk(uint32_t rate, uint32_t bits_cfg, i2s_slot_mode_t ch) { // I2S Init esp_err_t bsp_i2s_reconfig_clk(uint32_t rate, uint32_t bits_cfg, i2s_slot_mode_t ch) { // I2S Init
esp_err_t ret = ESP_OK; esp_err_t ret = ESP_OK;
i2s_std_config_t std_cfg = { i2s_std_config_t std_cfg = {
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(rate), .clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(rate),
@@ -78,7 +77,7 @@ static void audio_player_callback(audio_player_cb_ctx_t *ctx) {
void Audio_Init(void) void Audio_Init(void)
{ {
i2s_std_config_t std_cfg = { i2s_std_config_t std_cfg = {
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(44100), .clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(16000), // 音频采样率,使用16kHz以减少CPU占用
.slot_cfg = I2S_STD_PHILIP_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO), .slot_cfg = I2S_STD_PHILIP_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO),
.gpio_cfg = BSP_I2S_GPIO_CFG, .gpio_cfg = BSP_I2S_GPIO_CFG,
}; };
@@ -91,8 +90,8 @@ void Audio_Init(void)
.mute_fn = audio_mute_function, .mute_fn = audio_mute_function,
.write_fn = bsp_i2s_write, .write_fn = bsp_i2s_write,
.clk_set_fn = bsp_i2s_reconfig_clk, .clk_set_fn = bsp_i2s_reconfig_clk,
.priority = 3, .priority = 4,
.coreID = 1 .coreID = 1 // 运行在0号核,避免与lvgl抢占资源
}; };
ret = audio_player_new(config); ret = audio_player_new(config);
if (ret != ESP_OK) { if (ret != ESP_OK) {
@@ -124,12 +123,25 @@ void Play_Music(const char* directory, const char* fileName)
} else { } else {
snprintf(filePath, maxPathLength, "%s/%s", directory, fileName); snprintf(filePath, maxPathLength, "%s/%s", directory, fileName);
} }
ESP_LOGD(TAG, "Playing MP3 file: %s", filePath);
Music_File = Open_File(filePath); Music_File = Open_File(filePath);
if (!Music_File) { if (!Music_File) {
ESP_LOGE(TAG, "Failed to open MP3 file: %s", filePath); ESP_LOGE(TAG, "Failed to open MP3 file: %s", filePath);
return; 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; expected_event = AUDIO_PLAYER_CALLBACK_EVENT_PLAYING;
esp_err_t ret = audio_player_play(Music_File); esp_err_t ret = audio_player_play(Music_File);
if (ret != ESP_OK) { if (ret != ESP_OK) {
@@ -201,3 +213,6 @@ void Volume_adjustment(uint8_t Vol) {
Volume = Vol; Volume = Vol;
ESP_LOGI(TAG, "Volume set to %d", Volume); ESP_LOGI(TAG, "Volume set to %d", Volume);
} }
+9
View File
@@ -57,6 +57,15 @@ uint32_t Music_Elapsed(void);
uint16_t Music_Energy(void); uint16_t Music_Energy(void);
void Volume_adjustment(uint8_t Volume); void Volume_adjustment(uint8_t Volume);
// 供 C++ 层调用的“重新配置时钟”包装
esp_err_t bsp_i2s_reconfig_clk(uint32_t rate,
uint32_t bits_cfg,
i2s_slot_mode_t ch);
// 供 C++ 层直接用的 tx channel 句柄
extern i2s_chan_handle_t i2s_tx_chan;
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif
+43
View File
@@ -115,3 +115,46 @@ float BAT_Get_Volts(void)
} }
return BAT_analogVolts; return BAT_analogVolts;
} }
/**
* @brief 滞回锁定百分比(±1 % 死区)
* @param pct:实时算出的 0-100
* @retval 对外发布的稳定百分比
*/
int32_t BAT_Percent_Lock(int32_t pct)
{
static int32_t out = 0; // 上次对外值
if (pct >= out + 1) out = pct; // 上升门槛
if (pct <= out - 1) out = pct; // 下降门槛
return out;
}
static float lpf = 0.0f; // 上一次滤波结果
static bool first = true; // 首次赋值标志
float BAT_Get_Volts_Filtered(void)
{
float raw = BAT_Get_Volts(); // 你的原始函数
if (first) { lpf = raw; first = false; } // 第一次直接赋值
lpf = lpf + 0.05f * (raw - lpf); // α=0.05 越小刚越稳
return lpf;
}
/**
* @brief 返回电池剩余电量百分比(0~100)
* @note 基于 200 k + 100 k 分压,ADC 满量程 1.1 V
* @retval 0 已放空(≤ 3.0 V
* 100 满电(≥ 4.2 V) 这里给到4.1V 以放置电池达不到100%的电量
* 中间线性
*/
int32_t BAT_Get_Percent(void)
{
const float volts = BAT_Get_Volts_Filtered(); // 3.0 ~ 4.2 V -> 0 ~ 100 做映射
// 限幅
if (volts <= 3.0f) return 0;
if (volts >= 4.1f) return 100;
// 滞回比较器 + 线性映射
return BAT_Percent_Lock((int32_t)((volts - 3.0f) / (4.2f - 3.0f) * 100.0f + 0.5f)); // +0.5 四舍五入
}
+2
View File
@@ -24,6 +24,8 @@ extern float BAT_analogVolts;
void BAT_Init(void); void BAT_Init(void);
float BAT_Get_Volts(void); float BAT_Get_Volts(void);
int32_t BAT_Get_Percent(void);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif
+1 -1
View File
@@ -1,5 +1,5 @@
#include "ST77916.h" #include "ST77916.h"
#include "esp_lcd_panel_io.h"
#define LCD_OPCODE_WRITE_CMD (0x02ULL) #define LCD_OPCODE_WRITE_CMD (0x02ULL)
#define LCD_OPCODE_READ_CMD (0x0BULL) #define LCD_OPCODE_READ_CMD (0x0BULL)
+3 -4
View File
@@ -14,7 +14,6 @@ extern "C" {
#include "driver/gpio.h" #include "driver/gpio.h"
#include "driver/spi_master.h" #include "driver/spi_master.h"
#include "esp_timer.h" #include "esp_timer.h"
#include "esp_lcd_panel_io.h"
#include "esp_lcd_panel_io_interface.h" #include "esp_lcd_panel_io_interface.h"
#include "esp_intr_alloc.h" #include "esp_intr_alloc.h"
#include "esp_lcd_panel_ops.h" #include "esp_lcd_panel_ops.h"
@@ -34,8 +33,8 @@ extern "C" {
#define ESP_PANEL_HOST_SPI_ID_DEFAULT (SPI2_HOST) #define ESP_PANEL_HOST_SPI_ID_DEFAULT (SPI2_HOST)
#define ESP_PANEL_LCD_SPI_MODE (0) // 0/1/2/3, typically set to 0 #define ESP_PANEL_LCD_SPI_MODE (0) // 0/1/2/3, typically set to 0
#define ESP_PANEL_LCD_SPI_CLK_HZ (80 * 1000 * 1000) // Should be an integer divisor of 80M, typically set to 40M #define ESP_PANEL_LCD_SPI_CLK_HZ (40 * 1000 * 1000) // Should be an integer divisor of 80M, typically set to 40M
#define ESP_PANEL_LCD_SPI_TRANS_QUEUE_SZ (10) // Typically set to 10 #define ESP_PANEL_LCD_SPI_TRANS_QUEUE_SZ (40) // Typically set to 10
#define ESP_PANEL_LCD_SPI_CMD_BITS (32) // Typically set to 32 #define ESP_PANEL_LCD_SPI_CMD_BITS (32) // Typically set to 32
#define ESP_PANEL_LCD_SPI_PARAM_BITS (8) // Typically set to 8 #define ESP_PANEL_LCD_SPI_PARAM_BITS (8) // Typically set to 8
@@ -52,7 +51,7 @@ extern "C" {
#define EXAMPLE_LCD_BK_LIGHT_ON_LEVEL (1) #define EXAMPLE_LCD_BK_LIGHT_ON_LEVEL (1)
#define EXAMPLE_LCD_BK_LIGHT_OFF_LEVEL !EXAMPLE_LCD_BK_LIGHT_ON_LEVEL #define EXAMPLE_LCD_BK_LIGHT_OFF_LEVEL !EXAMPLE_LCD_BK_LIGHT_ON_LEVEL
#define ESP_PANEL_HOST_SPI_MAX_TRANSFER_SIZE (2048) #define ESP_PANEL_HOST_SPI_MAX_TRANSFER_SIZE (8192)
#define LEDC_HS_TIMER LEDC_TIMER_0 #define LEDC_HS_TIMER LEDC_TIMER_0
#define LEDC_LS_MODE LEDC_LOW_SPEED_MODE #define LEDC_LS_MODE LEDC_LOW_SPEED_MODE
+6 -7
View File
@@ -1,5 +1,5 @@
#include "LVGL_Driver.h" #include "LVGL_Driver.h"
#include "ST77916.h"
static const char *TAG_LVGL = "LVGL"; static const char *TAG_LVGL = "LVGL";
@@ -18,13 +18,12 @@ void example_increase_lvgl_tick(void *arg)
void example_lvgl_flush_cb(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map) void example_lvgl_flush_cb(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map)
{ {
esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t) drv->user_data; esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t) drv->user_data;
int offsetx1 = area->x1; // 立即通知LVGL传输开始,使用DMA传输
int offsetx2 = area->x2;
int offsety1 = area->y1;
int offsety2 = area->y2;
// copy a buffer's content to a specific area of the display
esp_lcd_panel_draw_bitmap(panel_handle, offsetx1, offsety1, offsetx2 +1, offsety2 + 1, color_map);
lv_disp_flush_ready(drv); lv_disp_flush_ready(drv);
// copy a buffer's content to a specific area of the display
// 异步传输数据
esp_lcd_panel_draw_bitmap(panel_handle, area->x1, area->y1, area->x2 + 1, area->y2 + 1, color_map);
} }
/*Read the touchpad*/ /*Read the touchpad*/
+2 -2
View File
@@ -13,9 +13,9 @@ extern "C" {
#include "lvgl.h" #include "lvgl.h"
#include "demos/lv_demos.h" #include "demos/lv_demos.h"
#include "ST77916.h"
#define LVGL_BUF_LEN (EXAMPLE_LCD_WIDTH * EXAMPLE_LCD_HEIGHT / 20)
#define LVGL_BUF_LEN (EXAMPLE_LCD_WIDTH * EXAMPLE_LCD_HEIGHT / 8) // 1 byte per pixel
#define EXAMPLE_LVGL_TICK_PERIOD_MS 2 #define EXAMPLE_LVGL_TICK_PERIOD_MS 2
extern lv_disp_draw_buf_t disp_buf; // contains internal graphic buffer(s) called draw buffer(s) extern lv_disp_draw_buf_t disp_buf; // contains internal graphic buffer(s) called draw buffer(s)
+1
View File
@@ -1,5 +1,6 @@
#include "LVGL_Example.h" #include "LVGL_Example.h"
#include "LVGL_Music.h" #include "LVGL_Music.h"
#include "ST77916.h"
#include <demos/lv_demos.h> #include <demos/lv_demos.h>
// #include <demos/music/lv_demo_music_main.h> // #include <demos/music/lv_demo_music_main.h>
// #include <demos/music/lv_demo_music_list.h> // #include <demos/music/lv_demo_music_list.h>
+1 -1
View File
@@ -1,5 +1,5 @@
#include "CST816.h" #include "CST816.h"
#include "esp_lcd_panel_io.h"
#define POINT_NUM_MAX (1) #define POINT_NUM_MAX (1)
+1 -1
View File
@@ -21,7 +21,7 @@ extern "C" {
#include "esp_err.h" #include "esp_err.h"
#include "esp_log.h" #include "esp_log.h"
#include "esp_check.h" #include "esp_check.h"
#include "esp_lcd_panel_io.h" #include "esp_lcd_panel_io_interface.h"
#include "esp_lcd_touch.h" #include "esp_lcd_touch.h"
#include "TCA9554PWR.h" #include "TCA9554PWR.h"
@@ -15,7 +15,7 @@
#include "sdkconfig.h" #include "sdkconfig.h"
#include "esp_err.h" #include "esp_err.h"
#include "driver/gpio.h" #include "driver/gpio.h"
#include "esp_lcd_panel_io.h" #include "esp_lcd_panel_io_interface.h" // Misaki 2025.9 fixed the bus is #include "esp_lcd_panel_io.h"
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/semphr.h" #include "freertos/semphr.h"
+1 -1
View File
@@ -344,7 +344,7 @@ void MIC_Speech_init()
afe_config.pcm_config.mic_num = 1; afe_config.pcm_config.mic_num = 1;
afe_config.pcm_config.ref_num = 1; afe_config.pcm_config.ref_num = 1;
afe_config.pcm_config.sample_rate = 16000; afe_config.pcm_config.sample_rate = 16000;
afe_config.wakenet_model_name = esp_srmodel_filter(MIC_Speech.models, ESP_WN_PREFIX, NULL); afe_config.wakenet_model_name = esp_srmodel_filter(MIC_Speech.models, ESP_WN_PREFIX, NULL); // 获取唤醒词模型
MIC_Speech.afe_data = MIC_Speech.afe_handle->create_from_config(&afe_config); MIC_Speech.afe_data = MIC_Speech.afe_handle->create_from_config(&afe_config);
// 注意两个任务被分配了不同的核心与优先级,这是为了防止AFE(Audio Front-End)内部环形缓冲区溢出 // 注意两个任务被分配了不同的核心与优先级,这是为了防止AFE(Audio Front-End)内部环形缓冲区溢出
+99
View File
@@ -0,0 +1,99 @@
//
// Created by misaki on 2025/9/4.
//
#include "ota_ws.h"
#include "esp_log.h"
#include "esp_websocket_client.h"
#include "esp_ota_ops.h"
#include "esp_http_client.h"
#include "esp_https_ota.h"
#include "cJSON.h"
#include "esp_system.h"
#include "esp_mac.h"
static const char *TAG = "OTA_WS";
#define FW_VERSION "1.0.1" // ←每次发布修改这里
#define WS_URL_FMT "ws://%s:%d/ws?mac=%02X%02X%02X%02X%02X%02X"
static esp_websocket_client_handle_t ws = NULL;
static void ota_start(const char *url)
{
esp_http_client_config_t config = {
.url = url,
.keep_alive_enable = true,
};
esp_https_ota_config_t ota_cfg = {
.http_config = &config
};
ESP_LOGI(TAG, "开始下载固件...");
esp_err_t ret = esp_https_ota(&ota_cfg);
if (ret == ESP_OK) {
ESP_LOGI(TAG, "下载完成,重启");
esp_restart();
} else {
ESP_LOGE(TAG, "OTA 失败:%s", esp_err_to_name(ret));
}
}
static void ws_event(void *arg, esp_event_base_t base, int32_t id, void *data)
{
esp_websocket_event_data_t *d = (esp_websocket_event_data_t *)data;
switch (id) {
case WEBSOCKET_EVENT_CONNECTED:
ESP_LOGI(TAG, "WebSocket 已连接");
break;
case WEBSOCKET_EVENT_DATA:
if (d->op_code == WS_TRANSPORT_OPCODES_TEXT) {
char *json = malloc(d->data_len + 1);
if (!json) break;
memcpy(json, d->data_ptr, d->data_len);
json[d->data_len] = 0;
cJSON *root = cJSON_Parse(json);
free(json);
if (root) {
cJSON *cmd = cJSON_GetObjectItem(root, "cmd");
if (cmd && strcmp(cmd->valuestring, "upgrade") == 0) {
const char *url = cJSON_GetObjectItem(root, "url")->valuestring;
ESP_LOGI(TAG, "收到升级指令,URL=%s", url);
ota_start(url);
}
cJSON_Delete(root);
}
}
break;
case WEBSOCKET_EVENT_DISCONNECTED:
ESP_LOGW(TAG, "WS 断开,5 秒后重连...");
vTaskDelay(pdMS_TO_TICKS(5000));
esp_websocket_client_start(ws);
break;
default:
break;
}
}
esp_err_t ota_ws_start(const char *server_ip, uint16_t port)
{
if (ws) return ESP_OK;
uint8_t mac[6];
esp_efuse_mac_get_default(mac);
char uri[128];
snprintf(uri, sizeof(uri), WS_URL_FMT,
server_ip, port, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
esp_websocket_client_config_t cfg = {
.uri = uri,
.ping_interval_sec = 30,
};
ws = esp_websocket_client_init(&cfg);
esp_websocket_register_events(ws, WEBSOCKET_EVENT_ANY, ws_event, NULL);
esp_websocket_client_start(ws);
return ESP_OK;
}
+23
View File
@@ -0,0 +1,23 @@
//
// Created by misaki on 2025/9/4.
//
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include "esp_err.h"
/**
* @brief 启动 WebSocket 长连接,接收服务器推送的升级指令
* @param server_ip 服务器 IP 字符串,如 "192.168.1.100"
* @return esp_err_t
*/
esp_err_t ota_ws_start(const char *server_ip, uint16_t port);
#ifdef __cplusplus
}
#endif
+3 -3
View File
@@ -9,10 +9,10 @@ uint32_t Flash_Size = 0;
uint32_t SDCard_Size = 0; uint32_t SDCard_Size = 0;
esp_err_t s_example_write_file(const char *path, char *data) esp_err_t s_example_write_file(const char *path, const char *data, const char *type)
{ {
ESP_LOGI(SD_TAG, "Opening file %s", path); ESP_LOGI(SD_TAG, "Opening file %s", path);
FILE *f = fopen(path, "w"); FILE *f = fopen(path, type);
if (f == NULL) { if (f == NULL) {
ESP_LOGE(SD_TAG, "Failed to open file for writing"); ESP_LOGE(SD_TAG, "Failed to open file for writing");
return ESP_FAIL; return ESP_FAIL;
@@ -55,7 +55,7 @@ void SD_Init(void)
// If format_if_mount_failed is set to true, SD card will be partitioned and formatted in case when mounting fails. false true // If format_if_mount_failed is set to true, SD card will be partitioned and formatted in case when mounting fails. false true
esp_vfs_fat_sdmmc_mount_config_t mount_config = { esp_vfs_fat_sdmmc_mount_config_t mount_config = {
.format_if_mount_failed = true, .format_if_mount_failed = true,
.max_files = 5, .max_files = 10,
.allocation_unit_size = 16 * 1024 .allocation_unit_size = 16 * 1024
}; };
sdmmc_card_t *card; sdmmc_card_t *card;
+1 -1
View File
@@ -30,7 +30,7 @@ extern "C" {
esp_err_t SD_Card_CS_EN(void); esp_err_t SD_Card_CS_EN(void);
esp_err_t SD_Card_CS_Dis(void); esp_err_t SD_Card_CS_Dis(void);
esp_err_t s_example_write_file(const char *path, char *data); esp_err_t s_example_write_file(const char *path, const char *data, const char *type);
esp_err_t s_example_read_file(const char *path); esp_err_t s_example_read_file(const char *path);
extern uint32_t SDCard_Size; extern uint32_t SDCard_Size;
+118 -38
View File
@@ -11,49 +11,47 @@ wifi_ap_info_t *wifi_ap_list = NULL;
uint16_t wifi_ap_count = 0; uint16_t wifi_ap_count = 0;
ble_device_info_t *ble_device_list = NULL; ble_device_info_t *ble_device_list = NULL;
void Wireless_Init(void) esp_err_t Wireless_Init(void)
{ {
// Initialize NVS. // Initialize NVS.
esp_err_t ret = nvs_flash_init(); esp_err_t ret = nvs_flash_init(); // 初始化Flash
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { // 如果Flash有错误,则进行修复
ESP_ERROR_CHECK(nvs_flash_erase()); ESP_ERROR_CHECK(nvs_flash_erase()); // 清空Flash
ret = nvs_flash_init(); ret = nvs_flash_init(); // 重新初始化Flash
} }
ESP_ERROR_CHECK( ret ); ESP_ERROR_CHECK( ret ); // 检查错误
// WiFi
xTaskCreatePinnedToCore( // WiFi 手动调用Wifi初始化
WIFI_Init, // xTaskCreatePinnedToCore(
"WIFI task", // WIFI_Init,
4096, // "WIFI task",
NULL, // 4096,
1, // NULL,
NULL, // 1,
0); // NULL,
// BLE // 0);
xTaskCreatePinnedToCore( // BLE 当前工程不启用蓝牙
BLE_Init, // xTaskCreatePinnedToCore(
"BLE task", // BLE_Init,
4096, // "BLE task",
NULL, // 4096,
2, // NULL,
NULL, // 2,
0); // NULL,
// 0);
return ret;
} }
void WIFI_Init(void *arg) esp_err_t WIFI_Init(void *arg)
{ {
esp_netif_init(); esp_netif_init(); // 初始化网络接口
esp_event_loop_create_default(); esp_event_loop_create_default(); // 创建事件循环
esp_netif_create_default_wifi_sta(); esp_netif_create_default_wifi_sta(); // 创建默认的WiFi Station
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); // 创建WiFi初始化配置(使用默认配置)
esp_wifi_init(&cfg); esp_wifi_init(&cfg); // 初始化WiFi
esp_wifi_set_mode(WIFI_MODE_STA); esp_wifi_set_mode(WIFI_MODE_STA); // 设置WiFi模式为Station
esp_wifi_start(); esp_err_t ret = esp_wifi_start(); // 启动WiFi
return ret;
WIFI_NUM = WIFI_Scan();
printf("WIFI:%d\r\n",WIFI_NUM);
vTaskDelete(NULL);
} }
@@ -104,7 +102,7 @@ uint16_t WIFI_Scan(void)
return ap_count; return ap_count;
} }
/*
#define GATTC_TAG "GATTC_TAG" #define GATTC_TAG "GATTC_TAG"
#define SCAN_DURATION 5 #define SCAN_DURATION 5
#define MAX_DISCOVERED_DEVICES 100 #define MAX_DISCOVERED_DEVICES 100
@@ -268,6 +266,88 @@ uint16_t BLE_Scan(void)
Scan_finish = 1; Scan_finish = 1;
return BLE_NUM; return BLE_NUM;
} }
*/
// 下面是新增的实现
static EventGroupHandle_t s_wifi_event_group;
#define WIFI_CONNECTED_BIT BIT0
#define WIFI_FAIL_BIT BIT1
static const char *TAG = "WiFi_UTIL";
/* --------------------------- 事件回调 --------------------------- */
static void event_handler(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data)
{
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
}
}
/* --------------------------- 自动连接 --------------------------- */
bool WiFi_AutoConnect(const char *ssid, const char *password, uint8_t max_retry)
{
if (s_wifi_event_group == NULL) {
s_wifi_event_group = xEventGroupCreate();
}
/* 注册一次性事件监听 */
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
WIFI_EVENT_STA_DISCONNECTED,
&event_handler,
NULL,
NULL));
ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,
IP_EVENT_STA_GOT_IP,
&event_handler,
NULL,
NULL));
wifi_config_t wifi_config = { 0 };
strncpy((char *)wifi_config.sta.ssid, ssid, sizeof(wifi_config.sta.ssid));
strncpy((char *)wifi_config.sta.password, password, sizeof(wifi_config.sta.password));
wifi_config.sta.threshold.authmode = WIFI_AUTH_WPA2_PSK;
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
uint8_t retry_cnt = 0;
while (retry_cnt <= max_retry) {
ESP_LOGI(TAG, "WiFi connect attempt %u/%u to \"%s\"", retry_cnt + 1, max_retry + 1, ssid);
xEventGroupClearBits(s_wifi_event_group, WIFI_CONNECTED_BIT | WIFI_FAIL_BIT);
esp_wifi_connect();
EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
pdFALSE, pdFALSE,
pdMS_TO_TICKS(6000)); /* 6 秒超时 */
if (bits & WIFI_CONNECTED_BIT) {
ESP_LOGI(TAG, "WiFi connected");
goto success;
}
/* 失败,准备重试 */
retry_cnt++;
}
ESP_LOGE(TAG, "WiFi connect failed after %u retries", max_retry + 1);
return false;
success:
return true;
}
/* --------------------------- 断开连接 --------------------------- */
esp_err_t WiFi_Disconnect(void)
{
ESP_LOGI(TAG, "Disconnecting WiFi...");
return esp_wifi_disconnect();
}
// 获取WiFi AP列表 // 获取WiFi AP列表
uint16_t wireless_get_wifi_ap_list(wifi_ap_info_t *ap_list, uint16_t max_aps) { uint16_t wireless_get_wifi_ap_list(wifi_ap_info_t *ap_list, uint16_t max_aps) {
+24 -6
View File
@@ -12,9 +12,9 @@ extern "C" {
#include <stdio.h> #include <stdio.h>
#include <string.h> // For memcpy #include <string.h> // For memcpy
#include "esp_system.h" #include "esp_system.h"
#include "esp_bt.h" // #include "esp_bt.h"
#include "esp_gap_ble_api.h" // #include "esp_gap_ble_api.h"
#include "esp_bt_main.h" // #include "esp_bt_main.h"
// WiFi AP信息结构体 // WiFi AP信息结构体
typedef struct { typedef struct {
@@ -35,13 +35,31 @@ extern uint16_t BLE_NUM;
extern uint16_t WIFI_NUM; extern uint16_t WIFI_NUM;
extern bool Scan_finish; extern bool Scan_finish;
void Wireless_Init(void); esp_err_t Wireless_Init(void);
void WIFI_Init(void *arg); esp_err_t WIFI_Init(void *arg);
uint16_t WIFI_Scan(void); uint16_t WIFI_Scan(void);
void BLE_Init(void *arg); void BLE_Init(void *arg);
uint16_t BLE_Scan(void); uint16_t BLE_Scan(void);
// 新增接口函数 // 下面的函数为新增的函数
// Code By Misaki
/**
* @brief 自动连接指定 Wi-Fi,带重试
* @param ssid 目标热点名称
* @param password 密码
* @param max_retry 最大重连次数(0 表示仅尝试一次)
* @return true 成功连接
* false 达到重试上限仍未连上
*/
bool WiFi_AutoConnect(const char *ssid, const char *password, uint8_t max_retry);
/**
* @brief 断开当前 STA 连接
*/
esp_err_t WiFi_Disconnect(void);
// 接口函数
uint16_t wireless_get_wifi_ap_list(wifi_ap_info_t *ap_list, uint16_t max_aps); uint16_t wireless_get_wifi_ap_list(wifi_ap_info_t *ap_list, uint16_t max_aps);
uint16_t wireless_get_ble_device_list(ble_device_info_t *device_list, uint16_t max_devices); uint16_t wireless_get_ble_device_list(ble_device_info_t *device_list, uint16_t max_devices);
void wireless_print_wifi_aps(void); void wireless_print_wifi_aps(void);
+194
View File
@@ -198,3 +198,197 @@ extern "C" void app_main(void)
} }
} }
``` ```
```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<WifiConnectors>(
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);
}
}
}
```
```c++
// 有关lvgl_cpp的部分控件使用
/* LVGL 已经初始化,屏幕驱动已注册 */
auto scr = lvgl_cpp::Screen{}; // RAII,离开作用域自动 del
lv_scr_load(scr.raw()); // 让 LVGL 把它当活动屏幕
lvgl_cpp::Image img(scr);
img.bin("pic_no_alp_swap.bin", 720, 720)
.center();
/* 1. 创建按钮 */
/* 1. 黑色文字样式 */
static lvgl_cpp::Style txt_style;
txt_style.text_color(lv_color_black());
/* 2. 按钮 */
lvgl_cpp::Button btn{scr};
btn.size(150, 60).pos(40, 40);
/* 3. 先创建 Label 对象并保存,再链式调 */
lvgl_cpp::Label lbl{btn}; // 一定要存实例
lbl.text("Click Me")
.add_style(&txt_style.s, LV_PART_MAIN); // 样式作用到文字本身
/* 3. 注册点击事件 */
btn.on(LV_EVENT_CLICKED, [](lv_event_t*){
lvgl_cpp::Toast::show("Saved successfully !");
/* 2. 警告,3 s,顶部 */
lvgl_cpp::Toast::show("SD card missing", lvgl_cpp::Toast::Type::WARN, 3000, LV_ALIGN_TOP_MID, 20);
/* 3. 错误,1.5 s,底部右侧 */
lvgl_cpp::Toast::show("Network error", lvgl_cpp::Toast::Type::ERROR, 1500, LV_ALIGN_BOTTOM_RIGHT, -30);
ESP_LOGI("BTN", "pressed");
});
/* 1. 消息框 */
const char* btns[] = {"Yes", "No", ""};
auto mbox = lvgl_cpp::MsgBox::create("Hint", "Delete file ?", btns);
/* 3. 进度条(双向 + 动画) */
lvgl_cpp::Bar bar(scr);
bar.range(0, 100)
.start_value(20) // 左端
.value(80, LV_ANIM_ON) // 右端带动画
.anim_time(500)
.size(200, 15)
.align(LV_ALIGN_CENTER, nullptr, 0, 40);
/* 4. 折线:画一个 △ */
std::vector<lv_point_t> triangle = {{0,0}, {40,0}, {20,40}, {0,0}};
lvgl_cpp::Line line(scr);
line.points(triangle)
.y_invert(false)
.align(LV_ALIGN_CENTER, nullptr, 0, 100);
lvgl_cpp::Battery bat(scr);
bat.size(60, 30) // 手机经典尺寸
.percent(true) // 显示 50%
.onRead([]() -> uint8_t { // 替换成你的 ADC/INA219 回调
static uint8_t v = 100;
if (v) --v;
return v;
})
.align(LV_ALIGN_TOP_RIGHT, -10, 10); // 九宫格对齐
/* 3. 日期时间 */
lvgl_cpp::DateTime dt(scr);
dt.format("%m/%d %a %H:%M")
.onRead([](char* buf, size_t len){
/* 这里用 SNTP / RTC 填充,示例直接给假时间 */
snprintf(buf, len, "06/25 Tue 14:30");
})
.align(LV_ALIGN_BOTTOM_MID, nullptr, 0, -10);
lvgl_cpp::ColorPicker cp(scr);
cp.size(120, 120)
.align(LV_ALIGN_BOTTOM_MID, nullptr, 0, -20)
.on(LV_EVENT_VALUE_CHANGED, [&](lv_event_t*){
lv_color_t c = cp.color();
ESP_LOGI("CP", "rgb=%d,%d,%d", c.ch.red, c.ch.green_h, c.ch.blue);
});
```
+12 -1
View File
@@ -47,6 +47,16 @@ dependencies:
registry_url: https://components.espressif.com/ registry_url: https://components.espressif.com/
type: service type: service
version: 1.9.5 version: 1.9.5
espressif/esp_websocket_client:
component_hash: ac62982fcf9b266409c2299d2b6b1844122105b35163a3b7f8d0adaa9f7eb989
dependencies:
- name: idf
require: private
version: '>=5.0'
source:
registry_url: https://components.espressif.com/
type: service
version: 1.5.0
idf: idf:
source: source:
type: idf type: idf
@@ -62,8 +72,9 @@ direct_dependencies:
- chmorgan/esp-audio-player - chmorgan/esp-audio-player
- chmorgan/esp-libhelix-mp3 - chmorgan/esp-libhelix-mp3
- espressif/esp-sr - espressif/esp-sr
- espressif/esp_websocket_client
- idf - idf
- lvgl/lvgl - lvgl/lvgl
manifest_hash: 9bc1eee4314d6f75a0822d5e087c5603182eb609f99c1dd5726d7cea387e4d1d manifest_hash: 2f09fb29ee9918e287005c3aa0d08252d2551c70eed608468245557ab01ac54b
target: esp32s3 target: esp32s3
version: 2.0.0 version: 2.0.0
+11 -4
View File
@@ -1,10 +1,9 @@
#include <stdio.h> #include <stdio.h>
#include <esp_log.h> #include <esp_log.h>
#include <nvs.h>
#include <nvs_flash.h>
#include "err_handle.h" #include "err_handle.h"
#include "drivers_test.h" #include "drivers_test.h"
#include "CppHandle.h" #include "CppHandle.h"
@@ -34,9 +33,17 @@ void app_main(void)
// mic_speech_and_gif_and_music_test(); // mic_speech_and_gif_and_music_test();
// 初始化NVS
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);
Cpp_Hand(); Cpp_Hand();
ESP_GOTO(err); ESP_GOTO(err);
err: err:
ESP_LOGI("app_main", "app_main error"); ESP_LOGE("app_main", "app_main error");
} }
+40
View File
@@ -25,11 +25,30 @@ idf_component_register(SRCS "Bionic_sphere.c"
"../Lib/Display/Touch_Driver/esp_lcd_touch/esp_lcd_touch.c" # 触摸屏驱动库 "../Lib/Display/Touch_Driver/esp_lcd_touch/esp_lcd_touch.c" # 触摸屏驱动库
"../Lib/PWR_Key/PWR_Key.c" # PWR按键驱动库 "../Lib/PWR_Key/PWR_Key.c" # PWR按键驱动库
"../Lib/MIC_Driver/MIC_Speech.c" # 录音驱动库 "../Lib/MIC_Driver/MIC_Speech.c" # 录音驱动库
"../Lib/OTA_Driver/ota_ws.c" # OTA驱动库
# 业务代码(使用Cpp编写) # 业务代码(使用Cpp编写)
"../Bionic_Core/PetBaseClass/PetBaseClass.cpp" # 宠物基类库 "../Bionic_Core/PetBaseClass/PetBaseClass.cpp" # 宠物基类库
"../Bionic_Core/PetBaseClass/PetInterface.cpp" # 宠物接口层
"../Bionic_Core/PetBaseClass/PetObserver.cpp" # 宠物观察者库
"../Bionic_Core/PetBaseClass/PetDao.cpp" # 宠物数据访问层
"../Bionic_Core/OTAClass/OTAClass.cpp" # OTA类库 "../Bionic_Core/OTAClass/OTAClass.cpp" # OTA类库
"../Bionic_Core/OTAClass/HttpOtaUpdater.cpp"
"../Bionic_Core/CommClass/CommClass.cpp" # 通信类库 "../Bionic_Core/CommClass/CommClass.cpp" # 通信类库
"../Bionic_Core/CommClass/CommBean.cpp" # 通信交互实体
"../Bionic_Core/ToolsClass/ToolsClass.cpp" # 工具类库 "../Bionic_Core/ToolsClass/ToolsClass.cpp" # 工具类库
"../Bionic_Core/ToolsClass/AudioOutput/AudioOutput.cpp" # 音频输出类库
"../Bionic_Core/ToolsClass/LVGL_Render/LVGLRender.cpp" # LVGL渲染类库
"../Bionic_Core/ToolsClass/LVGL_Render/LvppApp/BaseApp.cpp"
"../Bionic_Core/ToolsClass/LVGL_Render/Lvpp/SiYuanHeiTiGoogleBan.c" # 中文字体库
"../Bionic_Core/ToolsClass/LVGL_Render/Lvpp/lvpp.cpp"
"../Bionic_Core/ToolsClass/SDFileManager/SDFileManager.cpp" # SD文件管理类库
"../Bionic_Core/ToolsClass/SpeechRecognizer/SpeechRecognizer.cpp" # 语音识别类库
"../Bionic_Core/ToolsClass/SpeechRecognizer/VadSlidingWindow.cpp"
"../Bionic_Core/ToolsClass/SpeechRecognizer/SimpleI2SForwarder.cpp"
"../Bionic_Core/ToolsClass/WifiConnectors/WifiConnectors.cpp" # WIFI连接类库
"../Bionic_Core/ToolsClass/ThreadManager/ThreadManager.cpp" # 线程管理类库
"../Bionic_Core/ToolsClass/Sys_Config/sys_conf_singleton.cpp" # 系统配置类库
"../Bionic_Core/ToolsClass/CppJson/cpp_json.cpp" # json类库
"../Bionic_Core/CppHandle/CppHandle.cpp" # C++&C兼容库 "../Bionic_Core/CppHandle/CppHandle.cpp" # C++&C兼容库
INCLUDE_DIRS "." INCLUDE_DIRS "."
"../test/driver_test" "../test/driver_test"
@@ -55,10 +74,21 @@ idf_component_register(SRCS "Bionic_sphere.c"
"../Lib/Display/Touch_Driver/esp_lcd_touch" "../Lib/Display/Touch_Driver/esp_lcd_touch"
"../Lib/PWR_Key" "../Lib/PWR_Key"
"../Lib/MIC_Driver" "../Lib/MIC_Driver"
"../Lib/OTA_Driver"
"../Bionic_Core/PetBaseClass" "../Bionic_Core/PetBaseClass"
"../Bionic_Core/OTAClass" "../Bionic_Core/OTAClass"
"../Bionic_Core/CommClass" "../Bionic_Core/CommClass"
"../Bionic_Core/ToolsClass" "../Bionic_Core/ToolsClass"
"../Bionic_Core/ToolsClass/AudioOutput"
"../Bionic_Core/ToolsClass/LVGL_Render"
"../Bionic_Core/ToolsClass/LVGL_Render/LvppApp"
"../Bionic_Core/ToolsClass/LVGL_Render/Lvpp"
"../Bionic_Core/ToolsClass/SDFileManager"
"../Bionic_Core/ToolsClass/SpeechRecognizer"
"../Bionic_Core/ToolsClass/WifiConnectors"
"../Bionic_Core/ToolsClass/ThreadManager"
"../Bionic_Core/ToolsClass/Sys_Config"
"../Bionic_Core/ToolsClass/CppJson"
"../Bionic_Core/CppHandle" "../Bionic_Core/CppHandle"
PRIV_REQUIRES # 私有依赖 PRIV_REQUIRES # 私有依赖
driver driver
@@ -69,5 +99,15 @@ idf_component_register(SRCS "Bionic_sphere.c"
unity unity
esp_lcd esp_lcd
lvgl__lvgl lvgl__lvgl
lvgl
fatfs fatfs
pthread
app_update
esp_http_client
esp_websocket_client
esp_https_ota
json
efuse
) )
target_compile_options(${COMPONENT_LIB} PUBLIC -std=gnu++17)
+6 -5
View File
@@ -1,6 +1,7 @@
dependencies: dependencies:
idf: ">=4.4" idf: '>=4.4'
lvgl/lvgl: "~8.3.0" lvgl/lvgl: ~8.3.0
chmorgan/esp-audio-player: "==1.0.7" chmorgan/esp-audio-player: ==1.0.7
chmorgan/esp-libhelix-mp3: "==1.0.3" chmorgan/esp-libhelix-mp3: ==1.0.3
espressif/esp-sr: "~1.9.4" espressif/esp-sr: ~1.9.4
espressif/esp_websocket_client: ^1.0.0
+4
View File
@@ -8,6 +8,9 @@
# Offset:起始地址(通常不写,让系统自动计算)。 # Offset:起始地址(通常不写,让系统自动计算)。
# Size:大小,比如 1M, 64K, 0x100000 等。 # Size:大小,比如 1M, 64K, 0x100000 等。
# Flags:可选标志,比如 encrypted(加密)。 # Flags:可选标志,比如 encrypted(加密)。
# 注意:因为没有设置工厂分区,那么每次烧录的时候程序会烧录在ota_0分区
# 根据官方文档OTA 数据分区的容量是 2 个 flash 扇区的大小(0x2000 字节)
# 那么可以计算出 OTA 数据分区大小最大为 64MB (2*16**3 *0x1000 / 1024 / 1024)MB
# Name, Type, SubType, Offset, Size, Flags # Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, , 0x6000, nvs, data, nvs, , 0x6000,
@@ -16,3 +19,4 @@ phy_init, data, phy, , 0x1000,
ota_0, app, ota_0, , 6M, ota_0, app, ota_0, , 6M,
ota_1, app, ota_1, , 6M, ota_1, app, ota_1, , 6M,
coredump, data, coredump,, 64K, coredump, data, coredump,, 64K,
sys_conf, data, fat, , 0xF9000,
1 # ESP32-S3 16 MB Flash, OTA + 文件系统
8 # Offset:起始地址(通常不写,让系统自动计算)。
9 # Size:大小,比如 1M, 64K, 0x100000 等。
10 # Flags:可选标志,比如 encrypted(加密)。
11 # 注意:因为没有设置工厂分区,那么每次烧录的时候程序会烧录在ota_0分区
12 # 根据官方文档OTA 数据分区的容量是 2 个 flash 扇区的大小(0x2000 字节)
13 # 那么可以计算出 OTA 数据分区大小最大为 64MB (2*16**3 *0x1000 / 1024 / 1024)MB
14 # Name, Type, SubType, Offset, Size, Flags
15 nvs, data, nvs, , 0x6000,
16 otadata, data, ota, , 0x2000,
19 ota_1, app, ota_1, , 6M,
20 coredump, data, coredump,, 64K,
21 sys_conf, data, fat, , 0xF9000,
22
+87 -583
View File
@@ -560,26 +560,26 @@ CONFIG_SR_MN_EN_NONE=y
# #
# Add Chinese speech commands # Add Chinese speech commands
# #
CONFIG_CN_SPEECH_COMMAND_ID0="da kai kong tiao" CONFIG_CN_SPEECH_COMMAND_ID0=""
CONFIG_CN_SPEECH_COMMAND_ID1="guan bi kong tiao" CONFIG_CN_SPEECH_COMMAND_ID1=""
CONFIG_CN_SPEECH_COMMAND_ID2="zeng da feng su" CONFIG_CN_SPEECH_COMMAND_ID2=""
CONFIG_CN_SPEECH_COMMAND_ID3="jian xiao feng su" CONFIG_CN_SPEECH_COMMAND_ID3=""
CONFIG_CN_SPEECH_COMMAND_ID4="sheng gao yi du" CONFIG_CN_SPEECH_COMMAND_ID4=""
CONFIG_CN_SPEECH_COMMAND_ID5="jiang di yi du" CONFIG_CN_SPEECH_COMMAND_ID5=""
CONFIG_CN_SPEECH_COMMAND_ID6="zhi re mo shi" CONFIG_CN_SPEECH_COMMAND_ID6=""
CONFIG_CN_SPEECH_COMMAND_ID7="zhi leng mo shi" CONFIG_CN_SPEECH_COMMAND_ID7=""
CONFIG_CN_SPEECH_COMMAND_ID8="song feng mo shi" CONFIG_CN_SPEECH_COMMAND_ID8=""
CONFIG_CN_SPEECH_COMMAND_ID9="jie neng mo shi" CONFIG_CN_SPEECH_COMMAND_ID9=""
CONFIG_CN_SPEECH_COMMAND_ID10="chu shi mo shi" CONFIG_CN_SPEECH_COMMAND_ID10=""
CONFIG_CN_SPEECH_COMMAND_ID11="jian kang mo shi" CONFIG_CN_SPEECH_COMMAND_ID11=""
CONFIG_CN_SPEECH_COMMAND_ID12="shui mian mo shi" CONFIG_CN_SPEECH_COMMAND_ID12=""
CONFIG_CN_SPEECH_COMMAND_ID13="da kai lan ya" CONFIG_CN_SPEECH_COMMAND_ID13=""
CONFIG_CN_SPEECH_COMMAND_ID14="guan bi lan ya" CONFIG_CN_SPEECH_COMMAND_ID14=""
CONFIG_CN_SPEECH_COMMAND_ID15="kai shi bo fang" CONFIG_CN_SPEECH_COMMAND_ID15=""
CONFIG_CN_SPEECH_COMMAND_ID16="zan ting bo fang" CONFIG_CN_SPEECH_COMMAND_ID16=""
CONFIG_CN_SPEECH_COMMAND_ID17="ding shi yi xiao shi" CONFIG_CN_SPEECH_COMMAND_ID17=""
CONFIG_CN_SPEECH_COMMAND_ID18="da kai dian deng" CONFIG_CN_SPEECH_COMMAND_ID18=""
CONFIG_CN_SPEECH_COMMAND_ID19="guan bi dian deng" CONFIG_CN_SPEECH_COMMAND_ID19=""
CONFIG_CN_SPEECH_COMMAND_ID20="" CONFIG_CN_SPEECH_COMMAND_ID20=""
CONFIG_CN_SPEECH_COMMAND_ID21="" CONFIG_CN_SPEECH_COMMAND_ID21=""
CONFIG_CN_SPEECH_COMMAND_ID22="" CONFIG_CN_SPEECH_COMMAND_ID22=""
@@ -766,9 +766,9 @@ CONFIG_CN_SPEECH_COMMAND_ID199=""
# #
# Compiler options # Compiler options
# #
CONFIG_COMPILER_OPTIMIZATION_DEBUG=y # CONFIG_COMPILER_OPTIMIZATION_DEBUG is not set
# CONFIG_COMPILER_OPTIMIZATION_SIZE is not set # CONFIG_COMPILER_OPTIMIZATION_SIZE is not set
# CONFIG_COMPILER_OPTIMIZATION_PERF is not set CONFIG_COMPILER_OPTIMIZATION_PERF=y
# CONFIG_COMPILER_OPTIMIZATION_NONE is not set # CONFIG_COMPILER_OPTIMIZATION_NONE is not set
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_ENABLE=y CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_ENABLE=y
# CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT is not set # CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT is not set
@@ -777,7 +777,8 @@ CONFIG_COMPILER_FLOAT_LIB_FROM_GCCLIB=y
CONFIG_COMPILER_OPTIMIZATION_ASSERTION_LEVEL=2 CONFIG_COMPILER_OPTIMIZATION_ASSERTION_LEVEL=2
# CONFIG_COMPILER_OPTIMIZATION_CHECKS_SILENT is not set # CONFIG_COMPILER_OPTIMIZATION_CHECKS_SILENT is not set
CONFIG_COMPILER_HIDE_PATHS_MACROS=y CONFIG_COMPILER_HIDE_PATHS_MACROS=y
# CONFIG_COMPILER_CXX_EXCEPTIONS is not set CONFIG_COMPILER_CXX_EXCEPTIONS=y
CONFIG_COMPILER_CXX_EXCEPTIONS_EMG_POOL_SIZE=0
CONFIG_COMPILER_CXX_RTTI=y CONFIG_COMPILER_CXX_RTTI=y
CONFIG_COMPILER_STACK_CHECK_MODE_NONE=y CONFIG_COMPILER_STACK_CHECK_MODE_NONE=y
# CONFIG_COMPILER_STACK_CHECK_MODE_NORM is not set # CONFIG_COMPILER_STACK_CHECK_MODE_NORM is not set
@@ -813,334 +814,10 @@ CONFIG_APPTRACE_LOCK_ENABLE=y
# #
# Bluetooth # Bluetooth
# #
CONFIG_BT_ENABLED=y # CONFIG_BT_ENABLED is not set
CONFIG_BT_BLUEDROID_ENABLED=y
# CONFIG_BT_NIMBLE_ENABLED is not set
# CONFIG_BT_CONTROLLER_ONLY is not set
CONFIG_BT_CONTROLLER_ENABLED=y
# CONFIG_BT_CONTROLLER_DISABLED is not set
#
# Bluedroid Options
#
CONFIG_BT_BTC_TASK_STACK_SIZE=3072
CONFIG_BT_BLUEDROID_PINNED_TO_CORE_0=y
# CONFIG_BT_BLUEDROID_PINNED_TO_CORE_1 is not set
CONFIG_BT_BLUEDROID_PINNED_TO_CORE=0
CONFIG_BT_BTU_TASK_STACK_SIZE=4352
# CONFIG_BT_BLUEDROID_MEM_DEBUG is not set
CONFIG_BT_BLUEDROID_ESP_COEX_VSC=y
CONFIG_BT_BLE_ENABLED=y
CONFIG_BT_GATTS_ENABLE=y
# CONFIG_BT_GATTS_PPCP_CHAR_GAP is not set
# CONFIG_BT_BLE_BLUFI_ENABLE is not set
CONFIG_BT_GATT_MAX_SR_PROFILES=8
CONFIG_BT_GATT_MAX_SR_ATTRIBUTES=100
# CONFIG_BT_GATTS_SEND_SERVICE_CHANGE_MANUAL is not set
CONFIG_BT_GATTS_SEND_SERVICE_CHANGE_AUTO=y
CONFIG_BT_GATTS_SEND_SERVICE_CHANGE_MODE=0
# CONFIG_BT_GATTS_ROBUST_CACHING_ENABLED is not set
# CONFIG_BT_GATTS_DEVICE_NAME_WRITABLE is not set
# CONFIG_BT_GATTS_APPEARANCE_WRITABLE is not set
CONFIG_BT_GATTC_ENABLE=y
CONFIG_BT_GATTC_MAX_CACHE_CHAR=40
CONFIG_BT_GATTC_NOTIF_REG_MAX=5
# CONFIG_BT_GATTC_CACHE_NVS_FLASH is not set
CONFIG_BT_GATTC_CONNECT_RETRY_COUNT=3
CONFIG_BT_BLE_SMP_ENABLE=y
# CONFIG_BT_SMP_SLAVE_CON_PARAMS_UPD_ENABLE is not set
# CONFIG_BT_BLE_SMP_ID_RESET_ENABLE is not set
# CONFIG_BT_STACK_NO_LOG is not set
#
# BT DEBUG LOG LEVEL
#
# CONFIG_BT_LOG_HCI_TRACE_LEVEL_NONE is not set
# CONFIG_BT_LOG_HCI_TRACE_LEVEL_ERROR is not set
CONFIG_BT_LOG_HCI_TRACE_LEVEL_WARNING=y
# CONFIG_BT_LOG_HCI_TRACE_LEVEL_API is not set
# CONFIG_BT_LOG_HCI_TRACE_LEVEL_EVENT is not set
# CONFIG_BT_LOG_HCI_TRACE_LEVEL_DEBUG is not set
# CONFIG_BT_LOG_HCI_TRACE_LEVEL_VERBOSE is not set
CONFIG_BT_LOG_HCI_TRACE_LEVEL=2
# CONFIG_BT_LOG_BTM_TRACE_LEVEL_NONE is not set
# CONFIG_BT_LOG_BTM_TRACE_LEVEL_ERROR is not set
CONFIG_BT_LOG_BTM_TRACE_LEVEL_WARNING=y
# CONFIG_BT_LOG_BTM_TRACE_LEVEL_API is not set
# CONFIG_BT_LOG_BTM_TRACE_LEVEL_EVENT is not set
# CONFIG_BT_LOG_BTM_TRACE_LEVEL_DEBUG is not set
# CONFIG_BT_LOG_BTM_TRACE_LEVEL_VERBOSE is not set
CONFIG_BT_LOG_BTM_TRACE_LEVEL=2
# CONFIG_BT_LOG_L2CAP_TRACE_LEVEL_NONE is not set
# CONFIG_BT_LOG_L2CAP_TRACE_LEVEL_ERROR is not set
CONFIG_BT_LOG_L2CAP_TRACE_LEVEL_WARNING=y
# CONFIG_BT_LOG_L2CAP_TRACE_LEVEL_API is not set
# CONFIG_BT_LOG_L2CAP_TRACE_LEVEL_EVENT is not set
# CONFIG_BT_LOG_L2CAP_TRACE_LEVEL_DEBUG is not set
# CONFIG_BT_LOG_L2CAP_TRACE_LEVEL_VERBOSE is not set
CONFIG_BT_LOG_L2CAP_TRACE_LEVEL=2
# CONFIG_BT_LOG_RFCOMM_TRACE_LEVEL_NONE is not set
# CONFIG_BT_LOG_RFCOMM_TRACE_LEVEL_ERROR is not set
CONFIG_BT_LOG_RFCOMM_TRACE_LEVEL_WARNING=y
# CONFIG_BT_LOG_RFCOMM_TRACE_LEVEL_API is not set
# CONFIG_BT_LOG_RFCOMM_TRACE_LEVEL_EVENT is not set
# CONFIG_BT_LOG_RFCOMM_TRACE_LEVEL_DEBUG is not set
# CONFIG_BT_LOG_RFCOMM_TRACE_LEVEL_VERBOSE is not set
CONFIG_BT_LOG_RFCOMM_TRACE_LEVEL=2
# CONFIG_BT_LOG_SDP_TRACE_LEVEL_NONE is not set
# CONFIG_BT_LOG_SDP_TRACE_LEVEL_ERROR is not set
CONFIG_BT_LOG_SDP_TRACE_LEVEL_WARNING=y
# CONFIG_BT_LOG_SDP_TRACE_LEVEL_API is not set
# CONFIG_BT_LOG_SDP_TRACE_LEVEL_EVENT is not set
# CONFIG_BT_LOG_SDP_TRACE_LEVEL_DEBUG is not set
# CONFIG_BT_LOG_SDP_TRACE_LEVEL_VERBOSE is not set
CONFIG_BT_LOG_SDP_TRACE_LEVEL=2
# CONFIG_BT_LOG_GAP_TRACE_LEVEL_NONE is not set
# CONFIG_BT_LOG_GAP_TRACE_LEVEL_ERROR is not set
CONFIG_BT_LOG_GAP_TRACE_LEVEL_WARNING=y
# CONFIG_BT_LOG_GAP_TRACE_LEVEL_API is not set
# CONFIG_BT_LOG_GAP_TRACE_LEVEL_EVENT is not set
# CONFIG_BT_LOG_GAP_TRACE_LEVEL_DEBUG is not set
# CONFIG_BT_LOG_GAP_TRACE_LEVEL_VERBOSE is not set
CONFIG_BT_LOG_GAP_TRACE_LEVEL=2
# CONFIG_BT_LOG_BNEP_TRACE_LEVEL_NONE is not set
# CONFIG_BT_LOG_BNEP_TRACE_LEVEL_ERROR is not set
CONFIG_BT_LOG_BNEP_TRACE_LEVEL_WARNING=y
# CONFIG_BT_LOG_BNEP_TRACE_LEVEL_API is not set
# CONFIG_BT_LOG_BNEP_TRACE_LEVEL_EVENT is not set
# CONFIG_BT_LOG_BNEP_TRACE_LEVEL_DEBUG is not set
# CONFIG_BT_LOG_BNEP_TRACE_LEVEL_VERBOSE is not set
CONFIG_BT_LOG_BNEP_TRACE_LEVEL=2
# CONFIG_BT_LOG_PAN_TRACE_LEVEL_NONE is not set
# CONFIG_BT_LOG_PAN_TRACE_LEVEL_ERROR is not set
CONFIG_BT_LOG_PAN_TRACE_LEVEL_WARNING=y
# CONFIG_BT_LOG_PAN_TRACE_LEVEL_API is not set
# CONFIG_BT_LOG_PAN_TRACE_LEVEL_EVENT is not set
# CONFIG_BT_LOG_PAN_TRACE_LEVEL_DEBUG is not set
# CONFIG_BT_LOG_PAN_TRACE_LEVEL_VERBOSE is not set
CONFIG_BT_LOG_PAN_TRACE_LEVEL=2
# CONFIG_BT_LOG_A2D_TRACE_LEVEL_NONE is not set
# CONFIG_BT_LOG_A2D_TRACE_LEVEL_ERROR is not set
CONFIG_BT_LOG_A2D_TRACE_LEVEL_WARNING=y
# CONFIG_BT_LOG_A2D_TRACE_LEVEL_API is not set
# CONFIG_BT_LOG_A2D_TRACE_LEVEL_EVENT is not set
# CONFIG_BT_LOG_A2D_TRACE_LEVEL_DEBUG is not set
# CONFIG_BT_LOG_A2D_TRACE_LEVEL_VERBOSE is not set
CONFIG_BT_LOG_A2D_TRACE_LEVEL=2
# CONFIG_BT_LOG_AVDT_TRACE_LEVEL_NONE is not set
# CONFIG_BT_LOG_AVDT_TRACE_LEVEL_ERROR is not set
CONFIG_BT_LOG_AVDT_TRACE_LEVEL_WARNING=y
# CONFIG_BT_LOG_AVDT_TRACE_LEVEL_API is not set
# CONFIG_BT_LOG_AVDT_TRACE_LEVEL_EVENT is not set
# CONFIG_BT_LOG_AVDT_TRACE_LEVEL_DEBUG is not set
# CONFIG_BT_LOG_AVDT_TRACE_LEVEL_VERBOSE is not set
CONFIG_BT_LOG_AVDT_TRACE_LEVEL=2
# CONFIG_BT_LOG_AVCT_TRACE_LEVEL_NONE is not set
# CONFIG_BT_LOG_AVCT_TRACE_LEVEL_ERROR is not set
CONFIG_BT_LOG_AVCT_TRACE_LEVEL_WARNING=y
# CONFIG_BT_LOG_AVCT_TRACE_LEVEL_API is not set
# CONFIG_BT_LOG_AVCT_TRACE_LEVEL_EVENT is not set
# CONFIG_BT_LOG_AVCT_TRACE_LEVEL_DEBUG is not set
# CONFIG_BT_LOG_AVCT_TRACE_LEVEL_VERBOSE is not set
CONFIG_BT_LOG_AVCT_TRACE_LEVEL=2
# CONFIG_BT_LOG_AVRC_TRACE_LEVEL_NONE is not set
# CONFIG_BT_LOG_AVRC_TRACE_LEVEL_ERROR is not set
CONFIG_BT_LOG_AVRC_TRACE_LEVEL_WARNING=y
# CONFIG_BT_LOG_AVRC_TRACE_LEVEL_API is not set
# CONFIG_BT_LOG_AVRC_TRACE_LEVEL_EVENT is not set
# CONFIG_BT_LOG_AVRC_TRACE_LEVEL_DEBUG is not set
# CONFIG_BT_LOG_AVRC_TRACE_LEVEL_VERBOSE is not set
CONFIG_BT_LOG_AVRC_TRACE_LEVEL=2
# CONFIG_BT_LOG_MCA_TRACE_LEVEL_NONE is not set
# CONFIG_BT_LOG_MCA_TRACE_LEVEL_ERROR is not set
CONFIG_BT_LOG_MCA_TRACE_LEVEL_WARNING=y
# CONFIG_BT_LOG_MCA_TRACE_LEVEL_API is not set
# CONFIG_BT_LOG_MCA_TRACE_LEVEL_EVENT is not set
# CONFIG_BT_LOG_MCA_TRACE_LEVEL_DEBUG is not set
# CONFIG_BT_LOG_MCA_TRACE_LEVEL_VERBOSE is not set
CONFIG_BT_LOG_MCA_TRACE_LEVEL=2
# CONFIG_BT_LOG_HID_TRACE_LEVEL_NONE is not set
# CONFIG_BT_LOG_HID_TRACE_LEVEL_ERROR is not set
CONFIG_BT_LOG_HID_TRACE_LEVEL_WARNING=y
# CONFIG_BT_LOG_HID_TRACE_LEVEL_API is not set
# CONFIG_BT_LOG_HID_TRACE_LEVEL_EVENT is not set
# CONFIG_BT_LOG_HID_TRACE_LEVEL_DEBUG is not set
# CONFIG_BT_LOG_HID_TRACE_LEVEL_VERBOSE is not set
CONFIG_BT_LOG_HID_TRACE_LEVEL=2
# CONFIG_BT_LOG_APPL_TRACE_LEVEL_NONE is not set
# CONFIG_BT_LOG_APPL_TRACE_LEVEL_ERROR is not set
CONFIG_BT_LOG_APPL_TRACE_LEVEL_WARNING=y
# CONFIG_BT_LOG_APPL_TRACE_LEVEL_API is not set
# CONFIG_BT_LOG_APPL_TRACE_LEVEL_EVENT is not set
# CONFIG_BT_LOG_APPL_TRACE_LEVEL_DEBUG is not set
# CONFIG_BT_LOG_APPL_TRACE_LEVEL_VERBOSE is not set
CONFIG_BT_LOG_APPL_TRACE_LEVEL=2
# CONFIG_BT_LOG_GATT_TRACE_LEVEL_NONE is not set
# CONFIG_BT_LOG_GATT_TRACE_LEVEL_ERROR is not set
CONFIG_BT_LOG_GATT_TRACE_LEVEL_WARNING=y
# CONFIG_BT_LOG_GATT_TRACE_LEVEL_API is not set
# CONFIG_BT_LOG_GATT_TRACE_LEVEL_EVENT is not set
# CONFIG_BT_LOG_GATT_TRACE_LEVEL_DEBUG is not set
# CONFIG_BT_LOG_GATT_TRACE_LEVEL_VERBOSE is not set
CONFIG_BT_LOG_GATT_TRACE_LEVEL=2
# CONFIG_BT_LOG_SMP_TRACE_LEVEL_NONE is not set
# CONFIG_BT_LOG_SMP_TRACE_LEVEL_ERROR is not set
CONFIG_BT_LOG_SMP_TRACE_LEVEL_WARNING=y
# CONFIG_BT_LOG_SMP_TRACE_LEVEL_API is not set
# CONFIG_BT_LOG_SMP_TRACE_LEVEL_EVENT is not set
# CONFIG_BT_LOG_SMP_TRACE_LEVEL_DEBUG is not set
# CONFIG_BT_LOG_SMP_TRACE_LEVEL_VERBOSE is not set
CONFIG_BT_LOG_SMP_TRACE_LEVEL=2
# CONFIG_BT_LOG_BTIF_TRACE_LEVEL_NONE is not set
# CONFIG_BT_LOG_BTIF_TRACE_LEVEL_ERROR is not set
CONFIG_BT_LOG_BTIF_TRACE_LEVEL_WARNING=y
# CONFIG_BT_LOG_BTIF_TRACE_LEVEL_API is not set
# CONFIG_BT_LOG_BTIF_TRACE_LEVEL_EVENT is not set
# CONFIG_BT_LOG_BTIF_TRACE_LEVEL_DEBUG is not set
# CONFIG_BT_LOG_BTIF_TRACE_LEVEL_VERBOSE is not set
CONFIG_BT_LOG_BTIF_TRACE_LEVEL=2
# CONFIG_BT_LOG_BTC_TRACE_LEVEL_NONE is not set
# CONFIG_BT_LOG_BTC_TRACE_LEVEL_ERROR is not set
CONFIG_BT_LOG_BTC_TRACE_LEVEL_WARNING=y
# CONFIG_BT_LOG_BTC_TRACE_LEVEL_API is not set
# CONFIG_BT_LOG_BTC_TRACE_LEVEL_EVENT is not set
# CONFIG_BT_LOG_BTC_TRACE_LEVEL_DEBUG is not set
# CONFIG_BT_LOG_BTC_TRACE_LEVEL_VERBOSE is not set
CONFIG_BT_LOG_BTC_TRACE_LEVEL=2
# CONFIG_BT_LOG_OSI_TRACE_LEVEL_NONE is not set
# CONFIG_BT_LOG_OSI_TRACE_LEVEL_ERROR is not set
CONFIG_BT_LOG_OSI_TRACE_LEVEL_WARNING=y
# CONFIG_BT_LOG_OSI_TRACE_LEVEL_API is not set
# CONFIG_BT_LOG_OSI_TRACE_LEVEL_EVENT is not set
# CONFIG_BT_LOG_OSI_TRACE_LEVEL_DEBUG is not set
# CONFIG_BT_LOG_OSI_TRACE_LEVEL_VERBOSE is not set
CONFIG_BT_LOG_OSI_TRACE_LEVEL=2
# CONFIG_BT_LOG_BLUFI_TRACE_LEVEL_NONE is not set
# CONFIG_BT_LOG_BLUFI_TRACE_LEVEL_ERROR is not set
CONFIG_BT_LOG_BLUFI_TRACE_LEVEL_WARNING=y
# CONFIG_BT_LOG_BLUFI_TRACE_LEVEL_API is not set
# CONFIG_BT_LOG_BLUFI_TRACE_LEVEL_EVENT is not set
# CONFIG_BT_LOG_BLUFI_TRACE_LEVEL_DEBUG is not set
# CONFIG_BT_LOG_BLUFI_TRACE_LEVEL_VERBOSE is not set
CONFIG_BT_LOG_BLUFI_TRACE_LEVEL=2
# end of BT DEBUG LOG LEVEL
CONFIG_BT_ACL_CONNECTIONS=4
CONFIG_BT_MULTI_CONNECTION_ENBALE=y
# CONFIG_BT_ALLOCATION_FROM_SPIRAM_FIRST is not set
# CONFIG_BT_BLE_DYNAMIC_ENV_MEMORY is not set
# CONFIG_BT_BLE_HOST_QUEUE_CONG_CHECK is not set
CONFIG_BT_SMP_ENABLE=y
CONFIG_BT_SMP_MAX_BONDS=15
# CONFIG_BT_BLE_ACT_SCAN_REP_ADV_SCAN is not set
CONFIG_BT_BLE_ESTAB_LINK_CONN_TOUT=30
CONFIG_BT_MAX_DEVICE_NAME_LEN=32
CONFIG_BT_BLE_RPA_TIMEOUT=900
CONFIG_BT_BLE_50_FEATURES_SUPPORTED=y
CONFIG_BT_BLE_42_FEATURES_SUPPORTED=y
# CONFIG_BT_BLE_HIGH_DUTY_ADV_INTERVAL is not set
# CONFIG_BT_ABORT_WHEN_ALLOCATION_FAILS is not set
# end of Bluedroid Options
#
# Controller Options
#
CONFIG_BT_CTRL_MODE_EFF=1
CONFIG_BT_CTRL_BLE_MAX_ACT=6
CONFIG_BT_CTRL_BLE_MAX_ACT_EFF=6
CONFIG_BT_CTRL_BLE_STATIC_ACL_TX_BUF_NB=0
CONFIG_BT_CTRL_PINNED_TO_CORE_0=y
# CONFIG_BT_CTRL_PINNED_TO_CORE_1 is not set
CONFIG_BT_CTRL_PINNED_TO_CORE=0
CONFIG_BT_CTRL_HCI_MODE_VHCI=y
# CONFIG_BT_CTRL_HCI_MODE_UART_H4 is not set
CONFIG_BT_CTRL_HCI_TL=1
CONFIG_BT_CTRL_ADV_DUP_FILT_MAX=30
CONFIG_BT_BLE_CCA_MODE_NONE=y
# CONFIG_BT_BLE_CCA_MODE_HW is not set
# CONFIG_BT_BLE_CCA_MODE_SW is not set
CONFIG_BT_BLE_CCA_MODE=0
CONFIG_BT_CTRL_HW_CCA_VAL=20
CONFIG_BT_CTRL_HW_CCA_EFF=0
CONFIG_BT_CTRL_CE_LENGTH_TYPE_ORIG=y
# CONFIG_BT_CTRL_CE_LENGTH_TYPE_CE is not set
# CONFIG_BT_CTRL_CE_LENGTH_TYPE_SD is not set
CONFIG_BT_CTRL_CE_LENGTH_TYPE_EFF=0
CONFIG_BT_CTRL_TX_ANTENNA_INDEX_0=y
# CONFIG_BT_CTRL_TX_ANTENNA_INDEX_1 is not set
CONFIG_BT_CTRL_TX_ANTENNA_INDEX_EFF=0
CONFIG_BT_CTRL_RX_ANTENNA_INDEX_0=y
# CONFIG_BT_CTRL_RX_ANTENNA_INDEX_1 is not set
CONFIG_BT_CTRL_RX_ANTENNA_INDEX_EFF=0
# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_N24 is not set
# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_N21 is not set
# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_N18 is not set
# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_N15 is not set
# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_N12 is not set
# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_N9 is not set
# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_N6 is not set
# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_N3 is not set
# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_N0 is not set
# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_P3 is not set
# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_P6 is not set
CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_P9=y
# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_P12 is not set
# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_P15 is not set
# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_P18 is not set
# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_P20 is not set
CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_EFF=11
CONFIG_BT_CTRL_BLE_ADV_REPORT_FLOW_CTRL_SUPP=y
CONFIG_BT_CTRL_BLE_ADV_REPORT_FLOW_CTRL_NUM=100
CONFIG_BT_CTRL_BLE_ADV_REPORT_DISCARD_THRSHOLD=20
CONFIG_BT_CTRL_BLE_SCAN_DUPL=y
CONFIG_BT_CTRL_SCAN_DUPL_TYPE_DEVICE=y
# CONFIG_BT_CTRL_SCAN_DUPL_TYPE_DATA is not set
# CONFIG_BT_CTRL_SCAN_DUPL_TYPE_DATA_DEVICE is not set
CONFIG_BT_CTRL_SCAN_DUPL_TYPE=0
CONFIG_BT_CTRL_SCAN_DUPL_CACHE_SIZE=100
CONFIG_BT_CTRL_DUPL_SCAN_CACHE_REFRESH_PERIOD=0
# CONFIG_BT_CTRL_BLE_MESH_SCAN_DUPL_EN is not set
# CONFIG_BT_CTRL_COEX_PHY_CODED_TX_RX_TLIM_EN is not set
CONFIG_BT_CTRL_COEX_PHY_CODED_TX_RX_TLIM_DIS=y
CONFIG_BT_CTRL_COEX_PHY_CODED_TX_RX_TLIM_EFF=0
#
# MODEM SLEEP Options
#
# CONFIG_BT_CTRL_MODEM_SLEEP is not set
# end of MODEM SLEEP Options
CONFIG_BT_CTRL_SLEEP_MODE_EFF=0
CONFIG_BT_CTRL_SLEEP_CLOCK_EFF=0
CONFIG_BT_CTRL_HCI_TL_EFF=1
# CONFIG_BT_CTRL_AGC_RECORRECT_EN is not set
# CONFIG_BT_CTRL_SCAN_BACKOFF_UPPERLIMITMAX is not set
# CONFIG_BT_BLE_ADV_DATA_LENGTH_ZERO_AUX is not set
CONFIG_BT_CTRL_CHAN_ASS_EN=y
CONFIG_BT_CTRL_LE_PING_EN=y
#
# BLE disconnect when instant passed
#
# CONFIG_BT_CTRL_BLE_LLCP_CONN_UPDATE is not set
# CONFIG_BT_CTRL_BLE_LLCP_CHAN_MAP_UPDATE is not set
# CONFIG_BT_CTRL_BLE_LLCP_PHY_UPDATE is not set
# end of BLE disconnect when instant passed
# CONFIG_BT_CTRL_RUN_IN_FLASH_ONLY is not set
# end of Controller Options
#
# Common Options
#
CONFIG_BT_ALARM_MAX_NUM=50 CONFIG_BT_ALARM_MAX_NUM=50
# end of Common Options
# CONFIG_BT_HCI_LOG_DEBUG_EN is not set
# end of Bluetooth # end of Bluetooth
# CONFIG_BLE_MESH is not set
# #
# Console Library # Console Library
# #
@@ -1247,8 +924,7 @@ CONFIG_ESP_TLS_USE_DS_PERIPHERAL=y
# Wireless Coexistence # Wireless Coexistence
# #
CONFIG_ESP_COEX_ENABLED=y CONFIG_ESP_COEX_ENABLED=y
CONFIG_ESP_COEX_SW_COEXIST_ENABLE=y # CONFIG_ESP_COEX_EXTERNAL_COEXIST_ENABLE is not set
# CONFIG_ESP_COEX_POWER_MANAGEMENT is not set
# CONFIG_ESP_COEX_GPIO_DEBUG is not set # CONFIG_ESP_COEX_GPIO_DEBUG is not set
# end of Wireless Coexistence # end of Wireless Coexistence
@@ -1256,6 +932,7 @@ CONFIG_ESP_COEX_SW_COEXIST_ENABLE=y
# Common ESP-related # Common ESP-related
# #
CONFIG_ESP_ERR_TO_NAME_LOOKUP=y CONFIG_ESP_ERR_TO_NAME_LOOKUP=y
CONFIG_ESP_ALLOW_BSS_SEG_EXTERNAL_MEMORY=y
# end of Common ESP-related # end of Common ESP-related
# #
@@ -1327,7 +1004,7 @@ CONFIG_GPTIMER_ISR_HANDLER_IN_IRAM=y
# #
# ESP-Driver:SPI Configurations # ESP-Driver:SPI Configurations
# #
# CONFIG_SPI_MASTER_IN_IRAM is not set CONFIG_SPI_MASTER_IN_IRAM=y
CONFIG_SPI_MASTER_ISR_IN_IRAM=y CONFIG_SPI_MASTER_ISR_IN_IRAM=y
# CONFIG_SPI_SLAVE_IN_IRAM is not set # CONFIG_SPI_SLAVE_IN_IRAM is not set
CONFIG_SPI_SLAVE_ISR_IN_IRAM=y CONFIG_SPI_SLAVE_ISR_IN_IRAM=y
@@ -1413,7 +1090,7 @@ CONFIG_HTTPD_PURGE_BUF_LEN=32
# ESP HTTPS OTA # ESP HTTPS OTA
# #
# CONFIG_ESP_HTTPS_OTA_DECRYPT_CB is not set # 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 # end of ESP HTTPS OTA
# #
@@ -1509,6 +1186,7 @@ CONFIG_XTAL_FREQ=40
# end of Main XTAL Config # end of Main XTAL Config
CONFIG_ESP_SPI_BUS_LOCK_ISR_FUNCS_IN_IRAM=y CONFIG_ESP_SPI_BUS_LOCK_ISR_FUNCS_IN_IRAM=y
CONFIG_ESP_SPI_BUS_LOCK_FUNCS_IN_IRAM=y
# end of Hardware Settings # end of Hardware Settings
# #
@@ -1596,15 +1274,14 @@ CONFIG_SPIRAM_SPEED_80M=y
CONFIG_SPIRAM_SPEED=80 CONFIG_SPIRAM_SPEED=80
# CONFIG_SPIRAM_ECC_ENABLE is not set # CONFIG_SPIRAM_ECC_ENABLE is not set
CONFIG_SPIRAM_BOOT_INIT=y CONFIG_SPIRAM_BOOT_INIT=y
# CONFIG_SPIRAM_IGNORE_NOTFOUND is not set
# CONFIG_SPIRAM_USE_MEMMAP is not set # CONFIG_SPIRAM_USE_MEMMAP is not set
# CONFIG_SPIRAM_USE_CAPS_ALLOC is not set # CONFIG_SPIRAM_USE_CAPS_ALLOC is not set
CONFIG_SPIRAM_USE_MALLOC=y CONFIG_SPIRAM_USE_MALLOC=y
CONFIG_SPIRAM_MEMTEST=y CONFIG_SPIRAM_MEMTEST=y
CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL=16384 CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL=4096
# CONFIG_SPIRAM_TRY_ALLOCATE_WIFI_LWIP is not set CONFIG_SPIRAM_TRY_ALLOCATE_WIFI_LWIP=y
CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL=32768 CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL=65536
# CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY is not set CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY=y
# CONFIG_SPIRAM_ALLOW_NOINIT_SEG_EXTERNAL_MEMORY is not set # CONFIG_SPIRAM_ALLOW_NOINIT_SEG_EXTERNAL_MEMORY is not set
# end of SPI RAM config # end of SPI RAM config
# end of ESP PSRAM # end of ESP PSRAM
@@ -1636,13 +1313,12 @@ CONFIG_ESP32S3_ICACHE_ASSOCIATED_WAYS=8
CONFIG_ESP32S3_INSTRUCTION_CACHE_LINE_32B=y CONFIG_ESP32S3_INSTRUCTION_CACHE_LINE_32B=y
CONFIG_ESP32S3_INSTRUCTION_CACHE_LINE_SIZE=32 CONFIG_ESP32S3_INSTRUCTION_CACHE_LINE_SIZE=32
# CONFIG_ESP32S3_DATA_CACHE_16KB is not set # CONFIG_ESP32S3_DATA_CACHE_16KB is not set
CONFIG_ESP32S3_DATA_CACHE_32KB=y # CONFIG_ESP32S3_DATA_CACHE_32KB is not set
# CONFIG_ESP32S3_DATA_CACHE_64KB is not set CONFIG_ESP32S3_DATA_CACHE_64KB=y
CONFIG_ESP32S3_DATA_CACHE_SIZE=0x8000 CONFIG_ESP32S3_DATA_CACHE_SIZE=0x10000
# CONFIG_ESP32S3_DATA_CACHE_4WAYS is not set # CONFIG_ESP32S3_DATA_CACHE_4WAYS is not set
CONFIG_ESP32S3_DATA_CACHE_8WAYS=y CONFIG_ESP32S3_DATA_CACHE_8WAYS=y
CONFIG_ESP32S3_DCACHE_ASSOCIATED_WAYS=8 CONFIG_ESP32S3_DCACHE_ASSOCIATED_WAYS=8
# CONFIG_ESP32S3_DATA_CACHE_LINE_16B is not set
CONFIG_ESP32S3_DATA_CACHE_LINE_32B=y CONFIG_ESP32S3_DATA_CACHE_LINE_32B=y
# CONFIG_ESP32S3_DATA_CACHE_LINE_64B is not set # CONFIG_ESP32S3_DATA_CACHE_LINE_64B is not set
CONFIG_ESP32S3_DATA_CACHE_LINE_SIZE=32 CONFIG_ESP32S3_DATA_CACHE_LINE_SIZE=32
@@ -1679,7 +1355,7 @@ CONFIG_ESP_SYSTEM_MEMPROT_FEATURE_LOCK=y
CONFIG_ESP_SYSTEM_EVENT_QUEUE_SIZE=32 CONFIG_ESP_SYSTEM_EVENT_QUEUE_SIZE=32
CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=2304 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_CPU0=y
# CONFIG_ESP_MAIN_TASK_AFFINITY_CPU1 is not set # CONFIG_ESP_MAIN_TASK_AFFINITY_CPU1 is not set
# CONFIG_ESP_MAIN_TASK_AFFINITY_NO_AFFINITY is not set # CONFIG_ESP_MAIN_TASK_AFFINITY_NO_AFFINITY is not set
@@ -1757,11 +1433,11 @@ CONFIG_ESP_TIMER_IMPL_SYSTIMER=y
# Wi-Fi # Wi-Fi
# #
CONFIG_ESP_WIFI_ENABLED=y CONFIG_ESP_WIFI_ENABLED=y
CONFIG_ESP_WIFI_STATIC_RX_BUFFER_NUM=10 CONFIG_ESP_WIFI_STATIC_RX_BUFFER_NUM=2
CONFIG_ESP_WIFI_DYNAMIC_RX_BUFFER_NUM=32 CONFIG_ESP_WIFI_DYNAMIC_RX_BUFFER_NUM=32
CONFIG_ESP_WIFI_STATIC_TX_BUFFER=y CONFIG_ESP_WIFI_STATIC_TX_BUFFER=y
CONFIG_ESP_WIFI_TX_BUFFER_TYPE=0 CONFIG_ESP_WIFI_TX_BUFFER_TYPE=0
CONFIG_ESP_WIFI_STATIC_TX_BUFFER_NUM=16 CONFIG_ESP_WIFI_STATIC_TX_BUFFER_NUM=4
CONFIG_ESP_WIFI_CACHE_TX_BUFFER_NUM=32 CONFIG_ESP_WIFI_CACHE_TX_BUFFER_NUM=32
CONFIG_ESP_WIFI_STATIC_RX_MGMT_BUFFER=y CONFIG_ESP_WIFI_STATIC_RX_MGMT_BUFFER=y
# CONFIG_ESP_WIFI_DYNAMIC_RX_MGMT_BUFFER is not set # CONFIG_ESP_WIFI_DYNAMIC_RX_MGMT_BUFFER is not set
@@ -1769,9 +1445,9 @@ CONFIG_ESP_WIFI_DYNAMIC_RX_MGMT_BUF=0
CONFIG_ESP_WIFI_RX_MGMT_BUF_NUM_DEF=5 CONFIG_ESP_WIFI_RX_MGMT_BUF_NUM_DEF=5
# CONFIG_ESP_WIFI_CSI_ENABLED is not set # CONFIG_ESP_WIFI_CSI_ENABLED is not set
CONFIG_ESP_WIFI_AMPDU_TX_ENABLED=y CONFIG_ESP_WIFI_AMPDU_TX_ENABLED=y
CONFIG_ESP_WIFI_TX_BA_WIN=6 CONFIG_ESP_WIFI_TX_BA_WIN=2
CONFIG_ESP_WIFI_AMPDU_RX_ENABLED=y CONFIG_ESP_WIFI_AMPDU_RX_ENABLED=y
CONFIG_ESP_WIFI_RX_BA_WIN=6 CONFIG_ESP_WIFI_RX_BA_WIN=2
# CONFIG_ESP_WIFI_AMSDU_TX_ENABLED is not set # CONFIG_ESP_WIFI_AMSDU_TX_ENABLED is not set
CONFIG_ESP_WIFI_NVS_ENABLED=y CONFIG_ESP_WIFI_NVS_ENABLED=y
CONFIG_ESP_WIFI_TASK_PINNED_TO_CORE_0=y CONFIG_ESP_WIFI_TASK_PINNED_TO_CORE_0=y
@@ -1902,16 +1578,19 @@ CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=2048
CONFIG_FREERTOS_TIMER_QUEUE_LENGTH=10 CONFIG_FREERTOS_TIMER_QUEUE_LENGTH=10
CONFIG_FREERTOS_QUEUE_REGISTRY_SIZE=0 CONFIG_FREERTOS_QUEUE_REGISTRY_SIZE=0
CONFIG_FREERTOS_TASK_NOTIFICATION_ARRAY_ENTRIES=1 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_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 # CONFIG_FREERTOS_USE_APPLICATION_TASK_TAG is not set
# end of Kernel # end of Kernel
# #
# Port # Port
# #
CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER=y
# CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK is not set # CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK is not set
CONFIG_FREERTOS_TLSP_DELETION_CALLBACKS=y CONFIG_FREERTOS_TLSP_DELETION_CALLBACKS=y
# CONFIG_FREERTOS_TASK_PRE_DELETION_HOOK is not set # CONFIG_FREERTOS_TASK_PRE_DELETION_HOOK is not set
@@ -1923,6 +1602,7 @@ CONFIG_FREERTOS_TICK_SUPPORT_SYSTIMER=y
CONFIG_FREERTOS_CORETIMER_SYSTIMER_LVL1=y CONFIG_FREERTOS_CORETIMER_SYSTIMER_LVL1=y
# CONFIG_FREERTOS_CORETIMER_SYSTIMER_LVL3 is not set # CONFIG_FREERTOS_CORETIMER_SYSTIMER_LVL3 is not set
CONFIG_FREERTOS_SYSTICK_USES_SYSTIMER=y 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_PLACE_FUNCTIONS_INTO_FLASH is not set
# CONFIG_FREERTOS_CHECK_PORT_CRITICAL_COMPLIANCE is not set # CONFIG_FREERTOS_CHECK_PORT_CRITICAL_COMPLIANCE is not set
# end of Port # end of Port
@@ -2071,6 +1751,7 @@ CONFIG_LWIP_TCP_OOSEQ_MAX_PBUFS=4
CONFIG_LWIP_TCP_OVERSIZE_MSS=y CONFIG_LWIP_TCP_OVERSIZE_MSS=y
# CONFIG_LWIP_TCP_OVERSIZE_QUARTER_MSS is not set # CONFIG_LWIP_TCP_OVERSIZE_QUARTER_MSS is not set
# CONFIG_LWIP_TCP_OVERSIZE_DISABLE is not set # CONFIG_LWIP_TCP_OVERSIZE_DISABLE is not set
# CONFIG_LWIP_WND_SCALE is not set
CONFIG_LWIP_TCP_RTO_TIME=1500 CONFIG_LWIP_TCP_RTO_TIME=1500
# end of TCP # end of TCP
@@ -2602,10 +2283,6 @@ CONFIG_WL_SECTOR_SIZE=4096
# #
CONFIG_WIFI_PROV_SCAN_MAX_ENTRIES=16 CONFIG_WIFI_PROV_SCAN_MAX_ENTRIES=16
CONFIG_WIFI_PROV_AUTOSTOP_TIMEOUT=30 CONFIG_WIFI_PROV_AUTOSTOP_TIMEOUT=30
# CONFIG_WIFI_PROV_BLE_BONDING is not set
# CONFIG_WIFI_PROV_BLE_FORCE_ENCRYPTION is not set
# CONFIG_WIFI_PROV_BLE_NOTIFY is not set
# CONFIG_WIFI_PROV_KEEP_BLE_ON_AFTER_PROV is not set
CONFIG_WIFI_PROV_STA_ALL_CHANNEL_SCAN=y CONFIG_WIFI_PROV_STA_ALL_CHANNEL_SCAN=y
# CONFIG_WIFI_PROV_STA_FAST_SCAN is not set # CONFIG_WIFI_PROV_STA_FAST_SCAN is not set
# end of Wi-Fi Provisioning Manager # end of Wi-Fi Provisioning Manager
@@ -2635,6 +2312,13 @@ CONFIG_DSP_MAX_FFT_SIZE_4096=y
CONFIG_DSP_MAX_FFT_SIZE=4096 CONFIG_DSP_MAX_FFT_SIZE=4096
# end of DSP Library # 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 # LVGL configuration
# #
@@ -2667,8 +2351,8 @@ CONFIG_LV_MEM_BUF_MAX_NUM=16
# #
# HAL Settings # HAL Settings
# #
CONFIG_LV_DISP_DEF_REFR_PERIOD=30 CONFIG_LV_DISP_DEF_REFR_PERIOD=10
CONFIG_LV_INDEV_DEF_READ_PERIOD=30 CONFIG_LV_INDEV_DEF_READ_PERIOD=10
# CONFIG_LV_TICK_CUSTOM is not set # CONFIG_LV_TICK_CUSTOM is not set
CONFIG_LV_DPI_DEF=130 CONFIG_LV_DPI_DEF=130
# end of HAL Settings # end of HAL Settings
@@ -2681,10 +2365,10 @@ CONFIG_LV_DPI_DEF=130
# Drawing # Drawing
# #
CONFIG_LV_DRAW_COMPLEX=y CONFIG_LV_DRAW_COMPLEX=y
CONFIG_LV_SHADOW_CACHE_SIZE=0 CONFIG_LV_SHADOW_CACHE_SIZE=4
CONFIG_LV_CIRCLE_CACHE_SIZE=4 CONFIG_LV_CIRCLE_CACHE_SIZE=4
CONFIG_LV_LAYER_SIMPLE_BUF_SIZE=24576 CONFIG_LV_LAYER_SIMPLE_BUF_SIZE=10240
CONFIG_LV_IMG_CACHE_DEF_SIZE=0 CONFIG_LV_IMG_CACHE_DEF_SIZE=128
CONFIG_LV_GRADIENT_MAX_STOPS=2 CONFIG_LV_GRADIENT_MAX_STOPS=2
CONFIG_LV_GRAD_CACHE_DEF_SIZE=0 CONFIG_LV_GRAD_CACHE_DEF_SIZE=0
# CONFIG_LV_DITHER_GRADIENT is not set # CONFIG_LV_DITHER_GRADIENT is not set
@@ -2714,9 +2398,9 @@ CONFIG_LV_DISP_ROT_MAX_BUF=10240
# #
CONFIG_LV_USE_ASSERT_NULL=y CONFIG_LV_USE_ASSERT_NULL=y
CONFIG_LV_USE_ASSERT_MALLOC=y CONFIG_LV_USE_ASSERT_MALLOC=y
# CONFIG_LV_USE_ASSERT_STYLE is not set CONFIG_LV_USE_ASSERT_STYLE=y
# CONFIG_LV_USE_ASSERT_MEM_INTEGRITY is not set # CONFIG_LV_USE_ASSERT_MEM_INTEGRITY is not set
# CONFIG_LV_USE_ASSERT_OBJ is not set CONFIG_LV_USE_ASSERT_OBJ=y
CONFIG_LV_ASSERT_HANDLER_INCLUDE="assert.h" CONFIG_LV_ASSERT_HANDLER_INCLUDE="assert.h"
# end of Asserts # end of Asserts
@@ -2762,7 +2446,7 @@ CONFIG_LV_ATTRIBUTE_MEM_ALIGN_SIZE=1
CONFIG_LV_FONT_MONTSERRAT_12=y CONFIG_LV_FONT_MONTSERRAT_12=y
CONFIG_LV_FONT_MONTSERRAT_14=y CONFIG_LV_FONT_MONTSERRAT_14=y
CONFIG_LV_FONT_MONTSERRAT_16=y CONFIG_LV_FONT_MONTSERRAT_16=y
# CONFIG_LV_FONT_MONTSERRAT_18 is not set CONFIG_LV_FONT_MONTSERRAT_18=y
# CONFIG_LV_FONT_MONTSERRAT_20 is not set # CONFIG_LV_FONT_MONTSERRAT_20 is not set
# CONFIG_LV_FONT_MONTSERRAT_22 is not set # CONFIG_LV_FONT_MONTSERRAT_22 is not set
# CONFIG_LV_FONT_MONTSERRAT_24 is not set # CONFIG_LV_FONT_MONTSERRAT_24 is not set
@@ -2781,7 +2465,7 @@ CONFIG_LV_FONT_MONTSERRAT_16=y
# CONFIG_LV_FONT_MONTSERRAT_12_SUBPX is not set # CONFIG_LV_FONT_MONTSERRAT_12_SUBPX is not set
# CONFIG_LV_FONT_MONTSERRAT_28_COMPRESSED is not set # CONFIG_LV_FONT_MONTSERRAT_28_COMPRESSED is not set
# CONFIG_LV_FONT_DEJAVU_16_PERSIAN_HEBREW is not set # CONFIG_LV_FONT_DEJAVU_16_PERSIAN_HEBREW is not set
# CONFIG_LV_FONT_SIMSUN_16_CJK is not set CONFIG_LV_FONT_SIMSUN_16_CJK=y
# CONFIG_LV_FONT_UNSCII_8 is not set # CONFIG_LV_FONT_UNSCII_8 is not set
# CONFIG_LV_FONT_UNSCII_16 is not set # CONFIG_LV_FONT_UNSCII_16 is not set
# CONFIG_LV_FONT_CUSTOM is not set # CONFIG_LV_FONT_CUSTOM is not set
@@ -2789,7 +2473,7 @@ CONFIG_LV_FONT_MONTSERRAT_16=y
# CONFIG_LV_FONT_DEFAULT_MONTSERRAT_8 is not set # CONFIG_LV_FONT_DEFAULT_MONTSERRAT_8 is not set
# CONFIG_LV_FONT_DEFAULT_MONTSERRAT_12 is not set # CONFIG_LV_FONT_DEFAULT_MONTSERRAT_12 is not set
CONFIG_LV_FONT_DEFAULT_MONTSERRAT_14=y # CONFIG_LV_FONT_DEFAULT_MONTSERRAT_14 is not set
# CONFIG_LV_FONT_DEFAULT_MONTSERRAT_16 is not set # CONFIG_LV_FONT_DEFAULT_MONTSERRAT_16 is not set
# CONFIG_LV_FONT_DEFAULT_MONTSERRAT_18 is not set # CONFIG_LV_FONT_DEFAULT_MONTSERRAT_18 is not set
# CONFIG_LV_FONT_DEFAULT_MONTSERRAT_20 is not set # CONFIG_LV_FONT_DEFAULT_MONTSERRAT_20 is not set
@@ -2810,7 +2494,7 @@ CONFIG_LV_FONT_DEFAULT_MONTSERRAT_14=y
# CONFIG_LV_FONT_DEFAULT_MONTSERRAT_12_SUBPX is not set # CONFIG_LV_FONT_DEFAULT_MONTSERRAT_12_SUBPX is not set
# CONFIG_LV_FONT_DEFAULT_MONTSERRAT_28_COMPRESSED is not set # CONFIG_LV_FONT_DEFAULT_MONTSERRAT_28_COMPRESSED is not set
# CONFIG_LV_FONT_DEFAULT_DEJAVU_16_PERSIAN_HEBREW is not set # CONFIG_LV_FONT_DEFAULT_DEJAVU_16_PERSIAN_HEBREW is not set
# CONFIG_LV_FONT_DEFAULT_SIMSUN_16_CJK is not set CONFIG_LV_FONT_DEFAULT_SIMSUN_16_CJK=y
# CONFIG_LV_FONT_DEFAULT_UNSCII_8 is not set # CONFIG_LV_FONT_DEFAULT_UNSCII_8 is not set
# CONFIG_LV_FONT_DEFAULT_UNSCII_16 is not set # CONFIG_LV_FONT_DEFAULT_UNSCII_16 is not set
# CONFIG_LV_FONT_FMT_TXT_LARGE is not set # CONFIG_LV_FONT_FMT_TXT_LARGE is not set
@@ -2827,7 +2511,10 @@ CONFIG_LV_TXT_ENC_UTF8=y
CONFIG_LV_TXT_BREAK_CHARS=" ,.;:-_" CONFIG_LV_TXT_BREAK_CHARS=" ,.;:-_"
CONFIG_LV_TXT_LINE_BREAK_LONG_LEN=0 CONFIG_LV_TXT_LINE_BREAK_LONG_LEN=0
CONFIG_LV_TXT_COLOR_CMD="#" CONFIG_LV_TXT_COLOR_CMD="#"
# CONFIG_LV_USE_BIDI is not set CONFIG_LV_USE_BIDI=y
# CONFIG_LV_BIDI_DIR_LTR is not set
# CONFIG_LV_BIDI_DIR_RTL is not set
CONFIG_LV_BIDI_DIR_AUTO=y
# CONFIG_LV_USE_ARABIC_PERSIAN_CHARS is not set # CONFIG_LV_USE_ARABIC_PERSIAN_CHARS is not set
# end of Text Settings # end of Text Settings
@@ -2907,7 +2594,7 @@ CONFIG_LV_USE_GRID=y
# CONFIG_LV_USE_FS_WIN32 is not set # CONFIG_LV_USE_FS_WIN32 is not set
# CONFIG_LV_USE_FS_FATFS is not set # CONFIG_LV_USE_FS_FATFS is not set
# CONFIG_LV_USE_FS_LITTLEFS is not set # CONFIG_LV_USE_FS_LITTLEFS is not set
# CONFIG_LV_USE_PNG is not set CONFIG_LV_USE_PNG=y
# CONFIG_LV_USE_BMP is not set # CONFIG_LV_USE_BMP is not set
# CONFIG_LV_USE_SJPG is not set # CONFIG_LV_USE_SJPG is not set
CONFIG_LV_USE_GIF=y CONFIG_LV_USE_GIF=y
@@ -2974,16 +2661,17 @@ CONFIG_LOG_BOOTLOADER_LEVEL=3
CONFIG_FLASHMODE_DIO=y CONFIG_FLASHMODE_DIO=y
# CONFIG_FLASHMODE_DOUT is not set # CONFIG_FLASHMODE_DOUT is not set
CONFIG_MONITOR_BAUD=115200 CONFIG_MONITOR_BAUD=115200
CONFIG_OPTIMIZATION_LEVEL_DEBUG=y # CONFIG_OPTIMIZATION_LEVEL_DEBUG is not set
CONFIG_COMPILER_OPTIMIZATION_LEVEL_DEBUG=y # CONFIG_COMPILER_OPTIMIZATION_LEVEL_DEBUG is not set
CONFIG_COMPILER_OPTIMIZATION_DEFAULT=y # CONFIG_COMPILER_OPTIMIZATION_DEFAULT is not set
# CONFIG_OPTIMIZATION_LEVEL_RELEASE is not set # CONFIG_OPTIMIZATION_LEVEL_RELEASE is not set
# CONFIG_COMPILER_OPTIMIZATION_LEVEL_RELEASE is not set # CONFIG_COMPILER_OPTIMIZATION_LEVEL_RELEASE is not set
CONFIG_OPTIMIZATION_ASSERTIONS_ENABLED=y CONFIG_OPTIMIZATION_ASSERTIONS_ENABLED=y
# CONFIG_OPTIMIZATION_ASSERTIONS_SILENT is not set # CONFIG_OPTIMIZATION_ASSERTIONS_SILENT is not set
# CONFIG_OPTIMIZATION_ASSERTIONS_DISABLED is not set # CONFIG_OPTIMIZATION_ASSERTIONS_DISABLED is not set
CONFIG_OPTIMIZATION_ASSERTION_LEVEL=2 CONFIG_OPTIMIZATION_ASSERTION_LEVEL=2
# CONFIG_CXX_EXCEPTIONS is not set CONFIG_CXX_EXCEPTIONS=y
CONFIG_CXX_EXCEPTIONS_EMG_POOL_SIZE=0
CONFIG_STACK_CHECK_NONE=y CONFIG_STACK_CHECK_NONE=y
# CONFIG_STACK_CHECK_NORM is not set # CONFIG_STACK_CHECK_NORM is not set
# CONFIG_STACK_CHECK_STRONG is not set # CONFIG_STACK_CHECK_STRONG is not set
@@ -2992,199 +2680,15 @@ CONFIG_STACK_CHECK_NONE=y
# CONFIG_ESP32_APPTRACE_DEST_TRAX is not set # CONFIG_ESP32_APPTRACE_DEST_TRAX is not set
CONFIG_ESP32_APPTRACE_DEST_NONE=y CONFIG_ESP32_APPTRACE_DEST_NONE=y
CONFIG_ESP32_APPTRACE_LOCK_ENABLE=y CONFIG_ESP32_APPTRACE_LOCK_ENABLE=y
CONFIG_BLUEDROID_ENABLED=y # CONFIG_EXTERNAL_COEX_ENABLE is not set
# CONFIG_NIMBLE_ENABLED is not set # CONFIG_ESP_WIFI_EXTERNAL_COEXIST_ENABLE is not set
CONFIG_BTC_TASK_STACK_SIZE=3072
CONFIG_BLUEDROID_PINNED_TO_CORE_0=y
# CONFIG_BLUEDROID_PINNED_TO_CORE_1 is not set
CONFIG_BLUEDROID_PINNED_TO_CORE=0
CONFIG_BTU_TASK_STACK_SIZE=4352
# CONFIG_BLUEDROID_MEM_DEBUG is not set
CONFIG_GATTS_ENABLE=y
# CONFIG_GATTS_SEND_SERVICE_CHANGE_MANUAL is not set
CONFIG_GATTS_SEND_SERVICE_CHANGE_AUTO=y
CONFIG_GATTS_SEND_SERVICE_CHANGE_MODE=0
CONFIG_GATTC_ENABLE=y
# CONFIG_GATTC_CACHE_NVS_FLASH is not set
CONFIG_BLE_SMP_ENABLE=y
# CONFIG_SMP_SLAVE_CON_PARAMS_UPD_ENABLE is not set
# CONFIG_HCI_TRACE_LEVEL_NONE is not set
# CONFIG_HCI_TRACE_LEVEL_ERROR is not set
CONFIG_HCI_TRACE_LEVEL_WARNING=y
# CONFIG_HCI_TRACE_LEVEL_API is not set
# CONFIG_HCI_TRACE_LEVEL_EVENT is not set
# CONFIG_HCI_TRACE_LEVEL_DEBUG is not set
# CONFIG_HCI_TRACE_LEVEL_VERBOSE is not set
CONFIG_HCI_INITIAL_TRACE_LEVEL=2
# CONFIG_BTM_TRACE_LEVEL_NONE is not set
# CONFIG_BTM_TRACE_LEVEL_ERROR is not set
CONFIG_BTM_TRACE_LEVEL_WARNING=y
# CONFIG_BTM_TRACE_LEVEL_API is not set
# CONFIG_BTM_TRACE_LEVEL_EVENT is not set
# CONFIG_BTM_TRACE_LEVEL_DEBUG is not set
# CONFIG_BTM_TRACE_LEVEL_VERBOSE is not set
CONFIG_BTM_INITIAL_TRACE_LEVEL=2
# CONFIG_L2CAP_TRACE_LEVEL_NONE is not set
# CONFIG_L2CAP_TRACE_LEVEL_ERROR is not set
CONFIG_L2CAP_TRACE_LEVEL_WARNING=y
# CONFIG_L2CAP_TRACE_LEVEL_API is not set
# CONFIG_L2CAP_TRACE_LEVEL_EVENT is not set
# CONFIG_L2CAP_TRACE_LEVEL_DEBUG is not set
# CONFIG_L2CAP_TRACE_LEVEL_VERBOSE is not set
CONFIG_L2CAP_INITIAL_TRACE_LEVEL=2
# CONFIG_RFCOMM_TRACE_LEVEL_NONE is not set
# CONFIG_RFCOMM_TRACE_LEVEL_ERROR is not set
CONFIG_RFCOMM_TRACE_LEVEL_WARNING=y
# CONFIG_RFCOMM_TRACE_LEVEL_API is not set
# CONFIG_RFCOMM_TRACE_LEVEL_EVENT is not set
# CONFIG_RFCOMM_TRACE_LEVEL_DEBUG is not set
# CONFIG_RFCOMM_TRACE_LEVEL_VERBOSE is not set
CONFIG_RFCOMM_INITIAL_TRACE_LEVEL=2
# CONFIG_SDP_TRACE_LEVEL_NONE is not set
# CONFIG_SDP_TRACE_LEVEL_ERROR is not set
CONFIG_SDP_TRACE_LEVEL_WARNING=y
# CONFIG_SDP_TRACE_LEVEL_API is not set
# CONFIG_SDP_TRACE_LEVEL_EVENT is not set
# CONFIG_SDP_TRACE_LEVEL_DEBUG is not set
# CONFIG_SDP_TRACE_LEVEL_VERBOSE is not set
CONFIG_BTH_LOG_SDP_INITIAL_TRACE_LEVEL=2
# CONFIG_GAP_TRACE_LEVEL_NONE is not set
# CONFIG_GAP_TRACE_LEVEL_ERROR is not set
CONFIG_GAP_TRACE_LEVEL_WARNING=y
# CONFIG_GAP_TRACE_LEVEL_API is not set
# CONFIG_GAP_TRACE_LEVEL_EVENT is not set
# CONFIG_GAP_TRACE_LEVEL_DEBUG is not set
# CONFIG_GAP_TRACE_LEVEL_VERBOSE is not set
CONFIG_GAP_INITIAL_TRACE_LEVEL=2
CONFIG_BNEP_INITIAL_TRACE_LEVEL=2
# CONFIG_PAN_TRACE_LEVEL_NONE is not set
# CONFIG_PAN_TRACE_LEVEL_ERROR is not set
CONFIG_PAN_TRACE_LEVEL_WARNING=y
# CONFIG_PAN_TRACE_LEVEL_API is not set
# CONFIG_PAN_TRACE_LEVEL_EVENT is not set
# CONFIG_PAN_TRACE_LEVEL_DEBUG is not set
# CONFIG_PAN_TRACE_LEVEL_VERBOSE is not set
CONFIG_PAN_INITIAL_TRACE_LEVEL=2
# CONFIG_A2D_TRACE_LEVEL_NONE is not set
# CONFIG_A2D_TRACE_LEVEL_ERROR is not set
CONFIG_A2D_TRACE_LEVEL_WARNING=y
# CONFIG_A2D_TRACE_LEVEL_API is not set
# CONFIG_A2D_TRACE_LEVEL_EVENT is not set
# CONFIG_A2D_TRACE_LEVEL_DEBUG is not set
# CONFIG_A2D_TRACE_LEVEL_VERBOSE is not set
CONFIG_A2D_INITIAL_TRACE_LEVEL=2
# CONFIG_AVDT_TRACE_LEVEL_NONE is not set
# CONFIG_AVDT_TRACE_LEVEL_ERROR is not set
CONFIG_AVDT_TRACE_LEVEL_WARNING=y
# CONFIG_AVDT_TRACE_LEVEL_API is not set
# CONFIG_AVDT_TRACE_LEVEL_EVENT is not set
# CONFIG_AVDT_TRACE_LEVEL_DEBUG is not set
# CONFIG_AVDT_TRACE_LEVEL_VERBOSE is not set
CONFIG_AVDT_INITIAL_TRACE_LEVEL=2
# CONFIG_AVCT_TRACE_LEVEL_NONE is not set
# CONFIG_AVCT_TRACE_LEVEL_ERROR is not set
CONFIG_AVCT_TRACE_LEVEL_WARNING=y
# CONFIG_AVCT_TRACE_LEVEL_API is not set
# CONFIG_AVCT_TRACE_LEVEL_EVENT is not set
# CONFIG_AVCT_TRACE_LEVEL_DEBUG is not set
# CONFIG_AVCT_TRACE_LEVEL_VERBOSE is not set
CONFIG_AVCT_INITIAL_TRACE_LEVEL=2
# CONFIG_AVRC_TRACE_LEVEL_NONE is not set
# CONFIG_AVRC_TRACE_LEVEL_ERROR is not set
CONFIG_AVRC_TRACE_LEVEL_WARNING=y
# CONFIG_AVRC_TRACE_LEVEL_API is not set
# CONFIG_AVRC_TRACE_LEVEL_EVENT is not set
# CONFIG_AVRC_TRACE_LEVEL_DEBUG is not set
# CONFIG_AVRC_TRACE_LEVEL_VERBOSE is not set
CONFIG_AVRC_INITIAL_TRACE_LEVEL=2
# CONFIG_MCA_TRACE_LEVEL_NONE is not set
# CONFIG_MCA_TRACE_LEVEL_ERROR is not set
CONFIG_MCA_TRACE_LEVEL_WARNING=y
# CONFIG_MCA_TRACE_LEVEL_API is not set
# CONFIG_MCA_TRACE_LEVEL_EVENT is not set
# CONFIG_MCA_TRACE_LEVEL_DEBUG is not set
# CONFIG_MCA_TRACE_LEVEL_VERBOSE is not set
CONFIG_MCA_INITIAL_TRACE_LEVEL=2
# CONFIG_HID_TRACE_LEVEL_NONE is not set
# CONFIG_HID_TRACE_LEVEL_ERROR is not set
CONFIG_HID_TRACE_LEVEL_WARNING=y
# CONFIG_HID_TRACE_LEVEL_API is not set
# CONFIG_HID_TRACE_LEVEL_EVENT is not set
# CONFIG_HID_TRACE_LEVEL_DEBUG is not set
# CONFIG_HID_TRACE_LEVEL_VERBOSE is not set
CONFIG_HID_INITIAL_TRACE_LEVEL=2
# CONFIG_APPL_TRACE_LEVEL_NONE is not set
# CONFIG_APPL_TRACE_LEVEL_ERROR is not set
CONFIG_APPL_TRACE_LEVEL_WARNING=y
# CONFIG_APPL_TRACE_LEVEL_API is not set
# CONFIG_APPL_TRACE_LEVEL_EVENT is not set
# CONFIG_APPL_TRACE_LEVEL_DEBUG is not set
# CONFIG_APPL_TRACE_LEVEL_VERBOSE is not set
CONFIG_APPL_INITIAL_TRACE_LEVEL=2
# CONFIG_GATT_TRACE_LEVEL_NONE is not set
# CONFIG_GATT_TRACE_LEVEL_ERROR is not set
CONFIG_GATT_TRACE_LEVEL_WARNING=y
# CONFIG_GATT_TRACE_LEVEL_API is not set
# CONFIG_GATT_TRACE_LEVEL_EVENT is not set
# CONFIG_GATT_TRACE_LEVEL_DEBUG is not set
# CONFIG_GATT_TRACE_LEVEL_VERBOSE is not set
CONFIG_GATT_INITIAL_TRACE_LEVEL=2
# CONFIG_SMP_TRACE_LEVEL_NONE is not set
# CONFIG_SMP_TRACE_LEVEL_ERROR is not set
CONFIG_SMP_TRACE_LEVEL_WARNING=y
# CONFIG_SMP_TRACE_LEVEL_API is not set
# CONFIG_SMP_TRACE_LEVEL_EVENT is not set
# CONFIG_SMP_TRACE_LEVEL_DEBUG is not set
# CONFIG_SMP_TRACE_LEVEL_VERBOSE is not set
CONFIG_SMP_INITIAL_TRACE_LEVEL=2
# CONFIG_BTIF_TRACE_LEVEL_NONE is not set
# CONFIG_BTIF_TRACE_LEVEL_ERROR is not set
CONFIG_BTIF_TRACE_LEVEL_WARNING=y
# CONFIG_BTIF_TRACE_LEVEL_API is not set
# CONFIG_BTIF_TRACE_LEVEL_EVENT is not set
# CONFIG_BTIF_TRACE_LEVEL_DEBUG is not set
# CONFIG_BTIF_TRACE_LEVEL_VERBOSE is not set
CONFIG_BTIF_INITIAL_TRACE_LEVEL=2
# CONFIG_BTC_TRACE_LEVEL_NONE is not set
# CONFIG_BTC_TRACE_LEVEL_ERROR is not set
CONFIG_BTC_TRACE_LEVEL_WARNING=y
# CONFIG_BTC_TRACE_LEVEL_API is not set
# CONFIG_BTC_TRACE_LEVEL_EVENT is not set
# CONFIG_BTC_TRACE_LEVEL_DEBUG is not set
# CONFIG_BTC_TRACE_LEVEL_VERBOSE is not set
CONFIG_BTC_INITIAL_TRACE_LEVEL=2
# CONFIG_OSI_TRACE_LEVEL_NONE is not set
# CONFIG_OSI_TRACE_LEVEL_ERROR is not set
CONFIG_OSI_TRACE_LEVEL_WARNING=y
# CONFIG_OSI_TRACE_LEVEL_API is not set
# CONFIG_OSI_TRACE_LEVEL_EVENT is not set
# CONFIG_OSI_TRACE_LEVEL_DEBUG is not set
# CONFIG_OSI_TRACE_LEVEL_VERBOSE is not set
CONFIG_OSI_INITIAL_TRACE_LEVEL=2
# CONFIG_BLUFI_TRACE_LEVEL_NONE is not set
# CONFIG_BLUFI_TRACE_LEVEL_ERROR is not set
CONFIG_BLUFI_TRACE_LEVEL_WARNING=y
# CONFIG_BLUFI_TRACE_LEVEL_API is not set
# CONFIG_BLUFI_TRACE_LEVEL_EVENT is not set
# CONFIG_BLUFI_TRACE_LEVEL_DEBUG is not set
# CONFIG_BLUFI_TRACE_LEVEL_VERBOSE is not set
CONFIG_BLUFI_INITIAL_TRACE_LEVEL=2
# CONFIG_BLE_HOST_QUEUE_CONGESTION_CHECK is not set
CONFIG_SMP_ENABLE=y
# CONFIG_BLE_ACTIVE_SCAN_REPORT_ADV_SCAN_RSP_INDIVIDUALLY is not set
CONFIG_BLE_ESTABLISH_LINK_CONNECTION_TIMEOUT=30
# CONFIG_BT_NIMBLE_COEX_PHY_CODED_TX_RX_TLIM_EN is not set
CONFIG_BT_NIMBLE_COEX_PHY_CODED_TX_RX_TLIM_DIS=y
CONFIG_SW_COEXIST_ENABLE=y
CONFIG_ESP32_WIFI_SW_COEXIST_ENABLE=y
CONFIG_ESP_WIFI_SW_COEXIST_ENABLE=y
# CONFIG_MCPWM_ISR_IN_IRAM is not set # CONFIG_MCPWM_ISR_IN_IRAM is not set
# CONFIG_EVENT_LOOP_PROFILING is not set # CONFIG_EVENT_LOOP_PROFILING is not set
CONFIG_POST_EVENTS_FROM_ISR=y CONFIG_POST_EVENTS_FROM_ISR=y
CONFIG_POST_EVENTS_FROM_IRAM_ISR=y CONFIG_POST_EVENTS_FROM_IRAM_ISR=y
CONFIG_GDBSTUB_SUPPORT_TASKS=y CONFIG_GDBSTUB_SUPPORT_TASKS=y
CONFIG_GDBSTUB_MAX_TASKS=32 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_ESP32S3_DEEP_SLEEP_WAKEUP_DELAY=2000
CONFIG_ESP_SLEEP_DEEP_SLEEP_WAKEUP_DELAY=2000 CONFIG_ESP_SLEEP_DEEP_SLEEP_WAKEUP_DELAY=2000
CONFIG_ESP32S3_RTC_CLK_SRC_INT_RC=y CONFIG_ESP32S3_RTC_CLK_SRC_INT_RC=y
@@ -3209,7 +2713,7 @@ CONFIG_ESP32S3_DEFAULT_CPU_FREQ_240=y
CONFIG_ESP32S3_DEFAULT_CPU_FREQ_MHZ=240 CONFIG_ESP32S3_DEFAULT_CPU_FREQ_MHZ=240
CONFIG_SYSTEM_EVENT_QUEUE_SIZE=32 CONFIG_SYSTEM_EVENT_QUEUE_SIZE=32
CONFIG_SYSTEM_EVENT_TASK_STACK_SIZE=2304 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_DEFAULT=y
# CONFIG_CONSOLE_UART_CUSTOM is not set # CONFIG_CONSOLE_UART_CUSTOM is not set
# CONFIG_CONSOLE_UART_NONE is not set # CONFIG_CONSOLE_UART_NONE is not set
@@ -3250,19 +2754,19 @@ CONFIG_ESP32S3_BROWNOUT_DET_LVL=7
CONFIG_IPC_TASK_STACK_SIZE=1280 CONFIG_IPC_TASK_STACK_SIZE=1280
CONFIG_TIMER_TASK_STACK_SIZE=3584 CONFIG_TIMER_TASK_STACK_SIZE=3584
CONFIG_ESP32_WIFI_ENABLED=y CONFIG_ESP32_WIFI_ENABLED=y
CONFIG_ESP32_WIFI_STATIC_RX_BUFFER_NUM=10 CONFIG_ESP32_WIFI_STATIC_RX_BUFFER_NUM=2
CONFIG_ESP32_WIFI_DYNAMIC_RX_BUFFER_NUM=32 CONFIG_ESP32_WIFI_DYNAMIC_RX_BUFFER_NUM=32
CONFIG_ESP32_WIFI_STATIC_TX_BUFFER=y CONFIG_ESP32_WIFI_STATIC_TX_BUFFER=y
CONFIG_ESP32_WIFI_TX_BUFFER_TYPE=0 CONFIG_ESP32_WIFI_TX_BUFFER_TYPE=0
CONFIG_ESP32_WIFI_STATIC_TX_BUFFER_NUM=16 CONFIG_ESP32_WIFI_STATIC_TX_BUFFER_NUM=4
CONFIG_ESP32_WIFI_CACHE_TX_BUFFER_NUM=32 CONFIG_ESP32_WIFI_CACHE_TX_BUFFER_NUM=32
# CONFIG_ESP32_WIFI_CSI_ENABLED is not set # CONFIG_ESP32_WIFI_CSI_ENABLED is not set
CONFIG_ESP32_WIFI_AMPDU_TX_ENABLED=y CONFIG_ESP32_WIFI_AMPDU_TX_ENABLED=y
CONFIG_ESP32_WIFI_TX_BA_WIN=6 CONFIG_ESP32_WIFI_TX_BA_WIN=2
CONFIG_ESP32_WIFI_AMPDU_RX_ENABLED=y CONFIG_ESP32_WIFI_AMPDU_RX_ENABLED=y
CONFIG_ESP32_WIFI_AMPDU_RX_ENABLED=y CONFIG_ESP32_WIFI_AMPDU_RX_ENABLED=y
CONFIG_ESP32_WIFI_RX_BA_WIN=6 CONFIG_ESP32_WIFI_RX_BA_WIN=2
CONFIG_ESP32_WIFI_RX_BA_WIN=6 CONFIG_ESP32_WIFI_RX_BA_WIN=2
# CONFIG_ESP32_WIFI_AMSDU_TX_ENABLED is not set # CONFIG_ESP32_WIFI_AMSDU_TX_ENABLED is not set
CONFIG_ESP32_WIFI_NVS_ENABLED=y CONFIG_ESP32_WIFI_NVS_ENABLED=y
CONFIG_ESP32_WIFI_TASK_PINNED_TO_CORE_0=y CONFIG_ESP32_WIFI_TASK_PINNED_TO_CORE_0=y
+86 -582
View File
@@ -560,26 +560,26 @@ CONFIG_SR_MN_EN_NONE=y
# #
# Add Chinese speech commands # Add Chinese speech commands
# #
CONFIG_CN_SPEECH_COMMAND_ID0="da kai kong tiao" CONFIG_CN_SPEECH_COMMAND_ID0=""
CONFIG_CN_SPEECH_COMMAND_ID1="guan bi kong tiao" CONFIG_CN_SPEECH_COMMAND_ID1=""
CONFIG_CN_SPEECH_COMMAND_ID2="zeng da feng su" CONFIG_CN_SPEECH_COMMAND_ID2=""
CONFIG_CN_SPEECH_COMMAND_ID3="jian xiao feng su" CONFIG_CN_SPEECH_COMMAND_ID3=""
CONFIG_CN_SPEECH_COMMAND_ID4="sheng gao yi du" CONFIG_CN_SPEECH_COMMAND_ID4=""
CONFIG_CN_SPEECH_COMMAND_ID5="jiang di yi du" CONFIG_CN_SPEECH_COMMAND_ID5=""
CONFIG_CN_SPEECH_COMMAND_ID6="zhi re mo shi" CONFIG_CN_SPEECH_COMMAND_ID6=""
CONFIG_CN_SPEECH_COMMAND_ID7="zhi leng mo shi" CONFIG_CN_SPEECH_COMMAND_ID7=""
CONFIG_CN_SPEECH_COMMAND_ID8="song feng mo shi" CONFIG_CN_SPEECH_COMMAND_ID8=""
CONFIG_CN_SPEECH_COMMAND_ID9="jie neng mo shi" CONFIG_CN_SPEECH_COMMAND_ID9=""
CONFIG_CN_SPEECH_COMMAND_ID10="chu shi mo shi" CONFIG_CN_SPEECH_COMMAND_ID10=""
CONFIG_CN_SPEECH_COMMAND_ID11="jian kang mo shi" CONFIG_CN_SPEECH_COMMAND_ID11=""
CONFIG_CN_SPEECH_COMMAND_ID12="shui mian mo shi" CONFIG_CN_SPEECH_COMMAND_ID12=""
CONFIG_CN_SPEECH_COMMAND_ID13="da kai lan ya" CONFIG_CN_SPEECH_COMMAND_ID13=""
CONFIG_CN_SPEECH_COMMAND_ID14="guan bi lan ya" CONFIG_CN_SPEECH_COMMAND_ID14=""
CONFIG_CN_SPEECH_COMMAND_ID15="kai shi bo fang" CONFIG_CN_SPEECH_COMMAND_ID15=""
CONFIG_CN_SPEECH_COMMAND_ID16="zan ting bo fang" CONFIG_CN_SPEECH_COMMAND_ID16=""
CONFIG_CN_SPEECH_COMMAND_ID17="ding shi yi xiao shi" CONFIG_CN_SPEECH_COMMAND_ID17=""
CONFIG_CN_SPEECH_COMMAND_ID18="da kai dian deng" CONFIG_CN_SPEECH_COMMAND_ID18=""
CONFIG_CN_SPEECH_COMMAND_ID19="guan bi dian deng" CONFIG_CN_SPEECH_COMMAND_ID19=""
CONFIG_CN_SPEECH_COMMAND_ID20="" CONFIG_CN_SPEECH_COMMAND_ID20=""
CONFIG_CN_SPEECH_COMMAND_ID21="" CONFIG_CN_SPEECH_COMMAND_ID21=""
CONFIG_CN_SPEECH_COMMAND_ID22="" CONFIG_CN_SPEECH_COMMAND_ID22=""
@@ -766,9 +766,9 @@ CONFIG_CN_SPEECH_COMMAND_ID199=""
# #
# Compiler options # Compiler options
# #
CONFIG_COMPILER_OPTIMIZATION_DEBUG=y # CONFIG_COMPILER_OPTIMIZATION_DEBUG is not set
# CONFIG_COMPILER_OPTIMIZATION_SIZE is not set # CONFIG_COMPILER_OPTIMIZATION_SIZE is not set
# CONFIG_COMPILER_OPTIMIZATION_PERF is not set CONFIG_COMPILER_OPTIMIZATION_PERF=y
# CONFIG_COMPILER_OPTIMIZATION_NONE is not set # CONFIG_COMPILER_OPTIMIZATION_NONE is not set
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_ENABLE=y CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_ENABLE=y
# CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT is not set # CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT is not set
@@ -777,8 +777,9 @@ CONFIG_COMPILER_FLOAT_LIB_FROM_GCCLIB=y
CONFIG_COMPILER_OPTIMIZATION_ASSERTION_LEVEL=2 CONFIG_COMPILER_OPTIMIZATION_ASSERTION_LEVEL=2
# CONFIG_COMPILER_OPTIMIZATION_CHECKS_SILENT is not set # CONFIG_COMPILER_OPTIMIZATION_CHECKS_SILENT is not set
CONFIG_COMPILER_HIDE_PATHS_MACROS=y CONFIG_COMPILER_HIDE_PATHS_MACROS=y
# CONFIG_COMPILER_CXX_EXCEPTIONS is not set CONFIG_COMPILER_CXX_EXCEPTIONS=y
# CONFIG_COMPILER_CXX_RTTI is not set CONFIG_COMPILER_CXX_EXCEPTIONS_EMG_POOL_SIZE=0
CONFIG_COMPILER_CXX_RTTI=y
CONFIG_COMPILER_STACK_CHECK_MODE_NONE=y CONFIG_COMPILER_STACK_CHECK_MODE_NONE=y
# CONFIG_COMPILER_STACK_CHECK_MODE_NORM is not set # CONFIG_COMPILER_STACK_CHECK_MODE_NORM is not set
# CONFIG_COMPILER_STACK_CHECK_MODE_STRONG is not set # CONFIG_COMPILER_STACK_CHECK_MODE_STRONG is not set
@@ -813,334 +814,10 @@ CONFIG_APPTRACE_LOCK_ENABLE=y
# #
# Bluetooth # Bluetooth
# #
CONFIG_BT_ENABLED=y # CONFIG_BT_ENABLED is not set
CONFIG_BT_BLUEDROID_ENABLED=y
# CONFIG_BT_NIMBLE_ENABLED is not set
# CONFIG_BT_CONTROLLER_ONLY is not set
CONFIG_BT_CONTROLLER_ENABLED=y
# CONFIG_BT_CONTROLLER_DISABLED is not set
#
# Bluedroid Options
#
CONFIG_BT_BTC_TASK_STACK_SIZE=3072
CONFIG_BT_BLUEDROID_PINNED_TO_CORE_0=y
# CONFIG_BT_BLUEDROID_PINNED_TO_CORE_1 is not set
CONFIG_BT_BLUEDROID_PINNED_TO_CORE=0
CONFIG_BT_BTU_TASK_STACK_SIZE=4352
# CONFIG_BT_BLUEDROID_MEM_DEBUG is not set
CONFIG_BT_BLUEDROID_ESP_COEX_VSC=y
CONFIG_BT_BLE_ENABLED=y
CONFIG_BT_GATTS_ENABLE=y
# CONFIG_BT_GATTS_PPCP_CHAR_GAP is not set
# CONFIG_BT_BLE_BLUFI_ENABLE is not set
CONFIG_BT_GATT_MAX_SR_PROFILES=8
CONFIG_BT_GATT_MAX_SR_ATTRIBUTES=100
# CONFIG_BT_GATTS_SEND_SERVICE_CHANGE_MANUAL is not set
CONFIG_BT_GATTS_SEND_SERVICE_CHANGE_AUTO=y
CONFIG_BT_GATTS_SEND_SERVICE_CHANGE_MODE=0
# CONFIG_BT_GATTS_ROBUST_CACHING_ENABLED is not set
# CONFIG_BT_GATTS_DEVICE_NAME_WRITABLE is not set
# CONFIG_BT_GATTS_APPEARANCE_WRITABLE is not set
CONFIG_BT_GATTC_ENABLE=y
CONFIG_BT_GATTC_MAX_CACHE_CHAR=40
CONFIG_BT_GATTC_NOTIF_REG_MAX=5
# CONFIG_BT_GATTC_CACHE_NVS_FLASH is not set
CONFIG_BT_GATTC_CONNECT_RETRY_COUNT=3
CONFIG_BT_BLE_SMP_ENABLE=y
# CONFIG_BT_SMP_SLAVE_CON_PARAMS_UPD_ENABLE is not set
# CONFIG_BT_BLE_SMP_ID_RESET_ENABLE is not set
# CONFIG_BT_STACK_NO_LOG is not set
#
# BT DEBUG LOG LEVEL
#
# CONFIG_BT_LOG_HCI_TRACE_LEVEL_NONE is not set
# CONFIG_BT_LOG_HCI_TRACE_LEVEL_ERROR is not set
CONFIG_BT_LOG_HCI_TRACE_LEVEL_WARNING=y
# CONFIG_BT_LOG_HCI_TRACE_LEVEL_API is not set
# CONFIG_BT_LOG_HCI_TRACE_LEVEL_EVENT is not set
# CONFIG_BT_LOG_HCI_TRACE_LEVEL_DEBUG is not set
# CONFIG_BT_LOG_HCI_TRACE_LEVEL_VERBOSE is not set
CONFIG_BT_LOG_HCI_TRACE_LEVEL=2
# CONFIG_BT_LOG_BTM_TRACE_LEVEL_NONE is not set
# CONFIG_BT_LOG_BTM_TRACE_LEVEL_ERROR is not set
CONFIG_BT_LOG_BTM_TRACE_LEVEL_WARNING=y
# CONFIG_BT_LOG_BTM_TRACE_LEVEL_API is not set
# CONFIG_BT_LOG_BTM_TRACE_LEVEL_EVENT is not set
# CONFIG_BT_LOG_BTM_TRACE_LEVEL_DEBUG is not set
# CONFIG_BT_LOG_BTM_TRACE_LEVEL_VERBOSE is not set
CONFIG_BT_LOG_BTM_TRACE_LEVEL=2
# CONFIG_BT_LOG_L2CAP_TRACE_LEVEL_NONE is not set
# CONFIG_BT_LOG_L2CAP_TRACE_LEVEL_ERROR is not set
CONFIG_BT_LOG_L2CAP_TRACE_LEVEL_WARNING=y
# CONFIG_BT_LOG_L2CAP_TRACE_LEVEL_API is not set
# CONFIG_BT_LOG_L2CAP_TRACE_LEVEL_EVENT is not set
# CONFIG_BT_LOG_L2CAP_TRACE_LEVEL_DEBUG is not set
# CONFIG_BT_LOG_L2CAP_TRACE_LEVEL_VERBOSE is not set
CONFIG_BT_LOG_L2CAP_TRACE_LEVEL=2
# CONFIG_BT_LOG_RFCOMM_TRACE_LEVEL_NONE is not set
# CONFIG_BT_LOG_RFCOMM_TRACE_LEVEL_ERROR is not set
CONFIG_BT_LOG_RFCOMM_TRACE_LEVEL_WARNING=y
# CONFIG_BT_LOG_RFCOMM_TRACE_LEVEL_API is not set
# CONFIG_BT_LOG_RFCOMM_TRACE_LEVEL_EVENT is not set
# CONFIG_BT_LOG_RFCOMM_TRACE_LEVEL_DEBUG is not set
# CONFIG_BT_LOG_RFCOMM_TRACE_LEVEL_VERBOSE is not set
CONFIG_BT_LOG_RFCOMM_TRACE_LEVEL=2
# CONFIG_BT_LOG_SDP_TRACE_LEVEL_NONE is not set
# CONFIG_BT_LOG_SDP_TRACE_LEVEL_ERROR is not set
CONFIG_BT_LOG_SDP_TRACE_LEVEL_WARNING=y
# CONFIG_BT_LOG_SDP_TRACE_LEVEL_API is not set
# CONFIG_BT_LOG_SDP_TRACE_LEVEL_EVENT is not set
# CONFIG_BT_LOG_SDP_TRACE_LEVEL_DEBUG is not set
# CONFIG_BT_LOG_SDP_TRACE_LEVEL_VERBOSE is not set
CONFIG_BT_LOG_SDP_TRACE_LEVEL=2
# CONFIG_BT_LOG_GAP_TRACE_LEVEL_NONE is not set
# CONFIG_BT_LOG_GAP_TRACE_LEVEL_ERROR is not set
CONFIG_BT_LOG_GAP_TRACE_LEVEL_WARNING=y
# CONFIG_BT_LOG_GAP_TRACE_LEVEL_API is not set
# CONFIG_BT_LOG_GAP_TRACE_LEVEL_EVENT is not set
# CONFIG_BT_LOG_GAP_TRACE_LEVEL_DEBUG is not set
# CONFIG_BT_LOG_GAP_TRACE_LEVEL_VERBOSE is not set
CONFIG_BT_LOG_GAP_TRACE_LEVEL=2
# CONFIG_BT_LOG_BNEP_TRACE_LEVEL_NONE is not set
# CONFIG_BT_LOG_BNEP_TRACE_LEVEL_ERROR is not set
CONFIG_BT_LOG_BNEP_TRACE_LEVEL_WARNING=y
# CONFIG_BT_LOG_BNEP_TRACE_LEVEL_API is not set
# CONFIG_BT_LOG_BNEP_TRACE_LEVEL_EVENT is not set
# CONFIG_BT_LOG_BNEP_TRACE_LEVEL_DEBUG is not set
# CONFIG_BT_LOG_BNEP_TRACE_LEVEL_VERBOSE is not set
CONFIG_BT_LOG_BNEP_TRACE_LEVEL=2
# CONFIG_BT_LOG_PAN_TRACE_LEVEL_NONE is not set
# CONFIG_BT_LOG_PAN_TRACE_LEVEL_ERROR is not set
CONFIG_BT_LOG_PAN_TRACE_LEVEL_WARNING=y
# CONFIG_BT_LOG_PAN_TRACE_LEVEL_API is not set
# CONFIG_BT_LOG_PAN_TRACE_LEVEL_EVENT is not set
# CONFIG_BT_LOG_PAN_TRACE_LEVEL_DEBUG is not set
# CONFIG_BT_LOG_PAN_TRACE_LEVEL_VERBOSE is not set
CONFIG_BT_LOG_PAN_TRACE_LEVEL=2
# CONFIG_BT_LOG_A2D_TRACE_LEVEL_NONE is not set
# CONFIG_BT_LOG_A2D_TRACE_LEVEL_ERROR is not set
CONFIG_BT_LOG_A2D_TRACE_LEVEL_WARNING=y
# CONFIG_BT_LOG_A2D_TRACE_LEVEL_API is not set
# CONFIG_BT_LOG_A2D_TRACE_LEVEL_EVENT is not set
# CONFIG_BT_LOG_A2D_TRACE_LEVEL_DEBUG is not set
# CONFIG_BT_LOG_A2D_TRACE_LEVEL_VERBOSE is not set
CONFIG_BT_LOG_A2D_TRACE_LEVEL=2
# CONFIG_BT_LOG_AVDT_TRACE_LEVEL_NONE is not set
# CONFIG_BT_LOG_AVDT_TRACE_LEVEL_ERROR is not set
CONFIG_BT_LOG_AVDT_TRACE_LEVEL_WARNING=y
# CONFIG_BT_LOG_AVDT_TRACE_LEVEL_API is not set
# CONFIG_BT_LOG_AVDT_TRACE_LEVEL_EVENT is not set
# CONFIG_BT_LOG_AVDT_TRACE_LEVEL_DEBUG is not set
# CONFIG_BT_LOG_AVDT_TRACE_LEVEL_VERBOSE is not set
CONFIG_BT_LOG_AVDT_TRACE_LEVEL=2
# CONFIG_BT_LOG_AVCT_TRACE_LEVEL_NONE is not set
# CONFIG_BT_LOG_AVCT_TRACE_LEVEL_ERROR is not set
CONFIG_BT_LOG_AVCT_TRACE_LEVEL_WARNING=y
# CONFIG_BT_LOG_AVCT_TRACE_LEVEL_API is not set
# CONFIG_BT_LOG_AVCT_TRACE_LEVEL_EVENT is not set
# CONFIG_BT_LOG_AVCT_TRACE_LEVEL_DEBUG is not set
# CONFIG_BT_LOG_AVCT_TRACE_LEVEL_VERBOSE is not set
CONFIG_BT_LOG_AVCT_TRACE_LEVEL=2
# CONFIG_BT_LOG_AVRC_TRACE_LEVEL_NONE is not set
# CONFIG_BT_LOG_AVRC_TRACE_LEVEL_ERROR is not set
CONFIG_BT_LOG_AVRC_TRACE_LEVEL_WARNING=y
# CONFIG_BT_LOG_AVRC_TRACE_LEVEL_API is not set
# CONFIG_BT_LOG_AVRC_TRACE_LEVEL_EVENT is not set
# CONFIG_BT_LOG_AVRC_TRACE_LEVEL_DEBUG is not set
# CONFIG_BT_LOG_AVRC_TRACE_LEVEL_VERBOSE is not set
CONFIG_BT_LOG_AVRC_TRACE_LEVEL=2
# CONFIG_BT_LOG_MCA_TRACE_LEVEL_NONE is not set
# CONFIG_BT_LOG_MCA_TRACE_LEVEL_ERROR is not set
CONFIG_BT_LOG_MCA_TRACE_LEVEL_WARNING=y
# CONFIG_BT_LOG_MCA_TRACE_LEVEL_API is not set
# CONFIG_BT_LOG_MCA_TRACE_LEVEL_EVENT is not set
# CONFIG_BT_LOG_MCA_TRACE_LEVEL_DEBUG is not set
# CONFIG_BT_LOG_MCA_TRACE_LEVEL_VERBOSE is not set
CONFIG_BT_LOG_MCA_TRACE_LEVEL=2
# CONFIG_BT_LOG_HID_TRACE_LEVEL_NONE is not set
# CONFIG_BT_LOG_HID_TRACE_LEVEL_ERROR is not set
CONFIG_BT_LOG_HID_TRACE_LEVEL_WARNING=y
# CONFIG_BT_LOG_HID_TRACE_LEVEL_API is not set
# CONFIG_BT_LOG_HID_TRACE_LEVEL_EVENT is not set
# CONFIG_BT_LOG_HID_TRACE_LEVEL_DEBUG is not set
# CONFIG_BT_LOG_HID_TRACE_LEVEL_VERBOSE is not set
CONFIG_BT_LOG_HID_TRACE_LEVEL=2
# CONFIG_BT_LOG_APPL_TRACE_LEVEL_NONE is not set
# CONFIG_BT_LOG_APPL_TRACE_LEVEL_ERROR is not set
CONFIG_BT_LOG_APPL_TRACE_LEVEL_WARNING=y
# CONFIG_BT_LOG_APPL_TRACE_LEVEL_API is not set
# CONFIG_BT_LOG_APPL_TRACE_LEVEL_EVENT is not set
# CONFIG_BT_LOG_APPL_TRACE_LEVEL_DEBUG is not set
# CONFIG_BT_LOG_APPL_TRACE_LEVEL_VERBOSE is not set
CONFIG_BT_LOG_APPL_TRACE_LEVEL=2
# CONFIG_BT_LOG_GATT_TRACE_LEVEL_NONE is not set
# CONFIG_BT_LOG_GATT_TRACE_LEVEL_ERROR is not set
CONFIG_BT_LOG_GATT_TRACE_LEVEL_WARNING=y
# CONFIG_BT_LOG_GATT_TRACE_LEVEL_API is not set
# CONFIG_BT_LOG_GATT_TRACE_LEVEL_EVENT is not set
# CONFIG_BT_LOG_GATT_TRACE_LEVEL_DEBUG is not set
# CONFIG_BT_LOG_GATT_TRACE_LEVEL_VERBOSE is not set
CONFIG_BT_LOG_GATT_TRACE_LEVEL=2
# CONFIG_BT_LOG_SMP_TRACE_LEVEL_NONE is not set
# CONFIG_BT_LOG_SMP_TRACE_LEVEL_ERROR is not set
CONFIG_BT_LOG_SMP_TRACE_LEVEL_WARNING=y
# CONFIG_BT_LOG_SMP_TRACE_LEVEL_API is not set
# CONFIG_BT_LOG_SMP_TRACE_LEVEL_EVENT is not set
# CONFIG_BT_LOG_SMP_TRACE_LEVEL_DEBUG is not set
# CONFIG_BT_LOG_SMP_TRACE_LEVEL_VERBOSE is not set
CONFIG_BT_LOG_SMP_TRACE_LEVEL=2
# CONFIG_BT_LOG_BTIF_TRACE_LEVEL_NONE is not set
# CONFIG_BT_LOG_BTIF_TRACE_LEVEL_ERROR is not set
CONFIG_BT_LOG_BTIF_TRACE_LEVEL_WARNING=y
# CONFIG_BT_LOG_BTIF_TRACE_LEVEL_API is not set
# CONFIG_BT_LOG_BTIF_TRACE_LEVEL_EVENT is not set
# CONFIG_BT_LOG_BTIF_TRACE_LEVEL_DEBUG is not set
# CONFIG_BT_LOG_BTIF_TRACE_LEVEL_VERBOSE is not set
CONFIG_BT_LOG_BTIF_TRACE_LEVEL=2
# CONFIG_BT_LOG_BTC_TRACE_LEVEL_NONE is not set
# CONFIG_BT_LOG_BTC_TRACE_LEVEL_ERROR is not set
CONFIG_BT_LOG_BTC_TRACE_LEVEL_WARNING=y
# CONFIG_BT_LOG_BTC_TRACE_LEVEL_API is not set
# CONFIG_BT_LOG_BTC_TRACE_LEVEL_EVENT is not set
# CONFIG_BT_LOG_BTC_TRACE_LEVEL_DEBUG is not set
# CONFIG_BT_LOG_BTC_TRACE_LEVEL_VERBOSE is not set
CONFIG_BT_LOG_BTC_TRACE_LEVEL=2
# CONFIG_BT_LOG_OSI_TRACE_LEVEL_NONE is not set
# CONFIG_BT_LOG_OSI_TRACE_LEVEL_ERROR is not set
CONFIG_BT_LOG_OSI_TRACE_LEVEL_WARNING=y
# CONFIG_BT_LOG_OSI_TRACE_LEVEL_API is not set
# CONFIG_BT_LOG_OSI_TRACE_LEVEL_EVENT is not set
# CONFIG_BT_LOG_OSI_TRACE_LEVEL_DEBUG is not set
# CONFIG_BT_LOG_OSI_TRACE_LEVEL_VERBOSE is not set
CONFIG_BT_LOG_OSI_TRACE_LEVEL=2
# CONFIG_BT_LOG_BLUFI_TRACE_LEVEL_NONE is not set
# CONFIG_BT_LOG_BLUFI_TRACE_LEVEL_ERROR is not set
CONFIG_BT_LOG_BLUFI_TRACE_LEVEL_WARNING=y
# CONFIG_BT_LOG_BLUFI_TRACE_LEVEL_API is not set
# CONFIG_BT_LOG_BLUFI_TRACE_LEVEL_EVENT is not set
# CONFIG_BT_LOG_BLUFI_TRACE_LEVEL_DEBUG is not set
# CONFIG_BT_LOG_BLUFI_TRACE_LEVEL_VERBOSE is not set
CONFIG_BT_LOG_BLUFI_TRACE_LEVEL=2
# end of BT DEBUG LOG LEVEL
CONFIG_BT_ACL_CONNECTIONS=4
CONFIG_BT_MULTI_CONNECTION_ENBALE=y
# CONFIG_BT_ALLOCATION_FROM_SPIRAM_FIRST is not set
# CONFIG_BT_BLE_DYNAMIC_ENV_MEMORY is not set
# CONFIG_BT_BLE_HOST_QUEUE_CONG_CHECK is not set
CONFIG_BT_SMP_ENABLE=y
CONFIG_BT_SMP_MAX_BONDS=15
# CONFIG_BT_BLE_ACT_SCAN_REP_ADV_SCAN is not set
CONFIG_BT_BLE_ESTAB_LINK_CONN_TOUT=30
CONFIG_BT_MAX_DEVICE_NAME_LEN=32
CONFIG_BT_BLE_RPA_TIMEOUT=900
CONFIG_BT_BLE_50_FEATURES_SUPPORTED=y
CONFIG_BT_BLE_42_FEATURES_SUPPORTED=y
# CONFIG_BT_BLE_HIGH_DUTY_ADV_INTERVAL is not set
# CONFIG_BT_ABORT_WHEN_ALLOCATION_FAILS is not set
# end of Bluedroid Options
#
# Controller Options
#
CONFIG_BT_CTRL_MODE_EFF=1
CONFIG_BT_CTRL_BLE_MAX_ACT=6
CONFIG_BT_CTRL_BLE_MAX_ACT_EFF=6
CONFIG_BT_CTRL_BLE_STATIC_ACL_TX_BUF_NB=0
CONFIG_BT_CTRL_PINNED_TO_CORE_0=y
# CONFIG_BT_CTRL_PINNED_TO_CORE_1 is not set
CONFIG_BT_CTRL_PINNED_TO_CORE=0
CONFIG_BT_CTRL_HCI_MODE_VHCI=y
# CONFIG_BT_CTRL_HCI_MODE_UART_H4 is not set
CONFIG_BT_CTRL_HCI_TL=1
CONFIG_BT_CTRL_ADV_DUP_FILT_MAX=30
CONFIG_BT_BLE_CCA_MODE_NONE=y
# CONFIG_BT_BLE_CCA_MODE_HW is not set
# CONFIG_BT_BLE_CCA_MODE_SW is not set
CONFIG_BT_BLE_CCA_MODE=0
CONFIG_BT_CTRL_HW_CCA_VAL=20
CONFIG_BT_CTRL_HW_CCA_EFF=0
CONFIG_BT_CTRL_CE_LENGTH_TYPE_ORIG=y
# CONFIG_BT_CTRL_CE_LENGTH_TYPE_CE is not set
# CONFIG_BT_CTRL_CE_LENGTH_TYPE_SD is not set
CONFIG_BT_CTRL_CE_LENGTH_TYPE_EFF=0
CONFIG_BT_CTRL_TX_ANTENNA_INDEX_0=y
# CONFIG_BT_CTRL_TX_ANTENNA_INDEX_1 is not set
CONFIG_BT_CTRL_TX_ANTENNA_INDEX_EFF=0
CONFIG_BT_CTRL_RX_ANTENNA_INDEX_0=y
# CONFIG_BT_CTRL_RX_ANTENNA_INDEX_1 is not set
CONFIG_BT_CTRL_RX_ANTENNA_INDEX_EFF=0
# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_N24 is not set
# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_N21 is not set
# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_N18 is not set
# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_N15 is not set
# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_N12 is not set
# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_N9 is not set
# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_N6 is not set
# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_N3 is not set
# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_N0 is not set
# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_P3 is not set
# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_P6 is not set
CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_P9=y
# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_P12 is not set
# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_P15 is not set
# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_P18 is not set
# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_P20 is not set
CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_EFF=11
CONFIG_BT_CTRL_BLE_ADV_REPORT_FLOW_CTRL_SUPP=y
CONFIG_BT_CTRL_BLE_ADV_REPORT_FLOW_CTRL_NUM=100
CONFIG_BT_CTRL_BLE_ADV_REPORT_DISCARD_THRSHOLD=20
CONFIG_BT_CTRL_BLE_SCAN_DUPL=y
CONFIG_BT_CTRL_SCAN_DUPL_TYPE_DEVICE=y
# CONFIG_BT_CTRL_SCAN_DUPL_TYPE_DATA is not set
# CONFIG_BT_CTRL_SCAN_DUPL_TYPE_DATA_DEVICE is not set
CONFIG_BT_CTRL_SCAN_DUPL_TYPE=0
CONFIG_BT_CTRL_SCAN_DUPL_CACHE_SIZE=100
CONFIG_BT_CTRL_DUPL_SCAN_CACHE_REFRESH_PERIOD=0
# CONFIG_BT_CTRL_BLE_MESH_SCAN_DUPL_EN is not set
# CONFIG_BT_CTRL_COEX_PHY_CODED_TX_RX_TLIM_EN is not set
CONFIG_BT_CTRL_COEX_PHY_CODED_TX_RX_TLIM_DIS=y
CONFIG_BT_CTRL_COEX_PHY_CODED_TX_RX_TLIM_EFF=0
#
# MODEM SLEEP Options
#
# CONFIG_BT_CTRL_MODEM_SLEEP is not set
# end of MODEM SLEEP Options
CONFIG_BT_CTRL_SLEEP_MODE_EFF=0
CONFIG_BT_CTRL_SLEEP_CLOCK_EFF=0
CONFIG_BT_CTRL_HCI_TL_EFF=1
# CONFIG_BT_CTRL_AGC_RECORRECT_EN is not set
# CONFIG_BT_CTRL_SCAN_BACKOFF_UPPERLIMITMAX is not set
# CONFIG_BT_BLE_ADV_DATA_LENGTH_ZERO_AUX is not set
CONFIG_BT_CTRL_CHAN_ASS_EN=y
CONFIG_BT_CTRL_LE_PING_EN=y
#
# BLE disconnect when instant passed
#
# CONFIG_BT_CTRL_BLE_LLCP_CONN_UPDATE is not set
# CONFIG_BT_CTRL_BLE_LLCP_CHAN_MAP_UPDATE is not set
# CONFIG_BT_CTRL_BLE_LLCP_PHY_UPDATE is not set
# end of BLE disconnect when instant passed
# CONFIG_BT_CTRL_RUN_IN_FLASH_ONLY is not set
# end of Controller Options
#
# Common Options
#
CONFIG_BT_ALARM_MAX_NUM=50 CONFIG_BT_ALARM_MAX_NUM=50
# end of Common Options
# CONFIG_BT_HCI_LOG_DEBUG_EN is not set
# end of Bluetooth # end of Bluetooth
# CONFIG_BLE_MESH is not set
# #
# Console Library # Console Library
# #
@@ -1247,8 +924,7 @@ CONFIG_ESP_TLS_USE_DS_PERIPHERAL=y
# Wireless Coexistence # Wireless Coexistence
# #
CONFIG_ESP_COEX_ENABLED=y CONFIG_ESP_COEX_ENABLED=y
CONFIG_ESP_COEX_SW_COEXIST_ENABLE=y # CONFIG_ESP_COEX_EXTERNAL_COEXIST_ENABLE is not set
# CONFIG_ESP_COEX_POWER_MANAGEMENT is not set
# CONFIG_ESP_COEX_GPIO_DEBUG is not set # CONFIG_ESP_COEX_GPIO_DEBUG is not set
# end of Wireless Coexistence # end of Wireless Coexistence
@@ -1327,7 +1003,7 @@ CONFIG_GPTIMER_ISR_HANDLER_IN_IRAM=y
# #
# ESP-Driver:SPI Configurations # ESP-Driver:SPI Configurations
# #
# CONFIG_SPI_MASTER_IN_IRAM is not set CONFIG_SPI_MASTER_IN_IRAM=y
CONFIG_SPI_MASTER_ISR_IN_IRAM=y CONFIG_SPI_MASTER_ISR_IN_IRAM=y
# CONFIG_SPI_SLAVE_IN_IRAM is not set # CONFIG_SPI_SLAVE_IN_IRAM is not set
CONFIG_SPI_SLAVE_ISR_IN_IRAM=y CONFIG_SPI_SLAVE_ISR_IN_IRAM=y
@@ -1413,7 +1089,7 @@ CONFIG_HTTPD_PURGE_BUF_LEN=32
# ESP HTTPS OTA # ESP HTTPS OTA
# #
# CONFIG_ESP_HTTPS_OTA_DECRYPT_CB is not set # 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 # end of ESP HTTPS OTA
# #
@@ -1509,6 +1185,7 @@ CONFIG_XTAL_FREQ=40
# end of Main XTAL Config # end of Main XTAL Config
CONFIG_ESP_SPI_BUS_LOCK_ISR_FUNCS_IN_IRAM=y CONFIG_ESP_SPI_BUS_LOCK_ISR_FUNCS_IN_IRAM=y
CONFIG_ESP_SPI_BUS_LOCK_FUNCS_IN_IRAM=y
# end of Hardware Settings # end of Hardware Settings
# #
@@ -1601,9 +1278,9 @@ CONFIG_SPIRAM_BOOT_INIT=y
# CONFIG_SPIRAM_USE_CAPS_ALLOC is not set # CONFIG_SPIRAM_USE_CAPS_ALLOC is not set
CONFIG_SPIRAM_USE_MALLOC=y CONFIG_SPIRAM_USE_MALLOC=y
CONFIG_SPIRAM_MEMTEST=y CONFIG_SPIRAM_MEMTEST=y
CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL=16384 CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL=4096
# CONFIG_SPIRAM_TRY_ALLOCATE_WIFI_LWIP is not set CONFIG_SPIRAM_TRY_ALLOCATE_WIFI_LWIP=y
CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL=32768 CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL=65536
# CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY is not set # CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY is not set
# CONFIG_SPIRAM_ALLOW_NOINIT_SEG_EXTERNAL_MEMORY is not set # CONFIG_SPIRAM_ALLOW_NOINIT_SEG_EXTERNAL_MEMORY is not set
# end of SPI RAM config # end of SPI RAM config
@@ -1636,13 +1313,12 @@ CONFIG_ESP32S3_ICACHE_ASSOCIATED_WAYS=8
CONFIG_ESP32S3_INSTRUCTION_CACHE_LINE_32B=y CONFIG_ESP32S3_INSTRUCTION_CACHE_LINE_32B=y
CONFIG_ESP32S3_INSTRUCTION_CACHE_LINE_SIZE=32 CONFIG_ESP32S3_INSTRUCTION_CACHE_LINE_SIZE=32
# CONFIG_ESP32S3_DATA_CACHE_16KB is not set # CONFIG_ESP32S3_DATA_CACHE_16KB is not set
CONFIG_ESP32S3_DATA_CACHE_32KB=y # CONFIG_ESP32S3_DATA_CACHE_32KB is not set
# CONFIG_ESP32S3_DATA_CACHE_64KB is not set CONFIG_ESP32S3_DATA_CACHE_64KB=y
CONFIG_ESP32S3_DATA_CACHE_SIZE=0x8000 CONFIG_ESP32S3_DATA_CACHE_SIZE=0x10000
# CONFIG_ESP32S3_DATA_CACHE_4WAYS is not set # CONFIG_ESP32S3_DATA_CACHE_4WAYS is not set
CONFIG_ESP32S3_DATA_CACHE_8WAYS=y CONFIG_ESP32S3_DATA_CACHE_8WAYS=y
CONFIG_ESP32S3_DCACHE_ASSOCIATED_WAYS=8 CONFIG_ESP32S3_DCACHE_ASSOCIATED_WAYS=8
# CONFIG_ESP32S3_DATA_CACHE_LINE_16B is not set
CONFIG_ESP32S3_DATA_CACHE_LINE_32B=y CONFIG_ESP32S3_DATA_CACHE_LINE_32B=y
# CONFIG_ESP32S3_DATA_CACHE_LINE_64B is not set # CONFIG_ESP32S3_DATA_CACHE_LINE_64B is not set
CONFIG_ESP32S3_DATA_CACHE_LINE_SIZE=32 CONFIG_ESP32S3_DATA_CACHE_LINE_SIZE=32
@@ -1679,7 +1355,7 @@ CONFIG_ESP_SYSTEM_MEMPROT_FEATURE_LOCK=y
CONFIG_ESP_SYSTEM_EVENT_QUEUE_SIZE=32 CONFIG_ESP_SYSTEM_EVENT_QUEUE_SIZE=32
CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=2304 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_CPU0=y
# CONFIG_ESP_MAIN_TASK_AFFINITY_CPU1 is not set # CONFIG_ESP_MAIN_TASK_AFFINITY_CPU1 is not set
# CONFIG_ESP_MAIN_TASK_AFFINITY_NO_AFFINITY is not set # CONFIG_ESP_MAIN_TASK_AFFINITY_NO_AFFINITY is not set
@@ -1757,11 +1433,11 @@ CONFIG_ESP_TIMER_IMPL_SYSTIMER=y
# Wi-Fi # Wi-Fi
# #
CONFIG_ESP_WIFI_ENABLED=y CONFIG_ESP_WIFI_ENABLED=y
CONFIG_ESP_WIFI_STATIC_RX_BUFFER_NUM=10 CONFIG_ESP_WIFI_STATIC_RX_BUFFER_NUM=2
CONFIG_ESP_WIFI_DYNAMIC_RX_BUFFER_NUM=32 CONFIG_ESP_WIFI_DYNAMIC_RX_BUFFER_NUM=32
CONFIG_ESP_WIFI_STATIC_TX_BUFFER=y CONFIG_ESP_WIFI_STATIC_TX_BUFFER=y
CONFIG_ESP_WIFI_TX_BUFFER_TYPE=0 CONFIG_ESP_WIFI_TX_BUFFER_TYPE=0
CONFIG_ESP_WIFI_STATIC_TX_BUFFER_NUM=16 CONFIG_ESP_WIFI_STATIC_TX_BUFFER_NUM=4
CONFIG_ESP_WIFI_CACHE_TX_BUFFER_NUM=32 CONFIG_ESP_WIFI_CACHE_TX_BUFFER_NUM=32
CONFIG_ESP_WIFI_STATIC_RX_MGMT_BUFFER=y CONFIG_ESP_WIFI_STATIC_RX_MGMT_BUFFER=y
# CONFIG_ESP_WIFI_DYNAMIC_RX_MGMT_BUFFER is not set # CONFIG_ESP_WIFI_DYNAMIC_RX_MGMT_BUFFER is not set
@@ -1769,9 +1445,9 @@ CONFIG_ESP_WIFI_DYNAMIC_RX_MGMT_BUF=0
CONFIG_ESP_WIFI_RX_MGMT_BUF_NUM_DEF=5 CONFIG_ESP_WIFI_RX_MGMT_BUF_NUM_DEF=5
# CONFIG_ESP_WIFI_CSI_ENABLED is not set # CONFIG_ESP_WIFI_CSI_ENABLED is not set
CONFIG_ESP_WIFI_AMPDU_TX_ENABLED=y CONFIG_ESP_WIFI_AMPDU_TX_ENABLED=y
CONFIG_ESP_WIFI_TX_BA_WIN=6 CONFIG_ESP_WIFI_TX_BA_WIN=2
CONFIG_ESP_WIFI_AMPDU_RX_ENABLED=y CONFIG_ESP_WIFI_AMPDU_RX_ENABLED=y
CONFIG_ESP_WIFI_RX_BA_WIN=6 CONFIG_ESP_WIFI_RX_BA_WIN=2
# CONFIG_ESP_WIFI_AMSDU_TX_ENABLED is not set # CONFIG_ESP_WIFI_AMSDU_TX_ENABLED is not set
CONFIG_ESP_WIFI_NVS_ENABLED=y CONFIG_ESP_WIFI_NVS_ENABLED=y
CONFIG_ESP_WIFI_TASK_PINNED_TO_CORE_0=y CONFIG_ESP_WIFI_TASK_PINNED_TO_CORE_0=y
@@ -1902,16 +1578,19 @@ CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=2048
CONFIG_FREERTOS_TIMER_QUEUE_LENGTH=10 CONFIG_FREERTOS_TIMER_QUEUE_LENGTH=10
CONFIG_FREERTOS_QUEUE_REGISTRY_SIZE=0 CONFIG_FREERTOS_QUEUE_REGISTRY_SIZE=0
CONFIG_FREERTOS_TASK_NOTIFICATION_ARRAY_ENTRIES=1 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_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 # CONFIG_FREERTOS_USE_APPLICATION_TASK_TAG is not set
# end of Kernel # end of Kernel
# #
# Port # Port
# #
CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER=y
# CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK is not set # CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK is not set
CONFIG_FREERTOS_TLSP_DELETION_CALLBACKS=y CONFIG_FREERTOS_TLSP_DELETION_CALLBACKS=y
# CONFIG_FREERTOS_TASK_PRE_DELETION_HOOK is not set # CONFIG_FREERTOS_TASK_PRE_DELETION_HOOK is not set
@@ -1923,6 +1602,7 @@ CONFIG_FREERTOS_TICK_SUPPORT_SYSTIMER=y
CONFIG_FREERTOS_CORETIMER_SYSTIMER_LVL1=y CONFIG_FREERTOS_CORETIMER_SYSTIMER_LVL1=y
# CONFIG_FREERTOS_CORETIMER_SYSTIMER_LVL3 is not set # CONFIG_FREERTOS_CORETIMER_SYSTIMER_LVL3 is not set
CONFIG_FREERTOS_SYSTICK_USES_SYSTIMER=y 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_PLACE_FUNCTIONS_INTO_FLASH is not set
# CONFIG_FREERTOS_CHECK_PORT_CRITICAL_COMPLIANCE is not set # CONFIG_FREERTOS_CHECK_PORT_CRITICAL_COMPLIANCE is not set
# end of Port # end of Port
@@ -2071,6 +1751,7 @@ CONFIG_LWIP_TCP_OOSEQ_MAX_PBUFS=4
CONFIG_LWIP_TCP_OVERSIZE_MSS=y CONFIG_LWIP_TCP_OVERSIZE_MSS=y
# CONFIG_LWIP_TCP_OVERSIZE_QUARTER_MSS is not set # CONFIG_LWIP_TCP_OVERSIZE_QUARTER_MSS is not set
# CONFIG_LWIP_TCP_OVERSIZE_DISABLE is not set # CONFIG_LWIP_TCP_OVERSIZE_DISABLE is not set
# CONFIG_LWIP_WND_SCALE is not set
CONFIG_LWIP_TCP_RTO_TIME=1500 CONFIG_LWIP_TCP_RTO_TIME=1500
# end of TCP # end of TCP
@@ -2602,10 +2283,6 @@ CONFIG_WL_SECTOR_SIZE=4096
# #
CONFIG_WIFI_PROV_SCAN_MAX_ENTRIES=16 CONFIG_WIFI_PROV_SCAN_MAX_ENTRIES=16
CONFIG_WIFI_PROV_AUTOSTOP_TIMEOUT=30 CONFIG_WIFI_PROV_AUTOSTOP_TIMEOUT=30
# CONFIG_WIFI_PROV_BLE_BONDING is not set
# CONFIG_WIFI_PROV_BLE_FORCE_ENCRYPTION is not set
# CONFIG_WIFI_PROV_BLE_NOTIFY is not set
# CONFIG_WIFI_PROV_KEEP_BLE_ON_AFTER_PROV is not set
CONFIG_WIFI_PROV_STA_ALL_CHANNEL_SCAN=y CONFIG_WIFI_PROV_STA_ALL_CHANNEL_SCAN=y
# CONFIG_WIFI_PROV_STA_FAST_SCAN is not set # CONFIG_WIFI_PROV_STA_FAST_SCAN is not set
# end of Wi-Fi Provisioning Manager # end of Wi-Fi Provisioning Manager
@@ -2635,6 +2312,13 @@ CONFIG_DSP_MAX_FFT_SIZE_4096=y
CONFIG_DSP_MAX_FFT_SIZE=4096 CONFIG_DSP_MAX_FFT_SIZE=4096
# end of DSP Library # 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 # LVGL configuration
# #
@@ -2667,8 +2351,8 @@ CONFIG_LV_MEM_BUF_MAX_NUM=16
# #
# HAL Settings # HAL Settings
# #
CONFIG_LV_DISP_DEF_REFR_PERIOD=30 CONFIG_LV_DISP_DEF_REFR_PERIOD=10
CONFIG_LV_INDEV_DEF_READ_PERIOD=30 CONFIG_LV_INDEV_DEF_READ_PERIOD=10
# CONFIG_LV_TICK_CUSTOM is not set # CONFIG_LV_TICK_CUSTOM is not set
CONFIG_LV_DPI_DEF=130 CONFIG_LV_DPI_DEF=130
# end of HAL Settings # end of HAL Settings
@@ -2681,10 +2365,10 @@ CONFIG_LV_DPI_DEF=130
# Drawing # Drawing
# #
CONFIG_LV_DRAW_COMPLEX=y CONFIG_LV_DRAW_COMPLEX=y
CONFIG_LV_SHADOW_CACHE_SIZE=0 CONFIG_LV_SHADOW_CACHE_SIZE=4
CONFIG_LV_CIRCLE_CACHE_SIZE=4 CONFIG_LV_CIRCLE_CACHE_SIZE=4
CONFIG_LV_LAYER_SIMPLE_BUF_SIZE=24576 CONFIG_LV_LAYER_SIMPLE_BUF_SIZE=10240
CONFIG_LV_IMG_CACHE_DEF_SIZE=0 CONFIG_LV_IMG_CACHE_DEF_SIZE=128
CONFIG_LV_GRADIENT_MAX_STOPS=2 CONFIG_LV_GRADIENT_MAX_STOPS=2
CONFIG_LV_GRAD_CACHE_DEF_SIZE=0 CONFIG_LV_GRAD_CACHE_DEF_SIZE=0
# CONFIG_LV_DITHER_GRADIENT is not set # CONFIG_LV_DITHER_GRADIENT is not set
@@ -2714,9 +2398,9 @@ CONFIG_LV_DISP_ROT_MAX_BUF=10240
# #
CONFIG_LV_USE_ASSERT_NULL=y CONFIG_LV_USE_ASSERT_NULL=y
CONFIG_LV_USE_ASSERT_MALLOC=y CONFIG_LV_USE_ASSERT_MALLOC=y
# CONFIG_LV_USE_ASSERT_STYLE is not set CONFIG_LV_USE_ASSERT_STYLE=y
# CONFIG_LV_USE_ASSERT_MEM_INTEGRITY is not set # CONFIG_LV_USE_ASSERT_MEM_INTEGRITY is not set
# CONFIG_LV_USE_ASSERT_OBJ is not set CONFIG_LV_USE_ASSERT_OBJ=y
CONFIG_LV_ASSERT_HANDLER_INCLUDE="assert.h" CONFIG_LV_ASSERT_HANDLER_INCLUDE="assert.h"
# end of Asserts # end of Asserts
@@ -2762,7 +2446,7 @@ CONFIG_LV_ATTRIBUTE_MEM_ALIGN_SIZE=1
CONFIG_LV_FONT_MONTSERRAT_12=y CONFIG_LV_FONT_MONTSERRAT_12=y
CONFIG_LV_FONT_MONTSERRAT_14=y CONFIG_LV_FONT_MONTSERRAT_14=y
CONFIG_LV_FONT_MONTSERRAT_16=y CONFIG_LV_FONT_MONTSERRAT_16=y
# CONFIG_LV_FONT_MONTSERRAT_18 is not set CONFIG_LV_FONT_MONTSERRAT_18=y
# CONFIG_LV_FONT_MONTSERRAT_20 is not set # CONFIG_LV_FONT_MONTSERRAT_20 is not set
# CONFIG_LV_FONT_MONTSERRAT_22 is not set # CONFIG_LV_FONT_MONTSERRAT_22 is not set
# CONFIG_LV_FONT_MONTSERRAT_24 is not set # CONFIG_LV_FONT_MONTSERRAT_24 is not set
@@ -2781,7 +2465,7 @@ CONFIG_LV_FONT_MONTSERRAT_16=y
# CONFIG_LV_FONT_MONTSERRAT_12_SUBPX is not set # CONFIG_LV_FONT_MONTSERRAT_12_SUBPX is not set
# CONFIG_LV_FONT_MONTSERRAT_28_COMPRESSED is not set # CONFIG_LV_FONT_MONTSERRAT_28_COMPRESSED is not set
# CONFIG_LV_FONT_DEJAVU_16_PERSIAN_HEBREW is not set # CONFIG_LV_FONT_DEJAVU_16_PERSIAN_HEBREW is not set
# CONFIG_LV_FONT_SIMSUN_16_CJK is not set CONFIG_LV_FONT_SIMSUN_16_CJK=y
# CONFIG_LV_FONT_UNSCII_8 is not set # CONFIG_LV_FONT_UNSCII_8 is not set
# CONFIG_LV_FONT_UNSCII_16 is not set # CONFIG_LV_FONT_UNSCII_16 is not set
# CONFIG_LV_FONT_CUSTOM is not set # CONFIG_LV_FONT_CUSTOM is not set
@@ -2789,7 +2473,7 @@ CONFIG_LV_FONT_MONTSERRAT_16=y
# CONFIG_LV_FONT_DEFAULT_MONTSERRAT_8 is not set # CONFIG_LV_FONT_DEFAULT_MONTSERRAT_8 is not set
# CONFIG_LV_FONT_DEFAULT_MONTSERRAT_12 is not set # CONFIG_LV_FONT_DEFAULT_MONTSERRAT_12 is not set
CONFIG_LV_FONT_DEFAULT_MONTSERRAT_14=y # CONFIG_LV_FONT_DEFAULT_MONTSERRAT_14 is not set
# CONFIG_LV_FONT_DEFAULT_MONTSERRAT_16 is not set # CONFIG_LV_FONT_DEFAULT_MONTSERRAT_16 is not set
# CONFIG_LV_FONT_DEFAULT_MONTSERRAT_18 is not set # CONFIG_LV_FONT_DEFAULT_MONTSERRAT_18 is not set
# CONFIG_LV_FONT_DEFAULT_MONTSERRAT_20 is not set # CONFIG_LV_FONT_DEFAULT_MONTSERRAT_20 is not set
@@ -2810,7 +2494,7 @@ CONFIG_LV_FONT_DEFAULT_MONTSERRAT_14=y
# CONFIG_LV_FONT_DEFAULT_MONTSERRAT_12_SUBPX is not set # CONFIG_LV_FONT_DEFAULT_MONTSERRAT_12_SUBPX is not set
# CONFIG_LV_FONT_DEFAULT_MONTSERRAT_28_COMPRESSED is not set # CONFIG_LV_FONT_DEFAULT_MONTSERRAT_28_COMPRESSED is not set
# CONFIG_LV_FONT_DEFAULT_DEJAVU_16_PERSIAN_HEBREW is not set # CONFIG_LV_FONT_DEFAULT_DEJAVU_16_PERSIAN_HEBREW is not set
# CONFIG_LV_FONT_DEFAULT_SIMSUN_16_CJK is not set CONFIG_LV_FONT_DEFAULT_SIMSUN_16_CJK=y
# CONFIG_LV_FONT_DEFAULT_UNSCII_8 is not set # CONFIG_LV_FONT_DEFAULT_UNSCII_8 is not set
# CONFIG_LV_FONT_DEFAULT_UNSCII_16 is not set # CONFIG_LV_FONT_DEFAULT_UNSCII_16 is not set
# CONFIG_LV_FONT_FMT_TXT_LARGE is not set # CONFIG_LV_FONT_FMT_TXT_LARGE is not set
@@ -2827,7 +2511,10 @@ CONFIG_LV_TXT_ENC_UTF8=y
CONFIG_LV_TXT_BREAK_CHARS=" ,.;:-_" CONFIG_LV_TXT_BREAK_CHARS=" ,.;:-_"
CONFIG_LV_TXT_LINE_BREAK_LONG_LEN=0 CONFIG_LV_TXT_LINE_BREAK_LONG_LEN=0
CONFIG_LV_TXT_COLOR_CMD="#" CONFIG_LV_TXT_COLOR_CMD="#"
# CONFIG_LV_USE_BIDI is not set CONFIG_LV_USE_BIDI=y
# CONFIG_LV_BIDI_DIR_LTR is not set
# CONFIG_LV_BIDI_DIR_RTL is not set
CONFIG_LV_BIDI_DIR_AUTO=y
# CONFIG_LV_USE_ARABIC_PERSIAN_CHARS is not set # CONFIG_LV_USE_ARABIC_PERSIAN_CHARS is not set
# end of Text Settings # end of Text Settings
@@ -2907,7 +2594,7 @@ CONFIG_LV_USE_GRID=y
# CONFIG_LV_USE_FS_WIN32 is not set # CONFIG_LV_USE_FS_WIN32 is not set
# CONFIG_LV_USE_FS_FATFS is not set # CONFIG_LV_USE_FS_FATFS is not set
# CONFIG_LV_USE_FS_LITTLEFS is not set # CONFIG_LV_USE_FS_LITTLEFS is not set
# CONFIG_LV_USE_PNG is not set CONFIG_LV_USE_PNG=y
# CONFIG_LV_USE_BMP is not set # CONFIG_LV_USE_BMP is not set
# CONFIG_LV_USE_SJPG is not set # CONFIG_LV_USE_SJPG is not set
CONFIG_LV_USE_GIF=y CONFIG_LV_USE_GIF=y
@@ -2974,16 +2661,17 @@ CONFIG_LOG_BOOTLOADER_LEVEL=3
CONFIG_FLASHMODE_DIO=y CONFIG_FLASHMODE_DIO=y
# CONFIG_FLASHMODE_DOUT is not set # CONFIG_FLASHMODE_DOUT is not set
CONFIG_MONITOR_BAUD=115200 CONFIG_MONITOR_BAUD=115200
CONFIG_OPTIMIZATION_LEVEL_DEBUG=y # CONFIG_OPTIMIZATION_LEVEL_DEBUG is not set
CONFIG_COMPILER_OPTIMIZATION_LEVEL_DEBUG=y # CONFIG_COMPILER_OPTIMIZATION_LEVEL_DEBUG is not set
CONFIG_COMPILER_OPTIMIZATION_DEFAULT=y # CONFIG_COMPILER_OPTIMIZATION_DEFAULT is not set
# CONFIG_OPTIMIZATION_LEVEL_RELEASE is not set # CONFIG_OPTIMIZATION_LEVEL_RELEASE is not set
# CONFIG_COMPILER_OPTIMIZATION_LEVEL_RELEASE is not set # CONFIG_COMPILER_OPTIMIZATION_LEVEL_RELEASE is not set
CONFIG_OPTIMIZATION_ASSERTIONS_ENABLED=y CONFIG_OPTIMIZATION_ASSERTIONS_ENABLED=y
# CONFIG_OPTIMIZATION_ASSERTIONS_SILENT is not set # CONFIG_OPTIMIZATION_ASSERTIONS_SILENT is not set
# CONFIG_OPTIMIZATION_ASSERTIONS_DISABLED is not set # CONFIG_OPTIMIZATION_ASSERTIONS_DISABLED is not set
CONFIG_OPTIMIZATION_ASSERTION_LEVEL=2 CONFIG_OPTIMIZATION_ASSERTION_LEVEL=2
# CONFIG_CXX_EXCEPTIONS is not set CONFIG_CXX_EXCEPTIONS=y
CONFIG_CXX_EXCEPTIONS_EMG_POOL_SIZE=0
CONFIG_STACK_CHECK_NONE=y CONFIG_STACK_CHECK_NONE=y
# CONFIG_STACK_CHECK_NORM is not set # CONFIG_STACK_CHECK_NORM is not set
# CONFIG_STACK_CHECK_STRONG is not set # CONFIG_STACK_CHECK_STRONG is not set
@@ -2992,199 +2680,15 @@ CONFIG_STACK_CHECK_NONE=y
# CONFIG_ESP32_APPTRACE_DEST_TRAX is not set # CONFIG_ESP32_APPTRACE_DEST_TRAX is not set
CONFIG_ESP32_APPTRACE_DEST_NONE=y CONFIG_ESP32_APPTRACE_DEST_NONE=y
CONFIG_ESP32_APPTRACE_LOCK_ENABLE=y CONFIG_ESP32_APPTRACE_LOCK_ENABLE=y
CONFIG_BLUEDROID_ENABLED=y # CONFIG_EXTERNAL_COEX_ENABLE is not set
# CONFIG_NIMBLE_ENABLED is not set # CONFIG_ESP_WIFI_EXTERNAL_COEXIST_ENABLE is not set
CONFIG_BTC_TASK_STACK_SIZE=3072
CONFIG_BLUEDROID_PINNED_TO_CORE_0=y
# CONFIG_BLUEDROID_PINNED_TO_CORE_1 is not set
CONFIG_BLUEDROID_PINNED_TO_CORE=0
CONFIG_BTU_TASK_STACK_SIZE=4352
# CONFIG_BLUEDROID_MEM_DEBUG is not set
CONFIG_GATTS_ENABLE=y
# CONFIG_GATTS_SEND_SERVICE_CHANGE_MANUAL is not set
CONFIG_GATTS_SEND_SERVICE_CHANGE_AUTO=y
CONFIG_GATTS_SEND_SERVICE_CHANGE_MODE=0
CONFIG_GATTC_ENABLE=y
# CONFIG_GATTC_CACHE_NVS_FLASH is not set
CONFIG_BLE_SMP_ENABLE=y
# CONFIG_SMP_SLAVE_CON_PARAMS_UPD_ENABLE is not set
# CONFIG_HCI_TRACE_LEVEL_NONE is not set
# CONFIG_HCI_TRACE_LEVEL_ERROR is not set
CONFIG_HCI_TRACE_LEVEL_WARNING=y
# CONFIG_HCI_TRACE_LEVEL_API is not set
# CONFIG_HCI_TRACE_LEVEL_EVENT is not set
# CONFIG_HCI_TRACE_LEVEL_DEBUG is not set
# CONFIG_HCI_TRACE_LEVEL_VERBOSE is not set
CONFIG_HCI_INITIAL_TRACE_LEVEL=2
# CONFIG_BTM_TRACE_LEVEL_NONE is not set
# CONFIG_BTM_TRACE_LEVEL_ERROR is not set
CONFIG_BTM_TRACE_LEVEL_WARNING=y
# CONFIG_BTM_TRACE_LEVEL_API is not set
# CONFIG_BTM_TRACE_LEVEL_EVENT is not set
# CONFIG_BTM_TRACE_LEVEL_DEBUG is not set
# CONFIG_BTM_TRACE_LEVEL_VERBOSE is not set
CONFIG_BTM_INITIAL_TRACE_LEVEL=2
# CONFIG_L2CAP_TRACE_LEVEL_NONE is not set
# CONFIG_L2CAP_TRACE_LEVEL_ERROR is not set
CONFIG_L2CAP_TRACE_LEVEL_WARNING=y
# CONFIG_L2CAP_TRACE_LEVEL_API is not set
# CONFIG_L2CAP_TRACE_LEVEL_EVENT is not set
# CONFIG_L2CAP_TRACE_LEVEL_DEBUG is not set
# CONFIG_L2CAP_TRACE_LEVEL_VERBOSE is not set
CONFIG_L2CAP_INITIAL_TRACE_LEVEL=2
# CONFIG_RFCOMM_TRACE_LEVEL_NONE is not set
# CONFIG_RFCOMM_TRACE_LEVEL_ERROR is not set
CONFIG_RFCOMM_TRACE_LEVEL_WARNING=y
# CONFIG_RFCOMM_TRACE_LEVEL_API is not set
# CONFIG_RFCOMM_TRACE_LEVEL_EVENT is not set
# CONFIG_RFCOMM_TRACE_LEVEL_DEBUG is not set
# CONFIG_RFCOMM_TRACE_LEVEL_VERBOSE is not set
CONFIG_RFCOMM_INITIAL_TRACE_LEVEL=2
# CONFIG_SDP_TRACE_LEVEL_NONE is not set
# CONFIG_SDP_TRACE_LEVEL_ERROR is not set
CONFIG_SDP_TRACE_LEVEL_WARNING=y
# CONFIG_SDP_TRACE_LEVEL_API is not set
# CONFIG_SDP_TRACE_LEVEL_EVENT is not set
# CONFIG_SDP_TRACE_LEVEL_DEBUG is not set
# CONFIG_SDP_TRACE_LEVEL_VERBOSE is not set
CONFIG_BTH_LOG_SDP_INITIAL_TRACE_LEVEL=2
# CONFIG_GAP_TRACE_LEVEL_NONE is not set
# CONFIG_GAP_TRACE_LEVEL_ERROR is not set
CONFIG_GAP_TRACE_LEVEL_WARNING=y
# CONFIG_GAP_TRACE_LEVEL_API is not set
# CONFIG_GAP_TRACE_LEVEL_EVENT is not set
# CONFIG_GAP_TRACE_LEVEL_DEBUG is not set
# CONFIG_GAP_TRACE_LEVEL_VERBOSE is not set
CONFIG_GAP_INITIAL_TRACE_LEVEL=2
CONFIG_BNEP_INITIAL_TRACE_LEVEL=2
# CONFIG_PAN_TRACE_LEVEL_NONE is not set
# CONFIG_PAN_TRACE_LEVEL_ERROR is not set
CONFIG_PAN_TRACE_LEVEL_WARNING=y
# CONFIG_PAN_TRACE_LEVEL_API is not set
# CONFIG_PAN_TRACE_LEVEL_EVENT is not set
# CONFIG_PAN_TRACE_LEVEL_DEBUG is not set
# CONFIG_PAN_TRACE_LEVEL_VERBOSE is not set
CONFIG_PAN_INITIAL_TRACE_LEVEL=2
# CONFIG_A2D_TRACE_LEVEL_NONE is not set
# CONFIG_A2D_TRACE_LEVEL_ERROR is not set
CONFIG_A2D_TRACE_LEVEL_WARNING=y
# CONFIG_A2D_TRACE_LEVEL_API is not set
# CONFIG_A2D_TRACE_LEVEL_EVENT is not set
# CONFIG_A2D_TRACE_LEVEL_DEBUG is not set
# CONFIG_A2D_TRACE_LEVEL_VERBOSE is not set
CONFIG_A2D_INITIAL_TRACE_LEVEL=2
# CONFIG_AVDT_TRACE_LEVEL_NONE is not set
# CONFIG_AVDT_TRACE_LEVEL_ERROR is not set
CONFIG_AVDT_TRACE_LEVEL_WARNING=y
# CONFIG_AVDT_TRACE_LEVEL_API is not set
# CONFIG_AVDT_TRACE_LEVEL_EVENT is not set
# CONFIG_AVDT_TRACE_LEVEL_DEBUG is not set
# CONFIG_AVDT_TRACE_LEVEL_VERBOSE is not set
CONFIG_AVDT_INITIAL_TRACE_LEVEL=2
# CONFIG_AVCT_TRACE_LEVEL_NONE is not set
# CONFIG_AVCT_TRACE_LEVEL_ERROR is not set
CONFIG_AVCT_TRACE_LEVEL_WARNING=y
# CONFIG_AVCT_TRACE_LEVEL_API is not set
# CONFIG_AVCT_TRACE_LEVEL_EVENT is not set
# CONFIG_AVCT_TRACE_LEVEL_DEBUG is not set
# CONFIG_AVCT_TRACE_LEVEL_VERBOSE is not set
CONFIG_AVCT_INITIAL_TRACE_LEVEL=2
# CONFIG_AVRC_TRACE_LEVEL_NONE is not set
# CONFIG_AVRC_TRACE_LEVEL_ERROR is not set
CONFIG_AVRC_TRACE_LEVEL_WARNING=y
# CONFIG_AVRC_TRACE_LEVEL_API is not set
# CONFIG_AVRC_TRACE_LEVEL_EVENT is not set
# CONFIG_AVRC_TRACE_LEVEL_DEBUG is not set
# CONFIG_AVRC_TRACE_LEVEL_VERBOSE is not set
CONFIG_AVRC_INITIAL_TRACE_LEVEL=2
# CONFIG_MCA_TRACE_LEVEL_NONE is not set
# CONFIG_MCA_TRACE_LEVEL_ERROR is not set
CONFIG_MCA_TRACE_LEVEL_WARNING=y
# CONFIG_MCA_TRACE_LEVEL_API is not set
# CONFIG_MCA_TRACE_LEVEL_EVENT is not set
# CONFIG_MCA_TRACE_LEVEL_DEBUG is not set
# CONFIG_MCA_TRACE_LEVEL_VERBOSE is not set
CONFIG_MCA_INITIAL_TRACE_LEVEL=2
# CONFIG_HID_TRACE_LEVEL_NONE is not set
# CONFIG_HID_TRACE_LEVEL_ERROR is not set
CONFIG_HID_TRACE_LEVEL_WARNING=y
# CONFIG_HID_TRACE_LEVEL_API is not set
# CONFIG_HID_TRACE_LEVEL_EVENT is not set
# CONFIG_HID_TRACE_LEVEL_DEBUG is not set
# CONFIG_HID_TRACE_LEVEL_VERBOSE is not set
CONFIG_HID_INITIAL_TRACE_LEVEL=2
# CONFIG_APPL_TRACE_LEVEL_NONE is not set
# CONFIG_APPL_TRACE_LEVEL_ERROR is not set
CONFIG_APPL_TRACE_LEVEL_WARNING=y
# CONFIG_APPL_TRACE_LEVEL_API is not set
# CONFIG_APPL_TRACE_LEVEL_EVENT is not set
# CONFIG_APPL_TRACE_LEVEL_DEBUG is not set
# CONFIG_APPL_TRACE_LEVEL_VERBOSE is not set
CONFIG_APPL_INITIAL_TRACE_LEVEL=2
# CONFIG_GATT_TRACE_LEVEL_NONE is not set
# CONFIG_GATT_TRACE_LEVEL_ERROR is not set
CONFIG_GATT_TRACE_LEVEL_WARNING=y
# CONFIG_GATT_TRACE_LEVEL_API is not set
# CONFIG_GATT_TRACE_LEVEL_EVENT is not set
# CONFIG_GATT_TRACE_LEVEL_DEBUG is not set
# CONFIG_GATT_TRACE_LEVEL_VERBOSE is not set
CONFIG_GATT_INITIAL_TRACE_LEVEL=2
# CONFIG_SMP_TRACE_LEVEL_NONE is not set
# CONFIG_SMP_TRACE_LEVEL_ERROR is not set
CONFIG_SMP_TRACE_LEVEL_WARNING=y
# CONFIG_SMP_TRACE_LEVEL_API is not set
# CONFIG_SMP_TRACE_LEVEL_EVENT is not set
# CONFIG_SMP_TRACE_LEVEL_DEBUG is not set
# CONFIG_SMP_TRACE_LEVEL_VERBOSE is not set
CONFIG_SMP_INITIAL_TRACE_LEVEL=2
# CONFIG_BTIF_TRACE_LEVEL_NONE is not set
# CONFIG_BTIF_TRACE_LEVEL_ERROR is not set
CONFIG_BTIF_TRACE_LEVEL_WARNING=y
# CONFIG_BTIF_TRACE_LEVEL_API is not set
# CONFIG_BTIF_TRACE_LEVEL_EVENT is not set
# CONFIG_BTIF_TRACE_LEVEL_DEBUG is not set
# CONFIG_BTIF_TRACE_LEVEL_VERBOSE is not set
CONFIG_BTIF_INITIAL_TRACE_LEVEL=2
# CONFIG_BTC_TRACE_LEVEL_NONE is not set
# CONFIG_BTC_TRACE_LEVEL_ERROR is not set
CONFIG_BTC_TRACE_LEVEL_WARNING=y
# CONFIG_BTC_TRACE_LEVEL_API is not set
# CONFIG_BTC_TRACE_LEVEL_EVENT is not set
# CONFIG_BTC_TRACE_LEVEL_DEBUG is not set
# CONFIG_BTC_TRACE_LEVEL_VERBOSE is not set
CONFIG_BTC_INITIAL_TRACE_LEVEL=2
# CONFIG_OSI_TRACE_LEVEL_NONE is not set
# CONFIG_OSI_TRACE_LEVEL_ERROR is not set
CONFIG_OSI_TRACE_LEVEL_WARNING=y
# CONFIG_OSI_TRACE_LEVEL_API is not set
# CONFIG_OSI_TRACE_LEVEL_EVENT is not set
# CONFIG_OSI_TRACE_LEVEL_DEBUG is not set
# CONFIG_OSI_TRACE_LEVEL_VERBOSE is not set
CONFIG_OSI_INITIAL_TRACE_LEVEL=2
# CONFIG_BLUFI_TRACE_LEVEL_NONE is not set
# CONFIG_BLUFI_TRACE_LEVEL_ERROR is not set
CONFIG_BLUFI_TRACE_LEVEL_WARNING=y
# CONFIG_BLUFI_TRACE_LEVEL_API is not set
# CONFIG_BLUFI_TRACE_LEVEL_EVENT is not set
# CONFIG_BLUFI_TRACE_LEVEL_DEBUG is not set
# CONFIG_BLUFI_TRACE_LEVEL_VERBOSE is not set
CONFIG_BLUFI_INITIAL_TRACE_LEVEL=2
# CONFIG_BLE_HOST_QUEUE_CONGESTION_CHECK is not set
CONFIG_SMP_ENABLE=y
# CONFIG_BLE_ACTIVE_SCAN_REPORT_ADV_SCAN_RSP_INDIVIDUALLY is not set
CONFIG_BLE_ESTABLISH_LINK_CONNECTION_TIMEOUT=30
# CONFIG_BT_NIMBLE_COEX_PHY_CODED_TX_RX_TLIM_EN is not set
CONFIG_BT_NIMBLE_COEX_PHY_CODED_TX_RX_TLIM_DIS=y
CONFIG_SW_COEXIST_ENABLE=y
CONFIG_ESP32_WIFI_SW_COEXIST_ENABLE=y
CONFIG_ESP_WIFI_SW_COEXIST_ENABLE=y
# CONFIG_MCPWM_ISR_IN_IRAM is not set # CONFIG_MCPWM_ISR_IN_IRAM is not set
# CONFIG_EVENT_LOOP_PROFILING is not set # CONFIG_EVENT_LOOP_PROFILING is not set
CONFIG_POST_EVENTS_FROM_ISR=y CONFIG_POST_EVENTS_FROM_ISR=y
CONFIG_POST_EVENTS_FROM_IRAM_ISR=y CONFIG_POST_EVENTS_FROM_IRAM_ISR=y
CONFIG_GDBSTUB_SUPPORT_TASKS=y CONFIG_GDBSTUB_SUPPORT_TASKS=y
CONFIG_GDBSTUB_MAX_TASKS=32 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_ESP32S3_DEEP_SLEEP_WAKEUP_DELAY=2000
CONFIG_ESP_SLEEP_DEEP_SLEEP_WAKEUP_DELAY=2000 CONFIG_ESP_SLEEP_DEEP_SLEEP_WAKEUP_DELAY=2000
CONFIG_ESP32S3_RTC_CLK_SRC_INT_RC=y CONFIG_ESP32S3_RTC_CLK_SRC_INT_RC=y
@@ -3209,7 +2713,7 @@ CONFIG_ESP32S3_DEFAULT_CPU_FREQ_240=y
CONFIG_ESP32S3_DEFAULT_CPU_FREQ_MHZ=240 CONFIG_ESP32S3_DEFAULT_CPU_FREQ_MHZ=240
CONFIG_SYSTEM_EVENT_QUEUE_SIZE=32 CONFIG_SYSTEM_EVENT_QUEUE_SIZE=32
CONFIG_SYSTEM_EVENT_TASK_STACK_SIZE=2304 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_DEFAULT=y
# CONFIG_CONSOLE_UART_CUSTOM is not set # CONFIG_CONSOLE_UART_CUSTOM is not set
# CONFIG_CONSOLE_UART_NONE is not set # CONFIG_CONSOLE_UART_NONE is not set
@@ -3250,19 +2754,19 @@ CONFIG_ESP32S3_BROWNOUT_DET_LVL=7
CONFIG_IPC_TASK_STACK_SIZE=1280 CONFIG_IPC_TASK_STACK_SIZE=1280
CONFIG_TIMER_TASK_STACK_SIZE=3584 CONFIG_TIMER_TASK_STACK_SIZE=3584
CONFIG_ESP32_WIFI_ENABLED=y CONFIG_ESP32_WIFI_ENABLED=y
CONFIG_ESP32_WIFI_STATIC_RX_BUFFER_NUM=10 CONFIG_ESP32_WIFI_STATIC_RX_BUFFER_NUM=2
CONFIG_ESP32_WIFI_DYNAMIC_RX_BUFFER_NUM=32 CONFIG_ESP32_WIFI_DYNAMIC_RX_BUFFER_NUM=32
CONFIG_ESP32_WIFI_STATIC_TX_BUFFER=y CONFIG_ESP32_WIFI_STATIC_TX_BUFFER=y
CONFIG_ESP32_WIFI_TX_BUFFER_TYPE=0 CONFIG_ESP32_WIFI_TX_BUFFER_TYPE=0
CONFIG_ESP32_WIFI_STATIC_TX_BUFFER_NUM=16 CONFIG_ESP32_WIFI_STATIC_TX_BUFFER_NUM=4
CONFIG_ESP32_WIFI_CACHE_TX_BUFFER_NUM=32 CONFIG_ESP32_WIFI_CACHE_TX_BUFFER_NUM=32
# CONFIG_ESP32_WIFI_CSI_ENABLED is not set # CONFIG_ESP32_WIFI_CSI_ENABLED is not set
CONFIG_ESP32_WIFI_AMPDU_TX_ENABLED=y CONFIG_ESP32_WIFI_AMPDU_TX_ENABLED=y
CONFIG_ESP32_WIFI_TX_BA_WIN=6 CONFIG_ESP32_WIFI_TX_BA_WIN=2
CONFIG_ESP32_WIFI_AMPDU_RX_ENABLED=y CONFIG_ESP32_WIFI_AMPDU_RX_ENABLED=y
CONFIG_ESP32_WIFI_AMPDU_RX_ENABLED=y CONFIG_ESP32_WIFI_AMPDU_RX_ENABLED=y
CONFIG_ESP32_WIFI_RX_BA_WIN=6 CONFIG_ESP32_WIFI_RX_BA_WIN=2
CONFIG_ESP32_WIFI_RX_BA_WIN=6 CONFIG_ESP32_WIFI_RX_BA_WIN=2
# CONFIG_ESP32_WIFI_AMSDU_TX_ENABLED is not set # CONFIG_ESP32_WIFI_AMSDU_TX_ENABLED is not set
CONFIG_ESP32_WIFI_NVS_ENABLED=y CONFIG_ESP32_WIFI_NVS_ENABLED=y
CONFIG_ESP32_WIFI_TASK_PINNED_TO_CORE_0=y CONFIG_ESP32_WIFI_TASK_PINNED_TO_CORE_0=y
+1 -1
View File
@@ -91,7 +91,7 @@ void sd_card_module_test(void)
printf("3. 测试文件写入...\n"); printf("3. 测试文件写入...\n");
char test_file_path[] = MOUNT_POINT"/test.txt"; char test_file_path[] = MOUNT_POINT"/test.txt";
char test_data[] = "这是一段测试数据,用于验证SD卡写入功能。"; char test_data[] = "这是一段测试数据,用于验证SD卡写入功能。";
esp_err_t write_result = s_example_write_file(test_file_path, test_data); esp_err_t write_result = s_example_write_file(test_file_path, test_data, "w");
if(write_result == ESP_OK) { if(write_result == ESP_OK) {
printf(" 文件写入成功: %s\n", test_file_path); printf(" 文件写入成功: %s\n", test_file_path);
} else { } else {
+110 -1
View File
@@ -187,4 +187,113 @@
实际完成任务: 实际完成任务:
- [x] 1. 构建了业务层的基本框架(增加了底层驱动对于的C++兼容),业务代码采用C++编写,启用了RTTI(运行时类型识别) - [x] 1. 构建了业务层的基本框架(增加了底层驱动对于的C++兼容),业务代码采用C++编写,启用了RTTI(运行时类型识别)
- [ ] 2. #### Day11 2025.9.4
##### 主要目标:完成具体业务开发&各种优化
实际完成任务:
- [x] 1. 完成了ota功能的基本测试,测试通过
- [x] 2. 封装了一个模板线程类,支持创建来自单例类的成员函数线程,普通类的线程,普通函数线程
- [x] 3. 封装了一个Wifi模块类,支持Wifi的各种基本配置
#### Day12 2025.9.9(前三天在打数学建模国赛)
##### 主要目标:完成具体业务开发&各种优化
实际完成任务:
- [x] 1. 完整封装并拓展了SD卡文件管理类,支持基本文件管理功能
- [x] 2. 简单封装了LVGL渲染类,已经封装好了gif渲染功能
- [x] 3. 修复了硬件厂商提供的驱动的Bug
- [x] 4. 初步定义了宠物基类的抽象信息
#### Day13 2025.9.12(前两天在忙考研复习)
##### 主要目标:完成具体业务开发&各种优化
实际完成任务:
- [x] 1. 完成了对音频播放类的完整C++封装,测试通过
- [x] 2. 修复了LVGL渲染类当中的一些小bug
- [x] 3. 增加了一些CPU资源占用的日志打印函数,运行在主线程当中
- [x] 4. 完善了底层通信类的封装,基于websocket,尚未测试
#### Day14 2025.9.15
##### 主要目标:完成具体业务开发&各种优化
实际完成任务:
- [x] 1. 历时两天,完整且完美的设计了宠物类,使用到了多种设计模式,完成了低耦合,高内聚的完美代码,测试也完美通过。
- [x] 2. 顺便完善了底层通信类的封装,基于websocket,基准测试通过,但存在一点很小的线程bug,似乎是来自于esp32 idf底层的问题,待解决
#### Day15 2025.9.16
##### 主要目标:完成具体业务开发&各种优化
实际完成任务:
- [x] 1. 完成了语音识别的C++业务层封装,测试通过
- [x] 2. 试着测试了一下LVGL_GIF渲染+音乐播放+语音识别的组合简单优化后,
发现lvgl渲染略显卡顿,语音识别有缓冲区空警告,不过无伤大雅,还需要进一步深度优化。
#### Day15 2025.9.19(前两天在忙考研复习)
##### 主要目标:完成具体业务开发&各种优化
实际完成任务:
- [x] 1. 在优化几个主要的任务的CPU占有中,不断的调整任务优先级,效果并不明显,但突然注意到测试的音频为CD级别品质,
这种音频一般的MP3解码都非常吃力,而且产品也用不到这种级别的音频,因此只是简单的降低音频品质,
从44100Hz降低到16KHz,测试结果表明,音频播放任务CPU占有从30%降低到5%,在测试中,其余几个任务都及时地上CPU运行了
#### Day16 2025.9.23(前两天在忙考研复习)
##### 主要目标:完成具体业务开发&各种优化
实际完成任务:
- [x] 1. 基于cJSON进行了上层现代C++封装(cpp_json),作为脚手架使用
- [x] 2. 在esp32s3剩余的flash当中开辟了一个文件系统,用于保存设备重要配置信息
#### Day17 2025.9.24
##### 主要目标:完成具体业务开发&各种优化
实际完成任务:
- [x] 1. 优化了cpp_json的内容,使其更modern
- [x] 2. 稍微优化了一下系统配置类
- [x] 3. 增加了系统版本号,便于区分系统版本,方便OTA
- [x] 4. 重写OTA的逻辑,完成了Cpp的OTA封装,测试通过
#### Day18 2025.9.27
##### 主要目标:完成具体业务开发&各种优化
实际完成任务:
- [x] 1. 花了几天时间基于lvgl8.3封装了lvgl_cpp,写了一些基本需要的控件,支持链式调用
还存在一点点bug,不难fix
- [x] 2. 增加了中文字库,支持中文显示
- [x] 3. 修复和优化了一些地方
#### Day19 2025.9.29
##### 主要目标:完成具体业务开发&各种优化
实际完成任务:
- [x] 1. 完成了客户端服务端语音相关的交互,涉及内容较多,看代码改动即可
#### Day20 2025.10.9(考研复习)
##### 主要目标:完成具体业务开发&各种优化
实际完成任务:
- [x] 1. 应用界面增加了返回主页的按钮
#### Day20 2025.10.12(考研复习)
##### 主要目标:完成具体业务开发&各种优化
实际完成任务:
- [x] 1. 修复了gif渲染内存泄漏的严重bug
#### Day20 2025.10.13(考研复习)
##### 主要目标:完成具体业务开发&各种优化
实际完成任务:
- [x] 1. 将PetDao当中的cJSON API替换为cpp_json,完美通过测试
#### Day20 2025.10.14(考研复习)
##### 主要目标:完成具体业务开发&各种优化
实际完成任务:
- [x] 1. 整合已经实现的各种上层建筑,实现了一个宠物对话基本业务应用,用于样品测试展示用
- [x] 2. 重构了音频播放类,使其更modern,更加便于移植和拓展