Compare commits
10 Commits
5d79f88918
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
| ba5e47bc77 | |||
| 801138631e | |||
| a47e20cb64 | |||
| 6c4749ba0c | |||
| 48f208b2e6 | |||
| 4cc761aab3 | |||
| dc420c3b7a | |||
| 97fe13da26 | |||
| 4985fee7c2 | |||
| 28ceb0caf5 |
@@ -4,3 +4,5 @@ log
|
||||
# 组件库,配置好idf环境后,运行idf.py reconfigure就会自动生成出来
|
||||
managed_components
|
||||
.idea
|
||||
.venv
|
||||
.vscode
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
//
|
||||
// Created by misaki on 2025/9/24.
|
||||
//
|
||||
#include "CommBean.h"
|
||||
@@ -0,0 +1,7 @@
|
||||
//
|
||||
// Created by misaki on 2025/9/24.
|
||||
//
|
||||
#pragma once
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,403 @@
|
||||
//
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,15 +3,210 @@
|
||||
//
|
||||
|
||||
#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
|
||||
extern "C" {
|
||||
#endif
|
||||
// WebSocket事件类型
|
||||
enum class WebSocketEvent {
|
||||
CONNECTED,
|
||||
DISCONNECTED,
|
||||
DATA_RECEIVED,
|
||||
ERROR
|
||||
};
|
||||
|
||||
// WebSocket配置
|
||||
struct WebSocketConfig {
|
||||
std::string uri; // WebSocket服务器URI
|
||||
int reconnect_interval = 5000; // 重连间隔(ms)
|
||||
int heartbeat_interval = 30000; // 心跳间隔(ms)
|
||||
int max_reconnect_attempts = 10; // 最大重连次数
|
||||
bool auto_reconnect = true; // 是否自动重连
|
||||
};
|
||||
|
||||
// JSON数据回调
|
||||
using JsonDataCallback = std::function<void(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);
|
||||
|
||||
if (strcmp(type->valuestring, "sensor_data") == 0) {
|
||||
cJSON* value = cJSON_GetObjectItem(json, "value");
|
||||
if (value && cJSON_IsNumber(value)) {
|
||||
ESP_LOGI("App", "Sensor value: %.2f", value->valuedouble);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cJSON_Delete(json);
|
||||
}
|
||||
// 事件回调示例
|
||||
void onWebSocketEvent(WebSocketEvent event, const std::string& message) {
|
||||
switch (event) {
|
||||
case WebSocketEvent::CONNECTED:
|
||||
ESP_LOGI("App", "WebSocket connected: %s", message.c_str());
|
||||
break;
|
||||
case WebSocketEvent::DISCONNECTED:
|
||||
ESP_LOGI("App", "WebSocket disconnected: %s", message.c_str());
|
||||
break;
|
||||
case WebSocketEvent::DATA_RECEIVED:
|
||||
ESP_LOGI("App", "Received raw data: %s", message.c_str());
|
||||
break;
|
||||
case WebSocketEvent::ERROR:
|
||||
ESP_LOGE("App", "WebSocket error: %s", message.c_str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
|
||||
class WebSocketManager {
|
||||
public:
|
||||
// 获取单例实例
|
||||
static WebSocketManager* getInstance();
|
||||
|
||||
// 删除拷贝构造函数和赋值运算符
|
||||
WebSocketManager(const WebSocketManager&) = delete;
|
||||
WebSocketManager& operator=(const WebSocketManager&) = delete;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
// 初始化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
|
||||
};
|
||||
@@ -4,7 +4,457 @@
|
||||
#include "CppHandle.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() {
|
||||
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 秒
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
*/
|
||||
@@ -3,7 +3,57 @@
|
||||
//
|
||||
#include "OTAClass.h"
|
||||
#include "esp_log.h"
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <string>
|
||||
#include "SDFileManager.h"
|
||||
#include "CommClass.h"
|
||||
|
||||
|
||||
void OTAClass::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() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -4,18 +4,10 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
class OTAClass {
|
||||
public:
|
||||
static void Init(void);
|
||||
void Init(void);
|
||||
void Update(void);
|
||||
};
|
||||
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -2,15 +2,214 @@
|
||||
// Created by misaki on 2025/9/2.
|
||||
//
|
||||
#pragma once
|
||||
#include "PetInterface.h"
|
||||
#include "PetObserver.h"
|
||||
// 宠物基类
|
||||
// 宠物基本信息
|
||||
struct PetBaseInfo {
|
||||
///<! 宠物名称
|
||||
std::string pet_name;
|
||||
///<! 宠物生命值(1~100)
|
||||
int pet_hp;
|
||||
///<! 宠物亲密度(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);
|
||||
}
|
||||
/**
|
||||
* 移除观察者
|
||||
* @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);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
// 通知阶段变化
|
||||
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;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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"
|
||||
@@ -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;
|
||||
};
|
||||
@@ -0,0 +1,4 @@
|
||||
//
|
||||
// Created by misaki on 2025/9/14.
|
||||
//
|
||||
#include "PetObserver.h"
|
||||
@@ -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"
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
@@ -1,5 +1,107 @@
|
||||
//
|
||||
// Created by misaki on 2025/9/2.
|
||||
//
|
||||
#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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#include "ToolsClass.h"
|
||||
@@ -2,15 +2,78 @@
|
||||
// Created by misaki on 2025/9/2.
|
||||
//
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <mutex>
|
||||
/**
|
||||
* 本模块提供各种杂项工具类,基本都来源于对底层驱动的封装
|
||||
*
|
||||
*
|
||||
*/
|
||||
class ToolsClass {
|
||||
public:
|
||||
static ToolsClass* getInstance();
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
private:
|
||||
ToolsClass();
|
||||
~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
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
+1
@@ -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"]
|
||||
+1
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
@@ -1,8 +1,7 @@
|
||||
#include "PCM5101.h"
|
||||
|
||||
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;
|
||||
|
||||
uint8_t Volume = Volume_MAX - 2;
|
||||
@@ -24,8 +23,8 @@ 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);
|
||||
}
|
||||
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 ret = ESP_OK;
|
||||
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;
|
||||
i2s_std_config_t std_cfg = {
|
||||
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(rate),
|
||||
.slot_cfg = I2S_STD_PHILIP_SLOT_DEFAULT_CONFIG((i2s_data_bit_width_t)bits_cfg, ch),
|
||||
@@ -34,11 +33,11 @@ static esp_err_t bsp_i2s_reconfig_clk(uint32_t rate, uint32_t bits_cfg, i2s_slot
|
||||
ret |= i2s_channel_disable(i2s_tx_chan);
|
||||
ret |= i2s_channel_reconfig_std_clock(i2s_tx_chan, &std_cfg.clk_cfg);
|
||||
ret |= i2s_channel_reconfig_std_slot(i2s_tx_chan, &std_cfg.slot_cfg);
|
||||
ret |= i2s_channel_enable(i2s_tx_chan);
|
||||
ret |= i2s_channel_enable(i2s_tx_chan);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t audio_mute_function(AUDIO_PLAYER_MUTE_SETTING setting) { // audio mute function
|
||||
static esp_err_t audio_mute_function(AUDIO_PLAYER_MUTE_SETTING setting) { // audio mute function
|
||||
ESP_LOGI(TAG, "mute setting %d", setting);
|
||||
return ESP_OK;
|
||||
}
|
||||
@@ -78,7 +77,7 @@ static void audio_player_callback(audio_player_cb_ctx_t *ctx) {
|
||||
void Audio_Init(void)
|
||||
{
|
||||
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),
|
||||
.gpio_cfg = BSP_I2S_GPIO_CFG,
|
||||
};
|
||||
@@ -91,8 +90,8 @@ void Audio_Init(void)
|
||||
.mute_fn = audio_mute_function,
|
||||
.write_fn = bsp_i2s_write,
|
||||
.clk_set_fn = bsp_i2s_reconfig_clk,
|
||||
.priority = 3,
|
||||
.coreID = 1
|
||||
.priority = 4,
|
||||
.coreID = 1 // 运行在0号核,避免与lvgl抢占资源
|
||||
};
|
||||
ret = audio_player_new(config);
|
||||
if (ret != ESP_OK) {
|
||||
@@ -124,12 +123,25 @@ void Play_Music(const char* directory, const char* fileName)
|
||||
} else {
|
||||
snprintf(filePath, maxPathLength, "%s/%s", directory, fileName);
|
||||
}
|
||||
ESP_LOGD(TAG, "Playing MP3 file: %s", filePath);
|
||||
Music_File = Open_File(filePath);
|
||||
if (!Music_File) {
|
||||
ESP_LOGE(TAG, "Failed to open MP3 file: %s", filePath);
|
||||
return;
|
||||
}
|
||||
|
||||
// 跳过非音频数据
|
||||
uint8_t buf[4];
|
||||
while (fread(buf, 1, 4, Music_File) == 4) {
|
||||
uint32_t head = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
|
||||
if ((head & 0xFFE00000) == 0xFFE00000) { // 找到 MP3 帧头
|
||||
fseek(Music_File, -4, SEEK_CUR); // 回退 4 字节
|
||||
break;
|
||||
}
|
||||
fseek(Music_File, -3, SEEK_CUR); // 逐字节滑动窗口
|
||||
}
|
||||
ESP_LOGI(TAG, "Skipped non-audio data, now at offset %ld", ftell(Music_File));
|
||||
|
||||
expected_event = AUDIO_PLAYER_CALLBACK_EVENT_PLAYING;
|
||||
esp_err_t ret = audio_player_play(Music_File);
|
||||
if (ret != ESP_OK) {
|
||||
@@ -201,3 +213,6 @@ void Volume_adjustment(uint8_t Vol) {
|
||||
Volume = Vol;
|
||||
ESP_LOGI(TAG, "Volume set to %d", Volume);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -57,6 +57,15 @@ uint32_t Music_Elapsed(void);
|
||||
uint16_t Music_Energy(void);
|
||||
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
|
||||
}
|
||||
#endif
|
||||
@@ -114,4 +114,47 @@ float BAT_Get_Volts(void)
|
||||
// printf("BAT voltage : %.2f V\r\n", 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 四舍五入
|
||||
}
|
||||
@@ -24,6 +24,8 @@ extern float BAT_analogVolts;
|
||||
void BAT_Init(void);
|
||||
float BAT_Get_Volts(void);
|
||||
|
||||
int32_t BAT_Get_Percent(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,5 +1,5 @@
|
||||
#include "ST77916.h"
|
||||
|
||||
#include "esp_lcd_panel_io.h"
|
||||
|
||||
#define LCD_OPCODE_WRITE_CMD (0x02ULL)
|
||||
#define LCD_OPCODE_READ_CMD (0x0BULL)
|
||||
@@ -239,7 +239,7 @@ int QSPI_Init(void){
|
||||
.data6_io_num = -1,
|
||||
.data7_io_num = -1,
|
||||
.max_transfer_sz = ESP_PANEL_HOST_SPI_MAX_TRANSFER_SIZE,
|
||||
.flags = SPICOMMON_BUSFLAG_MASTER,
|
||||
.flags = SPICOMMON_BUSFLAG_MASTER,
|
||||
.intr_flags = 0,
|
||||
};
|
||||
if(spi_bus_initialize(ESP_PANEL_HOST_SPI_ID_DEFAULT, &host_config, SPI_DMA_CH_AUTO) != ESP_OK){
|
||||
@@ -252,7 +252,7 @@ int QSPI_Init(void){
|
||||
.cs_gpio_num = ESP_PANEL_LCD_SPI_IO_CS,
|
||||
.dc_gpio_num = -1,
|
||||
.spi_mode = ESP_PANEL_LCD_SPI_MODE,
|
||||
.pclk_hz = 3 * 1000 * 1000,
|
||||
.pclk_hz = 3 * 1000 * 1000,
|
||||
.trans_queue_depth = ESP_PANEL_LCD_SPI_TRANS_QUEUE_SZ,
|
||||
.on_color_trans_done = NULL,
|
||||
.user_ctx = NULL,
|
||||
@@ -283,17 +283,17 @@ int QSPI_Init(void){
|
||||
printf("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\r\n");
|
||||
esp_err_t ret;
|
||||
int lcd_cmd = 0x04;
|
||||
uint8_t register_data[4];
|
||||
uint8_t register_data[4];
|
||||
size_t param_size = sizeof(register_data);
|
||||
lcd_cmd &= 0xff;
|
||||
lcd_cmd <<= 8;
|
||||
lcd_cmd |= LCD_OPCODE_READ_CMD << 24; // Use the read opcode instead of write
|
||||
ret = esp_lcd_panel_io_rx_param(io_handle, lcd_cmd, register_data, param_size);
|
||||
ret = esp_lcd_panel_io_rx_param(io_handle, lcd_cmd, register_data, param_size);
|
||||
if (ret == ESP_OK) {
|
||||
printf("Register 0x04 data: %02x %02x %02x %02x\n", register_data[0], register_data[1], register_data[2], register_data[3]);
|
||||
} else {
|
||||
printf("Failed to read register 0x04, error code: %d\n", ret);
|
||||
}
|
||||
}
|
||||
// panel_io_spi_del(io_handle);
|
||||
io_config.pclk_hz = ESP_PANEL_LCD_SPI_CLK_HZ;
|
||||
if(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)ESP_PANEL_HOST_SPI_ID_DEFAULT, &io_config, &io_handle) != ESP_OK){
|
||||
|
||||
@@ -14,7 +14,6 @@ extern "C" {
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/spi_master.h"
|
||||
#include "esp_timer.h"
|
||||
#include "esp_lcd_panel_io.h"
|
||||
#include "esp_lcd_panel_io_interface.h"
|
||||
#include "esp_intr_alloc.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_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_TRANS_QUEUE_SZ (10) // Typically set to 10
|
||||
#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 (40) // Typically set to 10
|
||||
#define ESP_PANEL_LCD_SPI_CMD_BITS (32) // Typically set to 32
|
||||
#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_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_LS_MODE LEDC_LOW_SPEED_MODE
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#include "LVGL_Driver.h"
|
||||
|
||||
#include "ST77916.h"
|
||||
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)
|
||||
{
|
||||
esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t) drv->user_data;
|
||||
int offsetx1 = area->x1;
|
||||
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);
|
||||
// 立即通知LVGL传输开始,使用DMA传输
|
||||
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*/
|
||||
@@ -89,7 +88,7 @@ void LVGL_Init(void)
|
||||
|
||||
lv_color_t *buf1 = heap_caps_malloc(LVGL_BUF_LEN * sizeof(lv_color_t), MALLOC_CAP_SPIRAM);
|
||||
assert(buf1);
|
||||
lv_color_t *buf2 = heap_caps_malloc(LVGL_BUF_LEN * sizeof(lv_color_t) , MALLOC_CAP_SPIRAM);
|
||||
lv_color_t *buf2 = heap_caps_malloc(LVGL_BUF_LEN * sizeof(lv_color_t) , MALLOC_CAP_SPIRAM);
|
||||
assert(buf2);
|
||||
lv_disp_draw_buf_init(&disp_buf, buf1, buf2, LVGL_BUF_LEN); // initialize LVGL draw buffers
|
||||
|
||||
@@ -119,7 +118,7 @@ void LVGL_Init(void)
|
||||
.callback = &example_increase_lvgl_tick,
|
||||
.name = "lvgl_tick"
|
||||
};
|
||||
|
||||
|
||||
esp_timer_handle_t lvgl_tick_timer = NULL;
|
||||
ESP_ERROR_CHECK(esp_timer_create(&lvgl_tick_timer_args, &lvgl_tick_timer));
|
||||
ESP_ERROR_CHECK(esp_timer_start_periodic(lvgl_tick_timer, EXAMPLE_LVGL_TICK_PERIOD_MS * 1000));
|
||||
|
||||
@@ -13,9 +13,9 @@ extern "C" {
|
||||
#include "lvgl.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
|
||||
|
||||
extern lv_disp_draw_buf_t disp_buf; // contains internal graphic buffer(s) called draw buffer(s)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "LVGL_Example.h"
|
||||
#include "LVGL_Music.h"
|
||||
#include "ST77916.h"
|
||||
#include <demos/lv_demos.h>
|
||||
// #include <demos/music/lv_demo_music_main.h>
|
||||
// #include <demos/music/lv_demo_music_list.h>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#include "CST816.h"
|
||||
|
||||
#include "esp_lcd_panel_io.h"
|
||||
|
||||
#define POINT_NUM_MAX (1)
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ extern "C" {
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_lcd_panel_io.h"
|
||||
#include "esp_lcd_panel_io_interface.h"
|
||||
#include "esp_lcd_touch.h"
|
||||
|
||||
#include "TCA9554PWR.h"
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
#include "sdkconfig.h"
|
||||
#include "esp_err.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/semphr.h"
|
||||
|
||||
|
||||
@@ -344,7 +344,7 @@ void MIC_Speech_init()
|
||||
afe_config.pcm_config.mic_num = 1;
|
||||
afe_config.pcm_config.ref_num = 1;
|
||||
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);
|
||||
|
||||
// 注意两个任务被分配了不同的核心与优先级,这是为了防止AFE(Audio Front-End)内部环形缓冲区溢出
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -9,10 +9,10 @@ uint32_t Flash_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);
|
||||
FILE *f = fopen(path, "w");
|
||||
FILE *f = fopen(path, type);
|
||||
if (f == NULL) {
|
||||
ESP_LOGE(SD_TAG, "Failed to open file for writing");
|
||||
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
|
||||
esp_vfs_fat_sdmmc_mount_config_t mount_config = {
|
||||
.format_if_mount_failed = true,
|
||||
.max_files = 5,
|
||||
.max_files = 10,
|
||||
.allocation_unit_size = 16 * 1024
|
||||
};
|
||||
sdmmc_card_t *card;
|
||||
|
||||
@@ -30,7 +30,7 @@ extern "C" {
|
||||
esp_err_t SD_Card_CS_EN(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);
|
||||
|
||||
extern uint32_t SDCard_Size;
|
||||
|
||||
+118
-38
@@ -11,49 +11,47 @@ wifi_ap_info_t *wifi_ap_list = NULL;
|
||||
uint16_t wifi_ap_count = 0;
|
||||
ble_device_info_t *ble_device_list = NULL;
|
||||
|
||||
void Wireless_Init(void)
|
||||
esp_err_t Wireless_Init(void)
|
||||
{
|
||||
// Initialize 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_err_t ret = nvs_flash_init(); // 初始化Flash
|
||||
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { // 如果Flash有错误,则进行修复
|
||||
ESP_ERROR_CHECK(nvs_flash_erase()); // 清空Flash
|
||||
ret = nvs_flash_init(); // 重新初始化Flash
|
||||
}
|
||||
ESP_ERROR_CHECK( ret );
|
||||
// WiFi
|
||||
xTaskCreatePinnedToCore(
|
||||
WIFI_Init,
|
||||
"WIFI task",
|
||||
4096,
|
||||
NULL,
|
||||
1,
|
||||
NULL,
|
||||
0);
|
||||
// BLE
|
||||
xTaskCreatePinnedToCore(
|
||||
BLE_Init,
|
||||
"BLE task",
|
||||
4096,
|
||||
NULL,
|
||||
2,
|
||||
NULL,
|
||||
0);
|
||||
ESP_ERROR_CHECK( ret ); // 检查错误
|
||||
|
||||
// WiFi 手动调用Wifi初始化
|
||||
// xTaskCreatePinnedToCore(
|
||||
// WIFI_Init,
|
||||
// "WIFI task",
|
||||
// 4096,
|
||||
// NULL,
|
||||
// 1,
|
||||
// NULL,
|
||||
// 0);
|
||||
// BLE 当前工程不启用蓝牙
|
||||
// xTaskCreatePinnedToCore(
|
||||
// BLE_Init,
|
||||
// "BLE task",
|
||||
// 4096,
|
||||
// NULL,
|
||||
// 2,
|
||||
// NULL,
|
||||
// 0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void WIFI_Init(void *arg)
|
||||
esp_err_t WIFI_Init(void *arg)
|
||||
{
|
||||
esp_netif_init();
|
||||
esp_event_loop_create_default();
|
||||
esp_netif_create_default_wifi_sta();
|
||||
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
|
||||
esp_wifi_init(&cfg);
|
||||
esp_wifi_set_mode(WIFI_MODE_STA);
|
||||
esp_wifi_start();
|
||||
|
||||
WIFI_NUM = WIFI_Scan();
|
||||
printf("WIFI:%d\r\n",WIFI_NUM);
|
||||
|
||||
vTaskDelete(NULL);
|
||||
esp_netif_init(); // 初始化网络接口
|
||||
esp_event_loop_create_default(); // 创建事件循环
|
||||
esp_netif_create_default_wifi_sta(); // 创建默认的WiFi Station
|
||||
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); // 创建WiFi初始化配置(使用默认配置)
|
||||
esp_wifi_init(&cfg); // 初始化WiFi
|
||||
esp_wifi_set_mode(WIFI_MODE_STA); // 设置WiFi模式为Station
|
||||
esp_err_t ret = esp_wifi_start(); // 启动WiFi
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -104,7 +102,7 @@ uint16_t WIFI_Scan(void)
|
||||
return ap_count;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
#define GATTC_TAG "GATTC_TAG"
|
||||
#define SCAN_DURATION 5
|
||||
#define MAX_DISCOVERED_DEVICES 100
|
||||
@@ -268,6 +266,88 @@ uint16_t BLE_Scan(void)
|
||||
Scan_finish = 1;
|
||||
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列表
|
||||
uint16_t wireless_get_wifi_ap_list(wifi_ap_info_t *ap_list, uint16_t max_aps) {
|
||||
|
||||
+24
-6
@@ -12,9 +12,9 @@ extern "C" {
|
||||
#include <stdio.h>
|
||||
#include <string.h> // For memcpy
|
||||
#include "esp_system.h"
|
||||
#include "esp_bt.h"
|
||||
#include "esp_gap_ble_api.h"
|
||||
#include "esp_bt_main.h"
|
||||
// #include "esp_bt.h"
|
||||
// #include "esp_gap_ble_api.h"
|
||||
// #include "esp_bt_main.h"
|
||||
|
||||
// WiFi AP信息结构体
|
||||
typedef struct {
|
||||
@@ -35,13 +35,31 @@ extern uint16_t BLE_NUM;
|
||||
extern uint16_t WIFI_NUM;
|
||||
extern bool Scan_finish;
|
||||
|
||||
void Wireless_Init(void);
|
||||
void WIFI_Init(void *arg);
|
||||
esp_err_t Wireless_Init(void);
|
||||
esp_err_t WIFI_Init(void *arg);
|
||||
uint16_t WIFI_Scan(void);
|
||||
void BLE_Init(void *arg);
|
||||
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_ble_device_list(ble_device_info_t *device_list, uint16_t max_devices);
|
||||
void wireless_print_wifi_aps(void);
|
||||
|
||||
@@ -197,4 +197,198 @@ extern "C" void app_main(void)
|
||||
std::this_thread::sleep_for(sleep_time);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
```c++
|
||||
#include "WebSocketManager.h"
|
||||
#include "cJSON.h"
|
||||
|
||||
// JSON数据回调示例
|
||||
void onJsonData(cJSON* json) {
|
||||
// 处理接收到的JSON数据
|
||||
cJSON* type = cJSON_GetObjectItem(json, "type");
|
||||
if (type && cJSON_IsString(type)) {
|
||||
ESP_LOGI("App", "Received message type: %s", type->valuestring);
|
||||
|
||||
if (strcmp(type->valuestring, "sensor_data") == 0) {
|
||||
cJSON* value = cJSON_GetObjectItem(json, "value");
|
||||
if (value && cJSON_IsNumber(value)) {
|
||||
ESP_LOGI("App", "Sensor value: %.2f", value->valuedouble);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cJSON_Delete(json);
|
||||
}
|
||||
|
||||
// 事件回调示例
|
||||
void onWebSocketEvent(WebSocketEvent event, const std::string& message) {
|
||||
switch (event) {
|
||||
case WebSocketEvent::CONNECTED:
|
||||
ESP_LOGI("App", "WebSocket connected: %s", message.c_str());
|
||||
break;
|
||||
case WebSocketEvent::DISCONNECTED:
|
||||
ESP_LOGI("App", "WebSocket disconnected: %s", message.c_str());
|
||||
break;
|
||||
case WebSocketEvent::DATA_RECEIVED:
|
||||
ESP_LOGI("App", "Received raw data: %s", message.c_str());
|
||||
break;
|
||||
case WebSocketEvent::ERROR:
|
||||
ESP_LOGE("App", "WebSocket error: %s", message.c_str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void app_main() {
|
||||
// 初始化WiFi
|
||||
WifiConnectors* wifi = WifiConnectors::getInstance();
|
||||
|
||||
// 连接WiFi(在实际应用中应该在单独线程中进行)
|
||||
ThreadConfig wifi_config;
|
||||
wifi_config.name = "WiFi_Connector";
|
||||
|
||||
auto wifi_thread = ThreadManager::createSingletonThread<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
@@ -47,6 +47,16 @@ dependencies:
|
||||
registry_url: https://components.espressif.com/
|
||||
type: service
|
||||
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:
|
||||
source:
|
||||
type: idf
|
||||
@@ -62,8 +72,9 @@ direct_dependencies:
|
||||
- chmorgan/esp-audio-player
|
||||
- chmorgan/esp-libhelix-mp3
|
||||
- espressif/esp-sr
|
||||
- espressif/esp_websocket_client
|
||||
- idf
|
||||
- lvgl/lvgl
|
||||
manifest_hash: 9bc1eee4314d6f75a0822d5e087c5603182eb609f99c1dd5726d7cea387e4d1d
|
||||
manifest_hash: 2f09fb29ee9918e287005c3aa0d08252d2551c70eed608468245557ab01ac54b
|
||||
target: esp32s3
|
||||
version: 2.0.0
|
||||
|
||||
+11
-4
@@ -1,10 +1,9 @@
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
#include <esp_log.h>
|
||||
#include <nvs.h>
|
||||
#include <nvs_flash.h>
|
||||
|
||||
#include "err_handle.h"
|
||||
|
||||
#include "drivers_test.h"
|
||||
#include "CppHandle.h"
|
||||
|
||||
@@ -34,9 +33,17 @@ void app_main(void)
|
||||
|
||||
// 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();
|
||||
|
||||
ESP_GOTO(err);
|
||||
err:
|
||||
ESP_LOGI("app_main", "app_main error");
|
||||
ESP_LOGE("app_main", "app_main error");
|
||||
}
|
||||
|
||||
@@ -25,11 +25,30 @@ idf_component_register(SRCS "Bionic_sphere.c"
|
||||
"../Lib/Display/Touch_Driver/esp_lcd_touch/esp_lcd_touch.c" # 触摸屏驱动库
|
||||
"../Lib/PWR_Key/PWR_Key.c" # PWR按键驱动库
|
||||
"../Lib/MIC_Driver/MIC_Speech.c" # 录音驱动库
|
||||
"../Lib/OTA_Driver/ota_ws.c" # OTA驱动库
|
||||
# 业务代码(使用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/HttpOtaUpdater.cpp"
|
||||
"../Bionic_Core/CommClass/CommClass.cpp" # 通信类库
|
||||
"../Bionic_Core/CommClass/CommBean.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兼容库
|
||||
INCLUDE_DIRS "."
|
||||
"../test/driver_test"
|
||||
@@ -55,10 +74,21 @@ idf_component_register(SRCS "Bionic_sphere.c"
|
||||
"../Lib/Display/Touch_Driver/esp_lcd_touch"
|
||||
"../Lib/PWR_Key"
|
||||
"../Lib/MIC_Driver"
|
||||
"../Lib/OTA_Driver"
|
||||
"../Bionic_Core/PetBaseClass"
|
||||
"../Bionic_Core/OTAClass"
|
||||
"../Bionic_Core/CommClass"
|
||||
"../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"
|
||||
PRIV_REQUIRES # 私有依赖
|
||||
driver
|
||||
@@ -69,5 +99,15 @@ idf_component_register(SRCS "Bionic_sphere.c"
|
||||
unity
|
||||
esp_lcd
|
||||
lvgl__lvgl
|
||||
lvgl
|
||||
fatfs
|
||||
pthread
|
||||
app_update
|
||||
esp_http_client
|
||||
esp_websocket_client
|
||||
esp_https_ota
|
||||
json
|
||||
efuse
|
||||
)
|
||||
|
||||
target_compile_options(${COMPONENT_LIB} PUBLIC -std=gnu++17)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
dependencies:
|
||||
idf: ">=4.4"
|
||||
lvgl/lvgl: "~8.3.0"
|
||||
chmorgan/esp-audio-player: "==1.0.7"
|
||||
chmorgan/esp-libhelix-mp3: "==1.0.3"
|
||||
espressif/esp-sr: "~1.9.4"
|
||||
idf: '>=4.4'
|
||||
lvgl/lvgl: ~8.3.0
|
||||
chmorgan/esp-audio-player: ==1.0.7
|
||||
chmorgan/esp-libhelix-mp3: ==1.0.3
|
||||
espressif/esp-sr: ~1.9.4
|
||||
espressif/esp_websocket_client: ^1.0.0
|
||||
|
||||
+5
-1
@@ -8,6 +8,9 @@
|
||||
# Offset:起始地址(通常不写,让系统自动计算)。
|
||||
# Size:大小,比如 1M, 64K, 0x100000 等。
|
||||
# Flags:可选标志,比如 encrypted(加密)。
|
||||
# 注意:因为没有设置工厂分区,那么每次烧录的时候程序会烧录在ota_0分区
|
||||
# 根据官方文档OTA 数据分区的容量是 2 个 flash 扇区的大小(0x2000 字节)
|
||||
# 那么可以计算出 OTA 数据分区大小最大为 64MB (2*16**3 *0x1000 / 1024 / 1024)MB
|
||||
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
nvs, data, nvs, , 0x6000,
|
||||
@@ -15,4 +18,5 @@ otadata, data, ota, , 0x2000,
|
||||
phy_init, data, phy, , 0x1000,
|
||||
ota_0, app, ota_0, , 6M,
|
||||
ota_1, app, ota_1, , 6M,
|
||||
coredump, data, coredump,, 64K,
|
||||
coredump, data, coredump,, 64K,
|
||||
sys_conf, data, fat, , 0xF9000,
|
||||
|
@@ -560,26 +560,26 @@ CONFIG_SR_MN_EN_NONE=y
|
||||
#
|
||||
# Add Chinese speech commands
|
||||
#
|
||||
CONFIG_CN_SPEECH_COMMAND_ID0="da kai kong tiao"
|
||||
CONFIG_CN_SPEECH_COMMAND_ID1="guan bi kong tiao"
|
||||
CONFIG_CN_SPEECH_COMMAND_ID2="zeng da feng su"
|
||||
CONFIG_CN_SPEECH_COMMAND_ID3="jian xiao feng su"
|
||||
CONFIG_CN_SPEECH_COMMAND_ID4="sheng gao yi du"
|
||||
CONFIG_CN_SPEECH_COMMAND_ID5="jiang di yi du"
|
||||
CONFIG_CN_SPEECH_COMMAND_ID6="zhi re mo shi"
|
||||
CONFIG_CN_SPEECH_COMMAND_ID7="zhi leng mo shi"
|
||||
CONFIG_CN_SPEECH_COMMAND_ID8="song feng mo shi"
|
||||
CONFIG_CN_SPEECH_COMMAND_ID9="jie neng mo shi"
|
||||
CONFIG_CN_SPEECH_COMMAND_ID10="chu shi mo shi"
|
||||
CONFIG_CN_SPEECH_COMMAND_ID11="jian kang mo shi"
|
||||
CONFIG_CN_SPEECH_COMMAND_ID12="shui mian mo shi"
|
||||
CONFIG_CN_SPEECH_COMMAND_ID13="da kai lan ya"
|
||||
CONFIG_CN_SPEECH_COMMAND_ID14="guan bi lan ya"
|
||||
CONFIG_CN_SPEECH_COMMAND_ID15="kai shi bo fang"
|
||||
CONFIG_CN_SPEECH_COMMAND_ID16="zan ting bo fang"
|
||||
CONFIG_CN_SPEECH_COMMAND_ID17="ding shi yi xiao shi"
|
||||
CONFIG_CN_SPEECH_COMMAND_ID18="da kai dian deng"
|
||||
CONFIG_CN_SPEECH_COMMAND_ID19="guan bi dian deng"
|
||||
CONFIG_CN_SPEECH_COMMAND_ID0=""
|
||||
CONFIG_CN_SPEECH_COMMAND_ID1=""
|
||||
CONFIG_CN_SPEECH_COMMAND_ID2=""
|
||||
CONFIG_CN_SPEECH_COMMAND_ID3=""
|
||||
CONFIG_CN_SPEECH_COMMAND_ID4=""
|
||||
CONFIG_CN_SPEECH_COMMAND_ID5=""
|
||||
CONFIG_CN_SPEECH_COMMAND_ID6=""
|
||||
CONFIG_CN_SPEECH_COMMAND_ID7=""
|
||||
CONFIG_CN_SPEECH_COMMAND_ID8=""
|
||||
CONFIG_CN_SPEECH_COMMAND_ID9=""
|
||||
CONFIG_CN_SPEECH_COMMAND_ID10=""
|
||||
CONFIG_CN_SPEECH_COMMAND_ID11=""
|
||||
CONFIG_CN_SPEECH_COMMAND_ID12=""
|
||||
CONFIG_CN_SPEECH_COMMAND_ID13=""
|
||||
CONFIG_CN_SPEECH_COMMAND_ID14=""
|
||||
CONFIG_CN_SPEECH_COMMAND_ID15=""
|
||||
CONFIG_CN_SPEECH_COMMAND_ID16=""
|
||||
CONFIG_CN_SPEECH_COMMAND_ID17=""
|
||||
CONFIG_CN_SPEECH_COMMAND_ID18=""
|
||||
CONFIG_CN_SPEECH_COMMAND_ID19=""
|
||||
CONFIG_CN_SPEECH_COMMAND_ID20=""
|
||||
CONFIG_CN_SPEECH_COMMAND_ID21=""
|
||||
CONFIG_CN_SPEECH_COMMAND_ID22=""
|
||||
@@ -766,9 +766,9 @@ CONFIG_CN_SPEECH_COMMAND_ID199=""
|
||||
#
|
||||
# Compiler options
|
||||
#
|
||||
CONFIG_COMPILER_OPTIMIZATION_DEBUG=y
|
||||
# CONFIG_COMPILER_OPTIMIZATION_DEBUG 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_ASSERTIONS_ENABLE=y
|
||||
# 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_CHECKS_SILENT is not set
|
||||
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_STACK_CHECK_MODE_NONE=y
|
||||
# CONFIG_COMPILER_STACK_CHECK_MODE_NORM is not set
|
||||
@@ -813,334 +814,10 @@ CONFIG_APPTRACE_LOCK_ENABLE=y
|
||||
#
|
||||
# Bluetooth
|
||||
#
|
||||
CONFIG_BT_ENABLED=y
|
||||
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_ENABLED is not set
|
||||
CONFIG_BT_ALARM_MAX_NUM=50
|
||||
# end of Common Options
|
||||
|
||||
# CONFIG_BT_HCI_LOG_DEBUG_EN is not set
|
||||
# end of Bluetooth
|
||||
|
||||
# CONFIG_BLE_MESH is not set
|
||||
|
||||
#
|
||||
# Console Library
|
||||
#
|
||||
@@ -1247,8 +924,7 @@ CONFIG_ESP_TLS_USE_DS_PERIPHERAL=y
|
||||
# Wireless Coexistence
|
||||
#
|
||||
CONFIG_ESP_COEX_ENABLED=y
|
||||
CONFIG_ESP_COEX_SW_COEXIST_ENABLE=y
|
||||
# CONFIG_ESP_COEX_POWER_MANAGEMENT is not set
|
||||
# CONFIG_ESP_COEX_EXTERNAL_COEXIST_ENABLE is not set
|
||||
# CONFIG_ESP_COEX_GPIO_DEBUG is not set
|
||||
# end of Wireless Coexistence
|
||||
|
||||
@@ -1256,6 +932,7 @@ CONFIG_ESP_COEX_SW_COEXIST_ENABLE=y
|
||||
# Common ESP-related
|
||||
#
|
||||
CONFIG_ESP_ERR_TO_NAME_LOOKUP=y
|
||||
CONFIG_ESP_ALLOW_BSS_SEG_EXTERNAL_MEMORY=y
|
||||
# end of Common ESP-related
|
||||
|
||||
#
|
||||
@@ -1327,7 +1004,7 @@ CONFIG_GPTIMER_ISR_HANDLER_IN_IRAM=y
|
||||
#
|
||||
# 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_SLAVE_IN_IRAM is not set
|
||||
CONFIG_SPI_SLAVE_ISR_IN_IRAM=y
|
||||
@@ -1413,7 +1090,7 @@ CONFIG_HTTPD_PURGE_BUF_LEN=32
|
||||
# ESP HTTPS OTA
|
||||
#
|
||||
# CONFIG_ESP_HTTPS_OTA_DECRYPT_CB is not set
|
||||
# CONFIG_ESP_HTTPS_OTA_ALLOW_HTTP is not set
|
||||
CONFIG_ESP_HTTPS_OTA_ALLOW_HTTP=y
|
||||
# end of ESP HTTPS OTA
|
||||
|
||||
#
|
||||
@@ -1509,6 +1186,7 @@ CONFIG_XTAL_FREQ=40
|
||||
# end of Main XTAL Config
|
||||
|
||||
CONFIG_ESP_SPI_BUS_LOCK_ISR_FUNCS_IN_IRAM=y
|
||||
CONFIG_ESP_SPI_BUS_LOCK_FUNCS_IN_IRAM=y
|
||||
# end of Hardware Settings
|
||||
|
||||
#
|
||||
@@ -1596,15 +1274,14 @@ CONFIG_SPIRAM_SPEED_80M=y
|
||||
CONFIG_SPIRAM_SPEED=80
|
||||
# CONFIG_SPIRAM_ECC_ENABLE is not set
|
||||
CONFIG_SPIRAM_BOOT_INIT=y
|
||||
# CONFIG_SPIRAM_IGNORE_NOTFOUND is not set
|
||||
# CONFIG_SPIRAM_USE_MEMMAP is not set
|
||||
# CONFIG_SPIRAM_USE_CAPS_ALLOC is not set
|
||||
CONFIG_SPIRAM_USE_MALLOC=y
|
||||
CONFIG_SPIRAM_MEMTEST=y
|
||||
CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL=16384
|
||||
# CONFIG_SPIRAM_TRY_ALLOCATE_WIFI_LWIP is not set
|
||||
CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL=32768
|
||||
# CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY is not set
|
||||
CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL=4096
|
||||
CONFIG_SPIRAM_TRY_ALLOCATE_WIFI_LWIP=y
|
||||
CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL=65536
|
||||
CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY=y
|
||||
# CONFIG_SPIRAM_ALLOW_NOINIT_SEG_EXTERNAL_MEMORY is not set
|
||||
# end of SPI RAM config
|
||||
# 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_SIZE=32
|
||||
# CONFIG_ESP32S3_DATA_CACHE_16KB is not set
|
||||
CONFIG_ESP32S3_DATA_CACHE_32KB=y
|
||||
# CONFIG_ESP32S3_DATA_CACHE_64KB is not set
|
||||
CONFIG_ESP32S3_DATA_CACHE_SIZE=0x8000
|
||||
# CONFIG_ESP32S3_DATA_CACHE_32KB is not set
|
||||
CONFIG_ESP32S3_DATA_CACHE_64KB=y
|
||||
CONFIG_ESP32S3_DATA_CACHE_SIZE=0x10000
|
||||
# CONFIG_ESP32S3_DATA_CACHE_4WAYS is not set
|
||||
CONFIG_ESP32S3_DATA_CACHE_8WAYS=y
|
||||
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_64B is not set
|
||||
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_TASK_STACK_SIZE=2304
|
||||
CONFIG_ESP_MAIN_TASK_STACK_SIZE=3584
|
||||
CONFIG_ESP_MAIN_TASK_STACK_SIZE=8192
|
||||
CONFIG_ESP_MAIN_TASK_AFFINITY_CPU0=y
|
||||
# CONFIG_ESP_MAIN_TASK_AFFINITY_CPU1 is not set
|
||||
# CONFIG_ESP_MAIN_TASK_AFFINITY_NO_AFFINITY is not set
|
||||
@@ -1757,11 +1433,11 @@ CONFIG_ESP_TIMER_IMPL_SYSTIMER=y
|
||||
# Wi-Fi
|
||||
#
|
||||
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_STATIC_TX_BUFFER=y
|
||||
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_STATIC_RX_MGMT_BUFFER=y
|
||||
# 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_CSI_ENABLED is not set
|
||||
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_RX_BA_WIN=6
|
||||
CONFIG_ESP_WIFI_RX_BA_WIN=2
|
||||
# CONFIG_ESP_WIFI_AMSDU_TX_ENABLED is not set
|
||||
CONFIG_ESP_WIFI_NVS_ENABLED=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_QUEUE_REGISTRY_SIZE=0
|
||||
CONFIG_FREERTOS_TASK_NOTIFICATION_ARRAY_ENTRIES=1
|
||||
# CONFIG_FREERTOS_USE_TRACE_FACILITY is not set
|
||||
CONFIG_FREERTOS_USE_TRACE_FACILITY=y
|
||||
CONFIG_FREERTOS_USE_STATS_FORMATTING_FUNCTIONS=y
|
||||
# CONFIG_FREERTOS_USE_LIST_DATA_INTEGRITY_CHECK_BYTES is not set
|
||||
# CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS is not set
|
||||
CONFIG_FREERTOS_VTASKLIST_INCLUDE_COREID=y
|
||||
CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS=y
|
||||
CONFIG_FREERTOS_RUN_TIME_COUNTER_TYPE_U32=y
|
||||
# CONFIG_FREERTOS_RUN_TIME_COUNTER_TYPE_U64 is not set
|
||||
# CONFIG_FREERTOS_USE_APPLICATION_TASK_TAG is not set
|
||||
# end of Kernel
|
||||
|
||||
#
|
||||
# Port
|
||||
#
|
||||
CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER=y
|
||||
# CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK is not set
|
||||
CONFIG_FREERTOS_TLSP_DELETION_CALLBACKS=y
|
||||
# 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_LVL3 is not set
|
||||
CONFIG_FREERTOS_SYSTICK_USES_SYSTIMER=y
|
||||
CONFIG_FREERTOS_RUN_TIME_STATS_USING_ESP_TIMER=y
|
||||
# CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH is not set
|
||||
# CONFIG_FREERTOS_CHECK_PORT_CRITICAL_COMPLIANCE is not set
|
||||
# end of Port
|
||||
@@ -2071,6 +1751,7 @@ CONFIG_LWIP_TCP_OOSEQ_MAX_PBUFS=4
|
||||
CONFIG_LWIP_TCP_OVERSIZE_MSS=y
|
||||
# CONFIG_LWIP_TCP_OVERSIZE_QUARTER_MSS is not set
|
||||
# CONFIG_LWIP_TCP_OVERSIZE_DISABLE is not set
|
||||
# CONFIG_LWIP_WND_SCALE is not set
|
||||
CONFIG_LWIP_TCP_RTO_TIME=1500
|
||||
# end of TCP
|
||||
|
||||
@@ -2602,10 +2283,6 @@ CONFIG_WL_SECTOR_SIZE=4096
|
||||
#
|
||||
CONFIG_WIFI_PROV_SCAN_MAX_ENTRIES=16
|
||||
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_FAST_SCAN is not set
|
||||
# end of Wi-Fi Provisioning Manager
|
||||
@@ -2635,6 +2312,13 @@ CONFIG_DSP_MAX_FFT_SIZE_4096=y
|
||||
CONFIG_DSP_MAX_FFT_SIZE=4096
|
||||
# end of DSP Library
|
||||
|
||||
#
|
||||
# ESP WebSocket client
|
||||
#
|
||||
# CONFIG_ESP_WS_CLIENT_ENABLE_DYNAMIC_BUFFER is not set
|
||||
# CONFIG_ESP_WS_CLIENT_SEPARATE_TX_LOCK is not set
|
||||
# end of ESP WebSocket client
|
||||
|
||||
#
|
||||
# LVGL configuration
|
||||
#
|
||||
@@ -2667,8 +2351,8 @@ CONFIG_LV_MEM_BUF_MAX_NUM=16
|
||||
#
|
||||
# HAL Settings
|
||||
#
|
||||
CONFIG_LV_DISP_DEF_REFR_PERIOD=30
|
||||
CONFIG_LV_INDEV_DEF_READ_PERIOD=30
|
||||
CONFIG_LV_DISP_DEF_REFR_PERIOD=10
|
||||
CONFIG_LV_INDEV_DEF_READ_PERIOD=10
|
||||
# CONFIG_LV_TICK_CUSTOM is not set
|
||||
CONFIG_LV_DPI_DEF=130
|
||||
# end of HAL Settings
|
||||
@@ -2681,10 +2365,10 @@ CONFIG_LV_DPI_DEF=130
|
||||
# Drawing
|
||||
#
|
||||
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_LAYER_SIMPLE_BUF_SIZE=24576
|
||||
CONFIG_LV_IMG_CACHE_DEF_SIZE=0
|
||||
CONFIG_LV_LAYER_SIMPLE_BUF_SIZE=10240
|
||||
CONFIG_LV_IMG_CACHE_DEF_SIZE=128
|
||||
CONFIG_LV_GRADIENT_MAX_STOPS=2
|
||||
CONFIG_LV_GRAD_CACHE_DEF_SIZE=0
|
||||
# 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_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_OBJ is not set
|
||||
CONFIG_LV_USE_ASSERT_OBJ=y
|
||||
CONFIG_LV_ASSERT_HANDLER_INCLUDE="assert.h"
|
||||
# end of Asserts
|
||||
|
||||
@@ -2762,7 +2446,7 @@ CONFIG_LV_ATTRIBUTE_MEM_ALIGN_SIZE=1
|
||||
CONFIG_LV_FONT_MONTSERRAT_12=y
|
||||
CONFIG_LV_FONT_MONTSERRAT_14=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_22 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_28_COMPRESSED 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_16 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_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_18 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_28_COMPRESSED 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_16 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_LINE_BREAK_LONG_LEN=0
|
||||
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
|
||||
# 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_FATFS 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_SJPG is not set
|
||||
CONFIG_LV_USE_GIF=y
|
||||
@@ -2974,16 +2661,17 @@ CONFIG_LOG_BOOTLOADER_LEVEL=3
|
||||
CONFIG_FLASHMODE_DIO=y
|
||||
# CONFIG_FLASHMODE_DOUT is not set
|
||||
CONFIG_MONITOR_BAUD=115200
|
||||
CONFIG_OPTIMIZATION_LEVEL_DEBUG=y
|
||||
CONFIG_COMPILER_OPTIMIZATION_LEVEL_DEBUG=y
|
||||
CONFIG_COMPILER_OPTIMIZATION_DEFAULT=y
|
||||
# CONFIG_OPTIMIZATION_LEVEL_DEBUG is not set
|
||||
# CONFIG_COMPILER_OPTIMIZATION_LEVEL_DEBUG is not set
|
||||
# CONFIG_COMPILER_OPTIMIZATION_DEFAULT is not set
|
||||
# CONFIG_OPTIMIZATION_LEVEL_RELEASE is not set
|
||||
# CONFIG_COMPILER_OPTIMIZATION_LEVEL_RELEASE is not set
|
||||
CONFIG_OPTIMIZATION_ASSERTIONS_ENABLED=y
|
||||
# CONFIG_OPTIMIZATION_ASSERTIONS_SILENT is not set
|
||||
# CONFIG_OPTIMIZATION_ASSERTIONS_DISABLED is not set
|
||||
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_NORM 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_NONE=y
|
||||
CONFIG_ESP32_APPTRACE_LOCK_ENABLE=y
|
||||
CONFIG_BLUEDROID_ENABLED=y
|
||||
# CONFIG_NIMBLE_ENABLED 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_EXTERNAL_COEX_ENABLE is not set
|
||||
# CONFIG_ESP_WIFI_EXTERNAL_COEXIST_ENABLE is not set
|
||||
# CONFIG_MCPWM_ISR_IN_IRAM is not set
|
||||
# CONFIG_EVENT_LOOP_PROFILING is not set
|
||||
CONFIG_POST_EVENTS_FROM_ISR=y
|
||||
CONFIG_POST_EVENTS_FROM_IRAM_ISR=y
|
||||
CONFIG_GDBSTUB_SUPPORT_TASKS=y
|
||||
CONFIG_GDBSTUB_MAX_TASKS=32
|
||||
# CONFIG_OTA_ALLOW_HTTP is not set
|
||||
CONFIG_OTA_ALLOW_HTTP=y
|
||||
CONFIG_ESP32S3_DEEP_SLEEP_WAKEUP_DELAY=2000
|
||||
CONFIG_ESP_SLEEP_DEEP_SLEEP_WAKEUP_DELAY=2000
|
||||
CONFIG_ESP32S3_RTC_CLK_SRC_INT_RC=y
|
||||
@@ -3209,7 +2713,7 @@ CONFIG_ESP32S3_DEFAULT_CPU_FREQ_240=y
|
||||
CONFIG_ESP32S3_DEFAULT_CPU_FREQ_MHZ=240
|
||||
CONFIG_SYSTEM_EVENT_QUEUE_SIZE=32
|
||||
CONFIG_SYSTEM_EVENT_TASK_STACK_SIZE=2304
|
||||
CONFIG_MAIN_TASK_STACK_SIZE=3584
|
||||
CONFIG_MAIN_TASK_STACK_SIZE=8192
|
||||
CONFIG_CONSOLE_UART_DEFAULT=y
|
||||
# CONFIG_CONSOLE_UART_CUSTOM is not set
|
||||
# CONFIG_CONSOLE_UART_NONE is not set
|
||||
@@ -3250,19 +2754,19 @@ CONFIG_ESP32S3_BROWNOUT_DET_LVL=7
|
||||
CONFIG_IPC_TASK_STACK_SIZE=1280
|
||||
CONFIG_TIMER_TASK_STACK_SIZE=3584
|
||||
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_STATIC_TX_BUFFER=y
|
||||
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_CSI_ENABLED is not set
|
||||
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_RX_BA_WIN=6
|
||||
CONFIG_ESP32_WIFI_RX_BA_WIN=6
|
||||
CONFIG_ESP32_WIFI_RX_BA_WIN=2
|
||||
CONFIG_ESP32_WIFI_RX_BA_WIN=2
|
||||
# CONFIG_ESP32_WIFI_AMSDU_TX_ENABLED is not set
|
||||
CONFIG_ESP32_WIFI_NVS_ENABLED=y
|
||||
CONFIG_ESP32_WIFI_TASK_PINNED_TO_CORE_0=y
|
||||
|
||||
+86
-582
@@ -560,26 +560,26 @@ CONFIG_SR_MN_EN_NONE=y
|
||||
#
|
||||
# Add Chinese speech commands
|
||||
#
|
||||
CONFIG_CN_SPEECH_COMMAND_ID0="da kai kong tiao"
|
||||
CONFIG_CN_SPEECH_COMMAND_ID1="guan bi kong tiao"
|
||||
CONFIG_CN_SPEECH_COMMAND_ID2="zeng da feng su"
|
||||
CONFIG_CN_SPEECH_COMMAND_ID3="jian xiao feng su"
|
||||
CONFIG_CN_SPEECH_COMMAND_ID4="sheng gao yi du"
|
||||
CONFIG_CN_SPEECH_COMMAND_ID5="jiang di yi du"
|
||||
CONFIG_CN_SPEECH_COMMAND_ID6="zhi re mo shi"
|
||||
CONFIG_CN_SPEECH_COMMAND_ID7="zhi leng mo shi"
|
||||
CONFIG_CN_SPEECH_COMMAND_ID8="song feng mo shi"
|
||||
CONFIG_CN_SPEECH_COMMAND_ID9="jie neng mo shi"
|
||||
CONFIG_CN_SPEECH_COMMAND_ID10="chu shi mo shi"
|
||||
CONFIG_CN_SPEECH_COMMAND_ID11="jian kang mo shi"
|
||||
CONFIG_CN_SPEECH_COMMAND_ID12="shui mian mo shi"
|
||||
CONFIG_CN_SPEECH_COMMAND_ID13="da kai lan ya"
|
||||
CONFIG_CN_SPEECH_COMMAND_ID14="guan bi lan ya"
|
||||
CONFIG_CN_SPEECH_COMMAND_ID15="kai shi bo fang"
|
||||
CONFIG_CN_SPEECH_COMMAND_ID16="zan ting bo fang"
|
||||
CONFIG_CN_SPEECH_COMMAND_ID17="ding shi yi xiao shi"
|
||||
CONFIG_CN_SPEECH_COMMAND_ID18="da kai dian deng"
|
||||
CONFIG_CN_SPEECH_COMMAND_ID19="guan bi dian deng"
|
||||
CONFIG_CN_SPEECH_COMMAND_ID0=""
|
||||
CONFIG_CN_SPEECH_COMMAND_ID1=""
|
||||
CONFIG_CN_SPEECH_COMMAND_ID2=""
|
||||
CONFIG_CN_SPEECH_COMMAND_ID3=""
|
||||
CONFIG_CN_SPEECH_COMMAND_ID4=""
|
||||
CONFIG_CN_SPEECH_COMMAND_ID5=""
|
||||
CONFIG_CN_SPEECH_COMMAND_ID6=""
|
||||
CONFIG_CN_SPEECH_COMMAND_ID7=""
|
||||
CONFIG_CN_SPEECH_COMMAND_ID8=""
|
||||
CONFIG_CN_SPEECH_COMMAND_ID9=""
|
||||
CONFIG_CN_SPEECH_COMMAND_ID10=""
|
||||
CONFIG_CN_SPEECH_COMMAND_ID11=""
|
||||
CONFIG_CN_SPEECH_COMMAND_ID12=""
|
||||
CONFIG_CN_SPEECH_COMMAND_ID13=""
|
||||
CONFIG_CN_SPEECH_COMMAND_ID14=""
|
||||
CONFIG_CN_SPEECH_COMMAND_ID15=""
|
||||
CONFIG_CN_SPEECH_COMMAND_ID16=""
|
||||
CONFIG_CN_SPEECH_COMMAND_ID17=""
|
||||
CONFIG_CN_SPEECH_COMMAND_ID18=""
|
||||
CONFIG_CN_SPEECH_COMMAND_ID19=""
|
||||
CONFIG_CN_SPEECH_COMMAND_ID20=""
|
||||
CONFIG_CN_SPEECH_COMMAND_ID21=""
|
||||
CONFIG_CN_SPEECH_COMMAND_ID22=""
|
||||
@@ -766,9 +766,9 @@ CONFIG_CN_SPEECH_COMMAND_ID199=""
|
||||
#
|
||||
# Compiler options
|
||||
#
|
||||
CONFIG_COMPILER_OPTIMIZATION_DEBUG=y
|
||||
# CONFIG_COMPILER_OPTIMIZATION_DEBUG 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_ASSERTIONS_ENABLE=y
|
||||
# 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_CHECKS_SILENT is not set
|
||||
CONFIG_COMPILER_HIDE_PATHS_MACROS=y
|
||||
# CONFIG_COMPILER_CXX_EXCEPTIONS is not set
|
||||
# CONFIG_COMPILER_CXX_RTTI is not set
|
||||
CONFIG_COMPILER_CXX_EXCEPTIONS=y
|
||||
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_NORM is not set
|
||||
# CONFIG_COMPILER_STACK_CHECK_MODE_STRONG is not set
|
||||
@@ -813,334 +814,10 @@ CONFIG_APPTRACE_LOCK_ENABLE=y
|
||||
#
|
||||
# Bluetooth
|
||||
#
|
||||
CONFIG_BT_ENABLED=y
|
||||
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_ENABLED is not set
|
||||
CONFIG_BT_ALARM_MAX_NUM=50
|
||||
# end of Common Options
|
||||
|
||||
# CONFIG_BT_HCI_LOG_DEBUG_EN is not set
|
||||
# end of Bluetooth
|
||||
|
||||
# CONFIG_BLE_MESH is not set
|
||||
|
||||
#
|
||||
# Console Library
|
||||
#
|
||||
@@ -1247,8 +924,7 @@ CONFIG_ESP_TLS_USE_DS_PERIPHERAL=y
|
||||
# Wireless Coexistence
|
||||
#
|
||||
CONFIG_ESP_COEX_ENABLED=y
|
||||
CONFIG_ESP_COEX_SW_COEXIST_ENABLE=y
|
||||
# CONFIG_ESP_COEX_POWER_MANAGEMENT is not set
|
||||
# CONFIG_ESP_COEX_EXTERNAL_COEXIST_ENABLE is not set
|
||||
# CONFIG_ESP_COEX_GPIO_DEBUG is not set
|
||||
# end of Wireless Coexistence
|
||||
|
||||
@@ -1327,7 +1003,7 @@ CONFIG_GPTIMER_ISR_HANDLER_IN_IRAM=y
|
||||
#
|
||||
# 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_SLAVE_IN_IRAM is not set
|
||||
CONFIG_SPI_SLAVE_ISR_IN_IRAM=y
|
||||
@@ -1413,7 +1089,7 @@ CONFIG_HTTPD_PURGE_BUF_LEN=32
|
||||
# ESP HTTPS OTA
|
||||
#
|
||||
# CONFIG_ESP_HTTPS_OTA_DECRYPT_CB is not set
|
||||
# CONFIG_ESP_HTTPS_OTA_ALLOW_HTTP is not set
|
||||
CONFIG_ESP_HTTPS_OTA_ALLOW_HTTP=y
|
||||
# end of ESP HTTPS OTA
|
||||
|
||||
#
|
||||
@@ -1509,6 +1185,7 @@ CONFIG_XTAL_FREQ=40
|
||||
# end of Main XTAL Config
|
||||
|
||||
CONFIG_ESP_SPI_BUS_LOCK_ISR_FUNCS_IN_IRAM=y
|
||||
CONFIG_ESP_SPI_BUS_LOCK_FUNCS_IN_IRAM=y
|
||||
# end of Hardware Settings
|
||||
|
||||
#
|
||||
@@ -1601,9 +1278,9 @@ CONFIG_SPIRAM_BOOT_INIT=y
|
||||
# CONFIG_SPIRAM_USE_CAPS_ALLOC is not set
|
||||
CONFIG_SPIRAM_USE_MALLOC=y
|
||||
CONFIG_SPIRAM_MEMTEST=y
|
||||
CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL=16384
|
||||
# CONFIG_SPIRAM_TRY_ALLOCATE_WIFI_LWIP is not set
|
||||
CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL=32768
|
||||
CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL=4096
|
||||
CONFIG_SPIRAM_TRY_ALLOCATE_WIFI_LWIP=y
|
||||
CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL=65536
|
||||
# CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY is not set
|
||||
# CONFIG_SPIRAM_ALLOW_NOINIT_SEG_EXTERNAL_MEMORY is not set
|
||||
# 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_SIZE=32
|
||||
# CONFIG_ESP32S3_DATA_CACHE_16KB is not set
|
||||
CONFIG_ESP32S3_DATA_CACHE_32KB=y
|
||||
# CONFIG_ESP32S3_DATA_CACHE_64KB is not set
|
||||
CONFIG_ESP32S3_DATA_CACHE_SIZE=0x8000
|
||||
# CONFIG_ESP32S3_DATA_CACHE_32KB is not set
|
||||
CONFIG_ESP32S3_DATA_CACHE_64KB=y
|
||||
CONFIG_ESP32S3_DATA_CACHE_SIZE=0x10000
|
||||
# CONFIG_ESP32S3_DATA_CACHE_4WAYS is not set
|
||||
CONFIG_ESP32S3_DATA_CACHE_8WAYS=y
|
||||
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_64B is not set
|
||||
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_TASK_STACK_SIZE=2304
|
||||
CONFIG_ESP_MAIN_TASK_STACK_SIZE=3584
|
||||
CONFIG_ESP_MAIN_TASK_STACK_SIZE=8192
|
||||
CONFIG_ESP_MAIN_TASK_AFFINITY_CPU0=y
|
||||
# CONFIG_ESP_MAIN_TASK_AFFINITY_CPU1 is not set
|
||||
# CONFIG_ESP_MAIN_TASK_AFFINITY_NO_AFFINITY is not set
|
||||
@@ -1757,11 +1433,11 @@ CONFIG_ESP_TIMER_IMPL_SYSTIMER=y
|
||||
# Wi-Fi
|
||||
#
|
||||
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_STATIC_TX_BUFFER=y
|
||||
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_STATIC_RX_MGMT_BUFFER=y
|
||||
# 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_CSI_ENABLED is not set
|
||||
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_RX_BA_WIN=6
|
||||
CONFIG_ESP_WIFI_RX_BA_WIN=2
|
||||
# CONFIG_ESP_WIFI_AMSDU_TX_ENABLED is not set
|
||||
CONFIG_ESP_WIFI_NVS_ENABLED=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_QUEUE_REGISTRY_SIZE=0
|
||||
CONFIG_FREERTOS_TASK_NOTIFICATION_ARRAY_ENTRIES=1
|
||||
# CONFIG_FREERTOS_USE_TRACE_FACILITY is not set
|
||||
CONFIG_FREERTOS_USE_TRACE_FACILITY=y
|
||||
CONFIG_FREERTOS_USE_STATS_FORMATTING_FUNCTIONS=y
|
||||
# CONFIG_FREERTOS_USE_LIST_DATA_INTEGRITY_CHECK_BYTES is not set
|
||||
# CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS is not set
|
||||
CONFIG_FREERTOS_VTASKLIST_INCLUDE_COREID=y
|
||||
CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS=y
|
||||
CONFIG_FREERTOS_RUN_TIME_COUNTER_TYPE_U32=y
|
||||
# CONFIG_FREERTOS_RUN_TIME_COUNTER_TYPE_U64 is not set
|
||||
# CONFIG_FREERTOS_USE_APPLICATION_TASK_TAG is not set
|
||||
# end of Kernel
|
||||
|
||||
#
|
||||
# Port
|
||||
#
|
||||
CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER=y
|
||||
# CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK is not set
|
||||
CONFIG_FREERTOS_TLSP_DELETION_CALLBACKS=y
|
||||
# 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_LVL3 is not set
|
||||
CONFIG_FREERTOS_SYSTICK_USES_SYSTIMER=y
|
||||
CONFIG_FREERTOS_RUN_TIME_STATS_USING_ESP_TIMER=y
|
||||
# CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH is not set
|
||||
# CONFIG_FREERTOS_CHECK_PORT_CRITICAL_COMPLIANCE is not set
|
||||
# end of Port
|
||||
@@ -2071,6 +1751,7 @@ CONFIG_LWIP_TCP_OOSEQ_MAX_PBUFS=4
|
||||
CONFIG_LWIP_TCP_OVERSIZE_MSS=y
|
||||
# CONFIG_LWIP_TCP_OVERSIZE_QUARTER_MSS is not set
|
||||
# CONFIG_LWIP_TCP_OVERSIZE_DISABLE is not set
|
||||
# CONFIG_LWIP_WND_SCALE is not set
|
||||
CONFIG_LWIP_TCP_RTO_TIME=1500
|
||||
# end of TCP
|
||||
|
||||
@@ -2602,10 +2283,6 @@ CONFIG_WL_SECTOR_SIZE=4096
|
||||
#
|
||||
CONFIG_WIFI_PROV_SCAN_MAX_ENTRIES=16
|
||||
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_FAST_SCAN is not set
|
||||
# end of Wi-Fi Provisioning Manager
|
||||
@@ -2635,6 +2312,13 @@ CONFIG_DSP_MAX_FFT_SIZE_4096=y
|
||||
CONFIG_DSP_MAX_FFT_SIZE=4096
|
||||
# end of DSP Library
|
||||
|
||||
#
|
||||
# ESP WebSocket client
|
||||
#
|
||||
# CONFIG_ESP_WS_CLIENT_ENABLE_DYNAMIC_BUFFER is not set
|
||||
# CONFIG_ESP_WS_CLIENT_SEPARATE_TX_LOCK is not set
|
||||
# end of ESP WebSocket client
|
||||
|
||||
#
|
||||
# LVGL configuration
|
||||
#
|
||||
@@ -2667,8 +2351,8 @@ CONFIG_LV_MEM_BUF_MAX_NUM=16
|
||||
#
|
||||
# HAL Settings
|
||||
#
|
||||
CONFIG_LV_DISP_DEF_REFR_PERIOD=30
|
||||
CONFIG_LV_INDEV_DEF_READ_PERIOD=30
|
||||
CONFIG_LV_DISP_DEF_REFR_PERIOD=10
|
||||
CONFIG_LV_INDEV_DEF_READ_PERIOD=10
|
||||
# CONFIG_LV_TICK_CUSTOM is not set
|
||||
CONFIG_LV_DPI_DEF=130
|
||||
# end of HAL Settings
|
||||
@@ -2681,10 +2365,10 @@ CONFIG_LV_DPI_DEF=130
|
||||
# Drawing
|
||||
#
|
||||
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_LAYER_SIMPLE_BUF_SIZE=24576
|
||||
CONFIG_LV_IMG_CACHE_DEF_SIZE=0
|
||||
CONFIG_LV_LAYER_SIMPLE_BUF_SIZE=10240
|
||||
CONFIG_LV_IMG_CACHE_DEF_SIZE=128
|
||||
CONFIG_LV_GRADIENT_MAX_STOPS=2
|
||||
CONFIG_LV_GRAD_CACHE_DEF_SIZE=0
|
||||
# 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_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_OBJ is not set
|
||||
CONFIG_LV_USE_ASSERT_OBJ=y
|
||||
CONFIG_LV_ASSERT_HANDLER_INCLUDE="assert.h"
|
||||
# end of Asserts
|
||||
|
||||
@@ -2762,7 +2446,7 @@ CONFIG_LV_ATTRIBUTE_MEM_ALIGN_SIZE=1
|
||||
CONFIG_LV_FONT_MONTSERRAT_12=y
|
||||
CONFIG_LV_FONT_MONTSERRAT_14=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_22 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_28_COMPRESSED 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_16 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_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_18 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_28_COMPRESSED 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_16 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_LINE_BREAK_LONG_LEN=0
|
||||
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
|
||||
# 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_FATFS 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_SJPG is not set
|
||||
CONFIG_LV_USE_GIF=y
|
||||
@@ -2974,16 +2661,17 @@ CONFIG_LOG_BOOTLOADER_LEVEL=3
|
||||
CONFIG_FLASHMODE_DIO=y
|
||||
# CONFIG_FLASHMODE_DOUT is not set
|
||||
CONFIG_MONITOR_BAUD=115200
|
||||
CONFIG_OPTIMIZATION_LEVEL_DEBUG=y
|
||||
CONFIG_COMPILER_OPTIMIZATION_LEVEL_DEBUG=y
|
||||
CONFIG_COMPILER_OPTIMIZATION_DEFAULT=y
|
||||
# CONFIG_OPTIMIZATION_LEVEL_DEBUG is not set
|
||||
# CONFIG_COMPILER_OPTIMIZATION_LEVEL_DEBUG is not set
|
||||
# CONFIG_COMPILER_OPTIMIZATION_DEFAULT is not set
|
||||
# CONFIG_OPTIMIZATION_LEVEL_RELEASE is not set
|
||||
# CONFIG_COMPILER_OPTIMIZATION_LEVEL_RELEASE is not set
|
||||
CONFIG_OPTIMIZATION_ASSERTIONS_ENABLED=y
|
||||
# CONFIG_OPTIMIZATION_ASSERTIONS_SILENT is not set
|
||||
# CONFIG_OPTIMIZATION_ASSERTIONS_DISABLED is not set
|
||||
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_NORM 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_NONE=y
|
||||
CONFIG_ESP32_APPTRACE_LOCK_ENABLE=y
|
||||
CONFIG_BLUEDROID_ENABLED=y
|
||||
# CONFIG_NIMBLE_ENABLED 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_EXTERNAL_COEX_ENABLE is not set
|
||||
# CONFIG_ESP_WIFI_EXTERNAL_COEXIST_ENABLE is not set
|
||||
# CONFIG_MCPWM_ISR_IN_IRAM is not set
|
||||
# CONFIG_EVENT_LOOP_PROFILING is not set
|
||||
CONFIG_POST_EVENTS_FROM_ISR=y
|
||||
CONFIG_POST_EVENTS_FROM_IRAM_ISR=y
|
||||
CONFIG_GDBSTUB_SUPPORT_TASKS=y
|
||||
CONFIG_GDBSTUB_MAX_TASKS=32
|
||||
# CONFIG_OTA_ALLOW_HTTP is not set
|
||||
CONFIG_OTA_ALLOW_HTTP=y
|
||||
CONFIG_ESP32S3_DEEP_SLEEP_WAKEUP_DELAY=2000
|
||||
CONFIG_ESP_SLEEP_DEEP_SLEEP_WAKEUP_DELAY=2000
|
||||
CONFIG_ESP32S3_RTC_CLK_SRC_INT_RC=y
|
||||
@@ -3209,7 +2713,7 @@ CONFIG_ESP32S3_DEFAULT_CPU_FREQ_240=y
|
||||
CONFIG_ESP32S3_DEFAULT_CPU_FREQ_MHZ=240
|
||||
CONFIG_SYSTEM_EVENT_QUEUE_SIZE=32
|
||||
CONFIG_SYSTEM_EVENT_TASK_STACK_SIZE=2304
|
||||
CONFIG_MAIN_TASK_STACK_SIZE=3584
|
||||
CONFIG_MAIN_TASK_STACK_SIZE=8192
|
||||
CONFIG_CONSOLE_UART_DEFAULT=y
|
||||
# CONFIG_CONSOLE_UART_CUSTOM is not set
|
||||
# CONFIG_CONSOLE_UART_NONE is not set
|
||||
@@ -3250,19 +2754,19 @@ CONFIG_ESP32S3_BROWNOUT_DET_LVL=7
|
||||
CONFIG_IPC_TASK_STACK_SIZE=1280
|
||||
CONFIG_TIMER_TASK_STACK_SIZE=3584
|
||||
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_STATIC_TX_BUFFER=y
|
||||
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_CSI_ENABLED is not set
|
||||
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_RX_BA_WIN=6
|
||||
CONFIG_ESP32_WIFI_RX_BA_WIN=6
|
||||
CONFIG_ESP32_WIFI_RX_BA_WIN=2
|
||||
CONFIG_ESP32_WIFI_RX_BA_WIN=2
|
||||
# CONFIG_ESP32_WIFI_AMSDU_TX_ENABLED is not set
|
||||
CONFIG_ESP32_WIFI_NVS_ENABLED=y
|
||||
CONFIG_ESP32_WIFI_TASK_PINNED_TO_CORE_0=y
|
||||
|
||||
@@ -91,7 +91,7 @@ void sd_card_module_test(void)
|
||||
printf("3. 测试文件写入...\n");
|
||||
char test_file_path[] = MOUNT_POINT"/test.txt";
|
||||
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) {
|
||||
printf(" 文件写入成功: %s\n", test_file_path);
|
||||
} else {
|
||||
|
||||
@@ -187,4 +187,113 @@
|
||||
实际完成任务:
|
||||
- [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,更加便于移植和拓展
|
||||
Reference in New Issue
Block a user