1. 统一在程序启动时打印配置信息
1.1:在 ota.cc 中添加日志打印 (推荐),添加打印函数 PrintOtaConfig
在解析 JSON 后添加打印语句,这样可以看到服务器返回的完整配置。
修改文件 : d:\netProject\AiXiaoZhi\main\ota.cc
在第 160 行后添加 MQTT 配置打印:
/*
* Specification: https://ccnphfhqs21z.feishu.cn/wiki/FjW6wZmisimNBBkov6OcmfvknVd
*/
bool Ota::CheckVersion() {
auto& board = Board::GetInstance();
auto app_desc = esp_app_get_description();
// Check if there is a new firmware version available
current_version_ = app_desc->version;
ESP_LOGI(TAG, "Current version: %s", current_version_.c_str());
std::string url = GetCheckVersionUrl();
if (url.length() < 10) {
ESP_LOGE(TAG, "Check version URL is not properly set");
return false;
}
auto http = SetupHttp();
std::string data = board.GetJson();
std::string method = data.length() > 0 ? "POST" : "GET";
http->SetContent(std::move(data));
if (!http->Open(method, url)) {
ESP_LOGE(TAG, "Failed to open HTTP connection");
return false;
}
auto status_code = http->GetStatusCode();
if (status_code != 200) {
ESP_LOGE(TAG, "Failed to check version, status code: %d", status_code);
return false;
}
data = http->ReadAll();
http->Close();
// Response: { "firmware": { "version": "1.0.0", "url": "http://" } }
// Parse the JSON response and check if the version is newer
// If it is, set has_new_version_ to true and store the new version and URL
cJSON *root = cJSON_Parse(data.c_str());
if (root == NULL) {
ESP_LOGE(TAG, "Failed to parse JSON response");
return false;
}
has_activation_code_ = false;
has_activation_challenge_ = false;
cJSON *activation = cJSON_GetObjectItem(root, "activation");
if (cJSON_IsObject(activation)) {
cJSON* message = cJSON_GetObjectItem(activation, "message");
if (cJSON_IsString(message)) {
activation_message_ = message->valuestring;
}
cJSON* code = cJSON_GetObjectItem(activation, "code");
if (cJSON_IsString(code)) {
activation_code_ = code->valuestring;
has_activation_code_ = true;
}
cJSON* challenge = cJSON_GetObjectItem(activation, "challenge");
if (cJSON_IsString(challenge)) {
activation_challenge_ = challenge->valuestring;
has_activation_challenge_ = true;
}
cJSON* timeout_ms = cJSON_GetObjectItem(activation, "timeout_ms");
if (cJSON_IsNumber(timeout_ms)) {
activation_timeout_ms_ = timeout_ms->valueint;
}
}
has_mqtt_config_ = false;
cJSON *mqtt = cJSON_GetObjectItem(root, "mqtt");
if (cJSON_IsObject(mqtt)) {
Settings settings("mqtt", true);
cJSON *item = NULL;
cJSON_ArrayForEach(item, mqtt) {
if (cJSON_IsString(item)) {
if (settings.GetString(item->string) != item->valuestring) {
settings.SetString(item->string, item->valuestring);
}
} else if (cJSON_IsNumber(item)) {
if (settings.GetInt(item->string) != item->valueint) {
settings.SetInt(item->string, item->valueint);
}
}
}
has_mqtt_config_ = true;
} else {
ESP_LOGI(TAG, "No mqtt section found !");
}
has_websocket_config_ = false;
cJSON *websocket = cJSON_GetObjectItem(root, "websocket");
if (cJSON_IsObject(websocket)) {
Settings settings("websocket", true);
cJSON *item = NULL;
cJSON_ArrayForEach(item, websocket) {
if (cJSON_IsString(item)) {
if (settings.GetString(item->string) != item->valuestring) {
settings.SetString(item->string, item->valuestring);
}
} else if (cJSON_IsNumber(item)) {
if (settings.GetInt(item->string) != item->valueint) {
settings.SetInt(item->string, item->valueint);
}
}
}
has_websocket_config_ = true;
} else {
ESP_LOGI(TAG, "No websocket section found!");
}
has_server_time_ = false;
cJSON *server_time = cJSON_GetObjectItem(root, "server_time");
if (cJSON_IsObject(server_time)) {
cJSON *timestamp = cJSON_GetObjectItem(server_time, "timestamp");
cJSON *timezone_offset = cJSON_GetObjectItem(server_time, "timezone_offset");
if (cJSON_IsNumber(timestamp)) {
// 设置系统时间
struct timeval tv;
double ts = timestamp->valuedouble;
// 如果有时区偏移,计算本地时间
if (cJSON_IsNumber(timezone_offset)) {
ts += (timezone_offset->valueint * 60 * 1000); // 转换分钟为毫秒
}
tv.tv_sec = (time_t)(ts / 1000); // 转换毫秒为秒
tv.tv_usec = (suseconds_t)((long long)ts % 1000) * 1000; // 剩余的毫秒转换为微秒
settimeofday(&tv, NULL);
has_server_time_ = true;
}
} else {
ESP_LOGW(TAG, "No server_time section found!");
}
has_new_version_ = false;
cJSON *firmware = cJSON_GetObjectItem(root, "firmware");
if (cJSON_IsObject(firmware)) {
cJSON *version = cJSON_GetObjectItem(firmware, "version");
if (cJSON_IsString(version)) {
firmware_version_ = version->valuestring;
}
cJSON *url = cJSON_GetObjectItem(firmware, "url");
if (cJSON_IsString(url)) {
firmware_url_ = url->valuestring;
}
if (cJSON_IsString(version) && cJSON_IsString(url)) {
// Check if the version is newer, for example, 0.1.0 is newer than 0.0.1
has_new_version_ = IsNewVersionAvailable(current_version_, firmware_version_);
if (has_new_version_) {
ESP_LOGI(TAG, "New version available: %s", firmware_version_.c_str());
} else {
ESP_LOGI(TAG, "Current is the latest version");
}
// If the force flag is set to 1, the given version is forced to be installed
cJSON *force = cJSON_GetObjectItem(firmware, "force");
if (cJSON_IsNumber(force) && force->valueint == 1) {
has_new_version_ = true;
}
}
} else {
ESP_LOGW(TAG, "No firmware section found!");
}
cJSON_Delete(root);
return true;
}
// Print all OTA configuration
void Ota::PrintOtaConfig() {
ESP_LOGI(TAG, "========================================");
ESP_LOGI(TAG, " OTA Server Configuration");
ESP_LOGI(TAG, "========================================");
Settings ota_url_settings("wifi", false);
ESP_LOGI(TAG, "OTA URL: %s", ota_url_settings.GetString("ota_url").c_str());
ESP_LOGI(TAG, "Has MQTT Config: %s", has_mqtt_config_ ? "YES" : "NO");
ESP_LOGI(TAG, "Has WebSocket Config: %s", has_websocket_config_ ? "YES" : "NO");
ESP_LOGI(TAG, "Has New Version: %s", has_new_version_ ? "YES" : "NO");
ESP_LOGI(TAG, "Has Server Time: %s", has_server_time_ ? "YES" : "NO");
if (has_mqtt_config_) {
Settings mqtt_settings("mqtt", false);
ESP_LOGI(TAG, "--- MQTT Details ---");
ESP_LOGI(TAG, " endpoint: %s", mqtt_settings.GetString("endpoint").c_str());
ESP_LOGI(TAG, " client_id: %s", mqtt_settings.GetString("client_id").c_str());
ESP_LOGI(TAG, " username: %s", mqtt_settings.GetString("username").c_str());
ESP_LOGI(TAG, " publish_topic: %s", mqtt_settings.GetString("publish_topic").c_str());
}
if (has_websocket_config_) {
Settings ws_settings("websocket", false);
ESP_LOGI(TAG, "--- WebSocket Details ---");
ESP_LOGI(TAG, " url: %s", ws_settings.GetString("url").c_str());
ESP_LOGI(TAG, " version: %d", ws_settings.GetInt("version", 1));
}
ESP_LOGI(TAG, "Current Version: %s", current_version_.c_str());
if (has_new_version_) {
ESP_LOGI(TAG, "New Version: %s", firmware_version_.c_str());
ESP_LOGI(TAG, "Firmware URL: %s", firmware_url_.c_str());
}
ESP_LOGI(TAG, "========================================");
}
1.2:设备启动时自动打印 (推荐),在application.cc中添加代码
在 Application::Start() 函数中调用,设备启动时自动显示配置信息。
修改文件 : d:\netProject\AiXiaoZhi\main\application.cc
在第 510 行(SystemInfo::PrintHeapStats(); 之后)添加:
// Print heap stats
SystemInfo::PrintHeapStats();
// Print OTA configuration on startup
Ota ota_debug;
ota_debug.CheckVersion();
ota_debug.PrintOtaConfig();
2. 在需要打印信息打印出来,ota.cc代码中打印出来
2.1 在解析 JSON 后添加打印语句,这样可以看到服务器返回的完整配置。
修改文件 : d:\netProject\AiXiaoZhi\main\ota.cc
在第 160 行后添加 MQTT 配置打印:
has_mqtt_config_ = true;
} else {
ESP_LOGI(TAG, "No mqtt section found!");
}
// Print MQTT configuration
if (has_mqtt_config_) {
Settings mqtt_settings("mqtt", false);
ESP_LOGI(TAG, "=== MQTT Configuration ===");
ESP_LOGI(TAG, " endpoint: %s", mqtt_settings.GetString("endpoint").c_str());
ESP_LOGI(TAG, " client_id: %s", mqtt_settings.GetString("client_id").c_str());
ESP_LOGI(TAG, " username: %s", mqtt_settings.GetString("username").c_str());
ESP_LOGI(TAG, " password: %s", mqtt_settings.GetString("password").c_str());
ESP_LOGI(TAG, " publish_topic: %s", mqtt_settings.GetString("publish_topic").c_str());
ESP_LOGI(TAG, " keepalive: %d", mqtt_settings.GetInt("keepalive", 240));
}
has_websocket_config_ = false;
2.2 在第 181 行后添加 WebSocket 配置打印:
has_websocket_config_ = true;
} else {
ESP_LOGI(TAG, "No websocket section found!");
}
// Print WebSocket configuration
if (has_websocket_config_) {
Settings ws_settings("websocket", false);
ESP_LOGI(TAG, "=== WebSocket Configuration ===");
ESP_LOGI(TAG, " url: %s", ws_settings.GetString("url").c_str());
ESP_LOGI(TAG, " token: %s", ws_settings.GetString("token").c_str());
ESP_LOGI(TAG, " version: %d", ws_settings.GetInt("version", 1));
}
has_server_time_ = false;
AiXiaoZhi/
├── .cache/clangd/ # Clang 索引缓存
├── .vscode/ # VSCode 配置
├── build/ # 编译输出目录
├── docs/ # 文档
│ ├── mqtt-udp.md # MQTT+UDP 协议文档
│ ├── websocket.md # WebSocket 协议文档
│ ├── mcp-protocol.md # MCP 设备控制协议
│ ├── mcp-usage.md # MCP 使用说明
│ └── project-architecture.md # 项目架构分析 (本文件)
├── main/ # 源代码主目录
│ ├── CMakeLists.txt # CMake 构建配置
│ ├── Kconfig.projbuild # 编译配置选项
│ ├── main.cc # 程序入口点
│ │
│ ├── application.cc/h # 应用核心 (单例)
│ ├── device_state.h # 设备状态定义
│ ├── device_state_event.cc/h # 设备状态事件
│ ├── ota.cc/h # OTA 升级
│ ├── settings.cc/h # NVS 设置存储
│ ├── system_info.cc/h # 系统信息
│ │
│ ├── audio/ # 音频子系统
│ │ ├── audio_codec.cc/h # 音频编解码抽象
│ │ ├── audio_service.cc/h # 音频服务 (多任务)
│ │ ├── audio_processor.h # 音频处理器接口
│ │ ├── wake_word.h # 唤醒词接口
│ │ ├── codecs/ # 编解码器实现
│ │ ├── processors/ # 音频处理器
│ │ │ ├── afe_audio_processor.cc/h # ESP-AFE 处理器
│ │ │ ├── no_audio_processor.cc/h # 空处理器
│ │ │ └── audio_debugger.cc/h # 音频调试器
│ │ └── wake_words/ # 唤醒词检测
│ │ ├── afe_wake_word.cc/h # ESP-AFE 唤醒词
│ │ ├── esp_wake_word.cc/h # ESP-SR 唤醒词
│ │ └── custom_wake_word.cc/h # 自定义唤醒词
│ │
│ ├── display/ # 显示子系统
│ │ ├── display.cc/h # 显示抽象基类
│ │ ├── lcd_display.cc/h # LCD 显示屏 (LVGL)
│ │ ├── oled_display.cc/h # OLED 显示屏
│ │ └── esplog_display.cc/h # ESP Log 显示
│ │
│ ├── protocols/ # 通信协议
│ │ ├── protocol.cc/h # 协议抽象基类
│ │ ├── mqtt_protocol.cc/h # MQTT + UDP 协议
│ │ └── websocket_protocol.cc/h # WebSocket 协议
│ │
│ ├── led/ # 指示灯控制
│ │ ├── led.h # LED 接口
│ │ ├── gpio_led.cc/h # GPIO LED
│ │ └── ... # 其他 LED 类型
│ │
│ ├── assets/ # 资源文件
│ │ └── lang_config.h # 多语言配置
│ │
│ ├── boards/ # 硬件平台 (90+ 种)
│ │ ├── common/ # 公共代码
│ │ │ ├── board.h/cc # Board 抽象基类
│ │ │ ├── network_interface.cc/h
│ │ │ └── audio_codec.cc/h
│ │ ├── m5stack-core-s3/ # M5Stack Core S3
│ │ ├── esp-box-3/ # ESP32-S3-BOX3
│ │ ├── xmini-c3/ # Xmini C3
│ │ └── ... # 其他 80+ 种开发板
│ │
│ └── mcp_server.cc/h # MCP 设备控制协议
│
├── managed_components/ # 托管组件
├── partitions/ # 分区表
├── scripts/ # 脚本工具
├── sdkconfig # SDK 配置 (自动生成)
├── sdkconfig.defaults # 默认配置
├── CMakeLists.txt # 顶层 CMake 配置
└── README.md # 项目说明
AiXiaoZhi/
├── .cache/clangd/ # Clang 索引缓存
├── .vscode/ # VSCode 配置
├── build/ # 编译输出目录
├── docs/ # 文档
│ ├── mqtt-udp.md # MQTT+UDP 协议文档
│ ├── websocket.md # WebSocket 协议文档
│ ├── mcp-protocol.md # MCP 设备控制协议
│ ├── mcp-usage.md # MCP 使用说明
│ └── project-architecture.md # 项目架构分析 (本文件)
├── main/ # 源代码主目录
│ ├── CMakeLists.txt # CMake 构建配置
│ ├── Kconfig.projbuild # 编译配置选项
│ ├── main.cc # 程序入口点
│ │
│ ├── application.cc/h # 应用核心 (单例)
│ ├── device_state.h # 设备状态定义
│ ├── device_state_event.cc/h # 设备状态事件
│ ├── ota.cc/h # OTA 升级
│ ├── settings.cc/h # NVS 设置存储
│ ├── system_info.cc/h # 系统信息
│ │
│ ├── audio/ # 音频子系统
│ │ ├── audio_codec.cc/h # 音频编解码抽象
│ │ ├── audio_service.cc/h # 音频服务 (多任务)
│ │ ├── audio_processor.h # 音频处理器接口
│ │ ├── wake_word.h # 唤醒词接口
│ │ ├── codecs/ # 编解码器实现
│ │ ├── processors/ # 音频处理器
│ │ │ ├── afe_audio_processor.cc/h # ESP-AFE 处理器
│ │ │ ├── no_audio_processor.cc/h # 空处理器
│ │ │ └── audio_debugger.cc/h # 音频调试器
│ │ └── wake_words/ # 唤醒词检测
│ │ ├── afe_wake_word.cc/h # ESP-AFE 唤醒词
│ │ ├── esp_wake_word.cc/h # ESP-SR 唤醒词
│ │ └── custom_wake_word.cc/h # 自定义唤醒词
│ │
│ ├── display/ # 显示子系统
│ │ ├── display.cc/h # 显示抽象基类
│ │ ├── lcd_display.cc/h # LCD 显示屏 (LVGL)
│ │ ├── oled_display.cc/h # OLED 显示屏
│ │ └── esplog_display.cc/h # ESP Log 显示
│ │
│ ├── protocols/ # 通信协议
│ │ ├── protocol.cc/h # 协议抽象基类
│ │ ├── mqtt_protocol.cc/h # MQTT + UDP 协议
│ │ └── websocket_protocol.cc/h # WebSocket 协议
│ │
│ ├── led/ # 指示灯控制
│ │ ├── led.h # LED 接口
│ │ ├── gpio_led.cc/h # GPIO LED
│ │ └── ... # 其他 LED 类型
│ │
│ ├── assets/ # 资源文件
│ │ └── lang_config.h # 多语言配置
│ │
│ ├── boards/ # 硬件平台 (90+ 种)
│ │ ├── common/ # 公共代码
│ │ │ ├── board.h/cc # Board 抽象基类
│ │ │ ├── network_interface.cc/h
│ │ │ └── audio_codec.cc/h
│ │ ├── m5stack-core-s3/ # M5Stack Core S3
│ │ ├── esp-box-3/ # ESP32-S3-BOX3
│ │ ├── xmini-c3/ # Xmini C3
│ │ └── ... # 其他 80+ 种开发板
│ │
│ └── mcp_server.cc/h # MCP 设备控制协议
│
├── managed_components/ # 托管组件
├── partitions/ # 分区表
├── scripts/ # 脚本工具
├── sdkconfig # SDK 配置 (自动生成)
├── sdkconfig.defaults # 默认配置
├── CMakeLists.txt # 顶层 CMake 配置
└── README.md # 项目说明