Flashing and configuration
From the zip to a running board, and the settings you will touch after the first boot.
You have a zip from the export dialog. This page takes it to a running board, on either PlatformIO or the Arduino IDE, and then covers the configuration you are most likely to have to touch afterwards: rotation, colour, touch calibration, the filesystem for images, and the handful of boards that need something extra.
Before you power anything. Check the wiring against your board's schematic, not just
against the preset. Board pinouts change between revisions, and a wrong backlight or reset pin
can damage a panel. The preset's verify note and the Configuration notes in the
generated README.md say what to look at.
What you need
- A toolchain. Either PlatformIO
(the VS Code extension or the CLI) or the
Arduino IDE. PlatformIO is
less work because
platformio.inicarries the board, the libraries and the build flags; the Arduino IDE needs a few files copied by hand. - The board core. ESP32 boards: Espressif's
esp32Arduino core (3.x for LVGL 9; the ESP32-P4 needs a recent one). STM32: the STM32duino core. RP2040 and RP2350: a Pico Arduino core. PlatformIO installs these itself from theplatformline. - A USB driver for the board's serial chip if Windows does not see a COM port - most cheap ESP32 boards use a CH340 or CP210x.
- A data-capable USB cable. Charge-only cables are the most common reason a board is never detected.
Route A: PlatformIO
- Unzip. Open the project folder (the one containing
platformio.ini) in VS Code with the PlatformIO extension, orcdinto it. - Plug the board in. If it has a BOOT button and is not detected for upload, hold BOOT while pressing RESET, then release BOOT.
- Build and upload:
The first run downloads the platform, the core and every library inpio run -t uploadlib_deps, which takes a few minutes. - If the project has image assets in
data/, upload the filesystem as well:
A board with resistive touch also haspio run -t uploadfsdata/touch_cal.txt, the touch calibration file with its starting values. Uploading it is optional - the board writes the file itself when it is missing - butuploadfsresets it, so the next boot calibrates again. - Open the monitor:
at 115200 baud. You should seepio device monitor<Name> starting..., any device probe results, andUI ready.
platformio.ini already sets monitor_speed, on an ESP32 the flash size,
the PSRAM kind and the partitions.csv the Board tab's Partitions setting wrote, the
TFT_eSPI defines where that library is used, and
-DLV_CONF_INCLUDE_SIMPLE -I . so the generated lv_conf.h is the one LVGL
picks up. Nothing needs copying anywhere.
Route B: Arduino IDE
- Unzip the project and keep its folder whole. The
.inohas to stay in the folder named after it, withsrc/and the other files beside it. If the IDE offers to move the sketch into a new folder, the folder has been renamed - say no and name it back, or it moves the.inoalone and leaves everything else behind. - Install the board core. For an ESP32, esp32 by Espressif Systems in the
Boards Manager, version 3.3 or newer - the release exports are built against. The README
names the oldest that builds your design: 3.0.0 at the least, 3.1.0 with GFX
Library for Arduino, 3.3.0 for anything that plays audio. A 2.x core does not search the
sketch folder and stops at
lv_conf.h: No such file. - Install the libraries. Open
LIBRARIES.md; for each entry in Install these, search for the quoted name in Sketch > Include Library > Manage Libraries and install it. Match the LVGL major version - 9.x or 8.3.x - to the one in the file's Versions table. lv_conf.h. On an ESP32, leave it where it is: the ESP32 core (3.0 or newer) reads it from the sketch folder, so each project keeps its own. On an STM32 or RP2040, copy it into your Arduinolibraries/folder, next to thelvglfolder (not inside it), replacing one left there by another project - the generated file enables only the fonts and widgets this design uses.- The driver configuration. On an ESP32 there is nothing to copy for any library: with
TFT_eSPI the core reads the generated
User_Setup.hfrom the sketch folder, ahead of the one inside the library. On an STM32 or RP2040, copy it overlibraries/TFT_eSPI/User_Setup.h. LovyanGFX, GFX Library for Arduino and esp_lcd are configured in the sketch folder on every chip. - Open the sketch. Double-click the
.ino. The IDE shows the files beside it as tabs and compiles everything undersrc/with them - the screens, fonts and images are insrc/ui/.srcis the one subfolder the Arduino IDE builds, so do not rename or move it. - Choose the board under Tools > Board - the FQBN on the Board
tab, and the Board core line in
LIBRARIES.md, name it. Then the settings that matter - on an ESP32 the README lists the first three for your module:- Flash Size: the module's - 16MB (128Mb) for an N16R8.
- PSRAM: on an ESP32-S3, QSPI PSRAM for an R2 or R4 module and OPI PSRAM for R8 or R16; Enabled on other ESP32s with PSRAM. The wrong kind finds no PSRAM. LVGL's memory pool is taken from PSRAM on these boards, and an RGB panel's framebuffer too, so without it the sketch stops as LVGL starts.
- Partition Scheme: the one the README names. On a 4 MB module that is
Huge APP (3MB No OTA/1MB SPIFFS) for the Big app layout or
No OTA (2MB APP/2MB SPIFFS) for Half and half - the same tables as the
partitions.csvbeside the sketch, so the size check and the filesystem uploader agree with what is flashed. On a larger module it is Custom, which needs ESP32 core 3.0 or newer. Thepartitions.csvis what gets flashed whichever is chosen. - Upload Speed: 921600 usually works; drop to 115200 if uploads fail.
- USB CDC On Boot: enabled on S3 and C3 boards that expose native USB, or the serial monitor stays silent.
- Upload, then open the serial monitor at 115200.
- Upload the images, if there are any - see below.
Uploading image assets
Images stored on the filesystem are flashed separately from the sketch. The symptom of
forgetting is every image drawing as an empty box, and ui_fs_init() printing a
warning per missing file on the serial monitor. data/README.md in the export lists
each file, the total against the partition, and the exact steps:
| Filesystem | PlatformIO | Arduino IDE |
|---|---|---|
| LittleFS | pio run -t uploadfs | The arduino-littlefs-upload plugin, then Ctrl+Shift+P > Upload LittleFS to Pico/ESP8266/ESP32. It reads partitions.csv from the sketch folder, so it works with any module and layout. |
| SPIFFS | pio run -t uploadfs | On a 4 MB module, the arduino-spiffs-upload plugin, then Ctrl+Shift+P > Upload SPIFFS to Pico/ESP8266/ESP32. It takes the filesystem's position from Tools > Partition Scheme and nowhere else, so that must be the scheme the README names - with Custom it stops with "Partitions not defined for this ESP32 board". On an 8 or 16 MB module no stock scheme matches, so it cannot be used: switch to LittleFS in Assets > Filesystem, or run the two commands data/README.md gives. |
| FFat | pio run -t uploadfs | No Arduino IDE uploader, and the ESP32 core has no tool that builds a FAT image. Use PlatformIO for the upload, or switch to LittleFS. |
| SD card | Copy the files to the card's root | Copy the files to the card's root. |
Both Arduino IDE plugins are for IDE 2: put the plugin's .vsix in
.arduinoIDE/plugins/ in your home folder (make plugins if it is not
there), restart the IDE, and close the Serial Monitor before uploading. Whichever tool you use,
the files must sit in a folder named data/ next to the .ino - which is
where the export puts them - and they must fit the filesystem partition: on an ESP32, the
Partitions setting on the Board tab.
First boot
A good boot prints the project name, a line saying whether the touch controller answered
(touch: GT911 answering at 0x5D, or touch: XPT2046 answering), a line
per device the hardware layer probed, then UI ready, and the panel shows the first
screen.
On a resistive touch panel the first boot calibrates touch first. A cross appears near each corner in turn; touch the centre of each one. The screen then says Touch calibrated, with which way round the axes turned out to be, or Calibration failed and why - tap to try again. The same happens after every new firmware upload, and whenever you hold a finger on the screen for a second while powering on. The serial monitor shows the raw x and y of every tap and the file as saved; see Calibrating on the board.
If the screen is blank or wrong, the serial output narrows it down fast:
- Nothing at all on serial - wrong port, wrong baud, or USB CDC not enabled on a native-USB board.
draw buffer allocation failed - lower DRAW_BUF_LINES- the buffer does not fit. Lower Buffer height in the export settings and re-export.- Boot loop with a backtrace - usually the same memory problem, or PSRAM configured in the sketch but disabled in the IDE.
UI readybut a white or black panel - the backlight pin or its active level, or the panel is not being initialised because the wrong library driver or bus is selected.- A device reported absent - wrong I2C address, wrong bus, missing pull-ups, or its reset line not wired (the CST816S needs one).
touch: nothing answering at 0x..- the touch controller's SDA, SCL, address or RESET.touch: XPT2046 not answering- its CS, MISO or SCK.touch: controller stopped answering - resetting it- the firmware found the controller silent and reset it. Once is harmless; a steady stream points at its RESET wiring or its power.
The troubleshooting page has the full symptom table.
Configuration after the first boot
Almost everything is a setting in the designer, and the right fix is to change it there and
re-export - the generated files are meant to be replaced, ui_events.c among them, so
keep your own code in a file of your own (Your own code).
The ones you are most likely to need:
| Symptom | Setting |
|---|---|
| The image is rotated or mirrored | Hardware > Display > Rotation. Touch follows the display rotation on every controller. |
| Colours are a negative | Hardware > Display > Invert. |
| Red and blue are swapped | Hardware > Display > Colour order, RGB or BGR. |
| Touch lands in the wrong place (capacitive) | Hardware > Touch: Swap X/Y, Invert X, Invert Y. Try Swap first if a horizontal drag moves vertically. |
| Touch lands in the wrong place, or is offset at the edges (resistive) | Calibrate again: hold a finger on the screen for a second while powering on. The calibration measures the axis swap and direction itself, so the switches only matter until it has run. With Hardware > Touch > Calibrate touch on the board switched off, enter the raw range by hand instead. |
| Touch stops working until the power is removed | Export again: older exports could leave an XPT2046 with its pen interrupt switched off, and did not reset I2C controllers. Current ones reset the controller on every boot and whenever it goes quiet. |
| Flicker, tearing, noise on the panel | Lower the SPI clock on the Display tab, or shorten the wires. RGB panels: check the PSRAM speed and the pixel clock. |
| Backlight off, or on when it should be off | Hardware > Display > Backlight: the pin and its active level. |
| Brightness is wrong, or cannot be changed on the device | Hardware > Display > Backlight > Default % is the level it starts at. To change it on the device, give a slider or a pair of buttons a Set backlight event. It dims only with PWM dimming on and the backlight on a GPIO; on an I/O expander it can only switch on and off. |
| The board reboots as the UI starts | Export settings > Buffer height, lower. Or enable PSRAM in the IDE to match the project. |
| A sensor reads its initial value forever | It was not found at boot: check its address on the Devices tab and the bus pins on the Buses tab against the serial output. |
For a quick experiment you can edit lcd_pins.h directly - every one of these is a
#define there - but carry the change back into the designer before the next export
or it will be lost.
Board-specific notes
- Cheap Yellow Display (2432S028R). Touch is on its own SPI bus (VSPI), separate from the
display's HSPI - the preset already says so. The GPIO 2, 12 and 15 strapping-pin warnings
are expected. The panel is landscape at rotation 0 (90 degrees is portrait), its colours are
RGB with inversion off, and the XPT2046's axes are swapped against the panel - all set in
the preset, and the touch calibration on first boot measures the axes anyway. The backlight
on GPIO 21 dims with PWM. A capacitive variant (2432S028C, CST820) has its own preset: GPIO
21 is its backlight too, so the CST820 is polled rather than read on its INT line, and it is
reset on GPIO 25 at every boot. Both presets add the CN1 connector (GPIO 27 SDA / 22 SCL) as
an I2C bus -
Wireon the resistive board,Wire1on the capacitive one, whose touch controller hasWire. - LCDWIKI 2.8" ESP32-32E (E32R28T). A Cheap Yellow Display alternative with the panel, touch and backlight on the same pins, which is why the Sunton preset draws on it - but the rest is wired differently. The RGB LED's red is GPIO 22, not 4, and GPIO 4 is the speaker amplifier's enable, pulled low to play. On the Sunton preset the red LED never lights and the speaker stays silent; use this board's own preset, which also adds the LED as three outputs, the BOOT button and the battery voltage on GPIO 34. Its SD card and SPI socket share GPIO 18/19/23, and with the panel and the touch panel each on their own SPI pins an ESP32 has no controller left for a third set - adding either stops touch answering, and the designer says so.
- Sunton 4.3" and 7" RGB. PSRAM must be enabled - OPI, 80 MHz - or the framebuffer will not allocate. The two boards' RGB data pin orders differ; confirm against your revision.
- LilyGO T-Display-S3. GPIO 15 must be driven high to power the LCD; the generated sketch does it before anything else.
- LilyGO T-Display-S3 AMOLED. No backlight pin; brightness is a controller command, which a Set backlight event sends.
- Waveshare ESP32-S3-Touch-LCD-4.3. Touch reset (EXIO1), backlight enable (EXIO2) and
panel reset (EXIO3) are behind a CH422G I/O expander on the I2C bus at GPIO 8/9. The preset
adds the bus and the expander, and the generated sketch brings them up before anything else
(
hw_init_early()), pulses both resets through the expander and switches the backlight on through it - on or off only, since an expander pin cannot dim. If the GT911 stops answering later, it is reset through the expander again without disturbing the bus. EXIO4 (SD card chip select) and EXIO5 (USB select) are left at their defaults. If the panel stays dark, the serial monitor'sCH422G: not foundline is the first thing to look for - it means the I2C bus is not answering. - Waveshare ESP32-S3-Touch-LCD-3.5B. The USB-C goes straight to the ESP32-S3 with no
serial bridge, so the serial monitor only shows anything with USB CDC On Boot
enabled - the PlatformIO export sets it, and the Arduino IDE needs it chosen in
Tools. PSRAM is OPI PSRAM. The panel reset (EXIO1) and the speaker
amplifier (EXIO7) are on a TCA9554 at 0x20, which comes up before the panel; touch, the
ES8311 codec, the AXP2101, the QMI8658 and the PCF85063 all share the I2C bus on GPIO 8/7,
and the preset adds every one of them. The AXS15231B ignores the row address over quad SPI,
so the export draws whole frames from a canvas in PSRAM. Several 3.5" ESP32-S3 boards look
alike: if the screen stays dark and the serial monitor says
TCA9554: not foundand nothing answers at 0x3B, it is a different board - the LCDWIKI ES3C35P, with its connectors labelled IO38/IO39 and IO45/IO46, is a common one, and has its own preset. - LCDWIKI 3.5" ESP32-S3 (ES3C35P). The ST77922 is a touch-and-display chip on quad SPI, driven through GFX Library for Arduino's ST77916 class with LCDWIKI's own register load. It has no row/column swap and wants windows aligned to four pixels, so the export draws whole frames from a canvas in PSRAM, which also rotates them. The panel's reset is the board's RESET button rather than a GPIO, so a USB or software restart never resets the glass: if the screen stays dark after flashing other firmware, unplug it once. Like the Waveshare board, the USB-C goes straight to the S3, so the serial monitor needs USB CDC On Boot. The preset adds the ES8311 (enabled by pulling GPIO 1 low), the 4-bit SD slot, the single WS2812 on GPIO 40, the BOOT button and the battery on GPIO 8.
- M5Stack Core2. The backlight and panel reset are controlled by the AXP192 power management chip over I2C. Initialise the PMIC (the M5Core2 library does it) before LVGL. The preset adds Port A (Grove, GPIO 32/33) as an I2C bus.
- WT32-SC01 / SC01 Plus. MISO is not broken out on the SC01 (leave it at -1); CS is tied low on the Plus (leave it at -1). Both have PSRAM and LVGL's memory pool lives there, so enable PSRAM in the IDE - QSPI PSRAM on the Plus, whose module is N16R2, not the OPI PSRAM most ESP32-S3 boards want.
- ESP32-S3 round GC9A01 with CST816S. The touch controller's RESET must be wired or it will not answer on I2C. The firmware also tells it not to fall asleep - asleep, it stops answering until touched.
- Raspberry Pi Pico. 264 KB of RAM: keep the draw buffer at roughly a tenth of the screen (the preset's buffer height already does).
- STM32. Needs the STM32duino core. SPI pins are remappable per variant - confirm against the datasheet. Panels go over SPI or TFT_eSPI's 8-bit parallel bus; none of the graphics libraries drives the FSMC or LTDC peripherals.
Re-exporting
Change the design, export again, unzip over the old folder. Everything is replaced,
src/ui/ui_events.c included, so copy back your own copy of it afterwards; if the new
export declares a new Call function stub, add its body to your copy from the fresh
ui_events.h. A file of your own beside the sketch is never touched, because no export
contains it. A changed LVGL version also means
re-installing the matching library and re-uploading any image assets, whose binary format differs
between 8 and 9.