Ollama + MimiClaw = 嵌入式AI
在资源受限的嵌入式硬件上运行 AI 代理,不需要云 API、PSRAM 或昂贵的开发板。本文介绍我是如何做到的。
AI编程/Vibe Coding 遇到问题需要帮助的,联系微信 ezpoda,免费咨询。
MimiClaw 吸引了我的注意 —— 一个完全用 C 语言编写的、在微小的 ESP32 芯片上运行的完整 AI 助手。没有 Linux,没有 Node.js,没有 Python 运行时。只有直接与 LLM API 通信的裸机固件。
但有一个陷阱。MimiClaw 仅支持云 API(Anthropic Claude、OpenAI GPT),并且需要一块带有 16MB Flash 和 8MB PSRAM 的 ESP32-S3 开发板。这是一块 5 美元的开发板,而且你还需要为每次 API 调用付费。
我想要不同的东西,我已经有了。我想在我的 ESP-WROOM-32 上运行它 —— 一块不到 4 美元就能买到的开发板 —— 并连接到我在本地机器上运行的 Ollama。没有云。没有 API 密钥。没有月度账单。
要实现这一点需要一些真正的工程工作。这是我学到的。
1、什么是 MimiClaw?
MimiClaw 是由 memovai 社区构建的开源 AI 助手。它将拇指大小的 ESP32 微控制器变成个人 AI 代理,你可以通过 Telegram 与它对话。
有趣的地方在于架构。它实现了完整的 ReAct 代理循环 —— 与 ChatGPT 插件和 LangChain 代理使用的模式相同 —— 但在纯 C 语言中运行,在只有单个 Chrome 标签页 RAM 一半的双核处理器上运行。
代理可以:
- 调用工具(网络搜索、文件管理、调度)
- 跨会话保持对话历史
- 在持久化内存文件中记住关于你的事情
- 使用 cron 系统调度自主任务
- 24/7 运行,功耗仅 0.5W
整个代码库大约 5,000 行 C 代码,构建在 ESP-IDF 框架上。它精简、专注,而且出人意料地强大。
2、问题:仅限云端、仅限大板
开箱即用,MimiClaw 有两个让我烦恼的限制。
2.1 不支持本地 LLM
LLM 代理层硬编码了 Anthropic 和 OpenAI 的 API URL。如果你想使用 Ollama、LM Studio 或任何本地模型服务器,根本无法将固件指向自定义端点。API URL 在编译时就被固定了。
对于任何在家运行本地模型的人来说 —— 现在这样的人很多 —— 这是一个致命问题。
2.2 仅限 ESP32-S3
固件是为带有 16MB Flash 和 8MB PSRAM 的 ESP32-S3 构建的。系统中的每个大缓冲区都使用 ESP-IDF 的 heap_caps_calloc(…, MALLOC_CAP_SPIRAM) 从 PSRAM 分配。分区表假设有 16MB 的 Flash。控制台驱动程序假设 USB Serial/JTAG 可用。
我有一块 2018 年购买的 ESP-WROOM-32。它有 4MB Flash,没有 PSRAM,大约有 300KB 内部 RAM。我决定试一试。
3、添加本地 LLM 支持(Ollama / LM Studio)
这是比较容易解决的问题,但需要在多个层面进行修改。
3.1 API 基础 URL
Ollama 和 LM Studio 都暴露了 OpenAI 兼容的 API。Ollama 运行在端口 11434,LM Studio 运行在端口 1234。请求格式与 OpenAI 的 /v1/chat/completions 完全相同 —— 你只需要指向不同的主机。
我向 LLM 代理层添加了一个可配置的基础 URL。设置后,它会覆盖默认的云 URL,同时保留所选提供者的正确 API 路径。
static const char *llm_api_url(void)
{
if (s_api_base_url[0]) {
const char *path = provider_is_openai()
? "/v1/chat/completions" : "/v1/messages";
size_t blen = strlen(s_api_base_url);
if (blen > 0 && s_api_base_url[blen - 1] == '/') blen--;
snprintf(s_url_buf, sizeof(s_url_buf), "%.*s%s",
(int)blen, s_api_base_url, path);
return s_url_buf;
}
return provider_is_openai() ? MIMI_OPENAI_API_URL : MIMI_LLM_API_URL;
}
3.2 HTTP vs HTTPS 自动检测
云 API 使用 HTTPS。本地服务器通常使用纯 HTTP。原始代码无条件地将 TLS 证书包附加到每个连接。在启用 TLS 的情况下连接到 http://192.168.1.243:11434 不会有好结果。
我添加了一个简单的方案检查 —— 如果 URL 以 https:// 开头,附加证书包。如果是 http://,跳过它:
bool is_https = (strncmp(url, "https://", 8) == 0);
esp_http_client_config_t config = {
.url = url,
.crt_bundle_attach = is_https ? esp_crt_bundle_attach : NULL,
// …
};
3.3 没有 API 密钥?没问题。
原始代码如果 s_api_key 为空,则拒绝进行任何 API 调用。但本地模型不需要 API 密钥。我放宽了检查,允许在配置基础 URL 时进行调用:
if (s_api_key[0] == '\0' && s_api_base_url[0] == '\0')
return ESP_ERR_INVALID_STATE;
3.4 通过 CLI 运行时配置
我添加了两个串行 CLI 命令,因此你可以在不重新编译的情况下配置所有内容:
mimi> set_model_provider openai
mimi> set_api_base_url http://192.168.1.243:11434
mimi> set_model gpt-oss-20b
所有设置都持久化到 NVS(非易失性存储)中,并在重启后保持。你也可以在 mimi_secrets.h 中在构建时设置它们:
#define MIMI_SECRET_MODEL_PROVIDER "openai"
#define MIMI_SECRET_API_BASE_URL "http://192.168.1.243:11434"
#define MIMI_SECRET_MODEL "gpt-oss-20b"
通过这些更改,MimiClaw 可以与任何 OpenAI 兼容的端点通信 —— Ollama、LM Studio、vLLM、text-generation-webui,你可以随意选择。
4、在 ESP-WROOM-32 上运行
这是困难的部分。ESP-WROOM-32 的 Flash 大约是 ESP32-S3 的 1/4,外部 RAM 为零。所有东西都必须能放下。
4.1 新的分区表
ESP32-S3 布局使用双 OTA 分区(用于安全的固件更新),这很好但两个应用程序槽需要 4MB。在 4MB 芯片上,这是整个 Flash。
我创建了一个新的分区表,只有一个工厂应用程序分区,没有 OTA:
# Name, Type, SubType, Offset, Size
nvs, data, nvs, 0x9000, 0x6000
phy_init, data, phy, 0xF000, 0x1000
factory, app, factory, 0x10000, 0x1C0000
spiffs, data, spiffs, 0x1D0000, 0x220000
coredump, data, coredump,0x3F0000, 0x10000
工厂应用程序分区是 1.75MB。编译后的二进制文件大小为 1.16MB —— 剩余 36% 的空间。SPIFFS 获得 2.1MB,足够用于个性文件、内存、会话和技能。
4.2 PSRAM 条件分配
这是关键更改。原始代码到处使用 heap_caps_calloc(…, MALLOC_CAP_SPIRAM) 进行大缓冲区分配。在没有 PSRAM 的芯片上,这些调用立即返回 NULL。
我向 mimi_config.h 添加了在编译时适应的分配宏:
#if CONFIG_SPIRAM
#define mimi_alloc(size) heap_caps_calloc(1, (size), MALLOC_CAP_SPIRAM)
#define mimi_realloc(p, s) heap_caps_realloc((p), (s), MALLOC_CAP_SPIRAM)
#else
#define mimi_alloc(size) calloc(1, (size))
#define mimi_realloc(p, s) realloc((p), (s))
#endif
然后,将 LLM 代理、代理循环和网络搜索工具中的每个 heap_caps_calloc/realloc 调用替换为 mimi_alloc/mimi_realloc。ESP32-S3 仍然像以前一样使用 PSRAM。ESP32 使用内部 RAM。相同的代码,不同的行为。
4.3 内存问题
这里的事情变得有趣了。第一次构建编译成功。我刷入它,启动它,WiFi 连接,Telegram 连接。我发送了 /start 并…… ESP_ERR_NO_MEM。
只有 60KB 的空闲内部 RAM。代理试图分配:

