Flashing and monitoring issues with es32s3 + lvgl


I am starting to develop a project using a GC9a01 display with LVGL and an ESP32S3, but I am having a flashing and monitoring issue. If I upload via idf.py flash, it throws an port error (already checked that the port is connected and selected correctly):

A fatal error occurred: Could not open COM18, the port is busy or doesn't exist.
(Cannot configure port, something went wrong. Original message: PermissionError(13, 'Um dispositivo conectado ao sistema nÒo estß funcionando.', None, 31))

To upload, I have always to press the RESET and BOOT button on the board to put it in upload mode to flash correctly, and then press RESET again to restart the chip (for context, I’m using the USB interface of the chip, not the UART-USB port).

There’s another similar problem with this: In the code, I have a lot of print methods: in the app_main function, the sensor task and in the gui task, but only the logs in the app_main function and one from the sensor task (it has a loop and should be logging many times, but happens only once), and nothing from the gui task gets output, which also makes difficult do debug:

The thing is that these problems only happen when I use LVGL. If i comment the gui task declaration (line 38 on main.cpp), they work fine (flashing works without the need to press buttons and logging works as expected):

I (980) spi_flash: flash io: dio
I (981) sleep: Configure to isolate all GPIO pins in sleep state
I (981) sleep: Enable automatic switching of GPIO sleep configuration
I (982) main_task: Started on CPU0
I (992) esp_psram: Reserving pool of 32K of internal memory for DMA/internal allocations
I (992) main_task: Calling app_main()
Hello world!
I (992) MAIN: test log
S0 1, S1: 1
I (992) main_task: Returned from app_main()
S0 1, S1: 1
S0 1, S1: 1
S0 1, S1: 1
S0 1, S1: 1
S0 1, S1: 1

What MCU/Processor/Board and compiler are you using?

I am using a ESP32-S3-N16R8 dev board (devkitc), connected to a GC9A01 display via SPI, using the ESP-IDF framework

What version are you using?

ESP-IDF 5.3.0
LVGL 9.0.1

Also using this driver for the display.

What do you want to achieve?

I want to achieve a better development workflow (without the need to press the RST and BOOT buttons, wich are annoying to do) and receive monitoring values as expected.

What have you tried so far?

I have tried with another board, switching the display driver settings (switching buffer mode) and change some things on the display code, commenting and uncommenting some lines, but I am in a kind of road block where I don’t know what else I can try to do.

Code to reproduce

The full project is available here.
I have tried to follow a bunch of examples, including the lv_port_esp32, but with some changes to fit into version 9
My display file:

#include <stdlib.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <esp_timer.h>
#include <esp_log.h>
#include "display.h"
#include "sdkconfig.h"
#include "lvgl.h"
extern "C" {
    #include "gc9a01/gc9a01.h"

#include "event/lv_example_event.h"

#define DISPLAY_W 240
#define DISPLAY_H 240


static const char* TAG = "DISPLAY";

static void lv_tick_task(void *arg);
static void flush_pixel(lv_display_t * disp, const lv_area_t * area, uint8_t * px_map);

SemaphoreHandle_t xGuiSemaphore;

void gui_task(void *pvParameter) {
    xGuiSemaphore = xSemaphoreCreateMutex();

    lv_display_t * display = lv_display_create(DISPLAY_W, DISPLAY_H);


//    GC9A01_DrawPixel(20, 20, 100);
//    GC9A01_DrawPixel(70, 70, 4369);
//    GC9A01_Update();

    ESP_LOGI(TAG, "starting drivers");

//    lvgl_driver_init();

    lv_display_set_flush_cb(display, flush_pixel);

//    void* buf1 = (lv_color_t*)heap_caps_malloc(DISPLAY_W * DISPLAY_H * sizeof(lv_color_t), MALLOC_CAP_SPIRAM);
    static lv_color_t* buf1[DISPLAY_W * DISPLAY_H / 4];                        /*Declare a buffer for 1/10 screen size*/
//    static lv_color_t* buf2[DISPLAY_W * DISPLAY_H / 4];                        /*Declare a buffer for 1/10 screen size*/
//    static lv_color_t * buf1 =  (lv_color_t *)heap_caps_malloc((DISPLAY_W * DISPLAY_H * sizeof(lv_color_t)), MALLOC_CAP_DMA | MALLOC_CAP_SPIRAM);
    lv_display_set_buffers(display, buf1, nullptr, sizeof(buf1), LV_DISPLAY_RENDER_MODE_PARTIAL);

    const esp_timer_create_args_t periodic_timer_args = {
            .callback = &lv_tick_task,
            .name = "periodic_gui"
    esp_timer_handle_t periodic_timer;
    ESP_ERROR_CHECK(esp_timer_create(&periodic_timer_args, &periodic_timer));
    ESP_ERROR_CHECK(esp_timer_start_periodic(periodic_timer, LV_TICK_PERIOD_MS * 1000));

    ESP_LOGI(TAG, "starting thing\n");

    while (1) {
        /* Delay 1 tick (assumes FreeRTOS tick is 10ms */

//        /* Try to take the semaphore, call lvgl related function on success */
        if (pdTRUE == xSemaphoreTake(xGuiSemaphore, portMAX_DELAY)) {


void flush_pixel(lv_display_t * disp, const lv_area_t * area, uint8_t * px_map)
    ESP_LOGI(TAG, "flashing pixel\n");

    uint16_t * buf16 = (uint16_t *)px_map; /*Let's say it's a 16 bit (RGB565) display*/
    int16_t x, y;

    for(y = area->y1; y <= area->y2; y++) {
        for(x = area->x1; x <= area->x2; x++) {
            // subtracting from display size to rotate 180 deg
            GC9A01_DrawPixel(DISPLAY_W - x, DISPLAY_H - y, *buf16);


    /* IMPORTANT!!!
     * Inform LVGL that you are ready with the flushing and buf is not used anymore*/

static void lv_tick_task(void *arg) {
    (void) arg;


I don’t know if that’s more of a lvgl or expressif issue, but I hope I can get some insight or tips to fix this.
Also, to clarify: the lvgl works on displaying to the screen. The performance is very low (~5fps on simple widget), but that’s another problem for future me