LVGL Studio Documentation
Everything you need to design, automate, and flash your ESP32 LCD project — from first widget to working firmware.
Getting Started
LVGL Studio is a visual IDE for ESP32 LCD projects. You design the interface, build the logic, generate C code, and flash it — all from your browser.
System Requirements
- Google Chrome or Microsoft Edge (Web Serial API required)
- ESP32 or ESP32-S3 board with an LCD panel
- USB cable (data, not charge-only)
- No local software needed — fully browser-based
Quick Start (5 steps)
- 1Register for a free account at lcdultrapro.com/register
- 2Log in and click "Open Editor" from your Dashboard
- 3Select your LCD panel from the top dropdown
- 4Drag widgets from the left palette onto the canvas
- 5Click "Generate Code" → download the ZIP → open in Arduino IDE → Upload
UI Editor
The canvas shows a pixel-accurate preview of your LCD at the selected resolution. Everything you see is exactly what appears on the hardware.
Canvas Controls
Drag widgetMove it anywhere on canvasResize handlesDrag corners/edges to resizeCtrl + clickMulti-select widgetsCtrl + C / VCopy & pasteCtrl + Z / YUndo / RedoRight-clickContext menu (bring to front, etc.)Delete keyRemove selected widgetArrow keysNudge 1 px at a timeProps Panel (right sidebar)
Select any widget to open its property panel. You can set position, size, font, colors, border, shadow, opacity, and more. Changes appear on the canvas instantly.
Widget IDs must be unique and contain only letters, numbers, and underscores (no spaces or accented characters). The ID becomes the C variable name.
Multi-screen Support
- 1Click the "+" button in the screen tabs bar at the top
- 2Give each screen a name (e.g. Screen_Main, Screen_Settings)
- 3Switch screens by clicking their tab
- 4Use the Navigate action in workflows to switch at runtime
Workflow Engine
The Workflow Editor lets you build logic without writing C code. Connect nodes with wires — the engine compiles everything to valid LVGL 8.4 firmware.
Basic Rules
- Every workflow must start with a Trigger node (touch, timer, or event)
- Connect the right side output port of one node to the left side input of another
- Multiple nodes can chain together in sequence
- Condition and Switch nodes have multiple output branches
- One project can have many independent workflows per screen
Palette Categories
Trigger Nodes
Every workflow starts with one of these. They fire when something happens: user touch, timer tick, or an event from another workflow.
Fires when the user interacts with an LCD widget (tap, swipe, value change). Select the target widget and the event type (CLICKED, VALUE_CHANGED, etc.).
Fires automatically every N milliseconds. Use for polling sensors, updating the display, or any periodic task. Set Repeat to 0 for infinite.
Fires when another workflow (on any screen) sends a named event via Emit Event. Perfect for cross-screen communication.
Logic Nodes
Evaluates a condition and routes the workflow to the True or False output. Compare a variable, widget value, or GPIO level against a threshold.
Repeats the connected Body branch N times. The loop counter is available as a C expression for use inside the loop. Done output fires after the last iteration.
Routes to different branches based on a variable's integer value. Like C switch/case. Supports 2–8 cases plus a Default branch.
Data Nodes
Stores a value in a named variable that lives on the current screen. Lost when switching screens. Sources: fixed number, slider value, ADC pin, C expression.
Same as Variable but persists across all screens. Any screen's workflow can read or write it. Use for app-wide state like user level, current mode, etc.
Performs arithmetic on two values and stores the result. Operations: + − × ÷ % min max |A|. Division is automatically guarded against division-by-zero.
CLAMP: keeps a value within [min, max]. MAP: re-scales a value from one range to another (like Arduino map()). Essential for ADC → display value conversion.
Generates a random integer in [min, max] using the ESP32 hardware RNG (true random, not pseudo-random). Stores the result in a variable.
Fills a text template with variable values and writes the result to an LCD Label. Template syntax: {varname} is replaced with the variable's current value.
Embedded Nodes
Reads/writes ESP32 Non-Volatile Storage (survives power-off). Uses the Arduino Preferences library. Namespace: group name (max 15 chars). Key: value name (max 15 chars).
Sends a named event to the global event bus. Any Event Listener node on any screen can receive it. Optionally attach a numeric payload.
Code Generation
When your design and workflow are ready, click the Generate Code button in the toolbar.
What is generated?
ui_screen_{name}.c — LVGL widget initialization for each screenui_screen_{name}.h — header file exporting the screen init functionui_workflows.c — all workflow callbacks (timers, events, GPIO)ui_helpers.c — shared helper functionsmain.ino — the Arduino main file with panel initializationpanel driver files — hardware-specific LCD init code
Using in Arduino IDE
- 1Download the generated .zip file
- 2Extract it anywhere on your computer
- 3Opening the main.ino file opens the whole project in Arduino IDE
- 4Install required libraries: LVGL 8.4, Arduino_GFX, and any sensor libraries
- 5Select your board: ESP32 Dev Module or ESP32-S3
- 6Click Upload
All generated code is LVGL 8.4 compatible. Make sure you install LVGL 8.x (not 9.x) from the Library Manager.
Flash to Device
LVGL Studio can flash compiled firmware directly to your ESP32 over USB — no Arduino IDE needed for the flash step.
Requirements
- Google Chrome or Edge (Firefox is not supported for Web Serial)
- USB cable connected to your ESP32
- ESP32 in download mode (usually automatic, or hold BOOT while pressing RESET)
Steps
- 1Click the Flash button in the editor toolbar
- 2Select your COM port from the browser dialog
- 3The editor runs a preflight check (coordinate validation)
- 4Firmware uploads automatically — progress bar shown
- 5Device resets and your UI appears on the LCD
Mirror Mode
Mirror mode streams a live JPEG snapshot of the canvas to the LCD over USB. Useful for quickly previewing layout changes without a full flash. Click the Mirror button to start/stop.
Tips & Rules
Variable Naming Rules
✅ Correct: temperature, adc_value, led_state, sensor1 ❌ Wrong: hőmérséklet (accented), adc value (space), 1_data (starts with number)
Variable vs Global Variable
Lives on one screen only. Lost when you navigate to another screen. Use for temporary calculations.
Survives screen navigation. Accessible from any screen's workflow. Use for app-wide state.
Common Workflow Recipes
[Timer: 1000ms]
→ [Action: Read BME280 → temp, hum]
→ [String Format: "🌡️ {temp}°C 💧{hum}%" → lbl_data][Trigger: slider_brightness / VALUE_CHANGED] → [Variable: bright = Slider value] → [Action: Set Backlight = bright] → [NVS Write: settings/brightness = bright] On startup (Timer 1x): [Timer: 100ms, once] → [NVS Read: settings/brightness → bright] → [Action: Set Backlight = bright]
Screen1 (sensor screen): [Timer: 500ms] → [Variable: temp = ADC pin 34] → [Condition: temp > 2900] → True → [Emit Event: "alarm"] Screen2 (dashboard screen): [Event Listener: "alarm"] → [Action: Show MsgBox "WARNING! Overheating!"] → [Action: GPIO Write pin 25 HIGH] ← red LED
Delay Node Warning
Always connect something after the Delay node. The Delay produces a non-blocking lv_timer one-shot — if nothing follows it, the generator may fall back to a blocking delay() which freezes the LCD display.
NVS Key Limits
Namespace and key names must be max 15 characters. Recommended namespaces: settings, calib, state, app.
Event Name Case Sensitivity
Event names are case-sensitive. "Alarm" and "alarm" are different events. Make sure the Emit and Listener nodes use exactly the same name.