logo elektroda
logo elektroda
X
logo elektroda

ESP32 and touch display - part 5 - LVGL in SquareLine Studio

p.kaczmarek2  7 4998 Cool? (+11)
📢 Listen (AI):

TL;DR

  • LVGL basics on the ESP32-2432S028R touch board, using SquareLine Studio to build a UI and test the esp32-smartdisplay-demo project.
  • SquareLine Studio generates C code, UI screens, image byte arrays, and event callbacks, so the LVGL interface compiles directly in PlatformIO.
  • SquareLine Studio offers a 30-day trial and a free but truncated Personal version, while the demo targets the ESP32-2432S028R board.
  • Buttons, sliders, checkboxes, switches, and a color picker work on the display, with lv_timer_handler handling updates and periodic LED/text changes.
  • The project-opening/export flow needed a manual workaround, and the TextArea keyboard did not appear automatically without extra configuration.
Generated by the language model.
.
Today we are learning the basics of LVGL, which is a "lightweight and versatile graphics library for embedded". To make things easier for us, in this topic I'll use its ready-made integration with the ESP32-2432S028R board and create the interface elements themselves in SquareLine Studio. Here we'll see what creating a user interface for an ESP32 board using the SquareLine visual editor might look like and then analyse how it works.

NOTE: This topic is based on SquareLine Studio which is somewhat truncated in the free version. If you want to write in LVGL manually, without SquareLine Studio, then I recommend waiting for the next part, where I will do the program without the UI visual editor.

This topic is a continuation of the story about the ESP32-2432S028R. Here are the previous parts:
ESP32 and touchscreen display - tutorial - part 1 - how to program? Basics .
ESP32 and touch display - part 2 - how to draw pixels, lines, shapes, performance issues
ESP32 and touch display - tutorial part 3 - interactions, games and fun .
ESP32 and touchscreen display - tutorial part 4 - web weather, APIs, JSON

This topic will be about LVGL, so it's worth quoting their documentation right away:
https://docs.lvgl.io/master/intro/index.html
LVGL examples:
https://docs.lvgl.io/master/examples.html
Each example above has its own source code, you can also run them in the project I will present here.

Running the esp32-smartdisplay-demo example .
We could manually integrate LVGL into our project, but why? Especially since I am trying to target the needs of beginners with this topic.... for this reason we will use a ready-made example available for free on Github. To download, we have the entire project in PlatformIO:
https://github.com/rzeldent/esp32-smartdisplay-demo
We download the repository as a zip or "clone" it via Git.
We open it in PlatformIO:
.
This project supports a wide range of boards with a variety of displays. Most importantly, however, it also supports our ESP32-2432S028R.
.
At first I was a bit confused about which version I should run, as there is esp32-2432S028R, esp32-2432S028Rv2 o esp32-2432S028Rv3. In my case, only the third option gave a picture. Just compile and upload and see what the effect is:
.
Well it doesn't look neat, but the effect is there, the graphics, the QR code and even the interactive button.
Let's examine the example code. Headers:
Code: C / C++
Log in, to see the code
.
The LVGL integration is already done for us - it is in esp32_smartdisplay.h.
The function that handles the button click event (we'll see how it's tacked on to the button later):
Code: C / C++
Log in, to see the code
.
The above function also changes the text of the label (the set_text_fmt version supports ForMaTing such as sprintf, printf).
The argument of the function is lv_event_t, which is an object representing an event ..
Push-button operation from turning:
Code: C / C++
Log in, to see the code
.
lv_disp_set_rotation sets rotation of the screen (lv_disp_rot_t enumeration).
And then there's the setup function - initialising the program:
Code: C / C++
Log in, to see the code
.
It also calls smartdisplay_init and ui_init, which can be written by us or by SquareLine Studio:
Code: C / C++
Log in, to see the code
.
Below, in turn, an extension is used to prepare the QR code. Everything is based on ready-made mechanisms:
Code: C / C++
Log in, to see the code
.
QR code documentation: https://docs.lvgl.io/master/libs/qrcode.html
The main loop code remains.
Code: C / C++
Log in, to see the code
.
In the main loop the most important thing is the regular call lv_timer_handler that is the LVGL update. In addition, there is an artificially added event handler every 500 ms in which, among other things, the colour of the LED is changed and the text on the screen is updated.

But that's not all... this project is not based on C code alone, we also have an SPJ file here:
.
This is a UI project made in SquareLine Studio. SquareLine Studio then converts it into C code, which we only then compile normally under ESP. Clever! Here there is no UI "intermediate format" like HTML or XML, we have the code straight away.

