Production-ready ESP-IDF component for integrating xAI's Grok models into ESP32 applications. Access 25+ state-of-the-art LLMs with vision, real-time search, function calling, and image generation capabilities — optimized for embedded systems.
- 25+ Grok Models - From fast mini models to powerful grok-4 with reasoning
- Chat Completions - Synchronous and streaming (SSE) responses
- Vision API - Multi-modal image understanding with grok-2-vision
- Image Generation - Create images with grok-2-image models
- Real-Time Search - Web, X (Twitter), News, and RSS grounding with citations
- Function Calling - Client-side tool execution with parallel support
- Responses API - Server-side agentic tools (webSearch, xSearch, codeExecution)
- Tokenization - Count tokens and estimate memory usage
- Conversation Helpers - Simplified multi-turn dialogue management
- Low Memory Footprint - Configurable buffers (2KB-32KB)
- Menuconfig Integration - Full ESP-IDF configuration system
- FreeRTOS Compatible - Thread-safe buffer pooling
- Modular Features - Disable unused features to save flash (~10KB savings)
- Automatic Retries - Configurable retry logic for reliability
- Debug Logging - Comprehensive ESP-LOG integration
- Installation
- Quick Start
- Hardware Requirements
- Configuration
- API Overview
- Examples
- Memory Management
- Model Selection
- Advanced Features
- Troubleshooting
- Contributing
- License
Add to your project's idf_component.yml:
dependencies:
plaipin/xai:
version: "^0.1.0"Or via command line:
idf.py add-dependency "plaipin/xai^0.1.0"Clone into your project's components directory:
cd your_project/components
git clone https://github.com/PlaiPin/xai.gitVisit console.x.ai to obtain your xAI API key.
Run idf.py menuconfig and navigate to:
xAI Configuration→Default xAI API Key→ Enter your API keyComponent config→ESP System Settings→Main task stack size→ Set to12288
#include "xai.h"
#include <string.h>
#include <stdio.h>
void app_main(void)
{
// Initialize WiFi first (not shown)
// Create xAI client
xai_client_t client = xai_create("xai-your-api-key");
if (!client) {
printf("Failed to create client\n");
return;
}
// Send a message
char *response = NULL;
size_t response_len = 0;
xai_err_t err = xai_text_completion(
client,
"Explain ESP32 in one sentence",
&response,
&response_len
);
if (err == XAI_OK) {
printf("Grok: %s\n", response);
free(response);
} else {
printf("Error: %s\n", xai_err_to_string(err));
}
xai_destroy(client);
}idf.py build
idf.py flash monitor- ESP32, ESP32-S2, ESP32-S3, or ESP32-C3
- 240KB RAM (for basic chat with grok-3-mini)
- WiFi connectivity
- TLS/SSL support (built into ESP-IDF)
- ESP32-S3 with 8MB Flash and 8MB PSRAM
- 320KB+ RAM available
- Stable WiFi with good signal strength
| Model | Recommended RAM | Best For |
|---|---|---|
grok-3-mini-latest |
240KB | Resource-constrained devices |
grok-3-fast-latest |
280KB | Low-latency responses |
grok-3-latest |
320KB | General-purpose (balanced) |
grok-4-latest |
400KB+ | Advanced reasoning tasks |
grok-2-vision-latest |
320KB | Image understanding |
All settings are configurable via idf.py menuconfig under xAI Configuration.
xAI Configuration
├── Default xAI API Key [Your API key]
├── xAI API Base URL [https://api.x.ai/v1]
├── Default Model [grok-3-latest]
│
├── Memory Configuration
│ ├── Maximum response size [8192 bytes]
│ └── Buffer pool size [2 buffers]
│
├── Feature Toggles
│ ├── Enable streaming [Yes]
│ ├── Enable vision [Yes]
│ ├── Enable tools [No]
│ ├── Enable search [Yes]
│ └── Enable conversation helper [Yes]
│
└── Network Settings
├── HTTP timeout [60000 ms]
└── Max retries [3]
For ESP32 with limited RAM (240KB-280KB):
Memory Configuration
├── Max response size: 2048-4096 bytes
├── Buffer pool: 1 buffer
Feature Toggles
├── Disable vision (saves ~1KB flash)
├── Disable tools (saves ~3KB flash)
└── Disable search (saves ~3KB flash)
For ESP32-S3 with PSRAM (512KB+):
Memory Configuration
├── Max response size: 16384-32768 bytes
├── Buffer pool: 3-4 buffers
Feature Toggles
├── Enable all features
// Create with default configuration
xai_client_t client = xai_create(api_key);
// Create with custom configuration
xai_config_t config = xai_config_default();
config.default_model = "grok-3-mini-fast-latest";
config.timeout_ms = 30000;
config.max_tokens = 512;
xai_client_t client = xai_create_config(&config);
// Destroy when done
xai_destroy(client);char *response = NULL;
xai_err_t err = xai_text_completion(client, "Hello!", &response, NULL);
if (err == XAI_OK) {
printf("%s\n", response);
free(response);
}xai_message_t messages[] = {
{.role = XAI_ROLE_SYSTEM, .content = "You are a helpful assistant."},
{.role = XAI_ROLE_USER, .content = "What is FreeRTOS?"}
};
xai_options_t options = xai_options_default();
options.temperature = 0.8f;
options.max_tokens = 200;
xai_response_t response;
xai_err_t err = xai_chat_completion(client, messages, 2, &options, &response);
if (err == XAI_OK) {
printf("Response: %s\n", response.content);
printf("Tokens used: %u\n", response.total_tokens);
xai_response_free(&response);
}void stream_callback(const char *chunk, size_t len, void *user_data) {
if (chunk == NULL) {
printf("\n[Stream complete]\n");
} else {
printf("%.*s", (int)len, chunk);
fflush(stdout);
}
}
xai_options_t options = xai_options_default();
options.stream = true;
xai_err_t err = xai_chat_completion_stream(
client, messages, count, &options, stream_callback, NULL
);xai_image_t image = {
.url = "https://example.com/image.jpg",
.detail = "auto"
};
xai_response_t response;
xai_err_t err = xai_vision_completion(
client,
"What's in this image?",
&image,
1,
&response
);
if (err == XAI_OK) {
printf("Analysis: %s\n", response.content);
xai_response_free(&response);
}// Simple web search
xai_response_t response;
xai_err_t err = xai_web_search(
client,
"What are the latest developments in ESP32?",
XAI_SEARCH_AUTO,
true, // return citations
&response
);
if (err == XAI_OK) {
printf("Response: %s\n", response.content);
// Display citations
for (size_t i = 0; i < response.citation_count; i++) {
printf("[%zu] %s\n", i+1, response.citations[i].url);
}
xai_response_free(&response);
}// X (Twitter) search with filters
xai_search_params_t *params = xai_search_params_x(
XAI_SEARCH_AUTO,
true, // return citations
NULL // all handles (or specify array)
);
xai_chat_completion_with_search(client, messages, count, params, &response);
xai_search_params_free(params);
// News search with country filter
params = xai_search_params_news(XAI_SEARCH_AUTO, true, "US");
// RSS search
params = xai_search_params_rss(XAI_SEARCH_AUTO, true, "https://feeds.example.com/rss");// Define tools
xai_tool_t tools[] = {
{
.name = "get_weather",
.description = "Get current weather for a location",
.parameters_json =
"{\"type\":\"object\","
"\"properties\":{"
"\"location\":{\"type\":\"string\"},"
"\"units\":{\"type\":\"string\",\"enum\":[\"celsius\",\"fahrenheit\"]}"
"},"
"\"required\":[\"location\"]}"
}
};
xai_response_t response;
xai_err_t err = xai_chat_completion_with_tools(
client, messages, count, tools, 1, &response
);
if (err == XAI_OK && response.tool_call_count > 0) {
// Execute tool calls
for (size_t i = 0; i < response.tool_call_count; i++) {
xai_tool_call_t *call = &response.tool_calls[i];
printf("Tool: %s\n", call->name);
printf("Args: %s\n", call->arguments);
// Execute function and return result...
}
}
xai_response_free(&response);xai_image_request_t request = {
.prompt = "A futuristic ESP32 microcontroller with glowing circuits",
.model = "grok-2-image-latest",
.n = 1,
.response_format = "url"
};
xai_image_response_t response;
xai_err_t err = xai_generate_image(client, &request, &response);
if (err == XAI_OK) {
for (size_t i = 0; i < response.image_count; i++) {
printf("Image URL: %s\n", response.images[i].url);
}
xai_image_response_free(&response);
}// Create conversation with system prompt
xai_conversation_t conv = xai_conversation_create(
"You are an expert in embedded systems."
);
// Add messages
xai_conversation_add_user(conv, "What is MQTT?");
// Get response (automatically includes history)
xai_response_t response;
xai_err_t err = xai_conversation_complete(client, conv, &response);
if (err == XAI_OK) {
// Add assistant response to history
xai_conversation_add_assistant(conv, response.content);
printf("Grok: %s\n", response.content);
xai_response_free(&response);
}
// Continue conversation
xai_conversation_add_user(conv, "How do I implement it on ESP32?");
xai_conversation_complete(client, conv, &response);
// ...
// Clean up
xai_conversation_destroy(conv);All examples include complete WiFi setup and error handling.
| Example | Description | Key Features |
|---|---|---|
| basic_chat | Simple text completion | Minimal setup, quick start |
| streaming_chat | Real-time streaming | SSE callbacks, live output |
| conversation | Multi-turn dialogue | History management, context |
| vision | Image understanding | Multi-modal, URL/base64 images |
| web_search | Grounded responses | Web/X/News search, citations |
| tools | Function calling | Client-side tools, parallel execution |
| image_generation | Create images | Text-to-image, URL/base64 output |
cd examples/basic_chat
idf.py menuconfig # Configure WiFi and API key
idf.py build flash monitorThe component uses a buffer pool for response handling:
// Configured in menuconfig
CONFIG_XAI_MAX_RESPONSE_SIZE = 8192 // Per-buffer size
CONFIG_XAI_BUFFER_POOL_SIZE = 2 // Number of buffersTotal heap usage: MAX_RESPONSE_SIZE × BUFFER_POOL_SIZE + ~20KB overhead
| Application Type | Response Size | Pool Size | Total RAM |
|---|---|---|---|
| Simple Q&A | 2048-4096 | 1 | ~25KB |
| General chat | 8192 | 2 | ~36KB |
| Long-form content | 16384 | 2 | ~52KB |
| Multi-threaded | 8192 | 3-4 | ~44-60KB |
Always free responses to prevent memory leaks:
xai_response_t response;
xai_chat_completion(client, messages, count, NULL, &response);
// Use response...
xai_response_free(&response); // Free all allocated memory// Count tokens before sending
uint32_t token_count;
xai_err_t err = xai_count_tokens(client, prompt, NULL, &token_count);
// Estimate memory needed
size_t estimated_bytes = xai_estimate_memory(token_count);
printf("Estimated memory: %u bytes\n", estimated_bytes);grok-4-latest- Advanced reasoning with thinking process (128K context)grok-4-fast- Faster version with reasoning (128K context)grok-4-fast-non-reasoning- No reasoning output (128K context)
grok-3-latest- Balanced performance (128K context) - RECOMMENDEDgrok-3-mini-latest- Efficient, low memory (128K context) - BEST FOR ESP32grok-3-fast-latest- Low latency (128K context)grok-3-mini-fast-latest- Minimal + fast (128K context)
grok-2-1212- December 2024 release (131K context)grok-2-vision-1212- Vision capabilities (8K context)
grok-2-image-latest- Text-to-image generation
// At client creation
xai_config_t config = xai_config_default();
config.default_model = "grok-3-mini-fast-latest";
xai_client_t client = xai_create_config(&config);
// Per request
xai_options_t options = xai_options_default();
options.model = "grok-4-latest";
xai_chat_completion(client, messages, count, &options, &response);const xai_model_info_t *info = xai_get_model_info("grok-3-latest");
if (info) {
printf("Model: %s\n", info->id);
printf("Max tokens: %u\n", info->max_tokens);
printf("Supports vision: %s\n", info->supports_vision ? "Yes" : "No");
printf("Supports tools: %s\n", info->supports_tools ? "Yes" : "No");
}Control thinking depth for grok-4 models:
xai_options_t options = xai_options_default();
options.model = "grok-4-latest";
options.reasoning_effort = "high"; // "low" or "high"
xai_chat_completion(client, messages, count, &options, &response);
// Access reasoning process
if (response.reasoning_content) {
printf("Thinking: %s\n", response.reasoning_content);
}Enable multiple simultaneous function calls:
xai_options_t options = xai_options_default();
options.parallel_function_calling = true;
options.tools = tool_array;
options.tool_count = tool_count;
xai_chat_completion(client, messages, count, &options, &response);
// Process multiple tool calls
for (size_t i = 0; i < response.tool_call_count; i++) {
execute_tool(&response.tool_calls[i]);
}Let xAI execute tools on their servers:
// Define server-side tools
xai_tool_t tools[] = {
xai_tool_web_search(NULL, NULL, false), // Web search
xai_tool_x_search(NULL, NULL, NULL, NULL, false, false), // X search
xai_tool_code_execution() // Python execution
};
xai_options_t options = xai_options_default();
options.model = "grok-4-latest"; // Requires grok-4
options.tools = tools;
options.tool_count = 3;
// xAI orchestrates tool execution automatically
xai_responses_completion(client, messages, count, tools, 3, &response);Note: Responses API requires significant memory (~5-8KB additional) and only works with grok-4 models. Not recommended for standard ESP32 unless specifically needed.
xai_search_source_t sources[2];
// Web source with domain filtering
sources[0].type = XAI_SOURCE_WEB;
sources[0].web.allowed_websites = (const char*[]){
"espressif.com", "esp32.com", NULL
};
sources[0].web.safe_search = true;
// X source with engagement filters
sources[1].type = XAI_SOURCE_X;
sources[1].x.post_favorite_count_min = 100; // Min 100 likes
sources[1].x.enable_image_understanding = true;
xai_search_params_t params = {
.mode = XAI_SEARCH_AUTO,
.return_citations = true,
.sources = sources,
.source_count = 2,
.max_results = 10
};xai_search_params_t *params = xai_search_params_news(XAI_SEARCH_ON, true, "US");
params->from_date = "2024-01-01";
params->to_date = "2024-12-31";
xai_chat_completion_with_search(client, messages, count, params, &response);Cause: Insufficient heap memory.
Solutions:
- Reduce
CONFIG_XAI_MAX_RESPONSE_SIZEin menuconfig - Decrease
CONFIG_XAI_BUFFER_POOL_SIZE - Use
grok-3-miniorgrok-3-mini-fastmodels - Free unused resources before API calls
- Enable PSRAM on ESP32-S3
Cause: Network connectivity issues.
Solutions:
- Verify WiFi is connected:
esp_netif_is_netif_up() - Check internet access:
ping 8.8.8.8 - Verify DNS resolution:
ping api.x.ai - Increase timeout:
CONFIG_XAI_HTTP_TIMEOUT_MS - Check firewall/proxy settings
Cause: Invalid or expired API key.
Solutions:
- Verify API key at console.x.ai
- Check for typos in key (should start with
xai-) - Ensure key is correctly set in menuconfig or code
- Regenerate key if compromised
Cause: Too many requests.
Solutions:
- Implement exponential backoff
- Reduce request frequency
- Check rate limits at console.x.ai
- Use streaming to reduce perceived latency
Cause: Request took too long.
Solutions:
- Increase
CONFIG_XAI_HTTP_TIMEOUT_MS(default: 60s) - Use faster models (
grok-3-fast,grok-3-mini-fast) - Reduce
max_tokensin options - Check network latency
Cause: Task stack too small.
Solutions:
- Increase main task stack:
CONFIG_ESP_MAIN_TASK_STACK_SIZE = 12288 - Increase custom task stack when creating tasks
- Reduce local variable sizes
- Use heap allocation for large buffers
menuconfig → xAI Configuration → Logging → Log level = 5 (Verbose)
printf("Free heap: %u bytes\n", esp_get_free_heap_size());
printf("Min free heap: %u bytes\n", esp_get_minimum_free_heap_size());
xai_chat_completion(client, messages, count, NULL, &response);
printf("After API call: %u bytes\n", esp_get_free_heap_size());esp_err_t err = esp_netif_init();
if (err != ESP_OK) {
printf("Network init failed: %s\n", esp_err_to_name(err));
}To minimize flash usage when building:
# Optimize for size
idf.py menuconfig
# Compiler options → Optimization Level → Optimize for size (-Os)
# Disable unused features
menuconfig → xAI Configuration → Feature Toggles
# Disable vision, tools, search if not neededFor large responses or multi-threading:
menuconfig → Component config → ESP32S3-Specific
# Support for external, SPI-connected RAM → Yes
# SPI RAM config → Initialize SPI RAM during startupContributions are welcome! Please see CONTRIBUTING.md for guidelines.
git clone https://github.com/PlaiPin/xai.git
cd xai
# Run examples
cd examples/basic_chat
idf.py buildPlease include:
- ESP-IDF version (
idf.py --version) - ESP32 variant (ESP32, ESP32-S3, etc.)
- Component version
- Full error logs with verbose logging enabled
- Minimal reproducible example
Open issues at: github.com/PlaiPin/xai/issues
This project is licensed under the Apache License 2.0 - see the LICENSE file for details.
- xAI Documentation: docs.x.ai
- xAI Console: console.x.ai
- ESP-IDF Documentation: docs.espressif.com/projects/esp-idf
- Component Registry: components.espressif.com
- GitHub Repository: github.com/PlaiPin/xai
This ESP-IDF component follows the design patterns of the Vercel AI SDK but is specifically optimized for embedded systems:
| Feature | Vercel AI SDK | xAI ESP-IDF Component |
|---|---|---|
| Platform | Node.js / Edge Runtime | ESP32 / FreeRTOS |
| Memory | Unlimited (V8 heap) | Configurable (2KB-32KB) |
| Streaming | ReadableStream API | SSE callbacks |
| Language | TypeScript | C (C99) |
| Tools | Native JavaScript | JSON schema + callbacks |
| Configuration | Runtime only | menuconfig + runtime |
| Transport | fetch() / Node http | esp_http_client |
Like the Vercel AI SDK, this component prioritizes:
- Type safety (via C structs with clear documentation)
- Ease of use (simple APIs with sensible defaults)
- Provider flexibility (easy to extend/modify)
- Streaming first (efficient SSE implementation)
But adapted for embedded constraints:
- Compile-time optimization (disable unused features)
- Explicit memory management (no garbage collection)
- Low-level control (direct buffer access, configurable pooling)
- FreeRTOS integration (task-safe, multi-threaded)
While xAI's API is OpenAI-compatible, some parameters are not supported:
stop(stop sequences)presence_penaltyfrequency_penaltyuser(user identification)size/quality/style(image generation)
These fields exist in the API structs but are ignored by the xAI API.
reasoning_effort(grok-4 models)parallel_function_calling(tool execution)- Search/grounding parameters (web, X, news, RSS)
- Responses API (server-side tools)