The world's first AI assistant(OpenClaw) on a $5 chip. No Linux. No Node.js. Just pure C
MimiClaw turns a tiny ESP32-S3 board into a personal AI assistant. Plug it into USB power, connect to WiFi, and talk to it through Telegram or a Feishu bot โ it handles any task you throw at it and evolves over time with local memory โ all on a chip the size of a thumb.
- Tiny โ No Linux, no Node.js, no bloat โ just pure C
- Handy โ Message it from Telegram or Feishu, it handles the rest
- Loyal โ Learns from memory, remembers across reboots
- Energetic โ USB power, 0.5 W, runs 24/7
- Lovable โ One ESP32-S3 board, $5, nothing else
You send a message on Telegram, Feishu, or a LAN WebSocket client. The ESP32-S3 picks it up over WiFi, feeds it into an agent loop โ the LLM thinks, calls tools, reads memory โ and sends the reply back. Supports both Anthropic (Claude) and OpenAI (GPT) as providers, switchable at runtime. Everything runs on a single $5 chip with all your data stored locally on flash.
The default quick start uses ESP32-S3-DevKitC-1 with no external peripherals attached. Plug into the correct USB port, configure WiFi/Bot/API key, flash the firmware, and verify from Telegram or Feishu.
- Prepare an ESP32-S3-DevKitC-1 and plug into the port labeled USB (not COM).
- Install ESP-IDF and clone this repository.
- Copy
main/mimi_secrets.h.exampletomain/mimi_secrets.hand fill in WiFi, bot, and API key. - Build and flash the firmware.
- Open Telegram or Feishu and send
/startorhelloto test the full agent path.
- An ESP32-S3-DevKitC-1 (default quick-start board; use a variant with 16 MB flash and 8 MB PSRAM)
- A USB Type-C cable
- A Telegram bot token, or a Feishu self-built app with bot capability and event subscription
- An Anthropic API key โ from console.anthropic.com, or an OpenAI API key โ from platform.openai.com
- No microphone, speaker, or display is required for the default quick start
# You need ESP-IDF v5.5+ installed first:
# https://docs.espressif.com/projects/esp-idf/en/v5.5.2/esp32s3/get-started/
git clone https://github.com/memovai/mimiclaw.git
cd mimiclaw
idf.py set-target esp32s3Ubuntu Install
Recommended baseline:
- Ubuntu 22.04/24.04
- Python >= 3.10
- CMake >= 3.16
- Ninja >= 1.10
- Git >= 2.34
- flex >= 2.6
- bison >= 3.8
- gperf >= 3.1
- dfu-util >= 0.11
libusb-1.0-0,libffi-dev,libssl-dev
Install and build on Ubuntu:
sudo apt-get update
sudo apt-get install -y git wget flex bison gperf python3 python3-pip python3-venv \
cmake ninja-build ccache libffi-dev libssl-dev dfu-util libusb-1.0-0
./scripts/setup_idf_ubuntu.sh
./scripts/build_ubuntu.shmacOS Install
Recommended baseline:
- macOS 12/13/14
- Xcode Command Line Tools
- Homebrew
- Python >= 3.10
- CMake >= 3.16
- Ninja >= 1.10
- Git >= 2.34
- flex >= 2.6
- bison >= 3.8
- gperf >= 3.1
- dfu-util >= 0.11
libusb,libffi,openssl
Install and build on macOS:
xcode-select --install
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
./scripts/setup_idf_macos.sh
./scripts/build_macos.shMimiClaw uses a two-layer config system: build-time defaults in mimi_secrets.h, with runtime overrides via the serial CLI. CLI values are stored in NVS flash and take priority over build-time values.
cp main/mimi_secrets.h.example main/mimi_secrets.hEdit main/mimi_secrets.h:
#define MIMI_SECRET_WIFI_SSID "YourWiFiName"
#define MIMI_SECRET_WIFI_PASS "YourWiFiPassword"
#define MIMI_SECRET_TG_TOKEN "" // leave empty if you only use Feishu
#define MIMI_SECRET_FEISHU_APP_ID ""
#define MIMI_SECRET_FEISHU_APP_SECRET ""
#define MIMI_SECRET_FEISHU_VERIFY_TOKEN ""
#define MIMI_SECRET_FEISHU_ENCRYPT_KEY ""
#define MIMI_SECRET_API_KEY "sk-ant-api03-xxxxx"
#define MIMI_SECRET_MODEL_PROVIDER "anthropic" // "anthropic" or "openai"
#define MIMI_SECRET_SEARCH_KEY "" // optional: Brave Search API key
#define MIMI_SECRET_PROXY_HOST "" // optional: e.g. "10.0.0.1"
#define MIMI_SECRET_PROXY_PORT "" // optional: e.g. "7897"Then build and flash:
# Clean build (required after any mimi_secrets.h change)
idf.py fullclean && idf.py build
# Find your serial port
ls /dev/cu.usb* # macOS
ls /dev/ttyACM* # Linux
# Flash and monitor (replace PORT with your port)
# USB adapter: likely /dev/cu.usbmodem11401 (macOS) or /dev/ttyACM0 (Linux)
idf.py -p PORT flash monitorRecommended first check:
- Telegram: send
/startto confirm connectivity - Feishu: send
helloto confirm callback ingress and outbound messaging - Send
helloto test the full agent path
Important: Plug into the correct USB port! Most ESP32-S3 boards have two USB-C ports. You must use the one labeled USB (native USB Serial/JTAG), not the one labeled COM (external UART bridge). Plugging into the wrong port will cause flash/monitor failures.
Quick-start reference board: the default path in this README assumes ESP32-S3-DevKitC-1. For the first boot, keep the board in its minimum form: USB cable only, no microphone, no speaker, no display.
This branch defaults to a no external voice gateway build. Text chat works without running tools/voice_gateway.py.
- Fill these build-time secrets in
main/mimi_secrets.h:
For a normal Feishu Bot setup, these are the fields that matter:
- Required:
App ID - Required:
App Secret - Recommended:
Receive Mode - Strongly recommended in
webhookmode:Verify Token - Optional in
webhookmode:Encrypt Key - Usually leave as-is:
Open API Base
#define MIMI_SECRET_FEISHU_APP_ID "cli_xxx" // required: Feishu App ID
#define MIMI_SECRET_FEISHU_APP_SECRET "xxx" // required: Feishu App Secret
#define MIMI_SECRET_FEISHU_RECEIVE_MODE "websocket" // recommended: board-side long connection; or use webhook
#define MIMI_SECRET_FEISHU_VERIFY_TOKEN "mimiclaw-feishu" // recommended in webhook mode
#define MIMI_SECRET_FEISHU_ENCRYPT_KEY "" // only needed for encrypted webhook callbacks
#define MIMI_SECRET_FEISHU_OPEN_API_BASE "https://open.feishu.cn" // usually keep default; override only for local stub validationApp IDandApp Secretare mandatory for the bot to workMIMI_SECRET_FEISHU_RECEIVE_MODEsupportswebsocketandwebhookwebsocketis the recommended default: the board opens the long connection itself, so you do not need a public callback URL- only
webhookmode needsVerify Token,Encrypt Key,/feishu/events, and a public callback path Verify Tokenis not strictly required by Feishu, but this project strongly recommends setting it in webhook mode- leave
Encrypt Keyempty until plaintext webhook callbacks are working
- In the Feishu Open Platform:
At minimum, fill in these items:
- create a self-built app
- enable bot capability
- subscribe to event
im.message.receive_v1 - if receive mode is
websocket:- no
Request URLis required - no public callback address is required
- no
- if receive mode is
webhook:- set request URL to
https://<public-address>/feishu/events - set
Verify Tokento the same value asMIMI_SECRET_FEISHU_VERIFY_TOKEN - leave
Encrypt Keyempty for plaintext callbacks, or set it to matchMIMI_SECRET_FEISHU_ENCRYPT_KEY
- set request URL to
- keep
MIMI_SECRET_FEISHU_OPEN_API_BASEon the official host unless you are using the local OpenAPI stub for validation
This firmware now supports encrypted Feishu event payloads:
- this applies only to
webhook - if
Encrypt Keyis empty, callbacks are handled as plaintext events - if
Encrypt Keyis configured, the firmware validatesX-Lark-Signatureand decrypts theencryptfield - keeping
Verify Tokenenabled is still recommended for an extra source check on URL verification and normal events
Recommended minimum setup:
-
Start with
App IDandApp Secret -
Set
Receive Modetowebsocket -
Only switch to
webhookif you explicitly need callback mode -
Enable
Encrypt Keyonly after webhook text ingress is stable -
Choose the ingress mode you want:
websocket:- the device opens a long connection to Feishu itself
- no public callback is required
- the truly required fields are just
App IDandApp Secret
webhook:- callback path is fixed at
/feishu/events - the shared HTTP service listens on port
18789 - if your board is not public, add reverse proxy / port forwarding / tunnel to
http://<device-lan-ip>:18789/feishu/events
- callback path is fixed at
- Smoke test after flashing:
- watch serial logs
- in
websocketmode, look forFeishu WebSocket long connection enabledandFeishu WS connected - in
webhookmode, look forFeishu callback registered at /feishu/events - if
Encrypt Keyis enabled in webhook mode, confirm both URL verification and event delivery succeed in the Feishu console - send
helloto the bot in Feishu - expect a text reply from MimiClaw
4.1 Local replay without the Feishu console
If you only want to validate the device-side /feishu/events path, run this from your dev machine:
./tools/run_feishu_replay.sh --scenario all --verify-token mimiclaw-feishuCommon examples:
# replay plaintext text / duplicate / image / file / audio / sticker callbacks
./tools/run_feishu_replay.sh --scenario all --verify-token mimiclaw-feishu
# replay encrypted duplicate delivery
./tools/run_feishu_replay.sh \
--scenario duplicate \
--verify-token mimiclaw-feishu \
--encrypt-key your_encrypt_key \
--encrypted
# print request and response bodies for debugging
./tools/run_feishu_replay.sh --scenario text --show-bodyNotes:
- the script targets
http://127.0.0.1:18789/feishu/eventsby default - the
duplicatescenario sends the sameevent_id/message_idtwice to verify firmware deduplication - under the default configuration,
image/file/audio/stickeronly validate the callback -> summary text -> Agent downgrade path
If you already enabled MIMI_FEISHU_GATEWAY_MEDIA_ENABLED=1, you can also exercise the real image/file download branch with a local OpenAPI stub.
- Point the device to your dev machine from the serial CLI:
mimi> set_feishu_open_api_base http://<your-host-ip>:19091
- Start the local Feishu OpenAPI stub:
python3 tools/feishu_openapi_stub.py --host 0.0.0.0 --port 19091-
In another terminal, start
voice_gateway.py -
Run validation with gateway expectations:
./tools/run_feishu_validate.sh \
--scenario image \
--verify-token mimiclaw-feishu \
--log-file ./logs/monitor.log \
--expect-media-mode gatewayNotes:
- the stub includes
image_key=img_replay_demoandfile_key=file_replay_demo --expect-media-mode gatewayrequiresgateway_parse fromto appear in logs- run
mimi> clear_feishu_open_api_baseto switch the device back to the official Feishu host
If you already save serial logs to a file, you can also run replay + validation together:
./tools/run_feishu_validate.sh \
--scenario all \
--verify-token mimiclaw-feishu \
--log-file ./logs/monitor.logThe validator checks:
- HTTP responses for each replayed callback
- the returned
challengeforurl_verification - whether
duplicateproducesSkip duplicate Feishu eventin logs - whether text/media scenarios produce the expected ingress log markers
If you want to run serial monitor + replay validation + log capture in one shot:
./tools/run_feishu_validate_live.sh \
--port /dev/ttyACM0 \
--scenario all \
--verify-token mimiclaw-feishuNotes:
- this script starts
idf.py -p PORT monitorin the background - monitor output is written to
logs/feishu-validate-*.log - when validation finishes, the monitor process is stopped and the log file is kept for review
Current limits:
- text messages go to the Agent as-is
- by default, image / file / audio / sticker / other non-text Feishu messages are downgraded into summary text before entering the Agent
- if you enable
MIMI_FEISHU_GATEWAY_MEDIA_ENABLED=1and runvoice_gateway.py, Feishuimage/filemessages are downloaded and sent through real vision / doc parsing;audio/sticker/otherstill stay in summary mode - outbound Feishu delivery uses
chat_id - repeated Feishu deliveries are lightly deduplicated by
event_id/message_idbefore entering the Agent
This is an optional extension. The default no-gateway build does not depend on it.
Not required for the default quick start. If you only want to get the board online, skip this section for now.
Start the local gateway (STT + image analysis endpoint):
python3 tools/voice_gateway.py \
--host 0.0.0.0 --port 8090 --model small --device cpu \
--vision-enabled- STT endpoint:
http://<your-host-ip>:8091/stt_upload - Vision endpoint:
http://<your-host-ip>:8091/vision_upload - Document endpoint:
http://<your-host-ip>:8091/doc_upload - By default, gateway tries loading API defaults from
main/mimi_secrets.h; you can override via--vision-endpoint/--vision-api-key/--vision-model
To actually enable these media extensions in firmware, also set these in main/mimi_config.h and rebuild:
#define MIMI_TELEGRAM_GATEWAY_MEDIA_ENABLED 1
#define MIMI_FEISHU_GATEWAY_MEDIA_ENABLED 1Both defaults are 0, which keeps the no-gateway text + summary behavior. After enabling them:
- Telegram regains real STT / vision / doc parsing for voice / photos / documents
- Feishu enables real download + vision / doc parsing for
image/file, while other media types still fall back to summaries
After gateway is running, execute:
./tools/run_doc_regression.sh --basic
./tools/run_doc_regression.sh --officeOr run with custom arguments:
python3 tools/doc_regression.py \
--manifest tools/doc_regression_manifest.example.json \
--base-url http://127.0.0.1:8091The script calls /doc_upload and validates format, extracted text length, keywords, parser prefix, and latency budget.
tools/doc_regression_manifest.office.example.json includes a real xlsx sample and an optional xls case (food_legacy.xls) which is skipped when missing.
Connect via serial to configure or debug. Config commands let you change settings without recompiling โ just plug in a USB cable anywhere.
Runtime config (saved to NVS, overrides build-time defaults):
mimi> wifi_set MySSID MyPassword # change WiFi network
mimi> set_tg_token 123456:ABC... # change Telegram bot token
mimi> set_feishu_app cli_xxx secret_xxx # set Feishu app_id / app_secret
mimi> set_feishu_receive_mode websocket # switch to board-side long connection
mimi> set_feishu_verify_token token_xxx # set Feishu Verify Token
mimi> set_feishu_encrypt_key key_xxx # set Feishu Encrypt Key
mimi> set_feishu_open_api_base http://127.0.0.1:19091 # override Feishu OpenAPI base (useful for local validation)
mimi> set_api_key sk-ant-api03-... # change API key (Anthropic or OpenAI)
mimi> set_model_provider openai # switch provider (anthropic|openai)
mimi> set_model gpt-4o # change LLM model
mimi> set_proxy 127.0.0.1 7897 # set HTTP proxy
mimi> clear_proxy # remove proxy
mimi> set_search_key BSA... # set Brave Search API key
mimi> config_show # show all config (masked)
mimi> config_reset # clear NVS, revert to build-time defaults
Note:
- Feishu
app_id/app_secret/receive_mode/verify_token/encrypt_key/open_api_basecan now be updated via CLI - NVS-stored Feishu config overrides build-time defaults
Debug & maintenance:
mimi> wifi_status # am I connected?
mimi> memory_read # see what the bot remembers
mimi> memory_write "content" # write to MEMORY.md
mimi> heap_info # how much RAM is free?
mimi> agent_stats # agent success rate / latency / failures
mimi> heartbeat_status # heartbeat counters / last run
mimi> heartbeat_now # trigger heartbeat immediately
mimi> cron_status # cron schedule + counters
mimi> cron_set 30 "task..." # run every 30 min
mimi> cron_now # trigger cron immediately
mimi> cron_clear # clear cron schedule
mimi> session_list # list all chat sessions
mimi> session_clear 12345 # wipe a conversation
mimi> heartbeat_trigger # manually trigger a heartbeat check
mimi> cron_start # start cron scheduler now
mimi> restart # reboot
Most ESP32-S3 dev boards expose two USB-C ports:
| Port | Use for |
|---|---|
| USB (JTAG) | idf.py flash, JTAG debugging |
| COM (UART) | REPL CLI, serial console |
REPL requires the UART (COM) port. The USB (JTAG) port does not support interactive REPL input.
Port details & recommended workflow
| Port | Label | Protocol |
|---|---|---|
| USB | USB / JTAG | Native USB Serial/JTAG |
| COM | UART / COM | External UART bridge (CP2102/CH340) |
The ESP-IDF console/REPL is configured to use UART by default (CONFIG_ESP_CONSOLE_UART_DEFAULT=y).
If you have both ports connected simultaneously:
- USB (JTAG) handles flash/download and provides secondary serial output
- UART (COM) provides the primary interactive console for the REPL
- macOS: both appear as
/dev/cu.usbmodem*or/dev/cu.usbserial-*โ runls /dev/cu.usb*to identify - Linux: USB (JTAG) โ
/dev/ttyACM0, UART โ/dev/ttyUSB0
Recommended workflow:
# Flash via USB (JTAG) port
idf.py -p /dev/cu.usbmodem11401 flash
# Open REPL via UART (COM) port
idf.py -p /dev/cu.usbserial-110 monitor
# or use any serial terminal: screen, minicom, PuTTY at 115200 baudMimiClaw stores everything as plain text files you can read and edit:
| File | What it is |
|---|---|
SOUL.md |
The bot's personality โ edit this to change how it behaves |
USER.md |
Info about you โ name, preferences, language |
AGENTS.md |
Behavior rules and safety constraints |
TOOLS.md |
Tool usage policy and priorities |
SKILLS.md |
Skill routing hints and trigger-style instruction rules |
IDENTITY.md |
Assistant identity and response consistency constraints |
HEARTBEAT.md |
Periodic internal task instructions (non-comment lines only) |
CRON.md |
Default cron schedule file (every_minutes + task) |
MEMORY.md |
Long-term memory โ things the bot should always remember |
daily/2026-02-05.md |
Daily notes โ what happened today |
HEARTBEAT.md |
Task list the bot checks periodically and acts on autonomously |
cron.json |
Scheduled jobs โ recurring or one-shot tasks created by the AI |
2026-02-05.md |
Daily notes โ what happened today |
sf0123456789abcdef.j |
Session history file in the current short hashed format (s<channel><hash>.j) |
MimiClaw supports tool calling for both Anthropic and OpenAI โ the LLM can call tools during a conversation and loop until the task is done (ReAct pattern).
| Tool | Description |
|---|---|
web_search |
Search the web via Brave Search API for current information |
get_current_time |
Fetch current date/time via HTTP and set the system clock |
get_device_info |
Read real runtime hardware information, including chip / CPU / flash / PSRAM / GPIO |
read_file |
Read a SPIFFS file (path must start with /spiffs/) |
write_file |
Write or overwrite a SPIFFS file (default allowlist: /spiffs/memory/, /spiffs/skills/) |
edit_file |
Find-and-replace in a SPIFFS file (default allowlist: /spiffs/memory/, /spiffs/skills/) |
list_dir |
List SPIFFS files, optionally filtered by prefix |
memory_write_long_term |
Overwrite long-term memory (/spiffs/memory/MEMORY.md) |
memory_append_today |
Append one note to today's daily memory |
cron_add |
Schedule a recurring or one-shot task (the LLM creates cron jobs on its own) |
cron_list |
List all scheduled cron jobs |
cron_remove |
Remove a cron job by ID |
To enable web search, set a Brave Search API key via MIMI_SECRET_SEARCH_KEY in mimi_secrets.h.
MimiClaw has a built-in cron scheduler that lets the AI schedule its own tasks. The LLM can create recurring jobs ("every N seconds") or one-shot jobs ("at unix timestamp") via the cron_add tool. When a job fires, its message is injected into the agent loop โ so the AI wakes up, processes the task, and responds.
Jobs are persisted to SPIFFS (cron.json) and survive reboots. Example use cases: daily summaries, periodic reminders, scheduled check-ins.
The heartbeat service periodically reads HEARTBEAT.md from SPIFFS and checks for actionable tasks. If uncompleted items are found (anything that isn't an empty line, a header, or a checked - [x] box), it sends a prompt to the agent loop so the AI can act on them autonomously.
This turns MimiClaw into a proactive assistant โ write tasks to HEARTBEAT.md and the bot will pick them up on the next heartbeat cycle (default: every 30 minutes).
- WebSocket gateway on port 18789 โ connect from your LAN with any WebSocket client
- Feishu Bot โ supports both board-side WebSocket long connection and
/feishu/eventswebhook ingress; text passthrough and summary fallback by default, with optional real gateway parsing forimage/file - OTA updates โ flash new firmware over WiFi, no USB needed
- Dual-core โ network I/O and AI processing run on separate CPU cores
- HTTP proxy โ CONNECT tunnel support for restricted networks
- Tool use โ ReAct agent loop with Anthropic tool use protocol
- Telegram media handling โ in the default no-gateway mode, voice/photos/documents fall back to media summaries; if you enable
MIMI_TELEGRAM_GATEWAY_MEDIA_ENABLED=1and runvoice_gateway.py, Telegram can use real STT / vision / doc parsing again - Feishu media handling โ in the default no-gateway mode, media falls back to summaries; if you enable
MIMI_FEISHU_GATEWAY_MEDIA_ENABLED=1and runvoice_gateway.py, Feishuimage/filecan use real download + vision / doc parsing
- Inbound security: Telegram allowlist (
allow_from) + WebSocket auth token - File tool safety boundaries: write default-limited to
/spiffs/memory/ - Reliability: retries/backoff for LLM and outbound delivery; drop status first, preserve final replies
- Budget guards: tool iterations, context size, tool output size, end-to-end timeout caps
- Memory governance: unify daily memory path and add dedicated memory write tools
- Observability:
run_id, stage-level latency logs,agent_statsdiagnostics command
Detailed tracking: docs/TODO.md.
- Multi-provider โ supports both Anthropic (Claude) and OpenAI (GPT), switchable at runtime
- Cron scheduler โ the AI can schedule its own recurring and one-shot tasks, persisted across reboots
- Heartbeat โ periodically checks a task file and prompts the AI to act autonomously
- Tool use โ ReAct agent loop with tool calling for both providers
Technical details live in the docs/ folder:
- docs/ARCHITECTURE.md โ system design, module map, task layout, memory budget, protocols, flash partitions
- docs/TODO.md โ feature gap tracker and roadmap
Please read CONTRIBUTING.md before opening issues or pull requests.
Thanks to everyone who has contributed to MimiClaw.
MIT
Inspired by OpenClaw and Nanobot. MimiClaw reimplements the core AI agent architecture for embedded hardware โ no Linux, no server, just a $5 chip.