So let's consider where the button press event is set, for example OnAddOneClicked?
This is automatically plugged in by SquareLine Studio:
.
The code above shows how the interface is created in C code. The panels and the relationships between them are created separately and the event handler functions are plugged in.
But this code is automatically generated from the aforementioned SPJ file, below is the relevant excerpt:
.
But we are still missing something. What does the callback ui_event_btnCount set?
This in turn is in the 'screens' folder, creating a 'screen' sets the callback:
Code: C / C++
Log in, to see the code
.
Analysing deeper:
- lv_obj_set_width - sets the width of the object
- lv_obj_set_height - sets the height of the object
- lv_obj_set_align - sets the alignment of an object relative to its parent
- lv_obj_set_x - sets the X position of an object
- lv_obj_set_y - sets the Y position of an object
- lv_obj_add_flag - adds specific flags to an object (e.g. enables the scrollbar)
- lv_obj_clear_flag - removes the specified flags from the object
- lv_label_set_text - sets the label display text
- lv_img_set_src - sets the image source to display
- lv_obj_add_event_cb - adds an event handling callback for the object

This explains a lot. All the code above is generated automatically. This is how SquareLine creates the UI in LVGL.

Similarly with images - images are converted directly into bytes in memory:
.

Installing SquareLine Studio .
It is time to conclude our programming 'investigation' and to try SquareLine Studio in practice. The program is paid, but a free trial version and a free but truncated Personal version (not for commercial use) are available:
https://squareline.io/pricing/licenses#licenseTable
Is it worth giving it a chance? Let's find out.
.
Download:
.
Trial enough for 30 days:
.
Upon start-up, we are greeted by an almost blank screen, but fortunately a bit of examples are available:
.
They look really good...


Repairing the SquareLine Studio project .
At this point I wanted to simply open the aforementioned SPF project from Github and try to refactor it, but surprisingly SquareLine failed here. It simply did not respond to opening the project. So far I don't know what's wrong there, but I managed to fix it in a simple way:
1. I created another SquareLine studio project
2. I saved it in a separate folder
3. I overwrote its SPF file with a file from Github
4. I copied the assets folder manually
We fired it up and... it works:
.
I don't know what was wrong with the previous project, maybe some metadata file was missing, someone added too many files to the .gitignore? One can speculate.


Basics of SquareLine Studio .
Finally we have an open-source SquareLine Studio project so it's time to try to modify it somehow.
Let's see what exactly this program will allow us to do. We'll check out what it's like to create different elements in it, plug events into them, etc.
Each element can be moved, edited and configured:
.
Other icons can be inserted here, also interactive of course (they can then be edited from within the code):
.
Then you can save and export the project, but.... there is the problem again :
.
What kind of path is this? It is not mine. The project remembers the path of the previous user. I opened the SPF file - indeed, it is sewn in here:
.
I pasted the path there, which is appropriate for me (inside the PlatformIO project).
The export was successful:
.
An image appeared in our PlatformIO project:
.
We compile and upload:
.
It works! It's really simple, you have to give them credit for that much.


Examples of UI elements from SquareLine Studio .
Perhaps let's start with the slider element, or Slider. The slider can be 'grabbed' and moved, which has the effect of triggering its change event. Additionally, its values (current, min, max) can also be set and read.
.
Once the slider is selected, we can edit its parameters and add events. One of the possible events is "VALUE_CHANGED", which is a change of value. There we enter the name of our function to be called. The field "Don't export function" allows us to use an existing function, otherwise SquareLine Studio creates it.
.
We export...
.
Indeed, the function has been generated. We can add something to it. Maybe we could try displaying the value from the slider on one of the labels?
Code: C / C++
Log in, to see the code
.
By the way, you can see a cool way to get an object from an event - lv_event_get_target ..
The get and set functions are also described in the documentation. We upload and test:
Our bar works:
.


Similarly, we have, for example, a checkbox component - a box to be ticked. We select it from the list and drag it into our view.
.
We add a change event to it:
.
Handle the event in code - just check the state of the object:
Code: C / C++
Log in, to see the code
.
This is how it presents itself in practice:
.
Clicking on a checkbox changes its status:

[  8680][I][main.cpp:49] MyCheckChange(): Checkbox is checked

[  9340][I][main.cpp:51] MyCheckChange(): Checkbox is unchecked

[ 11110][I][main.cpp:49] MyCheckChange(): Checkbox is checked

