I am using the Arduino IDE to create aircraft cockpit gauges for my simulator. This has worked great so far using TFT_eSPI and sprites. However, I have recently purchased a ST7701S display and AdaFruit Qualia ESP32S3 driver card. TFT does not support the ST7701. There is a GFX function that allows me to push an image to the display, but I also need one that enables me to push a needle sprite image to the display at various angles. I can’t find a solution to this in GFX.
I am looking at LVGL as an alternative, and believe that it supports the required functionality in terms of its image functions. Does it support the ST7701 display in the Arduino IDE as I can’t see a driver for this?
Hi @Sturmbock,
I’ve recently got LVGL working on one of these displays.
I have the Adafruit Qualia ESP32-S3 for RGB-666 Displays and a ST7701s display from Aliexpress 2.1inch 480x480 round display I think the lcd module is the same as the Adafruit display (Round RGB 666 TTL TFT Display - 2.1" 480x480 - Capacitive Touch - TL021WVC02CT-B1323)
I use the Arduino_GFX library from moononournation to set up the display.
I combined the demo code for the ESP32 board with the lvgl meter demo and it works fine. You’ll need to set up the lv_conf.h file in your library as described in the docs: Quick overview — LVGL documentation
Here is my code:
#include <Arduino_GFX_Library.h>
#include <lvgl.h>
Arduino_XCA9554SWSPI *expander = new Arduino_XCA9554SWSPI(
PCA_TFT_RESET, PCA_TFT_CS, PCA_TFT_SCK, PCA_TFT_MOSI,
&Wire, 0x3F);
Arduino_ESP32RGBPanel *rgbpanel = new Arduino_ESP32RGBPanel(
TFT_DE, TFT_VSYNC, TFT_HSYNC, TFT_PCLK,
TFT_R1, TFT_R2, TFT_R3, TFT_R4, TFT_R5,
TFT_G0, TFT_G1, TFT_G2, TFT_G3, TFT_G4, TFT_G5,
TFT_B1, TFT_B2, TFT_B3, TFT_B4, TFT_B5,
1 /* hsync_polarity */, 50 /* hsync_front_porch */, 2 /* hsync_pulse_width */, 44 /* hsync_back_porch */,
1 /* vsync_polarity */, 16 /* vsync_front_porch */, 2 /* vsync_pulse_width */, 18 /* vsync_back_porch */
);
Arduino_RGB_Display *gfx = new Arduino_RGB_Display(
// 2.1" 480x480 round display
480 /* width */, 480 /* height */, rgbpanel, 0 /* rotation */, true /* auto_flush */,
expander, GFX_NOT_DEFINED /* RST */, TL021WVC02_init_operations, sizeof(TL021WVC02_init_operations));
/* Change to your screen resolution */
static uint32_t screenWidth;
static uint32_t screenHeight;
static uint32_t bufSize;
static lv_disp_draw_buf_t draw_buf;
static lv_color_t *disp_draw_buf;
static lv_disp_drv_t disp_drv;
/* Display flushing */
void my_disp_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p) {
#ifndef DIRECT_MODE
uint32_t w = (area->x2 - area->x1 + 1);
uint32_t h = (area->y2 - area->y1 + 1);
#if (LV_COLOR_16_SWAP != 0)
gfx->draw16bitBeRGBBitmap(area->x1, area->y1, (uint16_t *)&color_p->full, w, h);
#else
gfx->draw16bitRGBBitmap(area->x1, area->y1, (uint16_t *)&color_p->full, w, h);
#endif
#endif // #ifndef DIRECT_MODE
lv_disp_flush_ready(disp);
}
static lv_obj_t * meter;
static void set_value(void * indic, int32_t v)
{
lv_meter_set_indicator_value(meter, (lv_meter_indicator_t *) indic, v);
}
void setup() {
Serial.begin(115200);
// Serial.setDebugOutput(true);
// while(!Serial);
Serial.println("Arduino_GFX LVGL Hello World example");
#ifdef GFX_EXTRA_PRE_INIT
GFX_EXTRA_PRE_INIT();
#endif
// Init Display
if (!gfx->begin()) {
Serial.println("gfx->begin() failed!");
}
gfx->fillScreen(BLACK);
#ifdef GFX_BL
pinMode(GFX_BL, OUTPUT);
digitalWrite(GFX_BL, HIGH);
#endif
lv_init();
screenWidth = gfx->width();
screenHeight = gfx->height();
#ifdef DIRECT_MODE
bufSize = screenWidth * screenHeight;
#else
bufSize = screenWidth * 40;
#endif
#ifdef ESP32
disp_draw_buf = (lv_color_t *)heap_caps_malloc(sizeof(lv_color_t) * bufSize, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
if (!disp_draw_buf) {
// remove MALLOC_CAP_INTERNAL flag try again
disp_draw_buf = (lv_color_t *)heap_caps_malloc(sizeof(lv_color_t) * bufSize, MALLOC_CAP_8BIT);
}
#else
disp_draw_buf = (lv_color_t *)malloc(sizeof(lv_color_t) * bufSize);
#endif
if (!disp_draw_buf) {
Serial.println("LVGL disp_draw_buf allocate failed!");
} else {
lv_disp_draw_buf_init(&draw_buf, disp_draw_buf, NULL, bufSize);
/* Initialize the display */
lv_disp_drv_init(&disp_drv);
/* Change the following line to your display resolution */
disp_drv.hor_res = screenWidth;
disp_drv.ver_res = screenHeight;
disp_drv.flush_cb = my_disp_flush;
disp_drv.draw_buf = &draw_buf;
#ifdef DIRECT_MODE
disp_drv.direct_mode = true;
#endif
lv_disp_drv_register(&disp_drv);
/* Initialize the (dummy) input device driver */
static lv_indev_drv_t indev_drv;
lv_indev_drv_init(&indev_drv);
indev_drv.type = LV_INDEV_TYPE_POINTER;
lv_indev_drv_register(&indev_drv);
meter = lv_meter_create(lv_scr_act());
lv_obj_center(meter);
lv_obj_set_size(meter, 480, 480);
/*Add a scale first*/
lv_meter_scale_t * scale = lv_meter_add_scale(meter);
lv_meter_set_scale_ticks(meter, scale, 41, 2, 10, lv_palette_main(LV_PALETTE_GREY));
lv_meter_set_scale_major_ticks(meter, scale, 8, 4, 15, lv_color_black(), 10);
lv_meter_indicator_t * indic;
/*Add a blue arc to the start*/
indic = lv_meter_add_arc(meter, scale, 3, lv_palette_main(LV_PALETTE_BLUE), 0);
lv_meter_set_indicator_start_value(meter, indic, 0);
lv_meter_set_indicator_end_value(meter, indic, 20);
/*Make the tick lines blue at the start of the scale*/
indic = lv_meter_add_scale_lines(meter, scale, lv_palette_main(LV_PALETTE_BLUE), lv_palette_main(LV_PALETTE_BLUE), false, 0);
lv_meter_set_indicator_start_value(meter, indic, 0);
lv_meter_set_indicator_end_value(meter, indic, 20);
/*Add a red arc to the end*/
indic = lv_meter_add_arc(meter, scale, 3, lv_palette_main(LV_PALETTE_RED), 0);
lv_meter_set_indicator_start_value(meter, indic, 80);
lv_meter_set_indicator_end_value(meter, indic, 100);
/*Make the tick lines red at the end of the scale*/
indic = lv_meter_add_scale_lines(meter, scale, lv_palette_main(LV_PALETTE_RED), lv_palette_main(LV_PALETTE_RED), false, 0);
lv_meter_set_indicator_start_value(meter, indic, 80);
lv_meter_set_indicator_end_value(meter, indic, 100);
/*Add a needle line indicator*/
indic = lv_meter_add_needle_line(meter, scale, 4, lv_palette_main(LV_PALETTE_GREY), -10);
/* Create simple label */
lv_obj_t *label = lv_label_create(lv_scr_act());
lv_label_set_text(label, "Hello Ardino and LVGL!");
lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);
/*Create an animation to set the value*/
lv_anim_t a;
lv_anim_init(&a);
lv_anim_set_exec_cb(&a, set_value);
lv_anim_set_var(&a, indic);
lv_anim_set_values(&a, 0, 100);
lv_anim_set_time(&a, 2000);
lv_anim_set_repeat_delay(&a, 100);
lv_anim_set_playback_time(&a, 500);
lv_anim_set_playback_delay(&a, 100);
lv_anim_set_repeat_count(&a, LV_ANIM_REPEAT_INFINITE);
lv_anim_start(&a);
Serial.println("Setup done");
}
}
void loop() {
lv_timer_handler(); /* let the GUI do its work */
#ifdef DIRECT_MODE
#if (LV_COLOR_16_SWAP != 0)
gfx->draw16bitBeRGBBitmap(0, 0, (uint16_t *)disp_draw_buf, screenWidth, screenHeight);
#else
gfx->draw16bitRGBBitmap(0, 0, (uint16_t *)disp_draw_buf, screenWidth, screenHeight);
#endif
#endif // #ifdef DIRECT_MODE
#ifdef CANVAS
gfx->flush();
#endif
delay(5);
}
Would you mind posting or sharing your lvgl_conf.h file?
Much appreciated for the other code!!
Hi @showdown, I’ve switched to v9 now since I posted this. But I have a backup of the v8 lv_conf.h that I think was the one I was using with the example code I posted.
Here is a gist: lv_conf_v8.h · GitHub