Lab - Parte 1
Lab 7 |
---|
Data limite para entrega: 16/11/2023 |
Entregue o código pelo repositório do Classroom |
- Fechar issues pertinentes ao conceito atingido na entrega |
Agora vamos começar mexer com o LVGL e criar nossa interface. A ideia é recriar uma interface de um termostato inspirado no produto a seguir:
KKmoon LCD Touch Screen Termostato Sistema de Aquecimento de Piso Elétrico Aquecimento de Água Termorregulador AC85-240 V Controlador de Temperatura Preto
Etapas
Quando começamos projetar uma interface homem máquina (IHM) é necessário analisarmos várias frentes:
- Usabilidade
- Acessibilidade
- Branding
- Implementação
A usabilidade irá indicar como as funções do produto estarão disponíveis e serão exibidas aos usuários, isso deve estar atrelada aos conceitos da marca do produto. O público alvo deve ser analisado e o produto deve ser acessível, para isso, muitos testes de usabilidade devem ser feitos para validar o conceito.
Muitas vezes o protótipo da interface esbarra em problemas técnicos e de implementação, muitas imagens e fontes impactam no tamanho total do firmware que pode impossibilitar a implementação da interface proposta, ou necessitar a adição de formas alternativas de armazenamento de dados. Microcontroladores de forma geral não possuem GPU (alguns sim, exemplo: STM32MP157 ) e isso impacta na performance da interface e muito provavelmente no gasto energético.
Implementando
Tip
Vamos usar muito a documentação do lvgl é importante que você tenha o site aberto e a consulte sempre:
O LVGL possui vasta documentação e muitos bons exemplos (testamos vários e todos funcionaram no embarcado), neste laboratório iremos usar alguns widgets do LVGL e aos poucos vamos customizando eles.
Preparando firmware
Vamos criar uma nova função chamada de void lv_termostato(void){ }
onde iremos fazer a implementação da interface do termostato. Além de criarmos esta função, teremos que modificar a task_lcd
para chamar a nova função.
Tarefa
Background
Notem que a interface a ser recriada possui fundo preto, para atingirmos tal objetivo com o LVGL iremos modificar a configuração padrão do LVGL que está localizada em config/lv_conf.h
. Neste arquivo procure pelo define LV_THEME_DEFAULT_DARK
e modifique para 1.
Tarefa
Tamanho da fonte
Vocês notaram que a fonte padrão do LCD está meio pequena? Conseguimos ajustar isso no LVGL escolhendo uma fonte maior. O LVGL disponibiliza alguns tamanhos de fonte de uma mesma classe (MontSerrat). A lista de fontes está no arquivo de configuração src/config/lv_conf.h
. Neste arquivo você deve encontrar algo como:
289 290 291 292 293 294 295 296 297 298 299 300 |
|
Para recriar a interface iremos usar um tamanho de fonte um pouco maior que o padrão (14) do LVGL, para isso iremos utilizar a fonte de tamanho 24. Isso é feito em duas etapas:
- Ativando a fonte no arquivo de configuração
- Configurando a nova fonte como padrão
Sistemas Embarcados
O LVGL não inclui todas as fontes por padrão (para o código não ficar muito grande). Sistemas embarcados sofrem com falta de memória.
Fonte padrão | Program Memory Usage |
---|---|
14 | Program Memory Usage : 235852 bytes 11,2 % Full |
24 | Program Memory Usage : 250904 bytes 12,0 % Full |
O LVGL não lida com fontes de forma avançada, cada letra da fonte é uma matriz que contém os pixels a serem associados. Quanto maior a fonte maior precisa ser essa matriz e mais memória de programa utiliza.
Parece pouca diferença né? Mas não é! Vamos sofrer um pouco com isso na nossa próxima APS.
Tarefa
Identificando widgets
A primeira etapa após ter a interface definida é identificar quais widgets podem ser utilizados para montar a interface.
Exercise
Answer
Podemos construir a tela toda usando apenas Botões e Labels.
- (a): lv_label
- (b): lv_button
Os botões que incluem uma imagem, como o de power, memória, ...., podem ser botões com "símbolos" no lugar do texto.
Botões
O lv_button
permite que criemos um ou mais botões, os botões podem ou não ter um label associado a ele. Podemos associar para cada botão uma função de handler que será chamada assim que um evento neste botão for detectado, a seguir alguns eventos que podem ser gerados por um objeto:
LV_EVENT_PRESSED
An object has been pressedLV_EVENT_PRESSING
An object is being pressed (called continuously while pressing)LV_EVENT_PRESS_LOST
An object is still being pressed but slid cursor/finger off of the objectLV_EVENT_SHORT_CLICKED
An object was pressed for a short period of time, then released. Not called if scrolled.LV_EVENT_LONG_PRESSED
An object has been pressed for at least the long_press_time specified in the input device driver. Not called if scrolled.- A lista completa pode ser acessada na página de Events: https://docs.lvgl.io/master/overview/event.html
O exemplo fornecido na função (lv_ex_btn_1
) cria um botão chamado btn1
e o alinha no centro da tela, a função event_handler
foi associada como callback deste widget, assim que um evento ocorrer a mesma será executada.
lv_obj_t * btn1 = lv_btn_create(lv_scr_act());
lv_obj_add_event_cb(btn1, event_handler, LV_EVENT_ALL, NULL);
lv_obj_align(btn1, LV_ALIGN_CENTER, 0, -40);
Depois cria um label
e o associa ao botão:
label = lv_label_create(btn1);
lv_label_set_text(label, "Teste");
lv_obj_center(label);
Conforme a documentação do lvgl para objetos Podemos alinhar um objeto em vários locais diferentes na tela. Isso é feito pela função lv_obj_align(obj, obj_ref, LV_ALIGN_________, x_ofs, y_ofs)
que recebe como parâmetro:
obj
is the object to align.obj_ref
is a reference object. obj will be aligned to it. Ifobj_ref = NULL
, then the parent ofobj
will be used.- The third argument is the type of alignment. These are the possible options:
Extraído da documentação.
Botão de power
Agora vamos criar os botões da interface proposta, primeiro iremos criar o botão de power modificando o botão atual de teste.
1. Posição
Na primeira etapa iremos alinhar o botão no canto inferior esquerdo conforme figura a seguir:
Um objeto pode ser alinhado com relação a um screen ou a um outro objetivo de referência. Nesse caso vamos usar o botão de power alinhado ao canto inferior da tela.
- Para alinhar com relação a tela usar:
lv_obj_align(obj, align, x, y);
- Para alinhar com relação a outro objeto usar:
lv_obj_align_to(obj_to_align, reference_obj, align, x, y);
Os parâmetros X e Y são um deslocamento do alinhamento e o argumento aling pode ser qualquer uma dos itens a seguir:
Utilizando a referência interna de um objeto:
LV_ALIGN_TOP_LEFT
LV_ALIGN_TOP_MID
LV_ALIGN_TOP_RIGHT
LV_ALIGN_BOTTOM_LEFT
LV_ALIGN_BOTTOM_MID
LV_ALIGN_BOTTOM_RIGHT
LV_ALIGN_LEFT_MID
LV_ALIGN_RIGHT_MID
LV_ALIGN_CENTER
Utilizando a referência externa de um objeto:
LV_ALIGN_OUT_TOP_LEFT
LV_ALIGN_OUT_TOP_MID
LV_ALIGN_OUT_TOP_RIGHT
LV_ALIGN_OUT_BOTTOM_LEFT
LV_ALIGN_OUT_BOTTOM_MID
LV_ALIGN_OUT_BOTTOM_RIGHT
LV_ALIGN_OUT_LEFT_TOP
LV_ALIGN_OUT_LEFT_MID
LV_ALIGN_OUT_LEFT_BOTTOM
LV_ALIGN_OUT_RIGHT_TOP
LV_ALIGN_OUT_RIGHT_MID
LV_ALIGN_OUT_RIGHT_BOTTOM
Pergunta
Answer
- O primeiro parametro é o obj a ser alinhado:
obj2
- O segundo o obj a ser usado de referência:
obj1
- Queremos alinhas a direita e no meio.
- Queremos o botão 2 "colado" no botão 1
Exercise
Answer
Agora o truque é que queremos alinhar com o "Bottom" do botão 1. Por isso
usamos o LV_ALIGN_BOTTOM_RIGHT
Exercise
2. Label/ Símbolo
No nosso código colocamos um "Label" dentro do objeto do botão, é por isso que aparecer o texto "Teste" no botão:
labelBtn1 = lv_label_create(btn1);
lv_label_set_text(labelBtn1, "[ " LV_SYMBOL_POWER);
lv_obj_center(labelBtn1);
Notem que utilizamos o lv_lavel_create
e passamos o btn1
para a funcão. Isso faz com que o label pertenća ao objeto btn1.
Agora vamos trocar o label do btn1 para ser mais condizente com o do nosso modelo de referência. No LVGL podemos utilizar alguns símbolos já pré definidos, os detalhes estão na documentação de fonts e listados aqui:
Notem que podemos usar o símbolo LV_SYMBOL_POWER
para recriar o botão da interface planejada, para isso é necessário alterar a linha que estamos escrevendo Teste
para usar o símbolo em questão. Por exemplo, para adicionarmos o [ seguido do símbolo de power:
lv_label_set_text(labelBtn1, "[ " LV_SYMBOL_POWER);
Vocês podem definir o label como variável global, permitindo assim que outra parte do código altera o valor escrito.
// global
static lv_obj_t * labelBtn1;
Task
- Altere o label para ser global
- Modifique o label do botão para mostrar o [ ⏻
- Teste na placa
Tip
O truque aqui foi juntar o [ e o power no mesmo botão,
+--------+
| |
| [ ⏻ |
| |
+--------+
^
| Botão
Poderíamos ter feito diferente:
+-------++-------+
| || |
| [ || ⏻ |
| || |
+-------++-------+
^ ^
| | Botão
| Apenas um label
3. Formatando
Iremos agora criar um estilo próprio e aplicar no botão, um estilo pode conter várias configurações de Tema para um objeto (ou para vários). O exemplo a seguir cria um style
com um fundo ROXO e uma borda VERDE.
void lv_termostato(void) {
static lv_style_t style;
lv_style_init(&style);
lv_style_set_bg_color(&style, lv_palette_main(LV_PALETTE_PURPLE));
lv_style_set_border_color(&style, lv_palette_main(LV_PALETTE_GREEN));
lv_style_set_border_width(&style, 5);
E então aplicamos o novo estilo ao botão:
lv_obj_t * btn1 = lv_btn_create(lv_scr_act());
lv_obj_add_event_cb(btn1, event_handler, LV_EVENT_ALL, NULL);
lv_obj_align(btn1, LV_ALIGN_BOTTOM_LEFT, 0, 0);
+ lv_obj_add_style(btn1, &style, 0);
Também podemos mudar o tamanho de um objeto, como no exemplo a seguir:
lv_obj_set_width(btn1, 60); lv_obj_set_height(btn1, 60);
Cores
Existem cores pré definidas no LVGL e para cada cor uma plate entre no link a seguir para acessar a paleta:
Para usar branco ou preto existe uma função:
- lv_color_white()
- lv_color_black()
Se quiser, pode formatar a sua própria cor em RGB, usando: lv_color_make(red, green, blue);
Task
Crie um novo tema para o botão faca sentido para o nosso termostato. Precisamos de fundo preto e sem borda!
- Crie o novo estilo
- Aplique ao botão
- Teste e verifique o resultado
Demais botões
Tarefa: Demais botões