Chegamos ao final da nossa jornada prática com a WT32-SC01 Plus! Ao longo dos últimos posts, aprendemos a configurar o ambiente, desenhar na tela, habilitar o toque e manipular widgets. Agora, vamos juntar todo esse conhecimento para construir a estrutura de uma aplicação real, que quase sempre envolve mais de uma tela.
Neste último tutorial prático, vamos aprender a criar e gerenciar múltiplas telas no SquareLine Studio e a navegar entre elas usando eventos de botão. Criaremos uma aplicação simples com duas telas, onde um botão em cada tela nos levará para a outra, demonstrando o fluxo de navegação básico essencial para qualquer projeto complexo, como menus de configuração, páginas de informação, etc.
Passo 1: Criando as Telas no SquareLine Studio
Primeiro, vamos projetar nossa interface com duas telas distintas.
- Abra o SquareLine Studio.
- No painel “Screens” no canto superior esquerdo, você verá a
Screen1
. Clique no pequeno ícone de+
para adicionar uma nova tela. Ela será nomeada automaticamente comoScreen2
. - Na
Screen1
:- Adicione um Label com o texto “Tela Principal”.
- Adicione um Button com o texto “Ir para Tela 2”.
- Selecione o botão e, na seção Events, crie um evento com
Trigger: CLICKED
eFunction: irParaTela2
.
- Na
Screen2
:- Selecione a
Screen2
no painel “Screens” para editá-la. - Adicione um Label com o texto “Tela Secundária”.
- Adicione um Button com o texto “Voltar para Tela 1”.
- Selecione este botão e crie um evento com
Trigger: CLICKED
eFunction: voltarParaTela1
.
- Selecione a
- Exporte os arquivos da UI e coloque-os na pasta do seu projeto Arduino.
Passo 2: A Lógica de Navegação em ui_events.cpp
A mágica da troca de telas acontece no arquivo ui_events.cpp
, onde implementaremos as duas funções que acabamos de definir no SquareLine.
Lembre-se de renomear ui_events.c
para ui_events.cpp
.
/*
* ui_events.cpp
* Lógica para a navegação entre telas.
*/
#include <Arduino.h>
#include "ui.h"
// Função chamada pelo botão na Tela 1
void irParaTela2(lv_event_t * e)
{
Serial.println("Navegando para a Tela 2...");
// Função do LVGL para carregar uma nova tela com uma animação.
// Parâmetros: (tela_destino, animação, duração, delay, deletar_anterior)
lv_scr_load_anim(ui_Screen2, LV_SCR_LOAD_ANIM_MOVE_LEFT, 500, 0, false);
}
// Função chamada pelo botão na Tela 2
void voltarParaTela1(lv_event_t * e)
{
Serial.println("Voltando para a Tela 1...");
lv_scr_load_anim(ui_Screen1, LV_SCR_LOAD_ANIM_MOVE_RIGHT, 500, 0, false);
}
Entendendo o Código:
lv_scr_load_anim(...)
: Esta é a função principal do LVGL para navegação.- O primeiro parâmetro (
ui_Screen2
ouui_Screen1
) é o ponteiro para o objeto da tela que queremos carregar. Assim como os widgets, o SquareLine Studio cria uma variável global para cada tela. LV_SCR_LOAD_ANIM_MOVE_LEFT
: É o tipo de animação. O LVGL oferece várias, comoFADE_ON
,OVER_TOP
, etc.MOVE_RIGHT
faz a animação inversa.500
: A duração da animação em milissegundos.0
: Um atraso (delay) antes de a animação começar.false
: Informa ao LVGL para não deletar a tela anterior da memória. Isso é útil para navegações rápidas de ida e volta. Em projetos com muitas telas e pouca RAM, você poderia setar paratrue
para liberar memória.
- O primeiro parâmetro (
Passo 3: O Código Principal (Inalterado)
Mais uma vez, nosso arquivo .ino
não precisa de nenhuma alteração. Ele serve como a base sólida que executa nossa aplicação, independentemente de quantas telas ou qual lógica de eventos tenhamos.
Aqui está o código completo para garantir que seu projeto funcione perfeitamente.
/*
* EXEMPLO 5: NAVEGAÇÃO ENTRE TELAS
* O código principal não muda. Toda a lógica está nos arquivos da UI.
*/
#define LGFX_USE_V1
#include <LovyanGFX.hpp>
#include <lvgl.h>
#include "ui.h"
// --- Configuração de Hardware para a LovyanGFX ---
class LGFX : public lgfx::LGFX_Device {
lgfx::Panel_ST7796 _panel_instance;
lgfx::Bus_Parallel8 _bus_instance;
lgfx::Light_PWM _light_instance;
lgfx::Touch_FT5x06 _touch_instance;
public:
LGFX(void) {
{ // BUS
auto cfg = _bus_instance.config();
cfg.port = 0; cfg.freq_write = 20000000;
cfg.pin_wr = 47; cfg.pin_rd = -1; cfg.pin_rs = 0;
cfg.pin_d0 = 9; cfg.pin_d1 = 46; cfg.pin_d2 = 3; cfg.pin_d3 = 8;
cfg.pin_d4 = 18; cfg.pin_d5 = 17; cfg.pin_d6 = 16; cfg.pin_d7 = 15;
_bus_instance.config(cfg);
_panel_instance.setBus(&_bus_instance);
}
{ // PANEL
auto cfg = _panel_instance.config();
cfg.pin_cs = -1; cfg.pin_rst = 4; cfg.pin_busy = -1;
cfg.memory_width = 320; cfg.memory_height = 480;
cfg.panel_width = 320; cfg.panel_height = 480;
cfg.offset_x = 0; cfg.offset_y = 0;
cfg.invert = true; cfg.rgb_order = false;
_panel_instance.config(cfg);
}
{ // BACKLIGHT
auto cfg = _light_instance.config();
cfg.pin_bl = 45; cfg.invert = false; cfg.freq = 44100; cfg.pwm_channel = 7;
_light_instance.config(cfg);
_panel_instance.setLight(&_light_instance);
}
{ // TOUCH
auto cfg = _touch_instance.config();
cfg.x_min = 0; cfg.x_max = 319;
cfg.y_min = 0; cfg.y_max = 479;
cfg.pin_int = 7;
cfg.bus_shared = true;
cfg.offset_rotation = 0;
cfg.i2c_port = 0; cfg.i2c_addr = 0x38;
cfg.pin_sda = 6; cfg.pin_scl = 5;
cfg.freq = 400000;
_touch_instance.config(cfg);
_panel_instance.setTouch(&_touch_instance);
}
setPanel(&_panel_instance);
}
};
// --- Configuração de Drivers e Buffers ---
LGFX gfx;
static const uint16_t screenWidth = 480;
static const uint16_t screenHeight = 320;
static lv_disp_draw_buf_t draw_buf;
static lv_color_t buf1[screenWidth * screenHeight / 10];
// --- Funções de Ponte para o LVGL ---
void my_disp_flush(lv_disp_drv_t *disp_drv, 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);
gfx.startWrite();
gfx.setAddrWindow(area->x1, area->y1, w, h);
gfx.pushPixels((uint16_t *)color_p, w * h, true);
gfx.endWrite();
lv_disp_flush_ready(disp_drv);
}
void my_touch_read(lv_indev_drv_t *indev_drv, lv_indev_data_t *data) {
uint16_t touchX, touchY;
bool touched = gfx.getTouch(&touchX, &touchY);
if (touched) {
data->point.x = touchX;
data->point.y = touchY;
data->state = LV_INDEV_STATE_PR;
} else {
data->state = LV_INDEV_STATE_REL;
}
}
// --- Aplicação Principal ---
void setup() {
Serial.begin(115200);
Serial.println("Iniciando Exemplo 5: Navegação entre Telas");
gfx.begin();
gfx.setRotation(1);
gfx.setBrightness(255);
lv_init();
lv_disp_draw_buf_init(&draw_buf, buf1, NULL, screenWidth * screenHeight / 10);
static lv_disp_drv_t disp_drv;
lv_disp_drv_init(&disp_drv);
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);
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_touch_read;
lv_indev_drv_register(&indev_drv);
ui_init();
Serial.println("Setup finalizado.");
}
void loop() {
lv_timer_handler();
lv_tick_inc(5);
delay(5);
}
Conclusão da Série
Parabéns! Você chegou ao final desta série de tutoriais para a WT32-SC01 Plus. Ao longo destes posts, você aprendeu a:
- Configurar o ambiente de desenvolvimento do zero.
- Controlar o display diretamente com a
LovyanGFX
. - Integrar a poderosa biblioteca
LVGL
com a ferramenta visualSquareLine Studio
. - Habilitar o toque e responder a eventos de botões.
- Manipular widgets para criar interfaces dinâmicas.
- Gerenciar e navegar entre múltiplas telas.
Com esta base sólida, você está mais do que preparado para criar seus próprios projetos complexos e com interfaces profissionais. A WT32-SC01 Plus é uma plataforma incrivelmente capaz, e agora você tem as ferramentas para explorar todo o seu potencial.
Espero que esta série tenha sido útil. Boas criações!