[ 11620][I][main.cpp:51] MyCheckChange(): Checkbox is unchecked


Similarly, we have the Switch control - or switch. Here, too, there are two states. Similarly, we select it from the list:
.
We add:
.
We set the event:
.
Similarly we add a function (change handler ) and test, it works:

[ 52807][I][main.cpp:50] MyToggleChange(): Toggle is ON

[ 55027][I][main.cpp:52] MyToggleChange(): Toggle is OFF

[ 55867][I][main.cpp:50] MyToggleChange(): Toggle is ON
.

Basics with basics, but there are more advanced controls here too. For example, there is colour selection:
.
We drag it onto the screen:
.
As before, we add a state change event to it:
.
As a test, I uploaded it as such to the display:
.
Now you just need to think about how to receive the colour information. On this platform the colours are 16-bit, below is an insight into the union/structures from the header of the library used:
Code: C / C++
Log in, to see the code
.
In our project we have LV_COLOR_16_SWAP defined as 1, so the green colour is split into two areas in memory. Taking this into account, I wrote a function that brings the colour values into the same range and displays them when the control changes:
Code: C / C++
Log in, to see the code
.
Testing:

[259878][I][main.cpp:41] MyColorChange(): Selected color: R=28, G=63, B=0

[259960][I][main.cpp:41] MyColorChange(): Selected color: R=32, G=63, B=0

[260041][I][main.cpp:41] MyColorChange(): Selected color: R=32, G=63, B=0

[260092][I][main.cpp:41] MyColorChange(): Selected color: R=32, G=63, B=0

[263961][I][main.cpp:41] MyColorChange(): Selected color: R=62, G=6, B=0

[264012][I][main.cpp:41] MyColorChange(): Selected color: R=62, G=4, B=0

[264071][I][main.cpp:41] MyColorChange(): Selected color: R=62, G=6, B=0

The colours correspond to what the control shows, although I could still flip them to full bytes (to the range 0-255).

Now perhaps something more interactive - a text field. Let's try simply adding it, dragging it into our view. The text field here is TextArea.
.
We compile, here is the result:
.
Unfortunately the keyboard doesn't show up on its own.... looks like this requires some additional configuration in the code though. A quick check of the documentation confirms my concerns:
https://docs.lvgl.io/master/widgets/keyboard.html
But about that another time...


Finally a little tip .
If you don't want the function called from the GUI to be created in the event file all the time then just tick:
.
This way we can create it once in our code in another file and only use it.

Of course LVGL offers much more, you can create views with scroll (sliders), you can switch between screens, etc, but about that another time.

Summary .
Here I have presented the basics of LVGL in combination with SquareLine Studio. LVGL offers a wide range of ready-made controls and mechanisms from which we can quickly create the UI of our project. SquareLine Studio offers a convenient, visual interface view editor for our application, which can then be exported directly into C code, which we compile at least in PlatformIO just like any other code we would write ourselves. SquareLine Studio even converts images to byte arrays in C files, and even with this there is no problem.
Unfortunately, SquareLine Studio is only available for free in a truncated or 30-day version, and the price of the full version is rather out of reach for hobbyists. For this reason, in the following sections we will probably already be creating interfaces manually, i.e. we will simply be writing code ourselves in the style of what we suspected today.
Among other things, I plan to discuss at least the aforementioned keyboard issue.
For now, I can only recommend examples from the LVGL documentation to the inquisitive once again:
https://docs.lvgl.io/master/examples.html
everything is to be fired up in the project I recommended, ready to go from there.
Finally, I'll ask - do you prefer to create UIs from code or in a visual editor? Or do you know of any alternative to SquareLine Studio worthy of consideration?

About Author
p.kaczmarek2
p.kaczmarek2 wrote 14418 posts with rating 12376 , helped 650 times. Been with us since 2014 year.

Comments

hwjn 10 Aug 2024 23:28

A free alternative to SquareLine Studio is EEZ Studio: https://www.envox.eu/studio/studio-introduction/ [Read more]

p.kaczmarek2 11 Aug 2024 08:10

Very interesting suggestion, I will definitely test it out: https://forum.lvgl.io/t/free-and-open-source-eez-studio/12954 [Read more]

tomzor 11 Aug 2024 13:14

A couple of observations like this: 1. I'm testing both SquareLine Studio and EEZ Studio and unfortunately (I don't know why) but EEZ Studio generated code that exceeded the ESP32 memory !!!, and by just... [Read more]

p.kaczmarek2 11 Aug 2024 17:03

