首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >小智AI ESP32S3 学习课件 - 第4周:工程结构与类图解析

小智AI ESP32S3 学习课件 - 第4周:工程结构与类图解析

作者头像
网罗开发
发布2025-11-14 13:49:33
发布2025-11-14 13:49:33
1880
举报
文章被收录于专栏:网罗开发网罗开发

📚 本周学习目标

  • 了解小智AI项目的代码目录结构
  • 理解Application、Board、ThingManager三大类的作用
  • 学会通过类图理解架构设计
  • 掌握分层架构和依赖关系
  • 完成main.cc入口函数跟踪练习

🎯 工程结构与类图解析

1. 代码目录介绍

项目整体结构
代码语言:javascript
复制
xiaozhi-esp32/
├── .gitignore                    # Git忽略文件
├── CMakeLists.txt                # 工程构建文件
├── LICENSE                       # MIT许可证
├── README.md                     # 项目说明文档
├── README_en.md                  # 英文说明文档
├── README_ja.md                  # 日文说明文档
├── partitions.csv                # 16M Flash分区表
├── partitions_32M_sensecap.csv   # 32M Flash分区表
├── partitions_4M.csv             # 4M Flash分区表
├── partitions_8M.csv              # 8M Flash分区表
├── sdkconfig.defaults            # 默认配置文件
├── sdkconfig.defaults.esp32c3    # ESP32-C3配置
├── sdkconfig.defaults.esp32s3    # ESP32-S3配置
├── docs/                         # 文档目录
├── scripts/                      # 脚本目录
├── .github/                      # GitHub Actions配置
└── main/                         # 主要源码目录
main目录详细结构
代码语言:javascript
复制
main/
├── main.cc                       # 程序入口文件
├── application.cc                 # 应用核心逻辑
├── application.h                  # 应用头文件
├── CMakeLists.txt                # 构建配置
├── Kconfig.projbuild             # 项目配置
├── boards/                       # 开发板实现
│   ├── bread-compact-wifi/       # 面包板WiFi版本
│   ├── bread-compact-ml307/      # 面包板4G版本
│   ├── bread-compact-esp32/      # 面包板ESP32版本
│   └── esp-box-3/                # ESP-BOX-3版本
├── audio_codecs/                 # 音频编解码器
│   ├── audio_codec.h             # 音频编解码器基类
│   ├── no_audio_codec.cc         # 无音频编解码器
│   ├── es8311_audio_codec.cc     # ES8311编解码器
│   └── es8388_audio_codec.cc     # ES8388编解码器
├── display/                      # 显示相关
│   ├── display.h                 # 显示基类
│   ├── no_display.cc             # 无显示设备
│   ├── oled_display.cc           # OLED显示
│   └── lcd_display.cc            # LCD显示
├── led/                          # LED控制
│   ├── led.h                     # LED基类
│   ├── no_led.cc                 # 无LED设备
│   ├── single_led.cc             # 单LED控制
│   └── circular_strip.cc         # 环形LED灯带
├── protocols/                    # 网络协议
│   ├── protocol.h                # 协议基类
│   ├── websocket_protocol.cc     # WebSocket协议
│   └── mqtt_protocol.cc          # MQTT协议
├── iot/                          # 物联网设备管理
│   ├── thing_manager.h           # 设备管理器
│   ├── thing.h                   # 设备基类
│   ├── speaker.cc                # 扬声器设备
│   ├── battery.cc                # 电池设备
│   └── backlight.cc              # 背光设备
├── audio_processing/             # 音频处理
│   ├── wake_word_detect.h        # 唤醒词检测
│   ├── wake_word_detect.cc       # 唤醒词检测实现
│   ├── audio_processor.h         # 音频处理器
│   └── audio_processor.cc       # 音频处理器实现
└── components/                   # 自定义组件
    ├── opus_encoder/              # Opus编码器
    ├── opus_decoder/              # Opus解码器
    └── ota/                      # OTA更新
关键文件说明

main.cc - 程序入口

代码语言:javascript
复制
// main.cc 程序入口
extern"C"void app_main(void) {
    // 初始化默认事件循环
    ESP_ERROR_CHECK(esp_event_loop_create_default());
    
    // 初始化NVS flash
    esp_err_t ret = nvs_flash_init();
    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
        ESP_LOGW(TAG, "Erasing NVS flash to fix corruption");
        ESP_ERROR_CHECK(nvs_flash_erase());
        ret = nvs_flash_init();
    }
    ESP_ERROR_CHECK(ret);
    
    // 启动应用程序
    Application::GetInstance().Start();
}

