DEV Community

Cover image for Control your ESP32 from an AI agent: MCP + a few lines of C++
Shaxzod Ahmedov
Shaxzod Ahmedov

Posted on

Control your ESP32 from an AI agent: MCP + a few lines of C++

Language models are finally good enough to act, not just chat — but giving one hands on real
hardware is still more plumbing than it should be. In this post I'll wire an ESP32 to an AI
agent using the Model Context Protocol (MCP), where every dashboard widget I declare in C++
becomes a tool the agent can call: read a temperature, flip a relay, set a threshold. The same
few lines also give me a real-time web dashboard for humans.

MCP in one paragraph

MCP is a small open protocol that lets an AI client (Claude Desktop, Claude Code, Cursor…)
discover and call tools exposed by a server. A tool is just a named function with a schema —
get_temperature, set_pump, and so on. The agent lists the tools, then calls them. That's it.

The library: declare widgets, get tools for free

I use RisalDash — an ESP32/ESP8266 library where you
declare widgets in C++ and it generates the whole web UI and the protocol. The same widget
declaration that powers the human dashboard also defines the agent's tools.

cpp
#include <RisalUI.h>
RisalUI dash("Greenhouse");

float temp = 24.3; bool pump = false; int target = 24;

void setup() {
  dash.gauge ("Temperature", &temp, 0, 50, "C");
  dash.toggle("Pump", &pump, [](bool on){ digitalWrite(PUMP_PIN, on); });
  dash.number("Target", &target, 10, 35);

  dash.enableMCP("a_secret_token");   // <-- this line turns the widgets into MCP tools
  dash.begin();
}

void loop() { dash.update(); }
Enter fullscreen mode Exit fullscreen mode

enableMCP() exposes GET /api/mcp/manifest on the device: every widget becomes a get_*
(read) and/or set_* (write) tool.

Connect an agent

The device speaks its own little manifest; a tiny bridge turns that into a real MCP server over
stdio. It's published on npm:

RISAL_ESP_URL=http://192.168.1.42 \
RISAL_MCP_TOKEN=a_secret_token \
npx risal-dash-mcp
Enter fullscreen mode Exit fullscreen mode

In Claude Desktop's config:

{
  "mcpServers": {
    "greenhouse": {
      "command": "npx",
      "args": ["risal-dash-mcp"],
      "env": {
        "RISAL_ESP_URL": "http://192.168.1.42",
        "RISAL_MCP_TOKEN": "a_secret_token"
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Now you can say:

"What's the greenhouse temperature, and turn the pump on if it's above 26°C."

The agent calls get_temperature, reasons, and calls set_pump — on your actual ESP32.

Why it stays tiny and offline

A nice side effect: the dashboard the device serves is offline-first. On first boot it raises
a Wi-Fi access point with a captive portal, you pick your network, and from then on it serves a
real-time UI from flash — system fonts, no CDN, no external requests. Values stream over WebSocket;
controls call back into your code.

It's also Zero-Waste: the linker (--gc-sections) strips widget types you don't use, so a
type you don't call costs 0 bytes, and one you do adds ~1.3–3.4 KB (its C++ + CSS + JS).
Builds for both ESP32 and ESP8266.

Try it without hardware

There's a live, code-to-dashboard demo you can poke in the browser (no board needed):
https://dash.risal.io/showcase

If you build something with it — or have ideas for the API or which device "tools" an LLM should
get — I'd love to hear it.

Top comments (0)