Someone willing to hold my hand?

Despite HOURS of trying, I can’t get LVGL working at all. Can’t even get examples to compile.

I’ve found so much conflicting and outright wrong / outdated information my head is spinning.

I really could use some 1-on-1 help.

Working on a Win11 desktop, Arduino IDE, RP2040 target.

Anyone up for the challenge? I’m in New Jersey.

I think I could help you out.

I am not in New Jersey but I have been told I am very thorough with the direction I give in a forum post.

Lets start off with a few question so I am able to gauge you knowledge level better.

How much programming experience do you have?
What programming languages do you know?
What is your programing language of choice?
Are you familiar with Linux?
Do you have WSL set up on your computer?

Now some hardware related questions.

What MCU are you running? You have the RP2040, just wanted this here for completeness
What display are you wanting to run?
What is the connection or “bus” the display is using when connected to your MCU (SPI, I8080, RGB)?
What is the resolution of the display?
What is the dolor depth of the display?
Does the display have a touch panel?
If the display has a touch panel what is the brand and model?
If the display has a touch panel what is the connection method to the MCU (SPI, I2C, other)?

Here are a few questions to get a better feel for what you are wanting to accomplish.

What GUI elements are you wanting to use?
How many GUI elements is there going to be?
Is there any external inputs like sensors that will be used to supply data to the display?
Is there a specific reason for using the RP2040 MCU?
Is there a specific reason for using the Arduino IDE?

Please answer all of the question to the best of your ability. If you have no clue what I am asking then please say that. If there is something that you do not understand maybe due to the acronyms I will go in depth as to why I am looking to know.

I don’t know your level of knowledge so please do not be offended by my questions if they seem really newbie. Let me know that you have a more advanced knowledge and we can go from there. For the most part I will be able to answer most questions you will have, If I don’t know the answer I will pretend like I do and come up with something that sounds really good. LOL, Nah, I won’t do that. What I will do is I will go and get myself an education on what you are asking and come back with an answer once I have that knowledge.

This will be quite a bit of back and forth so be patient. I know it can be frustrating at times. To make things move along a little bit faster when we get past some of the preliminary stuff I will set up a virtual machine running Windows 11 and we can both connect to it so I can show you what to do instead of trying to type it all out. I can even make a screen recording of the steps and send you the video so you can play it back as much as you like. I can even set things up so you can plug the RP2040 into your computer and it will tunnel that connection over the internet so it would be like you are plugging it directly into the virtual computer. This will allow us to flash the software to the RP2040 so you can see it work right in front of you.

3 Likes

Just to let you know I am located in Colorado so there is only a 2 hour time difference. That will make things a lot easier than if I was in say, Australia. The time difference is manageable.

Hi KD,

Thanks for your quick and thoughtful reply! I’ll try to answer as best I can below:

How much programming experience do you have?
Years. I’m mostly self-taught, but no stranger to applications level and UI programming for WinForms in VB and some c#. Also have done quite a bit of C embedded stuff for PIC and other CPUs. I’m an electronic designer. (www.steamsynth.com) I still have some fuzzy areas though. :-/
What programming languages do you know? C, VB, C#
What is your programing language of choice? Right now for this, embedded C.
Are you familiar with Linux? VERY little. Complete NOOB
Do you have WSL set up on your computer? WSL?

What MCU are you running? You have the RP2040, just wanted this here for completeness
What display are you wanting to run? Generic Chinese 2.4" TFT SPI 320x240. ILI9341.
What is the connection or “bus” the display is using when connected to your MCU (SPI, I8080, RGB)? SPI
What is the resolution of the display? 320x240
What is the dolor depth of the display? 16 bit (I think), but that’s really handled nicely with TFT_eSPI library.
Does the display have a touch panel? Yes. Works with other examples
If the display has a touch panel what is the brand and model? see above
If the display has a touch panel what is the connection method to the MCU (SPI, I2C, other)? SPI

What GUI elements are you wanting to use? Slider, button, radio button (is there one?), checkbox, popup windows
How many GUI elements is there going to be? 4 or 5 (at this time)
Is there any external inputs like sensors that will be used to supply data to the display? The data comes from the 4 ADC inputs, supplied by my custom PCB.
Is there a specific reason for using the RP2040 MCU? Actually the Seeed Xiao RP2040 - Size, cost, Speed, RAM size
Is there a specific reason for using the Arduino IDE? While I’m NOT a fan of the Argh-uino environment, it offers MANY free ready to use examples for a variety of platforms. Having worked professionally for years with MS Visual Studio, I’m most comfortable there.

