【花雕动手做】迷你小龙虾 MimiClaw 主程序 mimi 改进与升级方案:从即时优化到架构演进
ESP32‑S3 智能体 MimiClaw 的主入口如下:
/**
* @file mimi.c 《驴友花雕二次开发版本》
* @brief MimiClaw 主程序入口 - ESP32-S3 AI Agent(WS2812 + 双电机差速)
*/
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "esp_event.h"
#include "esp_system.h"
#include "esp_heap_caps.h"
#include "esp_spiffs.h"
#include "nvs_flash.h"
#include "mimi_config.h"
#include "bus/message_bus.h"
#include "wifi/wifi_manager.h"
#include "channels/telegram/telegram_bot.h"
#include "channels/feishu/feishu_bot.h"
#include "llm/llm_proxy.h"
#include "agent/agent_loop.h"
#include "memory/memory_store.h"
#include "memory/session_mgr.h"
#include "gateway/ws_server.h"
#include "cli/serial_cli.h"
#include "proxy/http_proxy.h"
#include "tools/tool_registry.h"
#include "cron/cron_service.h"
#include "heartbeat/heartbeat.h"
#include "skills/skill_loader.h"
#include "onboard/wifi_onboard.h"
#include "tools/tool_ws2812.h"
#include "tools/tool_motor.h"
static const char *TAG = "mimi";
static esp_err_t init_nvs(void) {
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, "NVS erased");
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
return ret;
}
static esp_err_t init_spiffs(void) {
esp_vfs_spiffs_conf_t conf = {
.base_path = MIMI_SPIFFS_BASE,
.max_files = 10,
.format_if_mount_failed = true,
};
esp_err_t ret = esp_vfs_spiffs_register(&conf);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "SPIFFS mount failed");
return ret;
}
size_t total = 0, used = 0;
esp_spiffs_info(NULL, &total, &used);
ESP_LOGI(TAG, "SPIFFS: total=%d, used=%d", (int)total, (int)used);
return ESP_OK;
}
static void outbound_dispatch_task(void *arg) {
ESP_LOGI(TAG, "Outbound dispatch started");
while (1) {
mimi_msg_t msg;
if (message_bus_pop_outbound(&msg, UINT32_MAX) != ESP_OK) continue;
if (strcmp(msg.channel, MIMI_CHAN_FEISHU) == 0) {
feishu_send_message(msg.chat_id, msg.content);
} else if (strcmp(msg.channel, MIMI_CHAN_TELEGRAM) == 0) {
telegram_send_message(msg.chat_id, msg.content);
} else if (strcmp(msg.channel, MIMI_CHAN_WEBSOCKET) == 0) {
ws_server_send(msg.chat_id, msg.content);
}
free(msg.content);
}
}
void app_main(void) {
ESP_LOGI(TAG, "========================================");
ESP_LOGI(TAG, " MimiClaw - ESP32-S3 AI Agent");
ESP_LOGI(TAG, "========================================");
ESP_LOGI(TAG, "Internal free: %d bytes", (int)heap_caps_get_free_size(MALLOC_CAP_INTERNAL));
ESP_LOGI(TAG, "PSRAM free: %d bytes", (int)heap_caps_get_free_size(MALLOC_CAP_SPIRAM));
// 基础系统
ESP_ERROR_CHECK(init_nvs());
ESP_ERROR_CHECK(esp_event_loop_create_default());
ESP_ERROR_CHECK(init_spiffs());
// 核心子系统
ESP_ERROR_CHECK(message_bus_init());
ESP_ERROR_CHECK(memory_store_init());
ESP_ERROR_CHECK(skill_loader_init());
ESP_ERROR_CHECK(session_mgr_init());
ESP_ERROR_CHECK(wifi_manager_init());
ESP_ERROR_CHECK(http_proxy_init());
ESP_ERROR_CHECK(telegram_bot_init());
ESP_ERROR_CHECK(feishu_bot_init());
ESP_ERROR_CHECK(llm_proxy_init());
ESP_ERROR_CHECK(tool_registry_init());
ESP_ERROR_CHECK(cron_service_init());
ESP_ERROR_CHECK(heartbeat_init());
ESP_ERROR_CHECK(agent_loop_init());
// 可选:显式初始化 LED 和电机
if (tool_ws2812_init() == ESP_OK) {
ESP_LOGI(TAG, "WS2812 initialized (9 LEDs on GPIO48)");
}
if (tool_motor_init() == ESP_OK) {
ESP_LOGI(TAG, "Motor driver initialized (2 motors, differential drive)");
}
// 串口命令行
ESP_ERROR_CHECK(serial_cli_init());
ESP_LOGI(TAG, "Serial CLI started");
// WiFi 连接
if (wifi_manager_start() == ESP_OK) {
wifi_manager_scan_and_print();
if (wifi_manager_wait_connected(30000) == ESP_OK) {
ESP_LOGI(TAG, "WiFi connected: %s", wifi_manager_get_ip());
} else {
ESP_LOGW(TAG, "WiFi timeout");
}
} else {
ESP_LOGW(TAG, "No WiFi credentials, enter onboarding");
wifi_onboard_start(WIFI_ONBOARD_MODE_CAPTIVE);
return;
}
wifi_onboard_start(WIFI_ONBOARD_MODE_ADMIN);
// 启动网络服务
xTaskCreatePinnedToCore(outbound_dispatch_task, "outbound", 16384, NULL, 5, NULL, 1);
agent_loop_start();
feishu_bot_start();
ws_server_start();
ESP_LOGI(TAG, "All services started! MimiClaw ready.");
// 硬编码电机测试(等待系统稳定)
vTaskDelay(pdMS_TO_TICKS(5000));
ESP_LOGI(TAG, ">>> Testing left motor forward 2 seconds <<<");
motor_control(0, 1);
vTaskDelay(pdMS_TO_TICKS(2000));
motor_control(0, 0);
vTaskDelay(pdMS_TO_TICKS(1000));
ESP_LOGI(TAG, ">>> Testing right motor forward 2 seconds <<<");
motor_control(1, 1);
vTaskDelay(pdMS_TO_TICKS(2000));
motor_control(1, 0);
ESP_LOGI(TAG, ">>> Motor test finished <<<");
}
/* FreeRTOS 堆栈溢出钩子函数 */
void vApplicationStackOverflowHook(TaskHandle_t xTask, char *pcTaskName) {
ESP_EARLY_LOGE(TAG, "!!! Stack overflow in task: %s !!!", pcTaskName ? pcTaskName : "unknown");
while (1);
}
## 一、立即改进(无需大规模重构)
1. 启动流程健壮性增强

