Some checks failed
Build / idf-build (push) Has been cancelled
- 创建 main/Kconfig.projbuild 声明所有自定义模块配置项 - 修复 fullclean 后 sdkconfig 丢失 CONFIG_MIMI_* 配置的问题 - 更新 AGENTS.md 添加认知修正栏目和模块开关文档 - 记录 Kconfig 踩坑讨论到 taolun.md
533 lines
21 KiB
Markdown
533 lines
21 KiB
Markdown
# 讨论记录
|
||
|
||
---
|
||
|
||
## 实施记录:时间同步 + NVS 稳定性 + LLM Provider Bug 修复
|
||
|
||
**日期**:2026-04-01
|
||
**分支**:`feature/time-sync-nvs-stability`
|
||
**状态**:已实现,待烧录验证
|
||
|
||
### 修复的核心 Bug
|
||
|
||
#### Bug 1: `llm_provider_init()` 无法从 NVS 加载 provider-specific API key
|
||
|
||
**文件**:`main/llm/llm_provider.c`
|
||
|
||
**问题**:`llm_provider_init()` 调用 `llm_provider_get_api_key(s_current_provider->name)` 加载当前 provider 的 key,但该函数对当前 provider 直接返回内存中的 `s_api_key`(初始为空字符串),导致 NVS 中的 `siliconflow_api_key`、`volcengine_api_key` 等永远不会被加载到内存。`s_base_url` 同理。
|
||
|
||
**修复**:改为直接通过 `get_provider_api_key_nvs_key()` 获取 NVS key 名,然后 `nvs_get_str()` 直接读取,绕过内存缓存。
|
||
|
||
#### Bug 2: 时间显示 1970
|
||
|
||
**文件**:新增 `main/time_sync/`
|
||
|
||
**问题**:ESP32 上电后 RTC 从 0 开始,没有 SNTP 客户端自动同步时间。
|
||
|
||
**修复**:新建 `time_sync` 模块,WiFi 连接后自动启动 SNTP,从 `ntp.ntsc.ac.cn` 同步时间。
|
||
|
||
#### Bug 3: Brownout 导致 NVS 损坏
|
||
|
||
**文件**:新增 `main/nvs_safety/`,修改 `sdkconfig.defaults`
|
||
|
||
**问题**:不同 USB 口供电能力不同,WiFi 峰值电流 300-500mA,供电不足时 Flash 写入中断导致 NVS 损坏。
|
||
|
||
**修复**:启用 Brownout Detection + 启动时 NVS 完整性校验与自动修复。
|
||
|
||
### 新增 CLI 命令
|
||
|
||
| 命令 | 功能 |
|
||
|------|------|
|
||
| `ntp_status` | 查看完整状态(时区 + 本地时间 + 同步状态 + NTP 服务器 + 上次同步时间) |
|
||
| `ntp_sync` | 立即手动同步一次 |
|
||
| `ntp_set <server>` | 设置 NTP 服务器(存 NVS,重启后生效) |
|
||
|
||
### 实施过程中的潜在问题与修复
|
||
|
||
#### 问题 1: `tool_set_timezone.c` 缺少 `<strings.h>`
|
||
|
||
**发现**:`strcasecmp()` 和 `strcasestr()` 在 POSIX 标准中定义在 `<strings.h>` 而非 `<string.h>` 中。某些编译器会报错。
|
||
|
||
**修复**:添加 `#include <strings.h>`。
|
||
|
||
#### 问题 2: `parse_and_set_time_from_date()` 中多余的 `tzset()` 调用
|
||
|
||
**发现**:在 `mktime()` 之后又调用了 `setenv("TZ", "UTC0", 1); tzset();`,这会把时区重新设为 UTC,导致后续 `localtime_r()` 返回 UTC 时间而非用户设置的时区时间。
|
||
|
||
**修复**:移除 `mktime()` 后的 `setenv`/`tzset()` 调用。时区恢复由调用方(`tool_set_timezone_execute`)在设置完系统时钟后通过 `setenv("TZ", resolved_tz, 1); tzset()` 处理。
|
||
|
||
#### 问题 3: `serial_cli.c` 中 `vTaskDelay` 的 FreeRTOS 头文件
|
||
|
||
**确认**:`freertos/task.h` 已在文件顶部包含(第 32 行),无需额外添加。
|
||
|
||
#### 问题 4: `time_sync.c` 中 `sntp_stop()` API 兼容性
|
||
|
||
**确认**:`sntp_stop()` 是 ESP-IDF 标准 SNTP API,在 `esp_sntp.h` 中定义,v5.x 和 v6.x 均支持。
|
||
|
||
#### 问题 5: `nvs_safety.c` 中 NVS 迭代器 API
|
||
|
||
**确认**:`nvs_iterator_t`、`nvs_entry_find()`、`nvs_entry_info()`、`nvs_entry_next()` 是 ESP-IDF 标准 NVS API,在 `nvs.h` 中定义。
|
||
|
||
### 最终文件变更清单
|
||
|
||
| 文件 | 操作 | 说明 |
|
||
|------|------|------|
|
||
| `main/llm/llm_provider.c` | **修复** | 修复 `llm_provider_init()` 加载逻辑 |
|
||
| `main/time_sync/time_sync.h` | 新建 | SNTP 时间同步头文件 |
|
||
| `main/time_sync/time_sync.c` | 新建 | SNTP 时间同步实现 |
|
||
| `main/nvs_safety/nvs_safety.h` | 新建 | NVS 完整性校验头文件 |
|
||
| `main/nvs_safety/nvs_safety.c` | 新建 | NVS 损坏检测与修复 |
|
||
| `main/mimi.c` | 修改 | WiFi 连接后调用 `time_sync_init()` + 启动时调用 `nvs_safety_check()` |
|
||
| `main/cli/serial_cli.c` | 修改 | `timezone_show` 改为 `ntp_status`,新增 `ntp_sync`、`ntp_set` 命令 |
|
||
| `main/tools/tool_set_timezone.c` | 修改 | 添加 `<strings.h>`,设置时区后检测时间有效性 |
|
||
| `main/mimi_config.h` | 修改 | 添加 `MIMI_NVS_KEY_NTP_SERVER` 和 `MIMI_DEFAULT_NTP_SERVER` |
|
||
| `main/CMakeLists.txt` | 修改 | 添加新源文件 |
|
||
| `sdkconfig.defaults` | 修改 | 启用 Brownout Detection + SNTP |
|
||
|
||
### 认知修复(编译错误避坑指南)
|
||
|
||
#### ESP-IDF v6.0 API 迁移要点
|
||
1. **SNTP API**:`sntp_*` → `esp_sntp_*`,组件属于 `lwip` 而非独立组件
|
||
2. **NVS API**:迭代器操作需要指针参数,记得释放迭代器
|
||
3. **类型系统**:`arg_str1` 是函数,结构体成员用 `struct arg_str`
|
||
4. **组件依赖**:v6.0 中 `esp_sntp` 组件不存在,无需添加
|
||
|
||
#### 常见错误模式
|
||
- **结构体类型错误**:混淆函数名和类型名
|
||
- **API 签名变化**:参数数量或类型改变
|
||
- **组件重组**:功能移动到其他组件
|
||
- **未使用代码**:及时清理未使用的函数和变量
|
||
|
||
#### 预防措施
|
||
1. 使用 `#ifdef` 保护平台相关常量(如 `WIFI_REASON_*`)
|
||
2. 检查函数返回值,特别是迭代器操作
|
||
3. 定期清理未使用的代码
|
||
4. 参考官方迁移指南和头文件声明
|
||
|
||
---
|
||
|
||
## 编译错误修复速查表
|
||
|
||
| 问题类型 | 错误示例 | 修复方案 | 涉及文件 |
|
||
|----------|----------|----------|----------|
|
||
| 结构体类型 | `arg_str1 *server` | 使用 `struct arg_str *server` | `serial_cli.c` |
|
||
| SNTP 弃用 | `sntp_init()` | 改用 `esp_sntp_init()` | `time_sync.c` |
|
||
| NVS 参数 | `nvs_entry_find(3个参数)` | 添加第4个参数 `&it` | `nvs_safety.c` |
|
||
| 未使用函数 | `provider_is_openai` | 删除函数定义 | `llm_proxy.c` |
|
||
| 组件依赖 | `esp_sntp` 组件不存在 | 移除依赖声明 | `CMakeLists.txt` |
|
||
| WiFi 常量 | `WIFI_REASON_ASSOC_EXPIRE` | 添加 `#ifdef` 保护 | `wifi_manager.c` |
|
||
|
||
---
|
||
|
||
## 讨论:系统时间同步 + NVS 配置稳定性修复
|
||
|
||
**日期**:2026-04-01
|
||
**目标**:修复两个关键问题 — 时间显示 1970、换 USB 口后配置不生效
|
||
|
||
### 问题 1:时间显示 1970-01-01
|
||
|
||
**现象**:
|
||
```
|
||
Current timezone: Asia/Shanghai [NVS]
|
||
Local time: 1970-01-01 00:00:23 GMT (Thursday)
|
||
```
|
||
|
||
**根因**:代码中没有初始化 SNTP/NTP 客户端。ESP32 上电后 RTC 时钟从 0 开始计时,`time(NULL)` 返回的就是 1970 年以来的秒数。目前只有 LLM 调用 `get_current_time` 工具时才会通过 HTTP 同步时间。
|
||
|
||
**修复方案**:
|
||
- 新建 `main/time_sync/time_sync.c`,使用 ESP-IDF 内置 SNTP 组件
|
||
- WiFi 连接成功后自动从 `ntp.ntsc.ac.cn` 同步时间
|
||
- 同步成功后自动应用已保存的时区配置
|
||
- `ntp_status` 命令显示完整时间状态(时区、本地时间、同步状态、NTP 服务器、上次同步时间)
|
||
|
||
### 问题 2:换 USB 口/电脑后不工作 + 模型配置不加载
|
||
|
||
**现象**:
|
||
- 插入其他 Type-C 口或电脑,设备不正常工作
|
||
- 需要在 Web 界面重新填写大模型 ID 和密钥,保存重启后才正常
|
||
- 命令行中模型状态显示异常
|
||
|
||
**根因分析**:
|
||
|
||
#### 2.1 LLM Provider 初始化 Bug(核心问题)
|
||
|
||
在 `llm_provider.c:260-284` 中,`llm_provider_init()` 通过 `llm_provider_get_api_key()` 加载当前 provider 的 API key:
|
||
|
||
```c
|
||
void llm_provider_init(void) {
|
||
const char *api_key = llm_provider_get_api_key(s_current_provider->name);
|
||
if (api_key) {
|
||
strncpy(s_api_key, api_key, sizeof(s_api_key) - 1);
|
||
}
|
||
```
|
||
|
||
但 `llm_provider_get_api_key()` 的逻辑是(`llm_provider.c:209-214`):
|
||
|
||
```c
|
||
const char *llm_provider_get_api_key(const char *provider_name) {
|
||
if (strcmp(provider_name, s_current_provider->name) == 0) {
|
||
return s_api_key; // 直接返回内存中的值,不从 NVS 读!
|
||
}
|
||
// 否则才从 NVS 加载...
|
||
```
|
||
|
||
**形成死循环**:`llm_provider_init()` 想从 NVS 加载 key → 调用 `llm_provider_get_api_key` → 发现是当前 provider → 直接返回内存中的 `s_api_key`(初始为空)→ 把空值复制给自己 → **NVS 中的 provider-specific key(如 `siliconflow_api_key`)永远不会被加载到内存**。
|
||
|
||
同理,`s_base_url` 也存在相同问题。
|
||
|
||
**为什么 Web 界面保存后能正常工作?**
|
||
|
||
Web 界面的 `/save` 处理函数(`wifi_onboard.c:377-408`)会**同时保存两份**:
|
||
```c
|
||
nvs_sync_field(root, "api_key", MIMI_NVS_LLM, MIMI_NVS_KEY_API_KEY); // 通用 key
|
||
nvs_sync_field(root, "api_key", MIMI_NVS_LLM, api_key_nvs); // provider-specific key
|
||
```
|
||
|
||
而 `llm_proxy_init()` 能正确加载 `MIMI_NVS_KEY_API_KEY`(通用 key),所以 Web 保存后能用。但如果只通过 CLI 的 `set_siliconflow_key` 设置(只保存到 `siliconflow_api_key`),上电后就不会被加载。
|
||
|
||
#### 2.2 Brownout(欠压)导致 NVS 损坏
|
||
|
||
ESP32-S3 开启 WiFi 时峰值电流可达 300-500mA,不同 USB 端口的供电能力差异很大。供电不足时:
|
||
- 可能导致静默重启或 Flash 写入中断
|
||
- `nvs_commit` 过程中断电会导致 NVS 数据损坏
|
||
- WiFi 不稳定但日志看起来"正常"
|
||
|
||
**修复方案**:
|
||
1. 修复 `llm_provider_init()` — 直接从 NVS 读取当前 provider 的 key 和 Base URL,绕过 `llm_provider_get_api_key` 的内存缓存
|
||
2. 新建 `main/nvs_safety/nvs_safety.c` — 启动时校验关键 NVS 命名空间的完整性,检测并修复损坏条目
|
||
3. 在 `sdkconfig.defaults` 中启用 ESP32-S3 的 Brownout Detection
|
||
|
||
### 改动清单
|
||
|
||
| 文件 | 操作 | 说明 |
|
||
|------|------|------|
|
||
| `main/llm/llm_provider.c` | **修复** | 修复 `llm_provider_init()` 加载逻辑 |
|
||
| `main/time_sync/time_sync.h` | 新建 | SNTP 时间同步头文件 |
|
||
| `main/time_sync/time_sync.c` | 新建 | SNTP 时间同步实现 |
|
||
| `main/nvs_safety/nvs_safety.h` | 新建 | NVS 完整性校验头文件 |
|
||
| `main/nvs_safety/nvs_safety.c` | 新建 | NVS 损坏检测与修复 |
|
||
| `main/mimi.c` | 修改 | WiFi 连接后调用 `time_sync_init()` + 启动时调用 `nvs_safety_check()` |
|
||
| `main/cli/serial_cli.c` | 修改 | `timezone_show` 增加 SNTP 同步状态 |
|
||
| `main/CMakeLists.txt` | 修改 | 添加新源文件和 `esp_netif` 依赖 |
|
||
| `sdkconfig.defaults` | 修改 | 启用 Brownout Detection |
|
||
|
||
### 问题清单
|
||
|
||
#### 1. Flash 大小配置错误
|
||
- **错误**:分区表需要 16MB,但 sdkconfig 配置为 2MB
|
||
- **修复**:`sdkconfig` 中 `CONFIG_ESPTOOLPY_FLASHSIZE` 改为 16MB
|
||
|
||
#### 2. WiFi 断开原因码未定义
|
||
- **错误**:`WIFI_REASON_ASSOC_EXPIRE` 等符号在 v6.0 中未定义
|
||
- **修复**:`wifi_manager.c` 中所有 reason code 添加 `#ifdef` 保护
|
||
|
||
#### 3. CMakeLists.txt 缺少源文件
|
||
- **错误**:`llm_provider.c` 未加入编译列表,导致链接错误
|
||
- **修复**:添加 `ota/ota_manager.c` 到 SRCS
|
||
|
||
#### 4. 头文件缺失(共 16 处)
|
||
| 文件 | 缺失头文件 | 原因 |
|
||
|------|-----------|------|
|
||
| `cli/serial_cli.c` | `llm/llm_provider.h` | `llm_provider_set_api_key` |
|
||
| `llm/llm_provider.c` | `esp_http_client.h` | `esp_http_client_set_header` |
|
||
| `bus/message_bus.c` | `freertos/FreeRTOS.h`, `freertos/queue.h` | `xQueueCreate`, `QueueHandle_t` |
|
||
| `wifi/wifi_manager.c` | `esp_event.h` | `esp_event_handler_instance_register` |
|
||
| `wifi/wifi_manager.c` | `freertos/FreeRTOS.h`, `freertos/task.h`, `freertos/event_groups.h` | `xEventGroupCreate`, `vTaskDelay` |
|
||
| `ota/ota_manager.c` | `esp_system.h` | `esp_restart` |
|
||
| `channels/telegram/telegram_bot.c` | `freertos/FreeRTOS.h`, `freertos/task.h` | `xTaskCreatePinnedToCore`, `vTaskDelay` |
|
||
| `tools/tool_registry.c` | `<stdlib.h>` | `free()` |
|
||
| `proxy/http_proxy.c` | `<sys/time.h>` | `struct timeval` |
|
||
| `gateway/ws_server.c` | `<stdint.h>` | `uint8_t` |
|
||
|
||
### ESP-IDF v6.0 API 兼容性验证
|
||
|
||
以下 API 在 v6.0 中**仍然存在**,无需修改:
|
||
- `esp_spiffs_info()` ✅
|
||
- `esp_websocket_client_send_bin()` ✅
|
||
- `esp_tls_set_conn_sockfd()` / `esp_tls_set_conn_state()` ✅
|
||
- `esp_console_new_repl_uart()` / `esp_console_new_repl_usb_serial_jtag()` ✅
|
||
- `esp_https_ota()` + `esp_https_ota_config_t` ✅
|
||
- `esp_wifi_set_config()` ✅
|
||
|
||
### 烧录说明
|
||
|
||
ESP32-S3 使用 **USB 口**(内置 USB Serial/JTAG 控制器)烧录:
|
||
```powershell
|
||
idf.py -p COMx flash monitor
|
||
```
|
||
- 插 USB 口(标记为 `USB`),不是 UART 口
|
||
- 如遇连接失败,按住 BOOT 键再插线进入下载模式
|
||
|
||
---
|
||
|
||
## 讨论:增加国内大模型厂商接入
|
||
|
||
**日期**:2026-03-31
|
||
**目标**:为 MimiClaw 增加硅基流动和火山方舟(豆包模型)接入
|
||
|
||
### 项目现状
|
||
- 当前支持:Anthropic (Claude)、OpenAI (GPT)
|
||
- 运行平台:ESP32-S3,纯 C 语言
|
||
- 交互方式:Telegram 机器人
|
||
|
||
### 国内厂商 API 兼容性
|
||
- **硅基流动**:OpenAI 兼容,Base URL `https://api.siliconflow.cn/v1`
|
||
- **火山方舟**:OpenAI 兼容,Base URL `https://ark.cn-beijing.volces.com/api/v3`
|
||
|
||
### 实现方案
|
||
由于两者都提供 OpenAI 兼容 API,可复用现有 OpenAI 集成代码,只需:
|
||
1. 修改 Base URL
|
||
2. 调整认证方式(Bearer Token)
|
||
3. 处理模型名称规范
|
||
|
||
### 待解决问题
|
||
1. 认证方式差异确认
|
||
2. 模型名称规范
|
||
3. 工具调用格式兼容性验证
|
||
|
||
---
|
||
|
||
## 讨论:NTP 时间管理 CLI 命令
|
||
|
||
**日期**:2026-04-01
|
||
**目标**:完善时间管理 CLI,支持手动同步、状态查看、自定义 NTP 服务器
|
||
|
||
### 背景
|
||
- 初始方案只有 `timezone_show` 显示时区和时间,缺少 NTP 同步状态和手动同步能力
|
||
- 用户需要能手动触发时间同步、查看上次同步时间、自定义 NTP 服务器
|
||
|
||
### 命令设计
|
||
|
||
| 命令 | 功能 |
|
||
|------|------|
|
||
| `ntp_status` | 查看完整状态(时区 + 本地时间 + 同步状态 + NTP 服务器 + 上次同步时间) |
|
||
| `ntp_sync` | 立即手动同步一次 |
|
||
| `ntp_set <server>` | 设置 NTP 服务器(存 NVS,重启后生效) |
|
||
|
||
- 默认 NTP 服务器:`ntp.ntsc.ac.cn`(中国科学院国家授时中心)
|
||
- 同步状态:`synced`(已同步)、`syncing`(同步中)、`not_synced`(未同步)
|
||
|
||
### `ntp_status` 输出示例
|
||
```
|
||
Current timezone: Asia/Shanghai [NVS]
|
||
Local time: 2026-04-01 18:30:45 CST (Wednesday)
|
||
Time sync: synced
|
||
NTP server: ntp.ntsc.ac.cn
|
||
Last synced: 2026-04-01 18:00:12
|
||
```
|
||
|
||
### `set_timezone` 联动更新
|
||
- 设置时区后,如果检测到系统时间仍为 1970(未同步),自动触发一次 HTTP 时间获取
|
||
- 确保用户设置时区后立刻看到正确的本地时间
|
||
|
||
### 改动文件
|
||
| 文件 | 操作 |
|
||
|------|------|
|
||
| `main/time_sync/time_sync.h` | 增加 `time_sync_get_last_synced()` 和 `time_sync_set_server()` |
|
||
| `main/time_sync/time_sync.c` | 记录上次同步时间戳,支持自定义 NTP 服务器 |
|
||
| `main/cli/serial_cli.c` | 将 `timezone_show` 改为 `ntp_status`,新增 `ntp_sync`、`ntp_set` 命令 |
|
||
| `main/tools/tool_set_timezone.c` | 设置时区后检测时间有效性,必要时触发 HTTP 时间同步 |
|
||
|
||
---
|
||
|
||
## 讨论:时区设置功能
|
||
|
||
**日期**:2026-04-01
|
||
**目标**:为 MimiClaw 添加可配置的时区支持,默认改为中国时区
|
||
|
||
### 背景
|
||
- 原默认时区为 `PST8PDT,M3.2.0,M11.1.0`(太平洋时间)
|
||
- 需要支持用户自定义时区,特别是中国用户(UTC+8)
|
||
- 交互方式从 Telegram 改为飞书
|
||
|
||
### 实现方案
|
||
|
||
#### 存储方式
|
||
- **NVS 存储**:使用 `system_config` namespace,key 为 `timezone`
|
||
- **Build-time 默认值**:`MIMI_TIMEZONE` 改为 `"CST-8"`
|
||
- **优先级**:NVS 值 > Build-time 值
|
||
|
||
#### CLI 命令
|
||
```
|
||
set_timezone <TZ> # 例如: set_timezone CST-8 或 set_timezone Asia/Shanghai
|
||
timezone_show # 显示当前时区配置和本地时间
|
||
```
|
||
|
||
#### LLM 工具
|
||
- 新增 `set_timezone` 工具,LLM 可通过对话设置时区
|
||
- 支持 POSIX 格式(`CST-8`)和城市名(`Asia/Shanghai`)
|
||
- 内置 18 个城市名映射表
|
||
|
||
### 改动文件
|
||
| 文件 | 操作 |
|
||
|------|------|
|
||
| `main/mimi_config.h` | 默认时区改为 `CST-8`,添加 `MIMI_NVS_KEY_TIMEZONE` |
|
||
| `main/tools/tool_set_timezone.h` | **新建** |
|
||
| `main/tools/tool_set_timezone.c` | **新建** |
|
||
| `main/tools/tool_registry.c` | include 新头文件 + 注册工具 |
|
||
| `main/cli/serial_cli.c` | 添加 `set_timezone` / `ntp_status` / `ntp_sync` / `ntp_set` 命令 |
|
||
| `main/CMakeLists.txt` | 添加 `tool_set_timezone.c` 到 SRCS |
|
||
|
||
### 支持的时区格式
|
||
- POSIX: `CST-8`, `JST-9`, `EST5EDT,M3.2.0,M11.1.0`, `UTC0`
|
||
- 城市名: Asia/Shanghai, Asia/Tokyo, America/New_York 等 18 个预设
|
||
|
||
---
|
||
|
||
## 讨论:编译时模块开关(模块化配置)
|
||
|
||
**日期**:2026-04-03
|
||
**分支**:`feature/module-config`
|
||
**目标**:通过编译时配置选择性禁用不需要的模块,减少固件体积,消除未配置模块的警告日志
|
||
|
||
### 问题背景
|
||
|
||
1. **Task Watchdog 超时**:ESP32-S3 运行一天后触发看门狗超时,怀疑是设计问题
|
||
2. **Telegram 警告日志**:用户使用飞书但未配置 Telegram,控制台每 5 秒打印 "No bot token configured"
|
||
3. **代码冗余**:Telegram、OpenAI 接口等未使用的模块仍然编译进固件
|
||
|
||
### 用户需求
|
||
|
||
用户希望:
|
||
1. 通过编译选项禁用不需要的模块(如 Telegram)
|
||
2. 从源码层面直接过滤不用的组件,减少代码体积
|
||
3. 禁用模块后不触发警告日志
|
||
|
||
### 实现方案
|
||
|
||
#### 方案选择:编译时条件编译(方案 02)
|
||
|
||
**优点**:
|
||
- 直接减少 Flash 占用
|
||
- 零 RAM 占用,不创建任务
|
||
- 从源头消除警告日志
|
||
- 实现简单
|
||
|
||
**缺点**:
|
||
- 切换模块需要重新编译
|
||
|
||
#### 技术实现
|
||
|
||
##### 1. 配置文件:`sdkconfig.defaults`
|
||
|
||
```c
|
||
// Channel Modules
|
||
CONFIG_MIMI_CHAN_TELEGRAM=n // 默认禁用
|
||
CONFIG_MIMI_CHAN_FEISHU=y // 默认启用
|
||
|
||
// Tool Modules
|
||
CONFIG_MIMI_TOOL_WEB_SEARCH=y
|
||
CONFIG_MIMI_TOOL_GPIO=n
|
||
|
||
// Optional Modules
|
||
CONFIG_MIMI_WS_SERVER=y
|
||
CONFIG_MIMI_WIFI_ONBOARD=y
|
||
CONFIG_MIMI_OTA=n
|
||
```
|
||
|
||
##### 2. CMakeLists.txt:条件编译源文件
|
||
|
||
```cmake
|
||
if(CONFIG_MIMI_CHAN_TELEGRAM)
|
||
list(APPEND srcs "channels/telegram/telegram_bot.c")
|
||
endif()
|
||
```
|
||
|
||
##### 3. 头文件 stub:调用方无感知
|
||
|
||
```c
|
||
// telegram_bot.h
|
||
#ifdef CONFIG_MIMI_CHAN_TELEGRAM
|
||
esp_err_t telegram_bot_init(void);
|
||
esp_err_t telegram_bot_start(void);
|
||
#else
|
||
static inline esp_err_t telegram_bot_init(void) { return ESP_OK; }
|
||
static inline esp_err_t telegram_bot_start(void) { return ESP_OK; }
|
||
#endif
|
||
```
|
||
|
||
### 修改文件清单
|
||
|
||
| 文件 | 修改内容 |
|
||
|------|----------|
|
||
| `sdkconfig.defaults` | 添加模块开关配置 |
|
||
| `main/CMakeLists.txt` | 条件编译源文件 |
|
||
| `main/channels/telegram/telegram_bot.h` | 添加 stub |
|
||
| `main/channels/feishu/feishu_bot.h` | 添加 stub |
|
||
| `main/gateway/ws_server.h` | 添加 stub |
|
||
| `main/onboard/wifi_onboard.h` | 添加 stub |
|
||
| `main/ota/ota_manager.h` | 添加 stub |
|
||
| `main/tools/tool_web_search.h` | 添加 stub |
|
||
| `main/tools/tool_gpio.h` | 添加 stub |
|
||
| `main/tools/tool_registry.c` | 条件注册工具 |
|
||
| `main/cli/serial_cli.c` | 条件注册 CLI 命令 |
|
||
| `main/mimi.c` | 条件调用模块 |
|
||
| `AGENTS.md` | 更新文档 |
|
||
|
||
### 模块依赖关系
|
||
|
||
- **Channel 模块**(telegram、feishu):
|
||
- 被 `mimi.c` 调用(init/start)
|
||
- 被 `outbound_dispatch_task` 调用(send)
|
||
- CLI 命令也需要条件编译
|
||
|
||
- **Tool 模块**(web_search、gpio):
|
||
- 被 `tool_registry.c` 调用(注册 + 执行)
|
||
- CLI 命令也需要条件编译
|
||
|
||
- **可选模块**(ws_server、wifi_onboard、ota):
|
||
- 被 `mimi.c` 调用
|
||
- `wifi_onboard` 禁用时需特殊处理(不能进入 captive portal)
|
||
|
||
### 注意事项
|
||
|
||
1. **OTA 模块**:目前未初始化,但已编译
|
||
2. **工具描述中的渠道提示**:cron_add 工具描述中提到 telegram,可保留(只是描述,不影响功能)
|
||
3. **get_time 工具**:使用 telegram.org 代理获取时间(技术原因,不属于 telegram_bot 模块)
|
||
|
||
### 预期效果
|
||
|
||
| 禁用项 | 效果 |
|
||
|--------|------|
|
||
| Telegram | 不编译 telegram_bot.c,无 5 秒警告,节省约 15KB+ Flash |
|
||
| GPIO | 不编译 tool_gpio.c,无 gpio_* 工具,节省约 5KB Flash |
|
||
| WebSocket | 不编译 ws_server.c,节省约 10KB Flash |
|
||
| WiFi Onboard | 不能进入 captive portal 模式,需其他方式配置 WiFi |
|
||
|
||
---
|
||
|
||
## 讨论:Kconfig 缺失导致模块开关失效
|
||
|
||
**日期**:2026-04-04
|
||
**问题**:编译后飞书命令消失、HTTP 配置页面不可用
|
||
|
||
### 现象
|
||
|
||
在 `sdkconfig.defaults` 中正确配置了 `CONFIG_MIMI_CHAN_FEISHU=y` 和 `CONFIG_MIMI_WIFI_ONBOARD=y`,执行 `idf.py fullclean && idf.py build` 后:
|
||
- 烧录后控制台没有飞书相关命令(`set_feishu_creds` 等)。
|
||
- 设备没有启动 HTTP 配置服务(`192.168.4.1` 无法访问)。
|
||
- 检查生成的 `sdkconfig` 文件,发现**完全没有**这些自定义配置项。
|
||
|
||
### 根因
|
||
|
||
ESP-IDF 的构建系统在生成 `sdkconfig` 时,**只会保留有 Kconfig 声明的配置项**。
|
||
|
||
- `sdkconfig.defaults` 仅用于提供默认值。
|
||
- 如果项目缺少 `Kconfig` 或 `Kconfig.projbuild` 文件来声明这些选项,ESP-IDF 会认为它们是无效配置并直接丢弃。
|
||
- 之前的版本可能 `sdkconfig` 是手动维护的或缓存未清理,但 `fullclean` 后重新生成时就会丢失这些"无名"配置。
|
||
|
||
### 修复方案
|
||
|
||
创建 `main/Kconfig.projbuild` 文件,声明所有自定义模块开关。
|
||
|
||
### 认知修正
|
||
|
||
**ESP-IDF 配置系统工作流**:
|
||
|
||
1. `Kconfig.projbuild`:**声明**配置项(告诉系统"这是什么")。
|
||
2. `sdkconfig.defaults`:提供**默认值**(告诉系统"默认选什么")。
|
||
3. `sdkconfig`:构建系统根据前两者**自动生成**(实际编译用的配置)。
|
||
4. `CMakeLists.txt`:读取 `sdkconfig` 中的值**决定编译哪些文件**。
|
||
|
||
**结论**:新增模块开关时,**必须**创建 Kconfig 声明,否则 `sdkconfig.defaults` 无效。
|