Command Palette

Search for a command to run...

UnylyUnyly
Весь каталог

LVGL ESP32 Simulator

БесплатноНе проверен

Headless LVGL simulator for ESP32 UI development that compiles C code, renders PNG screenshots, and exports widget trees without physical hardware.

GitHubEmbed

Описание

Headless LVGL simulator for ESP32 UI development that compiles C code, renders PNG screenshots, and exports widget trees without physical hardware.

README

MCP (Model Context Protocol) server that gives Claude visual feedback when writing LVGL UI code for ESP32. It compiles C code snippets in a headless LVGL simulator on Windows and Linux, captures a PNG screenshot and a JSON widget tree, and returns them through the MCP protocol. No hardware, no flashing, no SDL window needed.

┌─────────────┐     stdio (JSON-RPC)    ┌──────────────────┐
│  Claude Code │◄───────────────────────►│  MCP Server      │
│  (client)    │                         │  (Node.js)       │
└─────────────┘                          └────────┬─────────┘
                                                  │ compile + run
                                         ┌────────▼─────────┐
                                         │  LVGL Simulator   │
                                         │  (headless C)     │
                                         │  → PNG + JSON     │
                                         └──────────────────┘

Quick start (npm)

npm install -g lvgl-mcp-server

The simulator binary is downloaded automatically from GitHub Releases during install.

Add to your Claude Code config (.claude/settings.json or global settings):

{
  "mcpServers": {
    "lvgl-simulator": {
      "command": "npx",
      "args": ["lvgl-mcp-server"]
    }
  }
}

Done. Claude now has lvgl_render, lvgl_render_full, lvgl_inspect, and other LVGL tools.

Prerequisites

The prebuilt binary ships for Windows x64 and Linux x64. To build the simulator from source you need a C toolchain, CMake, and a build generator.

Windows

Tool Version Notes
Windows 10/11 x64 No WSL required
Visual Studio Build Tools 2019+ Only the C/C++ build tools workload (cl.exe)
CMake 3.16+ Included with ESP-IDF or install separately
Ninja 1.10+ Included with ESP-IDF or install separately
Node.js 18+ For the MCP server

If you have ESP-IDF installed, CMake and Ninja are already available.

Linux

Tool Version Notes
Linux x64 Any modern distribution
gcc or clang recent C compiler (cc by default; override with CC)
CMake 3.16+ Build configuration
Ninja or Make any Ninja preferred; falls back to Unix Makefiles
Node.js 18+ For the MCP server

On Debian/Ubuntu, install the build dependencies in one line:

sudo apt install build-essential cmake ninja-build

Setup from source (alternative)

If you prefer to build from source instead of using npm:

1. Clone

Windows (PowerShell):

git clone --recursive https://github.com/jaklys/Lvgl-mcp-esp32.git
cd Lvgl-mcp-esp32

Linux (bash):

git clone --recursive https://github.com/jaklys/Lvgl-mcp-esp32.git
cd Lvgl-mcp-esp32

If you already cloned without --recursive:

git submodule update --init --recursive

2. Build everything

The setup script validates tools, builds the simulator, and builds the MCP server.

Windows (PowerShell):

powershell -ExecutionPolicy Bypass -File scripts/setup.ps1

Or build manually:

# Simulator (opens a VS Developer Command Prompt internally)
scripts\build.bat

# MCP server
cd mcp-server
npm install
npm run build

Linux (bash):

# Full setup: validate tools, build simulator, build MCP server, print config snippet
./scripts/setup.sh

Or build just the simulator:

# Simulator (uses cc by default; Ninja if available, else Make)
./scripts/build.sh

# MCP server
cd mcp-server
npm install
npm run build

3. Configure Claude Code

Windows:

{
  "mcpServers": {
    "lvgl-simulator": {
      "command": "node",
      "args": ["C:/Users/YOUR_USER/path/to/Lvgl-mcp-esp32/mcp-server/dist/index.js"]
    }
  }
}

Linux:

{
  "mcpServers": {
    "lvgl-simulator": {
      "command": "node",
      "args": ["/home/YOUR_USER/path/to/Lvgl-mcp-esp32/mcp-server/dist/index.js"]
    }
  }
}

Replace the path with your actual project location. Forward slashes work on Windows too.

You can also set the project root explicitly if the auto-detection doesn't work:

{
  "mcpServers": {
    "lvgl-simulator": {
      "command": "node",
      "args": ["/home/YOUR_USER/path/to/Lvgl-mcp-esp32/mcp-server/dist/index.js"],
      "env": {
        "LVGL_PROJECT_ROOT": "/home/YOUR_USER/path/to/Lvgl-mcp-esp32"
      }
    }
  }
}

Usage

Once configured, Claude has access to these MCP tools:

lvgl_render — Render a code snippet

The main tool. Send LVGL C code and get back a screenshot. Your code runs inside a create_ui() function with a screen variable (lv_obj_t*) already available:

Use lvgl_render to show a button with a label:

lv_obj_t *btn = lv_button_create(screen);
lv_obj_set_size(btn, 200, 50);
lv_obj_center(btn);
lv_obj_t *label = lv_label_create(btn);
lv_label_set_text(label, "Click Me!");
lv_obj_center(label);

Returns:

  • PNG screenshot of the rendered UI
  • JSON widget tree with full style information (see JSON output format)
  • Render time

You can override the resolution per-call:

Use lvgl_render with width=320 height=240 and code:
lv_obj_t *label = lv_label_create(screen);
lv_label_set_text(label, "Small display");
lv_obj_center(label);

lvgl_render_full — Render a complete C file

For complex UIs with multiple functions, helper code, or custom includes. The file must define void create_ui(void):

#include "lvgl.h"

static void build_header(lv_obj_t *parent) {
    lv_obj_t *header = lv_obj_create(parent);
    lv_obj_set_size(header, lv_pct(100), 50);
    lv_obj_set_style_bg_color(header, lv_color_hex(0x003a57), 0);

    lv_obj_t *title = lv_label_create(header);
    lv_label_set_text(title, "My App");
    lv_obj_set_style_text_color(title, lv_color_white(), 0);
    lv_obj_center(title);
}

void create_ui(void) {
    lv_obj_t *screen = lv_screen_active();
    lv_obj_set_flex_flow(screen, LV_FLEX_FLOW_COLUMN);
    build_header(screen);

    lv_obj_t *body = lv_label_create(screen);
    lv_label_set_text(body, "Content area");
    lv_obj_set_flex_grow(body, 1);
}

lvgl_inspect — Get the widget tree

Returns a JSON representation of every widget: type, position, size, computed styles, plus widget-specific data (label text, slider value/min/max, checkbox state, etc.).

Use lvgl_inspect on the last rendered UI

Example output:

{
  "type": "lv_obj",
  "x": 0, "y": 0, "w": 800, "h": 480,
  "styles": {
    "bg_color": "#ffffff", "bg_opa": 255,
    "text_color": "#212121", "text_font_size": 16,
    "border_width": 0, "border_color": "#000000",
    "radius": 0,
    "pad_top": 0, "pad_bottom": 0, "pad_left": 0, "pad_right": 0, "pad_gap": 11,
    "opa": 255
  },
  "children": [
    {
      "type": "lv_button",
      "x": 300, "y": 215, "w": 200, "h": 50,
      "styles": {
        "bg_color": "#2196f3", "bg_opa": 255,
        "text_color": "#ffffff", "text_font_size": 14,
        "border_width": 0, "border_color": "#000000",
        "radius": 12,
        "pad_top": 11, "pad_bottom": 11, "pad_left": 20, "pad_right": 20, "pad_gap": 4,
        "opa": 255
      },
      "children": [
        {
          "type": "lv_label",
          "x": 56, "y": 14, "w": 87, "h": 21,
          "text": "Click Me!",
          "styles": { "text_color": "#ffffff", "text_font_size": 14, "..." : "..." }
        }
      ]
    }
  ]
}

lvgl_set_resolution — Change display size

Match your target hardware:

Use lvgl_set_resolution with width=320 height=240