FWIW - I have my custom design with the TFT and touch working nicely with the many TFT_eSPI examples, so hardware is NOT an issue here.

I understand we are getting to know each other, so I’m not offended at all. I’ve setup lots of projects and managed to work thru issues in the past, but this one has had me stumped every time I dive into it. I think must have tried getting LVGL working several times over the last few years, but always seem to get myself frustrated. Don’t know why this is so hard (for me?). I’m pretty sure it’s just some lame config file setting I’m missing. Grrrrrr.

Look forward to working with you - and THANKS!

Doug

1 Like

Hi Doug,

I’m quite new to the field and was frustrated with the very same experience. None of the examples are running out of the box; not even compile. However some complicated factory programs do work in my case. Therefore I have roughly did something on my board but when i decided to learn the basics of LVGL by using the example code blocks, there are always strange errors.

Computer: MacBook with Apple Silicon M1 Max
OS: OSX 12.6
Board: Wizee 2.8" - ESP32-Wroom-32
IDE: PlatformIO - VS Code
libraies
lvgl/lvgl@8.3.6
bodmer/TFT_eSPI@2.5.31

I’m not an experienced programmer however the errors i got are trivially from library dependencies or definition problems, so they’re not much related with programming experience i guess.

I found on Arduino forum a similar complaint.

I would love to get some basic help at this stage, or we can try doing some sort of pair programming? I’m located in Antalya, Turkiye. We’re 8 hrs ahead (GMT+3 zone) and I’m usually online on discord in weekdays…

The reason why people have a hard time is mostly because of using the Arduino IDE. I strongly suggest not using it. The reason why is because of all of the boiler plate code that gets used to map functions from the MCU’s SDK to the Arduino API. The Arduino IDE is made for Arduino’s and there is a lot of patchwork to use other MCU’s with it. It makes things a lot more confusing.

Unfortunately libraries TFT_eSPI are only made to work in the Arduino IDE.The display libraries that are made for the Arduino IDE are only partially MCU specific. So there are things that you will not have available like using DMA memory, without being able to use DMA memory you loose all benefits of running 2 frame buffers. There is a HUGE performance boost from using double buffering and DMA memory.

While I know that C is your strong language have you ever considered using something like MicroPython? IO ask this because it’s easy to compile and the majority of the complexity is already hammered out and brought into an easy to use API. You project is really simple and while Python is slower than C code MicroPython has worked around that by providing ways to get the code to compile as machine code to you get the speed that is seen in C code. This is best suited for math intensive operations.

I thought I would thrown that out there as a suggestion for ya.

We can go any route you want. You don’t have to use the Arduino IDE if you don’t want to, You would have to write your own code to use SPI, not that hard to do and you would need to write your own drivers for both the touch interface and also the display. This is a little bit more involved but not overly complex either.

You can stick with using The Arduino IDE if you would like as well. I can get you up and running with this fairly easily. The hand up that most people of is how to get the data from LVGL to TFT-eSPI so it renders. and how to read the touch input form TFT-eSPI.

Or using MicroPython.

