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 # 项目说明