Re 1. Maybe it's a bitmap issue? Show this code maybe. [Read more]

tomzor 11 Aug 2024 21:06

in EEZ Studio I don't even have (yet ) one bitmap, just this screen: https://obrazki.elektroda.pl/9057886700_1723402707_thumb.jpg . Perhaps the problem is EEZ Studio libraries i.e. eez-framework... [Read more]

h3c4 11 Aug 2024 22:50

NXP GUI Guider [Read more]

tomzor 27 Sep 2024 18:45

Has anyone tested the latest LVGL 9.3.0 libraries (lcd+dotouch together)? Because version 9.1.0 works correctly, but the latest version is some changes in the library, and for example lcd orientation... [Read more]

FAQ

TL;DR: For ESP32-2432S028R users, one tested board needed the 3rd build target, and “only the third option gave a picture.” This FAQ helps beginners run LVGL with SquareLine Studio in PlatformIO, export working C code, and fix path, project, memory, and touch-rotation problems. [#21186222]

Why it matters: This thread shows the fastest practical path from a blank ESP32 touch display to a working LVGL UI, while also documenting the failure cases that waste the most time.

Tool Cost/licensing noted in thread Reported ESP32 result Main limitation mentioned
SquareLine Studio 30-day trial; free Personal version is truncated and non-commercial Worked after export-path fixes Paid full version; some project/path quirks
EEZ Studio Described as a free alternative One user reported memory overflow on a single screen Possible framework or memory overhead
Manual LVGL code No editor cost mentioned Planned as next step More coding effort

Key insight: SquareLine Studio is effective for quick LVGL prototypes on ESP32, but the generated project metadata can break imports and exports. When that happens, copying the SPF/SPJ content into a fresh project often restores a working flow.

Quick Facts

  • The generated SquareLine screen file shown in the thread was created by SquareLine Studio 1.4.0 for LVGL 8.3.6, which matters for compatibility when reusing exported code. [#21186222]
  • The sample main loop updates UI data every 500 ms and still calls lv_timer_handler() continuously, which is the minimum pattern needed for responsive LVGL screens on ESP32. [#21186222]
  • Example widgets in the generated UI used fixed sizes such as 100 px × 50 px buttons and an 80 × 80 px image, showing how SquareLine converts visual layout into explicit LVGL object code. [#21186222]
  • The LVGL color format discussed is 16-bit RGB565. With LV_COLOR_16_SWAP = 1, green is split across two 3-bit fields, while red and blue use 5 bits each. [#21186222]
  • SquareLine Studio was described as offering a 30-day trial, while one commenter reported a free activation shown as valid until 2038, highlighting the licensing confusion hobby users may face. [#21186222]

What is LVGL and how does it help when building touch-screen interfaces on an ESP32?

LVGL is a graphics library for embedded systems that speeds up touch UI development on ESP32. "LVGL" is a graphics library that builds embedded user interfaces, offering ready-made widgets, events, and display handling for resource-limited boards. In the thread, it supplied labels, buttons, sliders, a color wheel, QR code support, and event callbacks, all usable on the ESP32-2432S028R touch display. [#21186222]

How do I run the esp32-smartdisplay-demo example in PlatformIO on an ESP32-2432S028R display board?

Open the GitHub project in PlatformIO, pick the board variant, then compile and upload. 1. Download or clone esp32-smartdisplay-demo. 2. Open it in PlatformIO and select the ESP32-2432S028R environment. 3. Build and flash, then test which board revision shows an image. The thread author got graphics, a QR code, and a working button after upload. [#21186222]

Why does the esp32-smartdisplay-demo project show an image only for esp32-2432S028Rv3 on some ESP32-2432S028R boards?

It happens because boards sold under the same ESP32-2432S028R name may match different project variants. In the reported test, esp32-2432S028R and ...Rv2 did not show a picture, while ...Rv3 did. The practical fix is to compile each supported variant and keep the one that initializes your panel correctly. [#21186222]

How does SquareLine Studio export a visual UI project into C code for LVGL and PlatformIO?

SquareLine Studio converts the visual layout directly into LVGL C source files. The thread shows generated code that creates objects with calls like lv_btn_create, lv_label_create, lv_obj_set_width, and lv_obj_add_event_cb, then compiles that output in PlatformIO like normal ESP32 code. Images are also converted into byte arrays stored in C files. [#21186222]

What is an SPF or SPJ file in SquareLine Studio, and how is it used in an LVGL project?

An SPF or SPJ file is the SquareLine project file that stores the UI design and export settings. "SPF/SPJ" is a project file format that stores a SquareLine UI layout, metadata, and export configuration, so the editor can regenerate LVGL C code for the target project. The thread notes an SPJ file in the demo and later discusses editing the SPF file to repair an export path. [#21186222]

Where in the generated LVGL code are button callbacks like OnAddOneClicked and ui_event_btnCount attached?

They are attached in the generated screen initialization code with lv_obj_add_event_cb(). The thread shows lines such as lv_obj_add_event_cb(ui_Rotate, ui_event_Rotate, LV_EVENT_ALL, NULL); and lv_obj_add_event_cb(ui_btnCount, ui_event_btnCount, LV_EVENT_ALL, NULL);. SquareLine generates these bindings automatically from the visual project’s event settings. [#21186222]

Why is lv_timer_handler() important in the main loop of an ESP32 LVGL application?

lv_timer_handler() keeps LVGL alive by processing redraws, animations, and input events. In the example, the author calls it inside loop() while a separate 500 ms block updates text and LED state. Without that continuous handler call, the touch UI would stop updating correctly even if the rest of the code kept running. [#21186222]

How can I fix a SquareLine Studio project that will not open after downloading it from GitHub?

Create a fresh project, then transplant the old project data into it. 1. Create a new SquareLine Studio project. 2. Save it in a separate folder. 3. Replace its SPF file with the GitHub version and copy the assets folder manually. The thread author used that workaround when the downloaded project would not open at all. [#21186222]

What causes SquareLine Studio to export to the wrong path stored in the project file, and how do I correct it?

The wrong export path is usually stored inside the imported project file from another machine. In the thread, SquareLine kept the previous user’s PlatformIO path, so export targeted a non-existent location. The fix was to open the SPF file, replace the saved path with the local project path, and export again successfully. [#21186222]

How do I add a slider, checkbox, switch, or color wheel in SquareLine Studio and handle its LVGL events in code?

Drag the widget onto the screen, assign an event in SquareLine, export, then fill in the generated callback. The thread uses VALUE_CHANGED for a slider, checks LV_STATE_CHECKED for a checkbox or switch, and reads the selected color from the color wheel inside an lv_event_t * handler. If you enable “Don’t export function,” you can keep the callback in your own source file. [#21186222]

What is the best way to read the selected RGB color from an LVGL color wheel when LV_COLOR_16_SWAP is enabled on ESP32?

Read the LVGL color value, then reconstruct RGB from the swapped 16-bit fields. The thread’s example uses lv_colorwheel_get_rgb(), combines green_l with green_h << 3, and left-shifts red and blue because they are 5-bit values. That matches an RGB565 layout where green has 6 bits and red and blue have 5 bits each. [#21186222]

Why does an LVGL TextArea on ESP32 not automatically show the keyboard, and what extra configuration is needed?

It does not appear automatically because the TextArea alone does not enable the LVGL keyboard workflow. The author added a TextArea, compiled it, and confirmed that the keyboard did not show on its own. He then checked the LVGL keyboard documentation and concluded that extra code configuration is required beyond simply placing the widget on screen. [#21186222]

SquareLine Studio vs EEZ Studio for ESP32 and LVGL: which works better for hobby projects with limited memory?

SquareLine Studio worked better in this thread’s ESP32 tests, while EEZ Studio looked promising but hit memory issues. SquareLine produced a running demo after path fixes, and its free options included a 30-day trial plus a limited Personal edition. EEZ Studio was praised as a free alternative, but one user said even a one-screen UI exceeded ESP32 memory. [#21188009]

What might make EEZ Studio-generated LVGL code exceed ESP32 memory even with a very simple single-screen UI?

Framework overhead or project configuration can push EEZ Studio output past ESP32 memory limits. One user reported that a single screen with no bitmaps still overflowed memory, even after adding -DEEZ_PLATFORM_ESP32 -DEEZ_FOR_LVGL and switching partitions to no_ota.csv. The same user suspected either eez-framework or incorrect usage, because a trivial “hello” test still worked. [#21188009]

How can I resolve the display and XPT2046 touch orientation mismatch in LVGL 9.3.0 when 9.1.0 worked correctly?

The thread does not provide a confirmed fix, so the safest working answer is to stay on LVGL 9.1.0 until your display and touch stack are verified. A later post reports that lcd+dotouch worked in 9.1.0, but in 9.3.0 the LCD orientation no longer matched XPT2046 touch orientation. That makes this a version-specific regression to isolate before upgrading. [#21241694]
Generated by the language model.
%}