Common ESP32 display sizes:

  • 320x240 (2.4" - 2.8" ILI9341/ST7789)
  • 480x320 (3.5" ILI9488)
  • 800x480 (5" - 7" displays, default)

lvgl://api-reference — LVGL API cheat sheet

MCP resource with a quick reference for LVGL 9.5 widgets, styles, layouts, colors, and symbols. Claude can read this to write correct code without guessing.

JSON output format

Every widget node in the JSON tree includes:

Field Description
type Widget class name (lv_obj, lv_button, lv_label, lv_slider, lv_bar, lv_arc, lv_switch, lv_checkbox, lv_dropdown, lv_textarea, lv_chart, lv_table, ...)
x, y Position relative to parent (pixels)
w, h Size (pixels)
styles Computed style properties (see below)
text Label text (for lv_label widgets)
value Current value (for lv_slider, lv_bar, lv_arc)
min, max Range (for lv_slider, lv_bar, lv_arc)
checked State (for lv_checkbox, lv_switch)
children Nested child widgets

Style properties

Every node includes a styles object with computed (resolved) values:

{
  "bg_color": "#2196f3",
  "bg_opa": 255,
  "text_color": "#ffffff",
  "text_font_size": 16,
  "border_width": 2,
  "border_color": "#d4af37",
  "radius": 12,
  "pad_top": 10,
  "pad_bottom": 10,
  "pad_left": 20,
  "pad_right": 20,
  "pad_gap": 8,
  "opa": 255
}

Colors are in #rrggbb hex format. Opacity values range from 0 (transparent) to 255 (opaque). Font size is in pixels.

Examples

The examples/ directory contains rendered output from the MCP server — PNG screenshots and corresponding JSON widget trees.

Note: these example renders were produced with LVGL 9.2 and may differ slightly when re-rendered with LVGL 9.5. The pre-2.0 example JSONs also use the old short widget type names (obj, btn, label, ...) instead of the prefixed names (lv_obj, lv_button, lv_label, ...) emitted since LVGL 9.5.

Demo renders

Screenshot JSON Description
01-button-slider.png JSON Button + slider basic layout
02-dashboard.png JSON Multi-card dashboard
03-esp32-small.png JSON ESP32 status at 320x240
04-esp32-small.png JSON ESP32 status (alternate)

E-BREW brewery control screens (real project)

All 8 screens from the E-BREW brewery controller rendered through the simulator at 800x480:

Screenshot JSON Screen
ebrew-01-menu.png JSON Main menu (3x2 tile grid)
ebrew-02-overview.png JSON Overview (temperatures + controls)
ebrew-03-loading.png JSON Loading splash screen
ebrew-04-pump.png JSON Pump control
ebrew-05-control.png JSON Relay switches
ebrew-06-thermostats.png JSON Thermostat settings
ebrew-07-graphs.png JSON Temperature graphs
ebrew-08-sysinfo.png JSON System info

How it works internally

  1. Claude sends C code via the lvgl_render tool
  2. The MCP server wraps the snippet in a template (adds #include "lvgl.h" and create_ui() boilerplate)
  3. The wrapped code is written to simulator/build/user_code.c
  4. CMake incrementally recompiles only the changed file and links against the pre-built LVGL library
  5. The resulting simulator binary (lvgl_sim.exe on Windows, lvgl_sim on Linux) runs headless: initializes LVGL, creates a framebuffer display, calls create_ui(), ticks the timer 10 times, then exports a PNG and a JSON widget tree
  6. The MCP server reads the PNG, base64-encodes it, and returns it alongside the widget tree

Compilation uses MSVC (cl.exe) on Windows — via a temporary batch file that sets up the Visual Studio environment — and gcc/clang (cc by default) on Linux. Incremental builds only recompile the user code file (~1-2 seconds).

Project structure

Lvgl-mcp-esp32/
├── simulator/                    Headless LVGL renderer (C)
│   ├── CMakeLists.txt            Build config (Ninja/Make + MSVC or gcc/clang)
│   ├── lv_conf.h                 LVGL config (32bpp, all widgets, snapshot)
│   ├── main.c                    CLI: --width --height --output-png --output-json
│   ├── hal/
│   │   └── display_driver.c      Framebuffer-only display (no SDL/window)
│   ├── export/
│   │   ├── screenshot.c          Framebuffer → PNG (stb_image_write)
│   │   └── widget_tree.c         lv_obj tree → JSON (with styles)
│   ├── templates/
│   │   └── user_code_wrapper.c   Template for wrapping code snippets
│   └── lib/
│       ├── lvgl/                  LVGL v9.5 (git submodule)
│       └── stb/stb_image_write.h PNG encoder (single header)
├── mcp-server/                   MCP server (TypeScript)
│   └── src/
│       ├── index.ts              Entry point, stdio transport
│       ├── tools/
│       │   ├── render.ts         lvgl_render + lvgl_render_full
│       │   ├── inspect.ts        lvgl_inspect + lvgl_get_styles
│       │   └── config.ts         lvgl_set_resolution
│       ├── simulator/
│       │   ├── compiler.ts       Invokes CMake/MSVC, manages user code
│       │   └── manager.ts        Orchestrates compile → run → collect
│       └── resources/
│           └── api-reference.ts  LVGL API cheat sheet
├── examples/                     Example renders (PNG + JSON)
│   ├── 01-button-slider.*        Basic widget demo
│   ├── 02-dashboard.*            Multi-card layout
│   └── ebrew-01..08-*.*          E-BREW brewery controller (8 screens)
├── .github/workflows/
│   ├── ci.yml                    CI: build + test on push/PR
│   └── release.yml               Release: build + package on v* tag
├── scripts/
│   ├── setup.ps1                 Windows: full setup (validate tools, build all)
│   ├── build.bat                 Windows: quick rebuild (simulator only)
│   ├── setup.sh                  Linux: full setup (validate tools, build all)
│   └── build.sh                  Linux: quick rebuild (simulator only)
└── README.md

Configuration

Setting Default Location
LVGL version v9.5.0 Git submodule branch
Color depth 32-bit (XRGB8888) simulator/lv_conf.h
Display resolution 800x480 lvgl_set_resolution tool or CLI args
Available fonts Montserrat 12, 14, 16, 20, 24 simulator/lv_conf.h
All LVGL widgets Enabled simulator/lv_conf.h
Flex + Grid layouts Enabled simulator/lv_conf.h

Troubleshooting

"CMake configure failed" — Make sure Visual Studio Build Tools are installed with the "Desktop development with C++" workload. The setup script looks for vcvarsall.bat in standard VS 2019/2022 paths.

"cl is not recognized" — The MCP server sets up the MSVC environment automatically via vcvarsall.bat. If you're building manually, run scripts\build.bat which handles this.

"cc: command not found" (Linux) — No C compiler is installed. Install the toolchain with sudo apt install build-essential (or the equivalent for your distribution). You can point the build at a specific compiler with the CC environment variable.

"ninja: command not found" (Linux) — Ninja is optional; the build falls back to Unix Makefiles. To use Ninja, install it with sudo apt install ninja-build.

Render takes >5 seconds — The first render after server start includes CMake configuration (~1s extra). Subsequent renders are incremental (recompile user_code.c only).

Wrong colors in PNG — LVGL uses XRGB8888 which is BGRA in memory on x86. The screenshot exporter handles the byte swizzle. If colors look wrong, check simulator/export/screenshot.c.

"ENOENT: no such file or directory" — The LVGL_PROJECT_ROOT environment variable may be needed. Set it in your MCP server config to the absolute path of the project root.

Author

Jan Machaček

from github.com/jaklys/lvgl-mcp-esp32

Установить LVGL ESP32 Simulator в Claude Desktop, Claude Code, Cursor

Рекомендуется · одна команда, все IDE
unyly install lvgl-esp32-simulator

Ставит в Claude Desktop, Claude Code, Cursor и VS Code — сам разбирается с npx, uvx и сборкой из исходников.

Впервые? Поставь CLI: curl -fsSL https://unyly.org/install | sh

Или настроить вручную

Выполни в терминале:

claude mcp add lvgl-esp32-simulator -- npx -y lvgl-mcp-server

Пошаговые гайды: как установить LVGL ESP32 Simulator

FAQ

LVGL ESP32 Simulator MCP бесплатный?

Да, LVGL ESP32 Simulator MCP бесплатный — установка в пару кликов через Unyly без оплаты.

Нужен ли API-ключ для LVGL ESP32 Simulator?

Нет, LVGL ESP32 Simulator работает без API-ключей и переменных окружения.

LVGL ESP32 Simulator — hosted или self-hosted?

Self-hosted: сервер запускается локально на твоей машине командой из раздела установки.

Как установить LVGL ESP32 Simulator в Claude Desktop, Claude Code или Cursor?

Открой LVGL ESP32 Simulator на unyly.org, выбери вкладку своего клиента (Claude Desktop, Claude Code, Cursor) и нажми Install — конфиг сгенерируется автоматически, без правки JSON.

Похожие MCP

Compare LVGL ESP32 Simulator with

Не уверен что выбрать?

Найди свой стек за 60 секунд

Автор?

Embed-бейдж для README

Похожее

Все в категории development