在 60KB 空闲的情况下分配 88KB 的缓冲区。数学上不成立。
我检查了实际数据大小。系统提示词约 3KB。新对话的聊天历史很小。LLM 响应缓冲区动态增长。没有理由在内存受限的芯片上需要这么大的缓冲区。
我使缓冲区大小成为条件:
#if CONFIG_SPIRAM
#define MIMI_LLM_STREAM_BUF_SIZE (32 * 1024)
#define MIMI_CONTEXT_BUF_SIZE (16 * 1024)
#else
#define MIMI_LLM_STREAM_BUF_SIZE (8 * 1024)
#define MIMI_CONTEXT_BUF_SIZE (4 * 1024)
#endif
新的总数:24KB。刷入后,LLM 调用成功,内部 RAM 剩余 100KB。TLS 握手不再失败。Telegram 消息正常发送。
4.4 构建脚本
最后一块是使切换目标变得容易:
scripts/build_macos.sh esp32 # 为 ESP-WROOM-32 构建
scripts/build_macos.sh # 为 ESP32-S3 构建(默认)
ESP-IDF 的 sdkconfig.defaults.<target> 机制处理其余部分 —— 它会根据目标名称自动选择正确的配置文件。
5、结果
MimiClaw 在我的 ESP-WROOM-32 上运行,连接到我本地网络上的 Ollama,通过 Telegram 聊天。没有云。没有 PSRAM。硬件成本不到 4 美元。
以下是 LLM 调用后的内存情况:
I (39081) agent: Free internal: 100324 bytes
在总共约 300KB 中有 100KB 空闲。为 WiFi、TLS 和 FreeRTOS 提供了舒适的余量。
二进制文件是 1.16MB,可容纳在 1.75MB 的工厂分区中,剩余 36% 的空间。ESP32-S3 构建完全不受影响 —— 没有回归,相同的缓冲区大小,相同的 PSRAM 分配。