The MicroPython route would get you up and running the fastest, Arduino is second and the “roll your own” would be the longest. You are familiar with object-orientation (C#) and that would make it leaps and bounds easier for you to understand Python. Someone coming from C would have a more difficult time.

WSL = Windows Subsystem for Linux.
WSL is the single best thing that Microsoft has done with Windows in the last 20 years. It allows you to run Linux applications on Windows. It does this in a manner that is not like using Vurtual Box or even Hyper-V. While it does use Hyper-V it is more integrated into the OS. You can add Linux applications to your Windows application list just like any other Windows application and when you run the application it works like any other Windows Application. Well the idea is it should, there can be some issues with it.

The reason why I brought up WSL is because the developers that wrote the majority of the code for the build systems did so on Linux. Things like the MCU SDK’s and the Arduino IDE are all designed to run on Linux and Windows is an after thought. It is easier to compile and flash running on Linux than it is on Windows. I use Windows and one of my things is creating build systems that will work on Windows properly. Not an easy task I will tell you that. Most people avoid it like the black plague. The first and top most priority is making sure the build system works on Linux so it is always going to be a smoother ride going that way. The build systems for Windows are hacked and patched together and are fraught with problems.

Do you have the datasheet for the display?

Here’s a data sheet for the ILI9341 display chip and a link to Amazon:
https://www.digikey.com/htmldatasheets/production/1640716/0/0/1/ili9341-datasheet.html

I hear what you are saying about not using certain dev tools and languages, but these are unfortunately not rabbit-holes I can afford to go down ATM. The product(s) I build simply don’t justify the efforts for the tiny quantities that I sell.

My realistic need is a simple pop-up window when you touch the main screen that contains a half dozen UI controls to adjust program parameters. Then another touch to hide the screen. This popup can be “static” (in memory) at all times and just shown / hidden, so I don’t think there would be issues with memory fragmenting, etc.

I’m probably stuck with the silly Arduino IDE due to items previously mentioned, so it would be great to simply get LVGL working with my setup, then I “should” be able to grok the rest. But maybe I’m attempting to oversimplify it due to my lack of knowledge. You don’t know what you don’t know - Ya know?

NO your not oversimplifying it. I was hoping to go the route that would give you the least amount of headaches in the future. Once you get used to the “Arduino” way of doing things it makes it harder to develop in any other way. That is because the Arduino IDE was purpose built for working with Arduino’s and the Arduino SDK so any libraries that get made to work in it will make things work using the Arduino API. It makes it hard to understand what really needs to be done because everything ends up getting hidden behind the SDK.

I figured out some ways to run LVGL example code using PlatformIO with Arduino Framwork.

I did this for both 2.8" and 7.0" Elecrow displays.

There are quite some library issues. I had no luck with Arduino IDE since the very beginning so PlatfromIO is quite a good alternative.

I’m not quite a forum guy so I don’t know how can I directly help but just let me know how I can help then I’ll try my best.

AYDIN

May I jump in on this discussion as well? I’m having the same kind of frustration trying to work with LVGL.

I am a retired IT professional, programming on and off for 30+ years in a variety of languages from Assembly, FORTRAN through Java and more recently, C and C++. Currently I’m working on a project with an ESP32-WROOM-32 (which I think is a bit dated, but has been fine so far). I did some Arduino stuff, but when I got an ESP32 I wanted to drop the Ard.-IDE in favor of ESP-IDF, precisely because of the underground stuff that Arduino encapsulates.

So my goal is to create a fairly straightforward UI (Buttons, menus, text input) on a 4" SPI display with touch ( ST7796 / XPT2046 ). I have been successful getting all the hardware to work, with TFT_eSPI on the Arduino IDE and with LovyanGFX in ESP-IDF. I discovered the LVGL_esp32_drivers earlier this week, and I have a project set up using that and lvgl 9.1.0 as components, and am trying to run the lvgl-included Demo programs, any or all of them, but I can’t get anything to compile. I think I have it all configured using idf.py menuconfig against LVGL 9.1.0 and LVGL_esp32_drivers. Seems all the examples I have found are using old versions of lvgl and I can’t seem to chase down all the broken references. And the documentation, frankly, sucks.

It would be a really big help to me to see some actual working code with these components. Can you help? FWIW, I am in US, CDT.

Thanks,
caj

It is actually pretty easy to do once you know how to go about doing it.

The whole build system from the users standpoint is geared around a single file idf_component.yml This file houses all of the dependencies you would need.

there is a component registry where you locate the bits and pieces you need.

dependencies:
  idf: "~5.0.6"
  lvgl/lvgl: "~9.1.0"
  esp_lcd_st7796: "~1.2.1"
  atanisoft/esp_lcd_touch_xpt2046: "~1.0.4"

The IDF build system uses cmake and you would need to have that installed to compile. You also need to supply the build rules for your project.

You will want your folder structure to be laid out like this.

project folder (folder)
    CMakeLists.txt (for defining the project)
    sdkconfig.defaults (config defaults file)
    main ( folder for header and source files, you can call this whatever you want)
        idf_component.yml  (component dependencies)
        Kconfig.projbuild (what is displayed in menuconfig)
        CMakeLists.txt (project cmake file, registers the component and sets the includes and sources)
        source and header files are located here

In the lowest level cmake file you want the follow.

cmake_minimum_required(VERSION 3.16)

include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(my_project)

and in the cmake file under the main directory you want the following

idf_component_register(SRCS "some_source_file_name1.c" "some_source_file_name2.c" INCLUDE_DIRS ".")

So that is the build system in a nutshell.

The code is pretty easy as well. you can use the examples provided by espressif

Now granted those examples use an older version of LVGL so you will need to make some changes to get it to work. The majority of the change are just name changes and there have been some convenience functions added to make things easier to do.

A lot of the API has only changed in naming. The names have been expanded and some have been simplified.

lv_disp_drv_t has become lv_display_t
lv_indev_drv_t has become lv_indev_t

lv_disp_drv_init has been replaced with lv_display_create(width, height)

You don’t set anything directly into the display structure anymore. Everything is handled using functions.

lv_display_set_color_format(display, format)
lv_display_set_flush_cb(display, callback_function)
lv_display_set_buffers(display, buf1, buf2, buf_size, render_mode)

If you want I can update one of the examples that espressif has for ya. Their examples are kind of a mess to look at and they can be really hard to understand what is happening. I can trim all of the BS out that you really don’t need to use and only leave the things in that will give you the best possible performance for the setup you have.

I am going to ask some questions and you may have already given this information but I am being lazy and not wanting to go back an reread your post. so if you already gave the information already I apologize a head of time for asking

MCU model: ESP32
PSRAM (None, 2mb, 4mb, 8mb, etc…):
SPIFLASH (8mb, 16mb, etc…):
PSRAM SPI speed (quad, octal, etc…):
Display IC: ST7796
Display Bus: SPI
Display resolution:
Display Color Depth:
Touch IC: XPT2046

If I had to guess I am going to say you have 2MB of quad PSRAM and 4MB of SPIFLASH. The display resolution is something along the lines of 480 x 320 with a 16 bit color depth. Just guessing at that.

Let me know what those things are so I know how to code things when I modify the example for you. I will comment each and every single step and explain what is going on and how it works.

Here you go. This is pseudo code, it has not been tested and is more than likely going to have some glitches and typos in it that need to be fixed.

If you have any issues lemme know.

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"

#include "esp_timer.h"
#include "esp_heap_caps.h"
#include "esp_lcd_panel_io.h"
#include "esp_lcd_panel_vendor.h"
#include "esp_lcd_panel_ops.h"
#include "esp_err.h"
#include "esp_log.h"
#include "esp_lcd_st7796.h"
#include "esp_lcd_touch_xpt2046.h"

#include "driver/spi_common.h"
#include "driver/gpio.h"
#include "rom/gpio.h"
#include "driver/spi_master.h"
#include "soc/spi_pins.h"

#include "lvgl.h"


/*
 * application settings
 */

#define TICK_PERIOD_MS  2

/*
 * Display settings
 */
#define WIDTH        480
#define HEIGHT       320
#define COLOR_DEPTH  16

/*
 * Pin definitions for the display
 * if you have the ability to choose the pins you are using
 * I strongly suggest using the hardware assigned pins as these are going
 * to be faster. I have set the pin numbers to the best ones to use for
 * getting the best performance
 */
#define LCD_HOST    SPI2_HOST
#define LCD_FREQ    (80 * 1000 * 1000)
#define LCD_BKL_ON  1
#define LCD_RST_ON  1
#define LCD_CLK     SPI2_IOMUX_PIN_NUM_CLK   // 14
#define LCD_MOSI    SPI2_IOMUX_PIN_NUM_MOSI  // 13
#define LCD_MISO    SPI2_IOMUX_PIN_NUM_MISO  // 12
#define LCD_DC      16
#define LCD_RST     17
#define LCD_CS      SPI2_IOMUX_PIN_NUM_CS    // 15
#define LCD_BKL     18


// change this to SPICOMMON_BUSFLAG_GPIO_PINS if
// not using the hardware SPI pin assignments
#define LCD_MUX   SPICOMMON_BUSFLAG_IOMUX_PINS


/*
 * Touch panel pin definitions.
 * These can sometimes share the same "bus" as the display
 * if that is what is done then the only thing you will need to change
 * is TP_CS, TP_RST, TP_RST_ON and possibly TP_FREQ
 */
#define TP_HOST    LCD_HOST
#define TP_FREQ    (1 * 1000 * 1000)
#define TP_MISO    LCD_MISO
#define TP_MOSI    LCD_MOSI
#define TP_CLK     LCD_CLK
#define TP_CS      0
#define TP_RST     0
#define TP_RST_ON  1

// If the bus that is used is different for the touch panel then set this to
// SPICOMMON_BUSFLAG_IOMUX_PINS if using hardware assigned SPI pins otherwise
// set it to SPICOMMON_BUSFLAG_GPIO_PINS
#define TP_MUX   LCD_MUX


#define _LCD_SPI_FLAGS  SPICOMMON_BUSFLAG_MASTER | LCD_MUX
#define _TP_SPI_FLAGS   SPICOMMON_BUSFLAG_MASTER | TP_MUX
#define _LCD_BUFFER_SIZE  (WIDTH * HEIGHT * (COLOR_DEPTH / 8) / 10)


// forward declarations

void tick_task_loop(void *pvParameters);
void app_task_loop(void *pvParameters);
void indev_read_cb(lv_indev_t *drv, lv_indev_data_t *data);
void display_rotate_cb(lv_event_t * e);
void display_flush_cb(lv_display_t *drv, lv_area_t *area, uint8_t *data);
bool bus_trans_done_cb(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_io_event_data_t *edata, void *user_ctx);
void create_gui(void);
void init_drivers(void);


void *buf1;
void *buf2;


lv_display_t *disp;
lv_indev_t *indev;

esp_lcd_panel_handle_t lcd_panel;
esp_lcd_spi_bus_handle_t lcd_spi_bus_handle;
esp_lcd_panel_io_handle_t lcd_panel_io_handle;
spi_bus_config_t lcd_spi_bus_config;
spi_bus_config_t tp_spi_bus_config;
esp_lcd_panel_io_spi_config_t lcd_panel_io_config;
esp_lcd_panel_dev_config_t lcd_panel_dev_config;

esp_lcd_touch_config_t tp_cfg;
esp_lcd_touch_handle_t tp;
esp_lcd_panel_io_handle_t tp_io_handle;
esp_lcd_panel_io_spi_config_t tp_io_config;

TaskHandle_t app_task = NULL;
TaskHandle_t tick_task = NULL;


#if LCD_BKL != -1
gpio_config_t bk_gpio_config;
#endif


// this is the startup code
void app_main(void)
{
    #if LCD_BKL != -1
        bk_gpio_config = {
            .mode = GPIO_MODE_OUTPUT,
            .pin_bit_mask = 1ULL << LCD_BKL
        };

        ESP_ERROR_CHECK(gpio_config(&bk_gpio_config));
        // turn off backlight, don't need to see the display doing anything
        // funky while it is being initilized
        gpio_set_level(LCD_BKL, !LCD_BKL_ON);
    #endif

    // initilize the drivers
    init_drivers();

    #if LCD_BKL != -1
        // turn the backlight on
        gpio_set_level(LCD_BKL, LCD_BKL_ON);
    #endif

    // create freertos tasks to handle the looping code, This allows us to
    // have pauses in the loops without causing a spinning wheel scenario that
    // eats up porocessor time.
    xTaskCreate(app_task_loop, "APPLOOP", STACK_SIZE, NULL, tskIDLE_PRIORITY, &app_task);
    xTaskCreate(tick_task_loop, "TICKLOOP", STACK_SIZE, NULL, tskIDLE_PRIORITY, &tick_task);
    vTaskStartScheduler();
}


// This sets up the hardware drivers for the display and also the touch panel
// It also creates the software driver in LVGL for both as well.
void init_drivers(void) 
{
    buf1 = heap_caps_malloc(_LCD_BUFFER_SIZE, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA);
    buf2 = heap_caps_malloc(_LCD_BUFFER_SIZE, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA);

    lv_init();

    // create the LVGL display
    disp = lv_display_create(WIDTH, HEIGHT);

    lcd_spi_bus_handle = (esp_lcd_spi_bus_handle_t)((uint32_t)LCD_HOST);

    lcd_spi_bus_config = (spi_bus_config_t){
        .sclk_io_num = LCD_CLK,
        .mosi_io_num = LCD_MOSI,
        .miso_io_num = LCD_MISO,
        .quadwp_io_num = -1,
        .quadhd_io_num = -1,
        .data4_io_num = -1,
        .data5_io_num = -1,
        .data6_io_num = -1,
        .data7_io_num = -1,
        .flags = SPICOMMON_BUSFLAG_MASTER | LCD_MUX,
        .max_transfer_sz = _LCD_BUFFER_SIZE
    };

    tp_spi_bus_config = (spi_bus_config_t){
        .sclk_io_num = TP_CLK,
        .mosi_io_num = TP_MOSI,
        .miso_io_num = TP_MISO,
        .quadwp_io_num = -1,
        .quadhd_io_num = -1,
        .data4_io_num = -1,
        .data5_io_num = -1,
        .data6_io_num = -1,
        .data7_io_num = -1,
        .flags = SPICOMMON_BUSFLAG_MASTER | TP_MUX,
        .max_transfer_sz = 512
    };

    lcd_panel_io_config = (esp_lcd_panel_io_spi_config_t){
        .trans_queue_depth = 1,
        .cs_gpio_num = LCD_CS,
        .dc_gpio_num = LCD_DC,
        .spi_mode = 0,
        .user_ctx = disp,
        .pclk_hz = LCD_FREQ,
        .on_color_trans_done = &bus_trans_done_cb,
        .lcd_cmd_bits = 8,
        .lcd_param_bits = 8,
        .flags = {
            .dc_low_on_data = 0,
            .sio_mode = 0,
            .lsb_first = 0,
            .cs_high_active = 0,
            .octal_mode = 0
        }
    }

    tp_io_config = (esp_lcd_panel_io_spi_config_t){
        .trans_queue_depth = 1,
        .cs_gpio_num = TP_CS,
        .dc_gpio_num = -1,
        .spi_mode = 0,
        .user_ctx = NULL,
        .pclk_hz = TP_FREQ,
        .on_color_trans_done = NULL,
        .lcd_cmd_bits = 8,
        .lcd_param_bits = 8,
        .flags = {
            .dc_low_on_data = 0,
            .sio_mode = 0,
            .lsb_first = 0,
            .cs_high_active = 0,
            .octal_mode = 0
        }
    }

    tp_cfg = (esp_lcd_touch_config_t){
        .x_max = WIDTH,
        .y_max = HEIGHT,
        .rst_gpio_num = TP_RST,
        .int_gpio_num = -1,
        .levels = {
            .reset = TP_RST_ON
        },
        .flags = {
            .swap_xy = 0,
            .mirror_x = 0,
            .mirror_y = 0
        }
    };

    lcd_panel_dev_config = (esp_lcd_panel_dev_config_t){
        .reset_gpio_num = LCD_RST,
        .rgb_endian = LCD_RGB_ENDIAN_RGB,
        .bits_per_pixel = COLOR_DEPTH,
        .flags = { .reset_active_high = LCD_RST_ON },
        .vendor_config = NULL
    }

    if (LCD_HOST == SPI2_HOST) {
        ESP_ERROR_CHECK(spi_bus_initialize(LCD_HOST, &spi_bus_config, SPI_DMA_CH1));
    } else {
        ESP_ERROR_CHECK(spi_bus_initialize(LCD_HOST, &spi_bus_config, SPI_DMA_CH2));
    }


    ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi(lcd_spi_bus_handle, &lcd_panel_io_config, &lcd_panel_io_handle));
    ESP_ERROR_CHECK(esp_lcd_new_panel_st7796(lcd_panel_io_handle, &lcd_panel_dev_config, &lcd_panel));

    ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(lcd_panel, true));
    ESP_ERROR_CHECK(esp_lcd_panel_reset(lcd_panel));
    ESP_ERROR_CHECK(esp_lcd_panel_init(lcd_panel));

    ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)TP_HOST, &tp_io_config, &tp_io_handle));
    ESP_ERROR_CHECK(esp_lcd_touch_new_spi_xpt2046(tp_io_handle, &tp_cfg, &tp));

    lv_display_set_flush_cb(disp, display_flush_cb);
    lv_display_set_buffers(buf1, buf2, _LCD_BUFFER_SIZE, LV_RENDER_MODE_PARTIAL);
    lv_display_set_driver_data(disp, lcd_panel);
    lv_display_add_event_cb(disp, &display_rotate_cb, LV_EVENT_RESOLUTION_CHANGED, lcd_panel);


    if (COLOR_DEPTH == 16) {
        lv_display_set_color_format(disp, LV_COLOR_FORMAT_RGB565);
    } else {
        lv_display_set_color_format(disp, LV_COLOR_FORMAT_RGB888);
    }

    indev = lv_indev_create(WIDTH, HEIGHT);
    lv_indev_set_read_cb(indev, &indev_read_cb);
    lv_indev_set_display(indev, disp);
    lv_indev_set_type(indev, LV_INDEV_TYPE_POINTER);
    lv_indev_enable(indev, true);
    lv_indev_set_driver_data(indev, tp);
    
    lv_display_set_user_data(disp, indev);
}