CMakeLists.txt - 构建配置

代码语言:javascript
复制
# main/CMakeLists.txt
idf_component_register(
    SRCS "main.cc"
         "application.cc"
         "boards/bread-compact-wifi/board.cc"
         "audio_codecs/audio_codec.cc"
         "display/display.cc"
         "led/led.cc"
         "protocols/protocol.cc"
         "iot/thing_manager.cc"
         "audio_processing/wake_word_detect.cc"
    INCLUDE_DIRS "."
    REQUIRES esp_wifi esp_http_client esp_websocket_client
)

2. Application、Board、ThingManager三大类的作用

Application类 - 应用核心管理

Application类职责

代码语言:javascript
复制
Application类职责:
├── 设备状态管理
│   ├── 空闲状态 (kDeviceStateIdle)
│   ├── 监听状态 (kDeviceStateListening)
│   ├── 说话状态 (kDeviceStateSpeaking)
│   └── 激活状态 (kDeviceStateActivating)
├── 音频处理协调
│   ├── 音频输入处理
│   ├── 音频输出处理
│   ├── 音频编解码
│   └── 音频重采样
├── 网络通信管理
│   ├── WebSocket连接
│   ├── 消息发送接收
│   └── 连接状态监控
├── 任务调度
│   ├── 主循环任务
│   ├── 后台任务
│   └── 定时任务
└── 系统管理
    ├── 固件更新
    ├── 设备重启
    └── 错误处理

Application类核心方法

代码语言:javascript
复制
class Application {
public:
    // 单例模式
    static Application& GetInstance();
    
    // 应用启动
    void Start();
    
    // 主循环
    void MainLoop();
    
    // 状态管理
    void SetDeviceState(DeviceState state);
    DeviceState GetDeviceState() const;
    
    // 音频处理
    void InputAudio();
    void OutputAudio();
    
    // 网络通信
    void StartListening();
    void StopListening();
    
    // 系统管理
    void CheckNewVersion();
    void Reboot();
    
private:
    volatile DeviceState device_state_;
    std::unique_ptr<Protocol> protocol_;
    std::unique_ptr<OpusEncoderWrapper> opus_encoder_;
    std::unique_ptr<OpusDecoderWrapper> opus_decoder_;
    // 其他成员变量...
};
Board类 - 硬件抽象层

Board类职责

代码语言:javascript
复制
Board类职责:
├── 硬件抽象
│   ├── 统一硬件接口
│   ├── 屏蔽硬件差异
│   └── 提供硬件访问
├── 设备管理
│   ├── 音频编解码器
│   ├── 显示设备
│   ├── LED控制
│   └── 按键处理
├── 网络连接
│   ├── WiFi连接
│   ├── 4G连接
│   └── 网络状态
└── 系统信息
    ├── 设备类型
    ├── 设备ID
    └── 系统状态

Board类层次结构

代码语言:javascript
复制
// 抽象基类
class Board {
public:
    virtual ~Board() = default;
    virtual std::string GetBoardType() = 0;
    virtual AudioCodec* GetAudioCodec() = 0;
    virtual Display* GetDisplay() = 0;
    virtual Led* GetLed() = 0;
    virtual void StartNetwork() = 0;
};

// WiFi板卡基类
class WifiBoard :public Board {
public:
    virtual void StartNetwork() override;
    virtual void EnterWifiConfigMode();
    virtual void ResetWifiConfiguration();
};

// 4G板卡基类
class Ml307Board :public Board {
public:
    virtual void StartNetwork() override;
    virtual void WaitForNetworkReady();
};
ThingManager类 - 物联网设备管理

ThingManager类职责

代码语言:javascript
复制
ThingManager类职责:
├── 设备管理
│   ├── 设备注册
│   ├── 设备查找
│   └── 设备状态
├── 属性管理
│   ├── 属性获取
│   ├── 属性设置
│   └── 属性监控
├── 方法调用
│   ├── 方法执行
│   ├── 参数传递
│   └── 结果返回
└── 数据交换
    ├── JSON序列化
    ├── 数据格式转换
    └── 协议适配

ThingManager类实现

代码语言:javascript
复制
class ThingManager {
public:
    // 单例模式
    static ThingManager& GetInstance();
    
    // 设备管理
    void AddThing(Thing* thing);
    void RemoveThing(Thing* thing);
    
    // 属性管理
    std::string GetDescriptorsJson();
    std::string GetStatesJson();
    
