为什么有了 zclaw 和 MimiClaw,我还折腾了个 ESPClaw
先说结论:不是重复造轮子,是解决不同的问题。
三个项目的定位
| 项目 | 目标 | 约束 |
|---|---|---|
| zclaw | 888 KiB 极限体积 | 功能必须为体积让路 |
| MimiClaw | 开箱即用的个人助手 | 需要 S3 + PSRAM |
| ESPClaw | 可扩展的 IoT Agent 平台 | 无 PSRAM 也要能跑 |
zclaw 是在做减法艺术,MimiClaw 是在做产品体验。
ESPClaw 在做加法——加工具、加 HAL、加渠道——但有个硬约束:C3 上也能跑。
技术上为什么要做
1. 无 PSRAM 运行 LLM Agent
这是 ESPClaw 的核心工程挑战。
C3 只有 400KB SRAM,去掉 ESP-IDF 栈、FreeRTOS、TLS 缓冲,留给你用的也就 100KB 左右。而 LLM 的 JSON 响应动辄几 KB。
怎么解?
流式 JSON 解析。不缓存完整响应,边收边解析,用完即弃。
// 8KB 环形缓冲区
#define LLM_RESPONSE_BUF_SIZE 8192
// 逐字符解析状态机
typedef struct {
json_parse_state_t state;
char key[64];
char value[512];
// ...
} json_stream_parser_t;
代价是代码复杂度上升,但换来的是:$2 的 C3 也能跑完整 Agent。
2. HAL 硬件抽象层
如果你接过嵌入式项目,应该体会过硬件耦合的痛苦。
ESPClaw 把所有硬件操作抽象成了 HAL:
// hal_pwm.h
esp_err_t hal_pwm_setup(uint8_t ch, int gpio, uint32_t freq, float duty);
esp_err_t hal_pwm_set_duty(uint8_t ch, float duty);
// hal_servo.h
esp_err_t hal_servo_attach(uint8_t id, int gpio);
esp_err_t hal_servo_write(uint8_t id, float angle);
// hal_adc.h
esp_err_t hal_adc_read_voltage(int ch, int *out_mv);
// hal_onewire.h
esp_err_t hal_onewire_read_temp(uint8_t bus, const uint8_t rom[8], float *temp);
工具层只调 HAL,不知道底层是 LEDC 还是别的。换芯片?改 HAL 实现。换驱动?改 HAL 实现。
这是正经项目该有的分层。
3. 40 个工具不是堆砌
工具多不代表有用,关键是有没有形成组合能力。
temp_read() → memory_set() → cron_schedule() → telegram_push()
这四个工具组合起来,就是一个完整的温度监控系统。用户不需要写代码,用自然语言描述需求,LLM 自己编排调用。
工具的定义是标准的:
typedef struct {
const char *name; // 工具名
const char *description; // LLM 理解用的描述
const char *input_schema; // JSON Schema
bool (*execute)(const char *input_json, char *result, size_t sz);
} tool_def_t;
写一个 C 函数,实现 execute,注册到 tool_registry,LLM 就能调用。
4. 平台自适应
platform.h 一个头文件,解决 C3/C5/S3 的差异:
#if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32C5
#define ESPCLAW_HAS_PSRAM 0
#define LLM_RESPONSE_BUF_SIZE 8192
#define MAX_HISTORY_TURNS 8
#else
#define ESPCLAW_HAS_PSRAM 1
#define LLM_RESPONSE_BUF_SIZE 32768
#define MAX_HISTORY_TURNS 24
#endif
同一份代码,编译到不同芯片,行为自动适配。
5. 9 个通知渠道的工程意义
不是炫技,是实际需求。
国内企业用钉钉、飞书、企业微信。海外用 Discord、Slack。IoT 场景用 MQTT。
ESPClaw 把 Channel 抽象成 vtable:
typedef struct {
const char *name;
esp_err_t (*start)(message_bus_t *bus);
bool (*is_available)(void);
} channel_ops_t;
所有渠道通过 Kconfig 按需编译,不用的渠道零开销。
实际能干什么
说几个实际场景:
1. 边缘数据采集
ADC 采样 → LLM 生成报告 → 定时推送到钉钉群。
不需要云服务器,不需要写后端,一块 C3 搞定。
2. 自然语言硬件控制
「把 2 号舵机转到 45 度」
LLM 解析意图 → 调用 servo_write → 返回确认。
3. 完全离线的私有 Agent
接 Ollama 本地部署,MQTT 收指令,GPIO 执行动作。数据全程不出局域网。
4. 你想做的任何东西
I2C、SPI、继电器、传感器——写个 HAL,注册个工具,LLM 就能控制。
和 zclaw/MimiClaw 的关系
不是竞品,是不同赛道。
- 想 888 KiB 极限?用 zclaw
- 想开箱即用?用 MimiClaw
- 想折腾硬件、做 IoT 项目?用 ESPClaw
它们甚至可以互相借鉴代码——都是 Pure C + ESP-IDF,架构相似。
当前状态
| 步骤 | 状态 | 内容 |
|---|---|---|
| Step 1-4 | 最小固件 → WiFi → 串口 → LLM 对话 | |
| Step 5-6 | ReAct Agent + 工具系统 + 持久记忆 | |
| Step 7 | 9 通知渠道 | |
| Step 8 | Cron 定时 + 速率限制 | |
| Step 8.5 | PWM/ADC/Servo/DS18B20 HAL | |
| Step 9 | OTA + 启动保护 + NVS 加密 |
代码在 GitHub,MIT 协议,欢迎挑刺。
如果你想试试
下载项目并且 执行 下面命令 有一个esp32s3单片机即可
idf.py build flash monitor
串口敲 /help 看命令,/tools 看已注册工具。
有问题直接提 issue,代码有问题就骂,我改。