// place the gui code here
void create_gui(void)
{
    // User code for GUI creation goes here
    lv_obj_t *scrn = lv_screen_active(disp);
    
    lv_obj_t *slider = lv_slider_create(scrn);
    lv_obj_set_size(slider, WIDTH - 30, HEIGHT / 10);
    lv_obj_center(slider);
}


// because we are using DMA memory and double buffering we need to know when the
// transfer has completed. This is done so LVGL doesn't write to a buffer that 
// is being transmitted to a display. DMA memory allows a buffer to be transmitted
// without using the CPU. This allows the program to continue running doing other
// things at the same time as the buffer being sent.
bool bus_trans_done_cb(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_io_event_data_t *edata, void *user_ctx)
{
    lv_display_t *disp_driver = (lv_display_t *)user_ctx;
    lv_display_flush_ready(disp_driver);
    return false;
}

// handles flushing the buffer data to the display
void display_flush_cb(lv_display_t *drv, lv_area_t *area, uint8_t *data)
{
    #if COLOR_DEPTH == 16
        // this is needed for SPI RGB565 displays. It is because the pixel 
        // order is reverse when the data gets sent, so we need to swap the 
        // bytes in each pixel
        lv_draw_sw_rgb565_swap(data, (uint32_t)_LCD_BUFFER_SIZE);
    #endif

    // copy a buffer's content to a specific area of the display
    esp_lcd_panel_draw_bitmap(lcd_panel, (int)area->x1, (int)area->y1, (int)area->x2 + 1, (int)area->y2 + 1, data);
}