    // 方法调用
    void Invoke(const cJSON* command);
    
private:
    std::vector<Thing*> things_;
    std::mutex things_mutex_;
};

3. 如何通过类图理解架构

系统架构图
代码语言:javascript
复制
小智AI系统架构:
┌─────────────────────────────────────────────────────────────┐
│                    Application Layer                        │
│  ┌─────────────────┐  ┌─────────────────┐  ┌─────────────────┐ │
│  │   Application   │  │   ThingManager  │  │    Protocol     │ │
│  │   (核心管理)     │  │   (设备管理)    │  │   (网络通信)    │ │
│  └─────────────────┘  └─────────────────┘  └─────────────────┘ │
└─────────────────────────────────────────────────────────────┘
                                │
                                ▼
┌─────────────────────────────────────────────────────────────┐
│                    Hardware Layer                           │
│  ┌─────────────────┐  ┌─────────────────┐  ┌─────────────────┐ │
│  │      Board      │  │   AudioCodec    │  │    Display      │ │
│  │   (硬件抽象)     │  │   (音频编解码)   │  │   (显示设备)    │ │
│  └─────────────────┘  └─────────────────┘  └─────────────────┘ │
│  ┌─────────────────┐  ┌─────────────────┐  ┌─────────────────┐ │
│  │       Led       │  │    Button       │  │   Network       │ │
│  │   (LED控制)     │  │   (按键处理)    │  │   (网络连接)    │ │
│  └─────────────────┘  └─────────────────┘  └─────────────────┘ │
└─────────────────────────────────────────────────────────────┘
类关系图
代码语言:javascript
复制
类关系说明:
├── Application
│   ├── 依赖 Board (通过GetInstance获取)
│   ├── 依赖 ThingManager (通过GetInstance获取)
│   ├── 包含 Protocol (智能指针)
│   ├── 包含 OpusEncoder (智能指针)
│   └── 包含 OpusDecoder (智能指针)
├── Board
│   ├── 抽象基类
│   ├── 派生类 WifiBoard
│   ├── 派生类 Ml307Board
│   └── 提供硬件访问接口
├── ThingManager
│   ├── 管理 Thing 对象集合
│   ├── 提供设备描述信息
│   └── 处理设备方法调用
└── Protocol
    ├── 抽象基类
    ├── 派生类 WebsocketProtocol
    └── 派生类 MqttProtocol
分层架构设计

分层架构优势

代码语言:javascript
复制
分层架构优势:
├── 职责分离
│   ├── 每层专注特定功能
│   ├── 降低耦合度
│   └── 提高可维护性
├── 可扩展性
│   ├── 易于添加新功能
│   ├── 支持硬件升级
│   └── 协议扩展
├── 可测试性
│   ├── 单元测试
│   ├── 集成测试
│   └── 模拟测试
└── 可复用性
    ├── 组件复用
    ├── 代码复用
    └── 设计复用

依赖关系

代码语言:javascript
复制
依赖关系:
├── 上层依赖下层
│   ├── Application 依赖 Board
│   ├── Application 依赖 ThingManager
│   └── Application 依赖 Protocol
├── 下层不依赖上层
│   ├── Board 不依赖 Application
│   ├── ThingManager 不依赖 Application
│   └── Protocol 不依赖 Application
└── 同层之间解耦
    ├── Board 与 ThingManager 解耦
    ├── Protocol 与 Board 解耦
    └── 通过接口交互

4. 上机练习:找到main.cc的入口函数,并跟踪到Application::Start()

实验目标
  • 理解程序启动流程
  • 跟踪Application::Start()调用
  • 掌握代码阅读技巧
实验步骤

步骤1:查看main.cc入口函数

代码语言:javascript
复制
// main/main.cc
extern"C"void app_main(void) {
    // 初始化默认事件循环
    ESP_ERROR_CHECK(esp_event_loop_create_default());
    
    // 初始化NVS flash
    esp_err_t ret = nvs_flash_init();
    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
        ESP_LOGW(TAG, "Erasing NVS flash to fix corruption");
        ESP_ERROR_CHECK(nvs_flash_erase());
        ret = nvs_flash_init();
    }
    ESP_ERROR_CHECK(ret);
    
    // 启动应用程序
    Application::GetInstance().Start();
}

步骤2:跟踪Application::GetInstance()

代码语言:javascript
复制
// main/application.h
class Application {
public:
    static Application& GetInstance() {
        static Application instance;
        return instance;
    }
    
    void Start();
    // 其他方法...
};

步骤3:跟踪Application::Start()方法