6、如何自己尝试
如果你想在 ESP32/ESP32-S3 上运行带有本地 LLM 的 MimiClaw,这是你需要的东西。
硬件
- ESP-WROOM-32 开发板(约 4 美元)或 ESP32-S3 开发板(约 10 美元)
- USB 线缆
- 一台在本地网络上运行 Ollama 或 LM Studio 的计算机
软件设置
安装 ESP-IDF v5.5+,按照官方指南
克隆仓库:
git clone https://github.com/manjunathshiva/mimiclaw.git
cd mimiclaw
git checkout feat/esp32-wroom32-support
- 配置密钥 —— 复制并编辑密钥文件:
cp main/mimi_secrets.h.example main/mimi_secrets.h
编辑 main/mimi_secrets.h:
#define MIMI_SECRET_WIFI_SSID "your-wifi"
#define MIMI_SECRET_WIFI_PASS "your-password"
#define MIMI_SECRET_TG_TOKEN "your-telegram-bot-token"
#define MIMI_SECRET_MODEL_PROVIDER "openai"
#define MIMI_SECRET_API_BASE_URL "http://YOUR_PC_IP:11434"
#define MIMI_SECRET_MODEL "your_ollama_model"
1) 在你的电脑上启动 Ollama:
ollama serve gpt-oss-20b
2) 构建和刷写:
scripts/build_macos.sh esp32 # 或 scripts/build_ubuntu.sh 用于 esp32-s3 或 idf.py fullclean && idf.py build
idf.py flash monitor
3) 在 Telegram 上与你的机器人交谈。 发送 /start,你应该得到由你的本地模型支持的响应。
运行时配置(无需重新刷写)
你可以从串行 CLI 更改所有内容:
mimi> set_api_base_url http://192.168.1.100:11434
mimi> set_model gpt-oss-20b
mimi> set_model_provider openai
mimi> config_show
想切换到 LM Studio?只需更改 URL 和端口:
mimi> set_api_base_url http://192.168.1.100:1234
mimi> set_model your-lm-studio-model
想回到云端?清除基础 URL 并设置你的 API 密钥:
mimi> clear_api_base_url
mimi> set_model_provider anthropic
mimi> set_api_key sk-ant-…
mimi> set_model claude-sonnet-4–5–20250514
7、结束语
嵌入式 AI 是一场内存游戏。 在一块只有 300KB RAM 的芯片上,每千字节都很重要。88KB 和 24KB 的缓冲区分配之间的区别就是"不工作"和"剩余 100KB 的正常工作"之间的区别。了解你的实际数据大小,而不仅仅是你的理论最大值。
本地 LLM 改变了经济学。 在闲置的笔记本电脑上运行 Ollama 意味着你的 4 美元微控制器拥有无限的、免费的、私有的 AI 推理能力。没有 API 密钥,没有速率限制,没有数据离开你的网络。对于家庭自动化、个人助手和业余爱好项目,这是一个更好的模型。
编译时适应胜过运行时检查。 使用 #if CONFIG_SPIRAM 在 PSRAM 和内部 RAM 分配之间切换意味着任一目标的零运行时开销。编译器完全剥离未使用的路径。相同的源代码,每个芯片的最优二进制文件。
ESP32 生态系统被低估了。 一块比一杯咖啡还便宜的芯片可以运行 WiFi、TLS、JSON 解析、HTTP 客户端、具有工具调用的完整代理循环和持久化存储 —— 全部在 C 语言中,全部在裸机上。ESP-IDF 框架非常完整。
原文链接: How I Ran a Personal AI Assistant on a $4 Microcontroller Using Ollama and MimiClaw
汇智网翻译整理,转载请标明出处