/* Rotate display and touch, when rotated screen in LVGL. Called when driver parameters are updated. */
void display_rotate_cb(lv_event_t * e)
{

    lv_display_t *d = (lv_display_t *)lv_event_get_user_data(e);
    esp_lcd_panel_handle_t lcd = (esp_lcd_panel_handle_t)lv_display_get_driver_data(d);
    lv_indev_t *idv = (lv_indev_t *)lv_display_get_user_data(d);
    esp_lcd_touch_handle_t t = (esp_lcd_touch_handle_t)lv_indev_get_driver_data(idv);    
    
    switch (lv_display_get_rotation(d)) {
        case LV_DISP_ROT_NONE:
            // Rotate LCD display
            esp_lcd_panel_swap_xy(lcd, false);
            esp_lcd_panel_mirror(lcd, true, false);

            // Rotate LCD touch
            esp_lcd_touch_set_mirror_y(t, false);
            esp_lcd_touch_set_mirror_x(t, false);
            break;
        case LV_DISP_ROT_90:
            // Rotate LCD display
            esp_lcd_panel_swap_xy(lcd, true);
            esp_lcd_panel_mirror(lcd, true, true);

            // Rotate LCD touch
            esp_lcd_touch_set_mirror_y(t, false);
            esp_lcd_touch_set_mirror_x(t, false);
            break;
        case LV_DISP_ROT_180:
            // Rotate LCD display
            esp_lcd_panel_swap_xy(lcd, false);
            esp_lcd_panel_mirror(lcd, false, true);

            // Rotate LCD touch
            esp_lcd_touch_set_mirror_y(t, false);
            esp_lcd_touch_set_mirror_x(t, false);
            break;
        case LV_DISP_ROT_270:
            // Rotate LCD display
            esp_lcd_panel_swap_xy(lcd, true);
            esp_lcd_panel_mirror(lcd, false, false);

            // Rotate LCD touch
            esp_lcd_touch_set_mirror_y(t, false);
            esp_lcd_touch_set_mirror_x(t, false);
            break;
    }
}