2. 任务优先级与堆栈大小规范

3. 内存监控增强
app_main 开头打印了总空闲内存,但后续未跟踪碎片化。建议:
每 60 秒通过心跳任务打印一次 heap_caps_get_free_size(MALLOC_CAP_INTERNAL) 与 MALLOC_CAP_SPIRAM,用于远程监控。
引入 ESP Insights 或自定义内存水位告警。
4. 日志系统优化
使用 日志分级过滤:将 ESP_LOGI 改为 ESP_LOGD 以降低生产环境串口输出。
增加日志转发:将重要日志通过 message_bus 发送到管理员会话(便于远程调试)。
5. 模块初始化顺序显式化
当前初始化顺序虽合理,但缺少失败回滚。建议:
使用 状态机枚举 记录初始化阶段,便于定位重启原因。
为每个 *_init() 增加反初始化函数,在严重错误时可尝试优雅停机。
## 二、架构升级方案(中长期演进)
1. 启动配置中心化
现状:所有初始化参数散落在各组件头文件或 menuconfig 中,调整需重编译。
升级方案:引入JSON 配置文件(存于 SPIFFS 或 LittleFS),在 app_main 早期加载:
typedef struct {
struct {
bool enable_ws2812;
bool enable_motor;
} hardware;
struct {
int outbound_stack_size;
int agent_priority;
} tasks;
} app_config_t;好处:同一固件可适配不同硬件变体(有无电机、LED),现场调试无需重刷固件。
2. 引入统一电源管理任务
电机与 WS2812 是耗电大户,当前无节电策略。升级方案:
创建 power_manager 任务,监听 MESSAGE_TYPE_IDLE 事件。
空闲超过 30 秒自动关闭 LED、使电机驱动进入低功耗模式。
3. 双核任务绑定优化
ESP32‑S3 为双核,当前仅将 outbound_dispatch_task 固定到 Core 1。建议:
Core 0:协议栈(PRO CPU)——处理 WiFi、LWIP、HTTP 服务。
Core 1:应用(APP CPU)——运行 Agent 推理、电机控制、LED 动画。
在 sdkconfig 中启用 CONFIG_FREERTOS_UNICORE=n,并通过 xTaskCreatePinnedToCore 显式分配。
4. 安全启动与固件 OTA 支持
当前缺失 OTA 能力,无法远程升级。应在 app_main 中增加:
#if CONFIG_MIMI_OTA_ENABLED
xTaskCreate(ota_monitor_task, "ota", 8192, NULL, 1, NULL);
#endif5. 外设自检与看门狗集成
添加硬件自检阶段(LED、电机、IMU 等),结果上报云端。
启用 任务看门狗 (TWDT) 监控 agent_loop_task、outbound_dispatch_task,防止死锁导致整机失联。
6. 通信总线抽象
outbound_dispatch_task 中硬编码了 if (strcmp) 分支,扩展新通道(如 WhatsApp、Discord)需修改此处。升级为注册回调机制:
typedef void (*outbound_handler_t)(const char *chat_id, const char *content);
void register_outbound_channel(const char *name, outbound_handler_t handler);## 三、具体代码修改示例(部分关键改进)
改进 app_main 中的 WiFi 配网逻辑
// 原代码
wifi_onboard_start(WIFI_ONBOARD_MODE_ADMIN); // 总是启动
// 改进后
if (wifi_manager_get_state() != WIFI_STATE_CONNECTED) {
wifi_onboard_start(WIFI_ONBOARD_MODE_CAPTIVE);
// 等待配网完成(阻塞或事件驱动)
} else {
// 仅当用户通过 CLI 请求时才启动 ADMIN 模式
ESP_LOGI(TAG, "WiFi already connected, admin onboard skipped");
}移除电机硬编码测试
// 使用条件编译
#if CONFIG_MIMI_FACTORY_TEST_ENABLE
vTaskDelay(pdMS_TO_TICKS(5000));
run_factory_motor_test();
#endif增加模块初始化失败的回滚处理
#define CHECK_INIT(expr, label) do { \
if ((expr) != ESP_OK) { \
ESP_LOGE(TAG, "Init failed at %s", #expr); \
goto label; \
} \
} while(0)
// 在 app_main 中使用
if (0) {
cleanup_memory:
memory_store_deinit();
cleanup_bus:
message_bus_deinit();
}## 四、升级后启动时序图(推荐流程)

## 五、总结
上述改进与升级方案覆盖了稳定性、可维护性、扩展性、功耗与安全五个维度。短期内可先实施第一部分“立即改进”以提升现场部署可靠性;中长期建议按架构升级路径迭代,最终使 MimiClaw 成为一个模块化、可配置、支持 OTA 的生产级 AI Agent 固件。





