MimiClaw 可通过飞书机器人接收指令、反馈结果。
在飞书开放平台创建企业自建应用并完成配置,步骤如下:
(1)创建企业自建应用进入应用管理页面后,在左侧菜单「凭证与基础信息」中,复制「App ID」和「App Secret」,妥善保存(后续配置 MimiClaw 需用到)。


(5)MimiClaw源码修改
修改mimi_secrets.h内的以下行,粘贴上一步的APP ID和AAPP SECRET
/* Feishu Bot */
#define MIMI_SECRET_FEISHU_APP_ID ""
#define MIMI_SECRET_FEISHU_APP_SECRET ""
编译并烧录固件。
在飞书APP内用创建的机器人进行对话吧。

编写可由MimiClaw调用的硬件控制技能,需要3步:
1. 编写硬件控制工具代码
2. 向MimiClaw注册工具
3. 编写给大模型看的skill文档
这样当我们向MimiClaw发出硬件控制的对话时,由大模型分析后转为调用工具代码,从而完成硬件控制操作。
下面以开关LED和WS2812为例来说明
(1)编写工具代码
在tools目录内新建tool_led.h和tool_led.c文件,
在main/tools/tool_led.c内, 添加以下代码:
#include "led_strip.h" // 引入 led_strip components
#define WS2812_GPIO 48 // 板载 WS2812 连接的 GPIO 引脚
static led_strip_handle_t s_led_strip = NULL; // LED 驱动句柄
static const char *TAG = "tool_led";
//============== WS2812 ==================/
// WS2812 初始化函数
esp_err_t ws2812_init(void)
{
if (s_led_strip != NULL) return ESP_OK; // 避免重复初始化
// LED 灯带配置
led_strip_config_t strip_cfg = {
.strip_gpio_num = WS2812_GPIO, // 连接的 GPIO 引脚
.max_leds = 1, // 灯珠数量(板载为 1 个)
.led_model = LED_MODEL_WS2812, // 灯珠型号为 WS2812
.color_component_format = LED_STRIP_COLOR_COMPONENT_FMT_GRB,
};
// RMT 外设配置(驱动 WS2812 需用到 RMT 外设)
led_strip_rmt_config_t rmt_cfg = {
.clk_src = RMT_CLK_SRC_DEFAULT,
.resolution_hz = 10 * 1000 * 1000,
.mem_block_symbols = 0,
.flags.with_dma = false, // 无需 DMA(单灯珠无需高速传输)
};
// 创建 RMT 驱动的 LED 设备
led_strip_new_rmt_device(&strip_cfg, &rmt_cfg, &s_led_strip);
led_strip_clear(s_led_strip); // 初始化后熄灭彩灯
return ESP_OK;
}
// WS2812 设置函数(r: 红色, g: 绿色, b: 蓝色,取值 0-255)
esp_err_t ws2812_set(uint8_t r, uint8_t g, uint8_t b)
{
ws2812_init(); // 确保驱动已初始化
led_strip_set_pixel(s_led_strip, 0, r, g, b); // 设置第 0 个灯珠颜色
led_strip_refresh(s_led_strip); // 刷新显示,使颜色生效
ESP_LOGI(TAG, "ws2812 set r:%d g:%d b:%d", r, g, b);
return ESP_OK;
}
// ws2812 on工具函数
esp_err_t tool_ws2812_on_execute(const char *in_json, char *out, size_t len) {
cJSON *root = cJSON_Parse(in_json);
if (!root) {
snprintf(out, len, "Error: invalid input");
return ESP_ERR_INVALID_ARG;
}
cJSON *red_obj = cJSON_GetObjectItem(root, "red");
uint8_t red = (red_obj && cJSON_IsNumber(red_obj)) ? (uint8_t)red_obj->valueint : 0;
cJSON *green_obj = cJSON_GetObjectItem(root, "green");
uint8_t green = (red_obj && cJSON_IsNumber(green_obj)) ? (uint8_t)green_obj->valueint : 0;
cJSON *blue_obj = cJSON_GetObjectItem(root, "blue");
uint8_t blue = (red_obj && cJSON_IsNumber(blue_obj)) ? (uint8_t)blue_obj->valueint : 0;
ws2812_set(red, green, blue);
snprintf(out, len, "WS2812 ON"); // 执行结果
return ESP_OK;
}
// ws2812 off 工具函数
esp_err_t tool_ws2812_off_execute(const char *in, char *out, size_t len) {
ws2812_set(0, 0, 0); // 红色:R=255, G=0, B=0
snprintf(out, len, "WS2812 OFF"); // 执行结果
return ESP_OK;
}
//=================== Led Tool =============================
// Led on工具函数
esp_err_t tool_led_on_execute(const char *in_json, char *out, size_t len) {
cJSON *root = cJSON_Parse(in_json);
if (!root) {
snprintf(out, len, "Error: invalid input");
return ESP_ERR_INVALID_ARG;
}
cJSON *pin_obj = cJSON_GetObjectItem(root, "pin");
uint8_t pin = (pin_obj && cJSON_IsNumber(pin_obj)) ? (uint8_t)pin_obj->valueint : 0;
cJSON *value_obj = cJSON_GetObjectItem(root, "value");
uint8_t value = (pin_obj && cJSON_IsNumber(value_obj)) ? (uint8_t)value_obj->valueint : 0;
if (gpio_set_direction(pin, GPIO_MODE_INPUT_OUTPUT) != ESP_OK ||
gpio_set_level(pin, value) != ESP_OK) {
snprintf(out, len, "Error: failed to turn on %d", pin);
cJSON_Delete(root);
return ESP_FAIL;
}
snprintf(out, len, "LED ON"); // 执行结果
return ESP_OK;
}
// Led off 工具函数
esp_err_t tool_led_off_execute(const char *in_json, char *out, size_t len) {
cJSON *root = cJSON_Parse(in_json);
if (!root) {
snprintf(out, len, "Error: invalid input");
return ESP_ERR_INVALID_ARG;
}
cJSON *pin_obj = cJSON_GetObjectItem(root, "pin");
uint8_t pin = (pin_obj && cJSON_IsNumber(pin_obj)) ? (uint8_t)pin_obj->valueint : 0;
if (gpio_set_direction(pin, GPIO_MODE_INPUT_OUTPUT) != ESP_OK ||
gpio_set_level(pin, 0) != ESP_OK) {
snprintf(out, len, "Error: failed to turn on %d", pin);
cJSON_Delete(root);
return ESP_FAIL;
}
snprintf(out, len, "LED OFF"); // 执行结果
return ESP_OK;
}
(2) 注册工具
在main/toos/tool_registry.c 文件的tool_registry_init函数中,添加以上四个工具的注册代码,如下:
// Register led_on
mimi_tool_t led_on = {
.name = "led_on",
.description = "Turn on the LED immediately. Use when user says 'led on pin val'.",
.input_schema_json =
"{\"type\":\"object\","
"\"properties\":{"
"\"pin\":{\"type\":\"integer\",\"description\":\"Pin(0-48)\"},"
"\"value\":{\"type\":\"integer\",\"description\":\"Value(0-255)\"}"
"},"
"\"required\":[\"pin\",\"value\"]}",
.execute = tool_led_on_execute, // 绑定执行函数
};
register_tool(&led_on);
// Register led_off
mimi_tool_t led_off = {
.name = "led_off",
.description = "Turn off the LED immediately. Use when user says 'led off pin'.",
.input_schema_json =
"{\"type\":\"object\","
"\"properties\":{"
"\"pin\":{\"type\":\"integer\",\"description\":\"Pin(0-48)\"}"
"},"
"\"required\":[\"pin\"]}",
.execute = tool_led_off_execute, // 绑定执行函数
};
register_tool(&led_off);
// Register ws2812_on
mimi_tool_t ws2812_on = {
.name = "ws2812_on",
.description = "Turn on the WS2812 immediately. Use when user says 'ws2812 on x,x,x'.",
.input_schema_json =
"{\"type\":\"object\","
"\"properties\":{"
"\"red\":{\"type\":\"integer\",\"description\":\"Red Value(0-255)\"},"
"\"green\":{\"type\":\"integer\",\"description\":\"Green Value(0-255)\"},"
"\"blue\":{\"type\":\"integer\",\"description\":\"Blue Value(0-255)\"}"
"},"
"\"required\":[\"red\",\"green\",\"blue\"]}",
.execute = tool_ws2812_on_execute, // 绑定执行函数
};
register_tool(&ws2812_on);
// Register ws2812_off
mimi_tool_t ws2812_off = {
.name = "ws2812_off",
.description = "Turn off the WS2812 immediately. Use when user says 'ws2812 off'.",
.input_schema_json = "{\"type\":\"object\",\"properties\":{},\"required\":[]}",
.execute = tool_ws2812_off_execute,
};
register_tool(&ws2812_off);
(3) 编写技能描述
这是最重要的一步,决定了AI是否能理解我们的对话描述并转为工具技能调用。
在spiffs_data/skills/目录下新建一个led-control.md文件,输入以下内容:
# WS2812 Control
Control ws2812 led.
## When to use
When the user asks to turn ws2812 on/off.
## How to use
1. To **ws2812 on**: use ws2812_on with red,green,blue (0-255)
2. To **ws2812 off**: use ws2812_off
## Example
User: "led on 4 255"
→ led_on { "pin": 4, "value": 255 }
User: "led off 4"
→ led_off { "pin": 4 }
User: "ws2812 on 255 128 0"
→ ws2812_on {"red": 255, "green": 128, "blue": 0}
User: "ws2812 off"
→ ws2812_off
编译并烧录固件。