int32_t last_touch_x = -1;
int32_t last_touch_y = -1;


void indev_read_cb(lv_indev_t *drv, lv_indev_data_t *data)
{
    uint16_t touchpad_x[1] = {0};
    uint16_t touchpad_y[1] = {0};
    uint8_t touchpad_cnt = 0;
    
    esp_lcd_touch_handle_t t = (esp_lcd_touch_handle_t)lv_indev_get_driver_data(drv);
    
    /* Read touch controller data */
    esp_lcd_touch_read_data(t);

    /* Get coordinates */
    bool touchpad_pressed = esp_lcd_touch_get_coordinates(t, touchpad_x, touchpad_y, NULL, &touchpad_cnt, 1);

    if (touchpad_pressed && touchpad_cnt > 0) {
        last_touch_x = (int32_t)touchpad_x[0];
        last_touch_y = (int32_t)touchpad_y[0];
        data->state = LV_INDEV_STATE_PRESSED;
    } else {
        data->state = LV_INDEV_STATE_RELEASED;
    }

    data->point.x = last_touch_x;
    data->point.y = last_touch_y;
}


void tick_task_loop(void *pvParameters)
{
    uint32_t *pulNotificationValue;
    while (1) {
        vTaskDelay(pdMS_TO_TICKS(LVGL_TICK_PERIOD_MS));
        /* Tell LVGL how many milliseconds has elapsed */
        lv_tick_inc(LVGL_TICK_PERIOD_MS);
        xTaskNotify(main_task, 1, eSetValueWithOverwrite);
    }
}


