04_complete_ui.ino
Arduino/lvgl_ttf_spi_demo/04_complete_ui.ino
/**
* Example 4: Complete UI Application
*
* This example combines all concepts into a functional application:
* 1. Multiple screens with navigation
* 2. Various UI widgets (buttons, labels, sliders, bars)
* 3. Real-time data updates
* 4. Interactive controls
* 5. State management
*
* Learning Objectives:
* - Build a complete application
* - Manage multiple screens
* - Combine different widgets
* - Handle complex interactions
* - Organize code for larger projects
*/
#include <lvgl.h>
#include <TFT_eSPI.h>
#include <SPI.h>
// ============================================================================
// DISPLAY CONFIGURATION
// ============================================================================
#define DISPLAY_WIDTH 320
#define DISPLAY_HEIGHT 240
#define DISPLAY_BL 15
#define DISPLAY_BUF_SIZE (DISPLAY_WIDTH * DISPLAY_HEIGHT / 10)
// ============================================================================
// GLOBAL OBJECTS
// ============================================================================
TFT_eSPI tft = TFT_eSPI();
static lv_color_t buf1[DISPLAY_BUF_SIZE];
static lv_color_t buf2[DISPLAY_BUF_SIZE];
// Screen objects
lv_obj_t *main_screen;
lv_obj_t *settings_screen;
lv_obj_t *info_screen;
// Main screen widgets
lv_obj_t *temp_label;
lv_obj_t *humidity_label;
lv_obj_t *pressure_label;
lv_obj_t *status_bar;
lv_obj_t *progress_bar;
// Settings screen widgets
lv_obj_t *brightness_slider;
lv_obj_t *brightness_label;
lv_obj_t *volume_slider;
lv_obj_t *volume_label;
// Application state
static float temperature = 22.5;
static float humidity = 45.0;
static float pressure = 1013.25;
static uint8_t brightness = 80;
static uint8_t volume = 50;
static uint32_t progress = 0;
// ============================================================================
// LVGL DISPLAY FLUSH CALLBACK
// ============================================================================
void display_flush(lv_display_t *disp, const lv_area_t *area, uint8_t *px_map) {
uint32_t w = (area->x2 - area->x1 + 1);
uint32_t h = (area->y2 - area->y1 + 1);
uint32_t pixelCount = w * h;
tft.startWrite();
tft.setAddrWindow(area->x1, area->y1, w, h);
tft.pushColors((uint16_t*)px_map, pixelCount, true);
tft.endWrite();
lv_display_flush_ready(disp);
}
// ============================================================================
// EVENT HANDLERS
// ============================================================================
/**
* Navigation: Switch to main screen
*/
void show_main_screen(lv_event_t *e) {
lv_scr_load(main_screen);
Serial.println("Switched to main screen");
}
/**
* Navigation: Switch to settings screen
*/
void show_settings_screen(lv_event_t *e) {
lv_scr_load(settings_screen);
Serial.println("Switched to settings screen");
}
/**
* Navigation: Switch to info screen
*/
void show_info_screen(lv_event_t *e) {
lv_scr_load(info_screen);
Serial.println("Switched to info screen");
}
/**
* Brightness slider event handler
*/
void brightness_slider_handler(lv_event_t *e) {
lv_obj_t *slider = lv_event_get_target(e);
int32_t value = lv_slider_get_value(slider);
brightness = value;
// Update label
char buffer[20];
snprintf(buffer, sizeof(buffer), "Brightness: %d%%", brightness);
lv_label_set_text(brightness_label, buffer);
// Update actual backlight (if connected)
analogWrite(DISPLAY_BL, map(brightness, 0, 100, 0, 255));
Serial.print("Brightness changed to: ");
Serial.print(brightness);
Serial.println("%");
}
/**
* Volume slider event handler
*/
void volume_slider_handler(lv_event_t *e) {
lv_obj_t *slider = lv_event_get_target(e);
int32_t value = lv_slider_get_value(slider);
volume = value;
// Update label
char buffer[20];
snprintf(buffer, sizeof(buffer), "Volume: %d%%", volume);
lv_label_set_text(volume_label, buffer);
Serial.print("Volume changed to: ");
Serial.print(volume);
Serial.println("%");
}
/**
* Refresh data button handler
*/
void refresh_data_handler(lv_event_t *e) {
// Simulate reading sensor data
temperature += (random(-10, 11) / 10.0);
humidity += (random(-5, 6) / 10.0);
pressure += (random(-5, 6) / 10.0);
// Clamp values
if (temperature < 15) temperature = 15;
if (temperature > 30) temperature = 30;
if (humidity < 20) humidity = 20;
if (humidity > 80) humidity = 80;
if (pressure < 980) pressure = 980;
if (pressure > 1050) pressure = 1050;
update_sensor_display();
Serial.println("Data refreshed");
}
/**
* Reset button handler
*/
void reset_handler(lv_event_t *e) {
temperature = 22.5;
humidity = 45.0;
pressure = 1013.25;
progress = 0;
update_sensor_display();
lv_bar_set_value(progress_bar, 0, LV_ANIM_ON);
Serial.println("Data reset");
}
// ============================================================================
// UI CREATION FUNCTIONS
// ============================================================================
/**
* Create navigation bar (common to all screens)
*/
void create_nav_bar(lv_obj_t *screen) {
// Create container for navigation buttons
lv_obj_t *nav_bar = lv_obj_create(screen);
lv_obj_set_size(nav_bar, DISPLAY_WIDTH, 35);
lv_obj_align(nav_bar, LV_ALIGN_BOTTOM_MID, 0, 0);
lv_obj_set_style_bg_color(nav_bar, lv_color_hex(0x333333), 0);
lv_obj_set_style_border_width(nav_bar, 0, 0);
lv_obj_set_style_pad_all(nav_bar, 5, 0);
lv_obj_clear_flag(nav_bar, LV_OBJ_FLAG_SCROLLABLE);
// Main button
lv_obj_t *main_btn = lv_btn_create(nav_bar);
lv_obj_set_size(main_btn, 90, 25);
lv_obj_align(main_btn, LV_ALIGN_LEFT_MID, 10, 0);
lv_obj_set_style_bg_color(main_btn, lv_color_hex(0x0066FF), 0);
lv_obj_t *main_label = lv_label_create(main_btn);
lv_label_set_text(main_label, "Main");
lv_obj_center(main_label);
lv_obj_add_event_cb(main_btn, show_main_screen, LV_EVENT_CLICKED, NULL);
// Settings button
lv_obj_t *settings_btn = lv_btn_create(nav_bar);
lv_obj_set_size(settings_btn, 90, 25);
lv_obj_align(settings_btn, LV_ALIGN_CENTER, 0, 0);
lv_obj_set_style_bg_color(settings_btn, lv_color_hex(0x666666), 0);
lv_obj_t *settings_label = lv_label_create(settings_btn);
lv_label_set_text(settings_label, "Settings");
lv_obj_center(settings_label);
lv_obj_add_event_cb(settings_btn, show_settings_screen, LV_EVENT_CLICKED, NULL);
// Info button
lv_obj_t *info_btn = lv_btn_create(nav_bar);
lv_obj_set_size(info_btn, 90, 25);
lv_obj_align(info_btn, LV_ALIGN_RIGHT_MID, -10, 0);
lv_obj_set_style_bg_color(info_btn, lv_color_hex(0x666666), 0);
lv_obj_t *info_label = lv_label_create(info_btn);
lv_label_set_text(info_label, "Info");
lv_obj_center(info_label);
lv_obj_add_event_cb(info_btn, show_info_screen, LV_EVENT_CLICKED, NULL);
}
/**
* Create main screen with sensor data
*/
void create_main_screen() {
main_screen = lv_obj_create(NULL);
lv_obj_set_style_bg_color(main_screen, lv_color_hex(0x001122), 0);
// Title
lv_obj_t *title = lv_label_create(main_screen);
lv_label_set_text(title, "Sensor Monitor");
lv_obj_set_style_text_color(title, lv_color_hex(0xFFFFFF), 0);
lv_obj_set_style_text_font(title, &lv_font_montserrat_18, 0);
lv_obj_align(title, LV_ALIGN_TOP_MID, 0, 5);
// Temperature display
lv_obj_t *temp_container = lv_obj_create(main_screen);
lv_obj_set_size(temp_container, 140, 50);
lv_obj_align(temp_container, LV_ALIGN_TOP_LEFT, 10, 35);
lv_obj_set_style_bg_color(temp_container, lv_color_hex(0x333333), 0);
lv_obj_set_style_border_width(temp_container, 2, 0);
lv_obj_set_style_border_color(temp_container, lv_color_hex(0xFF6600), 0);
lv_obj_t *temp_title = lv_label_create(temp_container);
lv_label_set_text(temp_title, "Temperature");
lv_obj_set_style_text_color(temp_title, lv_color_hex(0xFFFFFF), 0);
lv_obj_align(temp_title, LV_ALIGN_TOP_LEFT, 5, 2);
temp_label = lv_label_create(temp_container);
lv_obj_set_style_text_color(temp_label, lv_color_hex(0xFF6600), 0);
lv_obj_set_style_text_font(temp_label, &lv_font_montserrat_16, 0);
lv_obj_align(temp_label, LV_ALIGN_BOTTOM_LEFT, 5, -2);
// Humidity display
lv_obj_t *humidity_container = lv_obj_create(main_screen);
lv_obj_set_size(humidity_container, 140, 50);
lv_obj_align(humidity_container, LV_ALIGN_TOP_RIGHT, -10, 35);
lv_obj_set_style_bg_color(humidity_container, lv_color_hex(0x333333), 0);
lv_obj_set_style_border_width(humidity_container, 2, 0);
lv_obj_set_style_border_color(humidity_container, lv_color_hex(0x0066FF), 0);
lv_obj_t *humidity_title = lv_label_create(humidity_container);
lv_label_set_text(humidity_title, "Humidity");
lv_obj_set_style_text_color(humidity_title, lv_color_hex(0xFFFFFF), 0);
lv_obj_align(humidity_title, LV_ALIGN_TOP_LEFT, 5, 2);
humidity_label = lv_label_create(humidity_container);
lv_obj_set_style_text_color(humidity_label, lv_color_hex(0x0066FF), 0);
lv_obj_set_style_text_font(humidity_label, &lv_font_montserrat_16, 0);
lv_obj_align(humidity_label, LV_ALIGN_BOTTOM_LEFT, 5, -2);
// Pressure display
lv_obj_t *pressure_container = lv_obj_create(main_screen);
lv_obj_set_size(pressure_container, 140, 50);
lv_obj_align(pressure_container, LV_ALIGN_TOP_LEFT, 10, 95);
lv_obj_set_style_bg_color(pressure_container, lv_color_hex(0x333333), 0);
lv_obj_set_style_border_width(pressure_container, 2, 0);
lv_obj_set_style_border_color(pressure_container, lv_color_hex(0x00FF00), 0);
lv_obj_t *pressure_title = lv_label_create(pressure_container);
lv_label_set_text(pressure_title, "Pressure");
lv_obj_set_style_text_color(pressure_title, lv_color_hex(0xFFFFFF), 0);
lv_obj_align(pressure_title, LV_ALIGN_TOP_LEFT, 5, 2);
pressure_label = lv_label_create(pressure_container);
lv_obj_set_style_text_color(pressure_label, lv_color_hex(0x00FF00), 0);
lv_obj_set_style_text_font(pressure_label, &lv_font_montserrat_16, 0);
lv_obj_align(pressure_label, LV_ALIGN_BOTTOM_LEFT, 5, -2);
// Progress bar
lv_obj_t *progress_title = lv_label_create(main_screen);
lv_label_set_text(progress_title, "Progress");
lv_obj_set_style_text_color(progress_title, lv_color_hex(0xFFFFFF), 0);
lv_obj_align(progress_title, LV_ALIGN_TOP_LEFT, 10, 155);
progress_bar = lv_bar_create(main_screen);
lv_obj_set_size(progress_bar, 280, 20);
lv_obj_align(progress_bar, LV_ALIGN_TOP_LEFT, 10, 175);
lv_bar_set_range(progress_bar, 0, 100);
lv_bar_set_value(progress_bar, 0, LV_ANIM_ON);
lv_obj_set_style_bg_color(progress_bar, lv_color_hex(0x333333), 0);
lv_obj_set_style_bg_color(progress_bar, lv_color_hex(0x00AAFF), LV_PART_INDICATOR);
// Control buttons
lv_obj_t *refresh_btn = lv_btn_create(main_screen);
lv_obj_set_size(refresh_btn, 130, 30);
lv_obj_align(refresh_btn, LV_ALIGN_TOP_RIGHT, -10, 95);
lv_obj_set_style_bg_color(refresh_btn, lv_color_hex(0x00AA00), 0);
lv_obj_t *refresh_label = lv_label_create(refresh_btn);
lv_label_set_text(refresh_label, "Refresh");
lv_obj_center(refresh_label);
lv_obj_add_event_cb(refresh_btn, refresh_data_handler, LV_EVENT_CLICKED, NULL);
lv_obj_t *reset_btn = lv_btn_create(main_screen);
lv_obj_set_size(reset_btn, 130, 30);
lv_obj_align(reset_btn, LV_ALIGN_TOP_RIGHT, -10, 130);
lv_obj_set_style_bg_color(reset_btn, lv_color_hex(0xAA0000), 0);
lv_obj_t *reset_label = lv_label_create(reset_btn);
lv_label_set_text(reset_label, "Reset");
lv_obj_center(reset_label);
lv_obj_add_event_cb(reset_btn, reset_handler, LV_EVENT_CLICKED, NULL);
// Navigation bar
create_nav_bar(main_screen);
// Initial update
update_sensor_display();
}
/**
* Create settings screen
*/
void create_settings_screen() {
settings_screen = lv_obj_create(NULL);
lv_obj_set_style_bg_color(settings_screen, lv_color_hex(0x001122), 0);
// Title
lv_obj_t *title = lv_label_create(settings_screen);
lv_label_set_text(title, "Settings");
lv_obj_set_style_text_color(title, lv_color_hex(0xFFFFFF), 0);
lv_obj_set_style_text_font(title, &lv_font_montserrat_18, 0);
lv_obj_align(title, LV_ALIGN_TOP_MID, 0, 5);
// Brightness control
lv_obj_t *brightness_title = lv_label_create(settings_screen);
lv_label_set_text(brightness_title, "Brightness");
lv_obj_set_style_text_color(brightness_title, lv_color_hex(0xFFFFFF), 0);
lv_obj_align(brightness_title, LV_ALIGN_TOP_LEFT, 10, 35);
brightness_slider = lv_slider_create(settings_screen);
lv_obj_set_size(brightness_slider, 280, 20);
lv_obj_align(brightness_slider, LV_ALIGN_TOP_LEFT, 10, 55);
lv_slider_set_range(brightness_slider, 0, 100);
lv_slider_set_value(brightness_slider, brightness, LV_ANIM_OFF);
lv_obj_set_style_bg_color(brightness_slider, lv_color_hex(0x333333), 0);
lv_obj_set_style_bg_color(brightness_slider, lv_color_hex(0xFFFF00), LV_PART_INDICATOR);
lv_obj_set_style_bg_color(brightness_slider, lv_color_hex(0xFFFFFF), LV_PART_KNOB);
lv_obj_add_event_cb(brightness_slider, brightness_slider_handler, LV_EVENT_VALUE_CHANGED, NULL);
brightness_label = lv_label_create(settings_screen);
char buffer[20];
snprintf(buffer, sizeof(buffer), "Brightness: %d%%", brightness);
lv_label_set_text(brightness_label, buffer);
lv_obj_set_style_text_color(brightness_label, lv_color_hex(0xFFFF00), 0);
lv_obj_align(brightness_label, LV_ALIGN_TOP_LEFT, 10, 80);
// Volume control
lv_obj_t *volume_title = lv_label_create(settings_screen);
lv_label_set_text(volume_title, "Volume");
lv_obj_set_style_text_color(volume_title, lv_color_hex(0xFFFFFF), 0);
lv_obj_align(volume_title, LV_ALIGN_TOP_LEFT, 10, 110);
volume_slider = lv_slider_create(settings_screen);
lv_obj_set_size(volume_slider, 280, 20);
lv_obj_align(volume_slider, LV_ALIGN_TOP_LEFT, 10, 130);
lv_slider_set_range(volume_slider, 0, 100);
lv_slider_set_value(volume_slider, volume, LV_ANIM_OFF);
lv_obj_set_style_bg_color(volume_slider, lv_color_hex(0x333333), 0);
lv_obj_set_style_bg_color(volume_slider, lv_color_hex(0x00AAFF), LV_PART_INDICATOR);
lv_obj_set_style_bg_color(volume_slider, lv_color_hex(0xFFFFFF), LV_PART_KNOB);
lv_obj_add_event_cb(volume_slider, volume_slider_handler, LV_EVENT_VALUE_CHANGED, NULL);
volume_label = lv_label_create(settings_screen);
snprintf(buffer, sizeof(buffer), "Volume: %d%%", volume);
lv_label_set_text(volume_label, buffer);
lv_obj_set_style_text_color(volume_label, lv_color_hex(0x00AAFF), 0);
lv_obj_align(volume_label, LV_ALIGN_TOP_LEFT, 10, 155);
// Navigation bar
create_nav_bar(settings_screen);
}
/**
* Create info screen
*/
void create_info_screen() {
info_screen = lv_obj_create(NULL);
lv_obj_set_style_bg_color(info_screen, lv_color_hex(0x001122), 0);
// Title
lv_obj_t *title = lv_label_create(info_screen);
lv_label_set_text(title, "System Information");
lv_obj_set_style_text_color(title, lv_color_hex(0xFFFFFF), 0);
lv_obj_set_style_text_font(title, &lv_font_montserrat_18, 0);
lv_obj_align(title, LV_ALIGN_TOP_MID, 0, 5);
// System info labels
lv_obj_t *info_container = lv_obj_create(info_screen);
lv_obj_set_size(info_container, 300, 150);
lv_obj_align(info_container, LV_ALIGN_TOP_MID, 0, 35);
lv_obj_set_style_bg_color(info_container, lv_color_hex(0x222222), 0);
lv_obj_set_style_border_width(info_container, 2, 0);
lv_obj_set_style_border_color(info_container, lv_color_hex(0x00AAFF), 0);
lv_obj_clear_flag(info_container, LV_OBJ_FLAG_SCROLLABLE);
// Info text
lv_obj_t *info_text = lv_label_create(info_container);
lv_label_set_text(info_text,
"ESP32 + LVGL Demo\n\n"
"Platform: ESP32\n"
"Framework: Arduino\n"
"Display: TFT_eSPI\n"
"Graphics: LVGL 8.3\n\n"
"This is a complete UI\nexample demonstrating\nmultiple screens and\ninteractive widgets.");
lv_obj_set_style_text_color(info_text, lv_color_hex(0xFFFFFF), 0);
lv_obj_set_style_text_font(info_text, &lv_font_montserrat_14, 0);
lv_obj_align(info_text, LV_ALIGN_TOP_LEFT, 10, 10);
// Navigation bar
create_nav_bar(info_screen);
}
/**
* Update sensor data display
*/
void update_sensor_display() {
char buffer[30];
snprintf(buffer, sizeof(buffer), "%.1f°C", temperature);
lv_label_set_text(temp_label, buffer);
snprintf(buffer, sizeof(buffer), "%.1f%%", humidity);
lv_label_set_text(humidity_label, buffer);
snprintf(buffer, sizeof(buffer), "%.2f hPa", pressure);
lv_label_set_text(pressure_label, buffer);
}
// ============================================================================
// SETUP FUNCTION
// ============================================================================
void setup() {
Serial.begin(115200);
delay(1000);
Serial.println("\n\n=== LVGL Complete UI Example ===");
// Initialize display
SPI.begin();
tft.init();
tft.setRotation(0);
tft.fillScreen(TFT_BLACK);
tft.setSwapBytes(true);
pinMode(DISPLAY_BL, OUTPUT);
analogWrite(DISPLAY_BL, map(brightness, 0, 100, 0, 255));
// Initialize LVGL
lv_init();
// Create display object
lv_display_t *disp = lv_display_create(DISPLAY_WIDTH, DISPLAY_HEIGHT);
lv_display_set_buffers(disp, buf1, buf2, sizeof(buf1), LV_DISPLAY_RENDER_MODE_PARTIAL);
lv_display_set_flush_cb(disp, display_flush);
// Create all screens
create_main_screen();
create_settings_screen();
create_info_screen();
// Load main screen
lv_scr_load(main_screen);
Serial.println("Setup complete! Application ready.");
}
// ============================================================================
// LOOP FUNCTION
// ============================================================================
void loop() {
// Handle LVGL tasks
lv_timer_handler();
// Simulate sensor data updates
static unsigned long lastUpdate = 0;
if (millis() - lastUpdate >= 2000) { // Update every 2 seconds
// Small random variations
temperature += (random(-5, 6) / 10.0);
humidity += (random(-3, 4) / 10.0);
pressure += (random(-2, 3) / 10.0);
// Clamp values
if (temperature < 15) temperature = 15;
if (temperature > 30) temperature = 30;
if (humidity < 20) humidity = 20;
if (humidity > 80) humidity = 80;
if (pressure < 980) pressure = 980;
if (pressure > 1050) pressure = 1050;
// Update display if on main screen
if (lv_scr_act() == main_screen) {
update_sensor_display();
}
// Update progress bar
progress = (progress + 1) % 101;
lv_bar_set_value(progress_bar, progress, LV_ANIM_ON);
lastUpdate = millis();
}
delay(5);
}
// ============================================================================
// NOTES FOR STUDENTS
// ============================================================================
/*
* This example demonstrates:
*
* 1. Multiple Screens:
* - Create screens: lv_obj_create(NULL)
* - Switch screens: lv_scr_load(screen)
* - Get current screen: lv_scr_act()
*
* 2. Sliders:
* - Create: lv_slider_create(parent)
* - Set range: lv_slider_set_range(slider, min, max)
* - Get value: lv_slider_get_value(slider)
* - Set value: lv_slider_set_value(slider, value, anim)
*
* 3. Progress Bars:
* - Create: lv_bar_create(parent)
* - Set range: lv_bar_set_range(bar, min, max)
* - Set value: lv_bar_set_value(bar, value, anim)
*
* 4. Code Organization:
* - Separate functions for each screen
* - Event handlers in separate functions
* - State management with global variables
* - Update functions for dynamic content
*
* 5. Real-time Updates:
* - Update in loop() based on time
* - Only update visible screen
* - Use animations for smooth transitions
*
* Advanced Topics:
*
* - State Management:
* Use structs to organize application state
* Save/load settings from EEPROM or SPIFFS
*
* - Screen Transitions:
* Use animations for screen changes
* lv_scr_load_anim(screen, anim_type, time, delay, auto_del)
*
* - Widget Styling:
* Create custom themes
* Use style properties for consistent look
*
* - Data Binding:
* Connect widgets to data sources
* Update multiple widgets from one data change
*
* Try Adding:
* - More screens (alarms, logs, etc.)
* - Charts for data visualization
* - Lists for settings options
* - Keyboard for text input
* - More interactive widgets
*/
Articoli correlati
lv_conf.h
lv_conf.h — c source code from the Arduino learning materials (Arduino/CCIT4080_CL05_PKPD_Group2/lv_conf.h).
Leggi l'articolo →2.4_max30102.cpp
2.4_max30102.cpp — cpp source code from the Arduino learning materials (Arduino/CCIT4080_CL05_PKPD_Group2/src/Combination/2.4_and_max30102_test/2.4_max30102.cpp).
Leggi l'articolo →bluetooth.cpp
bluetooth.cpp — cpp source code from the Arduino learning materials (Arduino/CCIT4080_CL05_PKPD_Group2/src/Test_components/bluetooth_test/bluetooth.cpp).
Leggi l'articolo →ec11.cpp
ec11.cpp — cpp source code from the Arduino learning materials (Arduino/CCIT4080_CL05_PKPD_Group2/src/Test_components/button_test/ec11.cpp).
Leggi l'articolo →1.69_inch_display.cpp
1.69_inch_display.cpp — cpp source code from the Arduino learning materials (Arduino/CCIT4080_CL05_PKPD_Group2/src/Test_components/display_test/1.69_inch_display/1.69_inch_display.cpp).
Leggi l'articolo →2.4_inch_display.cpp
2.4_inch_display.cpp — cpp source code from the Arduino learning materials (Arduino/CCIT4080_CL05_PKPD_Group2/src/Test_components/display_test/2.4_inch_display/2.4_inch_display.cpp).
Leggi l'articolo →