通过MimiClaw+大模型,我们让ESP32具备了自主思考的能力,而通过技能,我们可以让设备具备行动的能力。
PWM,英文名Pulse Width Modulation,是脉冲宽度调制缩写,它是通过调节占空比的变化来调节信号、能量等的变化。
MimiClaw 是一款基于 ESP32-S3 芯片的超轻量级AI助手,适合嵌入式AI与物联网开发者快速部署本地化AI代理。本系列教程基于MimiClaw的Arduino移植版本进行讲解,小节主要讲解部署和测试。
就像我们用手机打开WiFi功能后可以浏览附近的可用WiFi。要将手机连接到热点,通常需要打开Wi-Fi设置应用程序,列出可用的网络,然后选择所需的热点。然后输入密码(或不输入密码),可以使用ESP32进行相同的操作。
近日,中国台湾大学电机工程学系副教授李宏毅在社交平台上传了一节公开课,以OpenClaw为例,介绍了AI Agent的运作原理。这堂课把最近火爆全网的OpenClaw从头到脚拆了一遍,讲清楚了这只“龙虾”到底是怎么工作的。
一块 30 块钱的开发板 + 一个大模型 API,就能做出可以听懂人话的智能硬件。 本文记录完整安装过程和踩坑经验,确保你跟着做就能跑通。
本文将从手绘架构图入手,逐层拆解 MimiClaw 的分层设计、核心模块、数据流转与底层实现,带你解剖这只“智能虾”的技术骨架,看懂在 C 语言加持下,AI 智能体如何以可穿戴设备的形态,在你身边稳稳运行、离线服务、主动响应。
本文介绍如何在不脱离 ArduinoIDE 可视化开发的前提下,通过一个名为 platform.local.txt 的小文件,实现对 ESP32 编译流程的精准控制。
本文将系统分析程序体积增长的五大根源,并提供经过验证的优化方案,帮助减小固件大小。
本文所DIY的语音助手设备端使用的是MicroPython、服务端是Python,对于很多开发者来说MicroPython入门没难度。
本小节使用音频开发框架实现一个音频录制到文件的示例。
I2S协议通过BCLK、LRCLK和DATA三线精准传输音频数据,但时序边沿、帧格式、时钟源等细节常引发噪声或断连。本文详解ESP32的I2S实现,从协议原理到ESP-IDF v5.x代码配置,助你避开常见陷阱,确保音频稳定传输。