代码语言:javascript
复制
// main/application.cc
void Application::Start() {
    // 获取板卡对象
    auto& board = Board::GetInstance();
    
    // 设置设备状态
    SetDeviceState(kDeviceStateStarting);
    
    // 获取显示和音频编解码器
    auto display = board.GetDisplay();
    auto codec = board.GetAudioCodec();
    
    // 配置音频编解码器
    opus_decode_sample_rate_ = codec->output_sample_rate();
    opus_decoder_ = std::make_unique<OpusDecoderWrapper>(opus_decode_sample_rate_, 1);
    opus_encoder_ = std::make_unique<OpusEncoderWrapper>(16000, 1, OPUS_FRAME_DURATION_MS);
    
    // 设置音频编解码器回调
    codec->OnInputReady([this, codec]() {
        BaseType_t higher_priority_task_woken = pdFALSE;
        xEventGroupSetBitsFromISR(event_group_, AUDIO_INPUT_READY_EVENT, &higher_priority_task_woken);
        return higher_priority_task_woken == pdTRUE;
    });
    
    codec->OnOutputReady([this]() {
        BaseType_t higher_priority_task_woken = pdFALSE;
        xEventGroupSetBitsFromISR(event_group_, AUDIO_OUTPUT_READY_EVENT, &higher_priority_task_woken);
        return higher_priority_task_woken == pdTRUE;
    });
    
    codec->Start();
    
    // 创建主循环任务
    xTaskCreate([](void* arg) {
        Application* app = (Application*)arg;
        app->MainLoop();
        vTaskDelete(NULL);
    }, "main_loop", 4096 * 2, this, 4, nullptr);
    
    // 启动网络
    board.StartNetwork();
    display->SetStatus(Lang::Strings::LOADING_PROTOCOL);
    
    // 初始化协议
    #ifdef CONFIG_CONNECTION_TYPE_WEBSOCKET
        protocol_ = std::make_unique<WebsocketProtocol>();
    #else
        protocol_ = std::make_unique<MqttProtocol>();
    #endif
    
    // 设置协议回调
    protocol_->OnNetworkError([this](conststd::string& message) {
        SetDeviceState(kDeviceStateIdle);
        Alert(Lang::Strings::ERROR, message.c_str(), "sad", Lang::Sounds::P3_EXCLAMATION);
    });
    
    protocol_->OnIncomingAudio([this](std::vector<uint8_t>&& data) {
        std::lock_guard<std::mutex> lock(mutex_);
        if (device_state_ == kDeviceStateSpeaking) {
            audio_decode_queue_.emplace_back(std::move(data));
        }
    });
    
    protocol_->Start();
    
    // 检查固件版本
    ota_.SetCheckVersionUrl(CONFIG_OTA_VERSION_URL);
    ota_.SetHeader("Device-Id", SystemInfo::GetMacAddress().c_str());
    ota_.SetHeader("Client-Id", board.GetUuid());
    ota_.SetHeader("Accept-Language", Lang::CODE);
    
    auto app_desc = esp_app_get_description();
    ota_.SetHeader("User-Agent", std::string(BOARD_NAME "/") + app_desc->version);
    
    xTaskCreate([](void* arg) {
        Application* app = (Application*)arg;
        app->CheckNewVersion();
        vTaskDelete(NULL);
    }, "check_new_version", 4096 * 2, this, 2, nullptr);
    
    // 初始化音频处理器(可选)
    #if CONFIG_USE_AUDIO_PROCESSOR
        audio_processor_.Initialize(codec->input_channels(), codec->input_reference());
        audio_processor_.OnOutput([this](std::vector<int16_t>&& data) {
            background_task_->Schedule([this, data = std::move(data)]() mutable {
                opus_encoder_->Encode(std::move(data), [this](std::vector<uint8_t>&& opus) {
                    Schedule([this, opus = std::move(opus)]() {
                        protocol_->SendAudio(opus);
                    });
                });
            });
        });
    #endif
    
    // 初始化唤醒词检测(可选)
    #if CONFIG_USE_WAKE_WORD_DETECT
        wake_word_detect_.Initialize(codec->input_channels(), codec->input_reference());
        wake_word_detect_.OnVadStateChange([this](bool speaking) {
            Schedule([this, speaking]() {
                if (device_state_ == kDeviceStateListening) {
                    if (speaking) {
                        voice_detected_ = true;
                    } else {
                        voice_detected_ = false;
                    }
                    auto led = Board::GetInstance().GetLed();
                    led->OnStateChanged();
                }
            });
        });
        
        wake_word_detect_.OnWakeWordDetected([this](conststd::string& wake_word) {
            Schedule([this, &wake_word]() {
                if (device_state_ == kDeviceStateIdle) {
                    // 处理唤醒词
                } elseif (device_state_ == kDeviceStateSpeaking) {
                    // 中止当前播放
                } elseif (device_state_ == kDeviceStateActivating) {
                    // 处理激活状态
                }
                wake_word_detect_.StartDetection();
            });
        });
        
        wake_word_detect_.StartDetection();
    #endif
    
    // 最终设置
    SetDeviceState(kDeviceStateIdle);
    esp_timer_start_periodic(clock_timer_handle_, 1000000);
}

