ESP32-S3中小智项目优化内容

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

相关推荐
阿斌.com9 小时前
Visual Studio 2026安装全攻略
ide·visual studio
我要神龙摆尾16 小时前
IDEA 阅读插件
java·ide·intellij-idea·idea-plugs·plugs
Mininglamp_271819 小时前
Cider量化配置与性能调优:在M系列Mac上压榨Mano-P的每一点推理性能
macos
梦幻通灵20 小时前
IDEA 实用快捷键【持续更新】
java·ide·intellij-idea
xuefuhe21 小时前
MacOS EAGAIN无法打开terminal
macos
传奇开心果编程2 天前
【SwiftUI入门练中学】第7课 动画与手势:让界面更灵动
学习·macos·ui·ios·swiftui·swift
风华同学2 天前
vscode远程连接
ide·vscode·编辑器
kiss strong2 天前
idea显示前进后退按钮
java·ide·intellij-idea
ShuiShenHuoLe2 天前
Visual Studio 2022 Community 下载指南
ide·visual studio