Creating a demo Gauge GUI for an ESP32 and a 3.5-inch TFT display can be an exciting project! Below is a high-level overview of how to set up your project, including the necessary components, libraries, and a basic example of how to structure your code.
Here to used the ESP32 board based 3.5inch touch display ILI9488 using the LVGL (Light and Versatile Graphics Library) and Bodmer's TFT_eSPI arduino Library. The LVGL is a popular free and open-source embedded graphics library to create UIs for arduino.
In this Setting up LVGL (Light and Versatile Graphics Library) on an ESP32 with a TFT LCD touchscreen display ILI9488 is a great way to create interactive user interfaces for your projects. Here’s a step-by-step guide to help you get started
Components Required
ESP-32 Module (38Pin)
3.5 inch TFT ILI9488 SPI Interface Module 480x320 with Touch Screen Display
10k Potentiometer -5nos
Jumper Wires
Circuit Diagram
3.5inch TFT display
TFT refer blog:
In the VCC pin, you can either use 5V or 3.3V depending upon if your J1 connection is open or closed,
J1 = open=5V; J1=close=3.3V.
LVGL refer:
Hardware Requirements
ESP32 Board: Any variant should work, but make sure it has enough GPIO pins.
TFT LCD Touchscreen: Common models include ILI9488
Connecting Wires: Jumper wires for connections.
Breadboard (optional): For easier connections.
Software Requirements
Arduino IDE: Make sure you have the latest version.
Select “File>Preferences>settings>Additional Boards Manager URLs” to fill the link.
Here are the steps to install the ESP32 board in Arduino IDE:
Open the Arduino IDE
Select File and then Preferences
Find the Additional Board Manager URLs field and activate the text box
Add the URL to the field
Click OK to save the changes
https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_dev_index.json,
https://arduino.esp8266.com/stable/package_esp8266com_index.json,
https://espressif.github.io/arduino-esp32/package_esp32_index.json
LVGL Library: Available through the Library Manager in the Arduino IDE.
Touch Driver Library: Depending on your touchscreen (e.g., TFT_eSPI)
Configuring LVGL
LVGL Configuration: You may need to configure LVGL for your specific display and touch screen. Create a lv_conf.h file or modify the existing one in the LVGL library folder:
Set LVGL_DISPLAY_HOR_RES and LVGL_DISPLAY_VER_RES to match your display's resolution.
Configure the display and touch input settings based on your library (e.g., TFT_eSPI settings).
TFT_eSPI Configuration:
A feature rich Arduino IDE compatible graphics and fonts library for 32-bit processors. The library is targeted at 32-bit processors, it has been performance optimised for RP2040, STM32, ESP8266 and ESP32 types, other 32-bit processors may be used but will use the slower generic Arduino interface calls. The library can be loaded using the Arduino IDE's Library Manager. Direct Memory Access (DMA) can be used with the ESP32, RP2040 and STM32 processors with SPI interface displays to improve rendering performance. DMA with a parallel interface (8 and 16-bit) is only supported with the RP2040.
Go to the TFT_eSPI library folder and open the User_Setup.h file to configure the pins according to your wiring.
Wiring the TFT Display
TFT Pin ESP32 Pin
VCC 3.3V
GND GND
CS GPIO 5
RESET GPIO EN
DC/RS GPIO 17
SDI(MOSI) GPIO 23
SCK GPIO 18
LED 3.3V (or PWM pin for brightness)
Wiring the Touchscreen (e.g., TFT_espi)
Touch Pin ESP32 Pin
T_CS GPIO 16
T_IRQ GPIO 21
T_DOUT GPIO 19
T_DIN GPIO 23
T_CLK GPIO 18
4. UI Library: User Interface (e.g., UI)
Arduino Code
Here’s a basic example to initialize the display and create a simple button using LVGL:
######################################################################
#include <lvgl.h>
#include <TFT_eSPI.h>
#include <ui.h>
/*Don't forget to set Sketchbook location in File/Preferences to the path of your UI project (the parent foder of this INO file)*/
/*Change to your screen resolution*/
static const uint16_t screenWidth = 480;
static const uint16_t screenHeight = 320;
static lv_disp_draw_buf_t draw_buf;
static lv_color_t buf[ screenWidth * screenHeight / 10 ];
TFT_eSPI tft = TFT_eSPI(screenWidth, screenHeight); /* TFT instance */
int Gauge1 =0;
int Gauge2 =0;
int Gauge3 =0;
int Gauge4 =0;
int Gauge5 =0;
int compass_value = 0; // Current compass value
bool compass_increasing = true; // Flag for increasing/decreasing compass
// Function declarations
void simulate_compass();
void update_compass_ui(int compass);
#if LV_USE_LOG != 0
/* Serial debugging */
void my_print(const char * buf)
{
Serial.printf(buf);
Serial.flush();
}
/* Display flushing */
void my_disp_flush( lv_disp_drv_t disp, const lv_area_t area, lv_color_t *color_p )
{
uint32_t w = ( area->x2 - area->x1 + 1 );
uint32_t h = ( area->y2 - area->y1 + 1 );
tft.startWrite();
tft.setAddrWindow( area->x1, area->y1, w, h );
tft.pushColors( ( uint16_t )&color_p->full, w h, true );
tft.endWrite();
lv_disp_flush_ready( disp );
}
/*Read the touchpad*/
void my_touchpad_read( lv_indev_drv_t indev_driver, lv_indev_data_t data )
{
uint16_t touchX = 0, touchY = 0;
bool touched = false;//tft.getTouch( &touchX, &touchY, 600 );
if( !touched )
{
data->state = LV_INDEV_STATE_REL;
}
else
{
data->state = LV_INDEV_STATE_PR;
/*Set the coordinates*/
data->point.x = touchX;
data->point.y = touchY;
Serial.print( "Data x " );
Serial.println( touchX );
Serial.print( "Data y " );
Serial.println( touchY );
}
}
void setup()
{
Serial.begin( 115200 ); /* prepare for possible serial debug */
String LVGL_Arduino = "Hello Arduino! ";
LVGL_Arduino += String('V') + lv_version_major() + "." + lv_version_minor() + "." + lv_version_patch();
Serial.println( LVGL_Arduino );
Serial.println( "I am LVGL_Arduino" );
lv_init();
#if LV_USE_LOG != 0
lv_log_register_print_cb( my_print ); /* register print function for debugging */
tft.begin(); /* TFT init */
tft.setRotation( 3 ); /* Landscape orientation, flipped */
lv_disp_draw_buf_init( &draw_buf, buf, NULL, screenWidth * screenHeight / 10 );
/*Initialize the display*/
static lv_disp_drv_t disp_drv;
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;
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;
indev_drv.read_cb = my_touchpad_read;
lv_indev_drv_register( &indev_drv );
ui_init();
Serial.println( "Setup done" );
}
// compass Simulation: Simulates the compass going from 0 to 10,000 and back with smoother transitions
void simulate_compass()
{
const int max_compass = 360; // Maximum compass value (10,000 compass)
const int compass_step = 2; // Smaller increment step for smoother transition (100 compass)
if (compass_increasing)
{
if (compass_value < max_compass)
{
compass_value += compass_step;
}
else
{
compass_increasing = false; // Start decrementing when max compass is reached
}
}
else
{
if (compass_value > 0)
{
compass_value -= compass_step;
}
else
{
compass_increasing = true; // Start incrementing when 0 compass is reached
}
}
update_compass_ui(compass_value); // Update the compass UI elements
}
// Update the LVGL UI elements based on compass value
void update_compass_ui(int compass)
{
// Update compass label with the current value
char compass_str[10];
sprintf(compass_str, "%d", compass); // Display the current compass value (e.g., 100, 200, etc.)
//lv_label_set_text(ui_Label_compass, compass_str);
// Update gauge needle based on compass value
// Map 0 compass to 0 degrees and 10,000 compass to 2500 degrees
int Needle_6R = map(compass, 0, 360, 0, 3600);
lv_img_set_angle(ui_Needle_6, Needle_6R);
}
void loop()
{
lv_timer_handler(); /* let the GUI do its work */
delay(5);
// Update gauge label with the current value
char Gauge1_str[10];
char Gauge2_str[10];
char Gauge3_str[10];
char Gauge4_str[10];
char Gauge5_str[10];
sprintf(Gauge1_str, "%d", Gauge1); // Display the current Gauge5 value (e.g., 100, 200, etc.)
sprintf(Gauge2_str, "%d", Gauge2); // Display the current Gauge5 value (e.g., 100, 200, etc.)
sprintf(Gauge3_str, "%d", Gauge3); // Display the current Gauge5 value (e.g., 100, 200, etc.)
sprintf(Gauge4_str, "%d", Gauge4); // Display the current Gauge5 value (e.g., 100, 200, etc.)
sprintf(Gauge5_str, "%d", Gauge5); // Display the current Gauge5 value (e.g., 100, 200, etc.)
//lv_label_set_text(ui_Label_G5, Gauge5_str);
// Update gauge needle based on Speed value
// Map 0 Speed to 0 degrees and 1200 km to 3600 degrees
int Gauge1 = map(analogRead(12), 0, 4096, 0, 100); // change analog pin as you desired
int Needle_1R = map(Gauge1, 0, 100, 450, 3250);
lv_img_set_angle(ui_Needle_1, Needle_1R);
int arc_1_value = map(Gauge1, 0, 100, 0, 100);
lv_arc_set_value(ui_Arc_1, arc_1_value); // Update the arc based on analog value
int Gauge2 = map(analogRead(14), 0, 4096, 0, 140); // change analog pin as you desired
int arc_2_value = map(Gauge2, 0, 140, 0, 140);
lv_arc_set_value(ui_Arc_2, arc_2_value); // Update the arc based on analog value
uilabel_set_property(ui_Label_G2, UILABEL_PROPERTY_TEXT, String(Gauge2).c_str());
uilabel_set_property(ui_Label_G21, UILABEL_PROPERTY_TEXT, String(Gauge2).c_str());
int Gauge3 = map(analogRead(27), 0, 4096, 0, 80); // change analog pin as you desired
uilabel_set_property(ui_Label_G3, UILABEL_PROPERTY_TEXT, String(Gauge3).c_str());
// Map Gauge value to the range of 0-100 for the arc
int arc_3_value = map(Gauge3, 0, 80, 0, 80);
lv_arc_set_value(ui_Arc_3, arc_3_value); // Update the arc based on analog value
int Gauge4 = map(analogRead(26), 0, 4096, 0, 230); // change analog pin as you desired
uilabel_set_property(ui_Label_G4, UILABEL_PROPERTY_TEXT, String(Gauge4).c_str());
// Map Gauge value to the range of 0-100 for the arc
int arc_4_value = map(Gauge4, 0, 230, 0, 230);
lv_arc_set_value(ui_Arc_4, arc_4_value); // Update the arc based on analog value
int Gauge5 = map(analogRead(25), 0, 4096, 0, 120); // change analog pin as you desired
int Needle_5R = map(Gauge5, 0, 120, 875, 3300);
lv_img_set_angle(ui_Needle_5, Needle_5R);
uilabel_set_property(ui_Label_G5, UILABEL_PROPERTY_TEXT, String(Gauge5).c_str());
// Map Gauge value to the range of 0-100 for the arc
int arc_5_value = map(Gauge5, 0, 120, 0, 120);
lv_arc_set_value(ui_Arc_5, arc_5_value); // Update the arc based on analog value
//int Gauge6 = map(analogRead(12), 0, 4096, 0, 3600); // change analog pin as you desired
//int Needle_6R = map(Gauge6, 0, 3600, 0, 3600);
//lv_img_set_angle(ui_Needle_6, Needle_6R);
simulate_compass(); // Simulate compass and update the UI
}
######################################################################
Final Steps
Upload the code to your ESP32 using the Arduino IDE.
Open the Serial Monitor to check for any debug messages.
Interact with the touchscreen to see your UI in action!
Tips
Adjust the pin assignments based on your wiring.
Explore LVGL's extensive documentation for more UI elements and functionalities.
Use LVGL's built-in tools to design and customize your interfaces.
This should give you a solid start on using LVGL with an ESP32 and a TFT LCD touchscreen. Happy coding!