步骤4:跟踪Board::GetInstance()

代码语言:javascript
复制
// main/boards/board.h
class Board {
public:
    static Board& GetInstance() {
        static Board* instance = static_cast<Board*>(create_board());
        return *instance;
    }
    
    virtual std::string GetBoardType() = 0;
    virtual AudioCodec* GetAudioCodec() = 0;
    virtual Display* GetDisplay() = 0;
    virtual Led* GetLed() = 0;
    virtual void StartNetwork() = 0;
};

步骤5:跟踪create_board()函数

代码语言:javascript
复制
// main/boards/bread-compact-wifi/board.cc
#define DECLARE_BOARD(BOARD_CLASS_NAME) \
void* create_board() { \
    return new BOARD_CLASS_NAME(); \
}

DECLARE_BOARD(BreadCompactWifiBoard)
实验总结

程序启动流程

代码语言:javascript
复制
程序启动流程:
1. app_main() 入口函数
2. 初始化事件循环
3. 初始化NVS flash
4. 调用 Application::GetInstance().Start()
5. 获取 Board 实例
6. 设置设备状态
7. 初始化音频编解码器
8. 创建主循环任务
9. 启动网络连接
10. 初始化协议
11. 检查固件版本
12. 初始化音频处理器
13. 初始化唤醒词检测
14. 设置最终状态

关键调用链

代码语言:javascript
复制
app_main()
└── Application::GetInstance().Start()
    ├── Board::GetInstance()
    ├── SetDeviceState()
    ├── 音频编解码器初始化
    ├── 主循环任务创建
    ├── 网络启动
    ├── 协议初始化
    ├── 固件版本检查
    ├── 音频处理器初始化
    └── 唤醒词检测初始化

🎯 本周学习总结

掌握内容

  1. ✅ 小智AI项目代码目录结构
  2. ✅ Application、Board、ThingManager三大类的作用
  3. ✅ 通过类图理解架构设计
  4. ✅ 分层架构和依赖关系
  5. ✅ main.cc入口函数跟踪

关键知识点

  • 代码结构:清晰的目录组织和文件分类
  • 三大核心类:Application(应用管理)、Board(硬件抽象)、ThingManager(设备管理)
  • 架构设计:分层架构、依赖关系、职责分离
  • 程序启动:从main.cc到Application::Start()的完整流程

📚 课后作业

基础作业

  1. 熟悉项目代码目录结构
  2. 理解三大核心类的作用
  3. 完成main.cc入口函数跟踪
  4. 绘制系统架构图

进阶作业

  1. 深入阅读Application::Start()方法
  2. 理解Board类的设计模式
  3. 分析ThingManager类的实现
  4. 研究类之间的依赖关系

学习资源

  • UML类图教程[1]
  • 设计模式[2]
  • 架构设计原则[3]

参考资料

[1]

UML类图教程: https://www.uml.org.cn/oobject/201211231.asp

[2]

设计模式: https://refactoring.guru/design-patterns

[3]

架构设计原则: https://martinfowler.com/architecture/

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-10-16,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 网罗开发 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 📚 本周学习目标
  • 🎯 工程结构与类图解析
    • 1. 代码目录介绍
      • 项目整体结构
      • main目录详细结构
      • 关键文件说明
    • 2. Application、Board、ThingManager三大类的作用
      • Application类 - 应用核心管理
      • Board类 - 硬件抽象层
      • ThingManager类 - 物联网设备管理
    • 3. 如何通过类图理解架构
      • 系统架构图
      • 类关系图
      • 分层架构设计
    • 4. 上机练习:找到main.cc的入口函数,并跟踪到Application::Start()
      • 实验目标
      • 实验步骤
      • 实验总结
  • 🎯 本周学习总结
    • 掌握内容
    • 关键知识点
  • 📚 课后作业
    • 基础作业
    • 进阶作业
    • 学习资源
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档