Embed an AI agent into your Arduino/ESP32 projects with ease.
DuinoClaw lets your ESP32 talk to an LLM (OpenAI), maintain conversation history, and execute custom tools (functions). Responses are dispatched on the main Arduino task via Claw.loop().
- ESP32 (Arduino framework via PlatformIO)
- Libraries:
ArduinoJson
Add to your platformio.ini:
lib_deps =
ArduinoJson
https://github.com/ArtronShop/DuinoClaw#include <Arduino.h>
#include <WiFi.h>
#include <DuinoClaw.h>
#include <Tools/GPIOTool.h>
void setup() {
Serial.begin(115200);
WiFi.begin("ssid", "password");
while (!WiFi.isConnected()) delay(50);
// Declare tools as static inside setup()
static GPIOTool gpio_tool;
Claw.registerTool(&gpio_tool);
Claw.onResponses([](bool ok, String message) {
Serial.println(ok ? "AI: " + message : "Error: " + message);
});
Claw.begin(OPEN_AI, GPT_5_4_MINI, "sk-...");
Claw.prompt("Set GPIO 5 to HIGH");
}
void loop() {
Claw.loop();
}Note: Always declare
Tool,ToolSet, and built-in tool objects asstaticinsidesetup(), not at the global scope. Global objects are constructed beforesetup()runs, which can cause a crash because the system is not fully initialized yet.
Initialize the AI agent.
| Parameter | Type | Options |
|---|---|---|
provider |
LLM_Provider_t |
OPEN_AI |
model |
LLM_Model_t |
GPT_5_5, GPT_5_4, GPT_5_4_MINI |
api_key |
const char* |
Your OpenAI API key |
Send a message to the AI.
wait = false(default) — async, result delivered viaonResponseswait = true— blocks until response received, returns the response string
Register a callback for AI responses.
Claw.onResponses([](bool ok, String message) {
// ok = false if an error occurred
// message = AI reply text, or error description
});Must be called in loop() to dispatch response callbacks on the main task.
Override the default system prompt before calling begin().
Claw.setSystemMessage("You are a helpful assistant for a smart home system.");
Claw.begin(OPEN_AI, GPT_5_4_MINI, api_key);Returns true while the AI is waiting for a response.
Start an interactive console on a Stream (e.g. Serial). Reads lines and sends them as prompts.
Claw.startConsole(Serial);Include and register any combination of built-in tools with Claw.registerTool().
Note: Declare all tool objects as
staticinsidesetup(). Do not declare them at the global scope.
Gets the current date and time via NTP.
#include <Tools/GetCurrentTimeTool.h>
void setup() {
// ...
static GetCurrentTimeTool time_tool(7 /* timezone offset */);
Claw.registerTool(&time_tool);
}| Parameter | Default | Description |
|---|---|---|
timezone |
7 |
UTC offset in hours |
ntp_server1 |
pool.ntp.org |
Primary NTP server |
Provides WiFi information tools.
#include <Tools/WiFiTool.h>
void setup() {
// ...
static WiFiTool wifi_tool;
Claw.registerTool(&wifi_tool);
}| Tool name | Description |
|---|---|
wifi_get_ip |
Get device IP address |
wifi_get_rssi |
Get signal strength (dBm) |
wifi_get_ssid |
Get connected network name |
wifi_get_mac |
Get device MAC address |
Provides GPIO control and reading tools.
#include <Tools/GPIOTool.h>
void setup() {
// ...
static GPIOTool gpio_tool;
Claw.registerTool(&gpio_tool);
}| Tool name | Parameters | Description |
|---|---|---|
gpio_digital_write |
pin, level (0/1) |
Set pin HIGH or LOW |
gpio_digital_read |
pin |
Read pin state (0 or 1) |
gpio_pwm_write |
pin, duty (0-255), frequency (Hz) |
Set PWM output |
gpio_analog_read |
pin |
Read ADC value (0-4095) |
gpio_pulse_in |
pin, state (0/1), timeout_us |
Measure pulse duration |
Override the system prompt to give the AI a specific role.
#include <Arduino.h>
#include <WiFi.h>
#include <DuinoClaw.h>
#include <Tools/GetCurrentTimeTool.h>
#include <Tools/GPIOTool.h>
void setup() {
Serial.begin(115200);
WiFi.begin("ssid", "password");
while (!WiFi.isConnected()) delay(50);
Claw.setSystemMessage(
"You are a smart home controller running on an ESP32. "
"Control GPIO pins and report sensor data. "
"Keep responses short and in plain text only."
);
static GetCurrentTimeTool time_tool(7);
Claw.registerTool(&time_tool);
static GPIOTool gpio_tool;
Claw.registerTool(&gpio_tool);
Claw.onResponses([](bool ok, String message) {
if (ok) {
Serial.println(message);
}
});
Claw.begin(OPEN_AI, GPT_5_4_MINI, "sk-...");
Claw.startConsole(Serial);
}
void loop() {
Claw.loop();
}Create a custom tool with parameters and an enum property.
#include <Arduino.h>
#include <WiFi.h>
#include <DuinoClaw.h>
static const char * COLORS[] = { "red", "green", "blue" };
void setup() {
Serial.begin(115200);
WiFi.begin("ssid", "password");
while (!WiFi.isConnected()) delay(50);
static Tool led_tool("set_led_color", "Set the RGB LED color");
led_tool.addEnumProperty("color", "LED color to set", COLORS, 3, true);
led_tool.onCall([](JsonObject args) -> String {
String color = args["color"].as<String>();
if (color == "red") { digitalWrite(25, HIGH); digitalWrite(26, LOW); digitalWrite(27, LOW); }
if (color == "green") { digitalWrite(25, LOW); digitalWrite(26, HIGH); digitalWrite(27, LOW); }
if (color == "blue") { digitalWrite(25, LOW); digitalWrite(26, LOW); digitalWrite(27, HIGH); }
return "LED set to " + color;
});
Claw.registerTool(&led_tool);
Claw.onResponses([](bool ok, String message) {
Serial.println(ok ? message : "Error: " + message);
});
Claw.begin(OPEN_AI, GPT_5_4_MINI, "sk-...");
Claw.prompt("Set the LED to blue");
}
void loop() {
Claw.loop();
}Control your ESP32 through Telegram using all available built-in tools.
#include <Arduino.h>
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <DuinoClaw.h>
#include <Tools/GetCurrentTimeTool.h>
#include <Tools/WiFiTool.h>
#include <Tools/GPIOTool.h>
#include <UniversalTelegramBot.h>
#define BOT_TOKEN "your_bot_token"
WiFiClientSecure secured_client;
UniversalTelegramBot bot(BOT_TOKEN, secured_client);
String last_chat_id;
void setup() {
Serial.begin(115200);
WiFi.begin("your_ssid", "your_password");
secured_client.setCACert(TELEGRAM_CERTIFICATE_ROOT);
while (!WiFi.isConnected()) delay(50);
// Sync time — required for Telegram TLS
configTime(0, 0, "pool.ntp.org");
time_t now = time(nullptr);
while (now < 24 * 3600) { delay(100); now = time(nullptr); }
static GetCurrentTimeTool time_tool(7);
Claw.registerTool(&time_tool);
static WiFiTool wifi_tool;
Claw.registerTool(&wifi_tool);
static GPIOTool gpio_tool;
Claw.registerTool(&gpio_tool);
Claw.onResponses([](bool ok, String message) {
if (last_chat_id.length() > 0) {
bot.sendMessage(last_chat_id, ok ? message : "Error: " + message);
}
});
Claw.begin(OPEN_AI, GPT_5_4_MINI, "your_openai_api_key");
}
void loop() {
Claw.loop();
static unsigned long last_check = 0;
if (millis() - last_check >= 1000) {
int n = bot.getUpdates(bot.last_message_received + 1);
while (n) {
for (int i = 0; i < n; i++) {
last_chat_id = bot.messages[i].chat_id;
String text = bot.messages[i].text;
if (text == "/start") {
bot.sendMessage(last_chat_id, "Hello! I am your ESP32 AI assistant.");
} else if (!Claw.isProcessing()) {
bot.sendMessage(last_chat_id, "Processing...");
Claw.prompt(text);
}
}
n = bot.getUpdates(bot.last_message_received + 1);
}
last_check = millis();
}
}| Type | Constant |
|---|---|
| String | Tool::TYPE_STRING |
| Number (float) | Tool::TYPE_NUMBER |
| Integer | Tool::TYPE_INTEGER |
MIT — ArtronShop Co., Ltd.