看到了 zclaw 重写了一个 可以在esp32跑的agent mini版本 openclaw

为什么有了 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 :white_check_mark: 最小固件 → WiFi → 串口 → LLM 对话
Step 5-6 :white_check_mark: ReAct Agent + 工具系统 + 持久记忆
Step 7 :white_check_mark: 9 通知渠道
Step 8 :white_check_mark: Cron 定时 + 速率限制
Step 8.5 :white_check_mark: PWM/ADC/Servo/DS18B20 HAL
Step 9 :arrows_counterclockwise: OTA + 启动保护 + NVS 加密

代码在 GitHub,MIT 协议,欢迎挑刺。

如果你想试试

下载项目并且 执行 下面命令 有一个esp32s3单片机即可

idf.py build flash monitor

串口敲 /help 看命令,/tools 看已注册工具。

有问题直接提 issue,代码有问题就骂,我改。