How a $5 chip runs 20 tools, a rule engine, rule chains, and an AI agent. Cloud optional.
Two systems on one chip. The AI creates. The rules execute.
The main loop() runs two systems. The Rule Loop evaluates automation rules every iteration - no network, no latency. The AI Loop activates only when a message arrives via Telegram, serial, or NATS.
Persistent automation. Reads sensors, checks conditions (gt, lt, eq, neq, change, always), fires actions (actuator, LED, GPIO, NATS, Telegram, Serial). Edge-triggered conditions fire once per transition, then auto-reset. Chain rules together with delays via chain_create. Message interpolation: {value}, {device_name}, {name:msg}. Survives reboots.
LLM via OpenRouter, Ollama, llama.cpp, or any OpenAI-compatible endpoint. HTTPS for cloud, HTTP for local (saves ~40% RAM). 20 tools. Up to 5 iterations per request. Creates rules, registers devices, reads sensors, writes files. The AI is the compiler - it turns intent into persistent automation.
Named sensors and actuators persisted to flash. Register once, reference by name everywhere. chip_temp is pre-registered on first boot. Eight sensor types, three actuator types. Clock sensors auto-register on boot.
Virtual clock sensors (clock_hour, clock_minute, clock_hhmm) auto-register on boot. NTP-synced with timezone and DST. Schedule anything - the same rule engine that watches temperature can watch the clock.
Devices talk to each other over NATS. Register nats_value sensors that subscribe to external subjects and feed data into the rule engine. The remote_chat tool lets one device's AI ask another a natural language question. Publish sensor data, trigger cross-device rules, build a mesh of $5 autonomous agents.
What it can read, what it can control, and how rules react.
Digital input pin. Returns HIGH (1) or LOW (0).
Analog input via ADC. Returns raw value (0-4095).
10K NTC thermistor. Automatic temperature conversion.
Light-dependent resistor. Returns light level reading.
Chip's internal temperature sensor. No external hardware needed.
NATS-subscribed sensor. Receives values from any NATS subject. No GPIO pin.
Text lines from UART1 serial port. Connects Arduinos, GPS, CO2 sensors. One device max.
Current hour (0-23). NTP-synced, POSIX timezone + DST.
Current minute (0-59). Updates every rule evaluation cycle.
Combined time as hour*100+minute (e.g., 1830 = 6:30 PM).
Digital output pin. Set HIGH or LOW.
Relay control. On/off switching with optional inverted logic.
PWM output. Variable duty cycle for dimmers, motors, servos.
Set a registered actuator's value by name. Auto on/off.
Set the onboard RGB LED to any color. Takes R,G,B values.
Write HIGH or LOW to a GPIO pin directly.
Publish a message to a NATS subject. Rule-triggered device-to-device messaging.
Send a Telegram alert. Configurable cooldown prevents flooding.
Send text over serial_text UART. Supports {value} and {device_name} interpolation.
Chain up to 5 actions with delays in a single tool call. The AI creates the chain. The firmware runs it.
Each tool maps to a real hardware or system capability.
led_set, gpio_write, gpio_read, temperature_read
device_register, device_list, device_remove, sensor_read, actuator_set
rule_create, rule_list, rule_delete, rule_enable, chain_create
device_info, file_read, file_write
nats_publish, serial_send, remote_chat
The onboard LED tells you what the agent is doing at a glance.
A 6-turn circular buffer stored on flash. Context persists across power cycles.
← oldest ··· newest → (write head)
Long-term preferences in /memory.txt on flash. The AI writes autonomously. Injected into every conversation. 512 chars. Survives reboots.
| MCU | ESP32-C6, ESP32-S3, ESP32-C3 (4MB flash) |
|---|---|
| Framework | Arduino + PlatformIO |
| Language | C++ (Arduino-flavored) |
| RAM Usage | ~280 KB with active conversation |
| Tools | 20 built-in (hardware, registry, rules, chains, filesystem, messaging) |
| Rule Engine | 7 conditions (incl. always/periodic, chained), 6 action types, edge-triggered, message interpolation ({value}, {device_name}, {name:msg}), /rules.json |
| Rule Chaining | chain_create tool, up to 5 steps with non-blocking delays, max depth 8, 672 bytes RAM, persisted to /rules.json |
| Device Registry | 10 sensor types (incl. 3 virtual clock, NATS, serial), 3 actuator types, persisted to flash |
| Serial Bridge | UART1, serial_text sensor + serial_send tool/action, 9600-115200 baud, text + JSON parsing |
| Clock Sensors | clock_hour, clock_minute, clock_hhmm - NTP-synced, POSIX timezone + DST |
| Telegram Alerts | Rule-triggered, no LLM, configurable cooldown, long polling, message interpolation |
| Persistent Memory | /memory.txt on flash, 512 char limit, AI-managed |
| JSON Parsing | Streaming token-by-token (ArduinoJson) |
| Buffer Strategy | Chunked HTTP reads, reused scratch buffers |
| Flash Storage | LittleFS partition for config, history, rules, and device registry |
| API Protocol | HTTPS with TLS 1.2, OpenAI-compatible chat completions, or HTTP for local LLMs (Ollama, llama.cpp) |
| NATS Support | Publish, subscribe, request/reply over TCP |
| NATS Sensors | nats_value type - subscribe to external NATS subjects, feed readings into rule engine, no hardware pin needed |
| Local LLM | Ollama, llama.cpp, any OpenAI-compatible endpoint. HTTP mode saves ~40% RAM. No API key needed. |
| Configuration | JSON config on flash - WiFi, API key, model, system prompt |
| Web Config | Browser UI at http://device.local/ - config, prompt, memory, status. REST API for scripting. |
| History Buffer | 6-turn circular buffer persisted to flash |
Three steps from zero to a working AI agent.
Clone the repo and upload with PlatformIO: pio run -t upload
Edit system_prompt.txt and config.json with your WiFi credentials and model. Add an API key for cloud LLMs, or point to a local Ollama endpoint - no key needed. Upload with pio run -t uploadfs
Send natural language via Telegram, serial console, or NATS - the agent handles the rest.
Change config, prompt, and memory from any browser - no USB, no reflashing. REST API for automation.