void app_task_loop(void *pvParameters)
{
    uint32_t *pulNotificationValue;

    create_gui();

     while (1) {
        // this is a stall until a tick increment has occured. when that has happened
        // this will get released to update the display
        // if you have other things that need to be done like checking pins
        // you will more than likely want to change this from a wait to a read
        // and if the value has been set then run the timer handler.
        // The purpose to this is to not have the CPU doing a spinning wheels
        // when doing nothing. This allows other things to function better and faster.
        xTaskNotifyWait(ULONG_MAX, ULONG_MAX, pulNotificationValue, portMAX_DELAY)
        lv_timer_handler();
     }
}

Lovely! This is wonderful, and most helpful. Just this little tidbit here is a really big help – I chased this for a long time yesterday:


I will spend some time studying this more in-depth today Thanks $1024K USD for this!!
caj

Look at the changelog between 8.3 and 9.0. It will explain a lot of the API changes that were made.

OK, will do. I began this odyssey with 9.1 so I have no exposure to the back versions. But with examples & demos, you just sort of expect them to work with a minimum of fuss, since that’s kind of the whole point. . .

Thanks again.

That would be ideal. But… There is an unlimited number of combinations of MCU’s displays, display sizes, display driver IC’s touch panel IC’s different connection methods between these things. There is no one size fits all that exists. Then you throw into the mix that there are other pieces of software in the mix and changes in those that take place that we have no control over. Trying to keep pace with LVGL alone can be difficult let alone also keeping pace with changes made in the other pieces of software. We have found that not providing examples of how to use specific devices is the best things to do, only showing what can be done with LVGL makes it easier to manage. If a user is wanting information for specific hardware it is easier to provide that on an as needed basis. You came here to ask for help and you got the examples needed for your specific use case.

one major version number changes will see wild changes to things like the API. so 8.x to 9.x there is a wild change. 9.0 to 9.1 there is not going to be those kinds of changes. There might be one or 2 things that might change but nothing like what is seen between 8.x and 9.x

I have 3 of the Elecrow 7" Crowpanels on order and I’d love to know how to load micropython+lvgl onto them. By any chance have you a pointer on how to do this?

I’m just beginning my journey with Micropython and LVGL. Ideally, I’ll prototype stuff on Mac Mini and then move it over to the display to test.