Lopaka Basics 10: e-paper e envio

Duas telas mostram a mesma placa. Puxe a tomada, e a brilhante apaga na hora. A outra mantém a imagem com zero energia, e ela ainda vai estar lá dias depois. Isso é e-paper.
Neste episódio, você vai descobrir como a tinta eletrônica guarda uma imagem sem energia, e por que ela tem dois jeitos de mudar ela. Depois você vai criar uma placa de sala de reunião no Lopaka para a biblioteca GxEPD2, atualizar só os minutos sem um flash, testar uma versão vermelha para um painel de três cores, publicar o design e compartilhar um link para ele. Antes de enviar, uma lista de verificação dos cinco erros que mais vemos.
O que você vai construir: uma placa de sala de 296×128 para um display e-paper de 2,9 polegadas com duas telas. Busy mostra a sala, a reunião, um bloco BUSY grande e os minutos restantes. Free mostra a próxima reunião. Do que você precisa: um navegador. Para executar a placa, um ESP32 e um módulo e-paper de 2,9 polegadas (Waveshare V2).
Como o e-paper funciona
Desconectado, ainda mostrando
Uma tela TFT como a do episódio 3 brilha com a própria luz, e a imagem dela dura só enquanto a energia durar. Corte a energia e ela fica escura.
E-paper funciona como tinta no papel. Quando a imagem é desenhada, ela fica sem energia nenhuma. Isso torna ele uma boa opção para placas, etiquetas e etiquetas de preço que mudam algumas vezes por hora e rodam com uma bateria.

Cápsulas com partículas pretas e brancas
Cada pixel de e-paper é cheio de cápsulas minúsculas. Dentro de cada uma flutuam partículas brancas e partículas pretas com cargas elétricas opostas. Entre dois eletrodos, uma tensão puxa uma cor para o topo, o lado para o qual você olha, e o pixel fica branco ou preto.
Depois a energia some, e as partículas ficam onde estão. Nenhuma energia é necessária para manter a imagem.

Atualização completa e atualização parcial
Mudar a imagem dá trabalho, e há dois jeitos de fazer isso:
- Uma atualização completa balança cada pixel. O painel inteiro pisca em preto e branco para apagar a imagem antiga e depois mostra a nova. No painel preto e branco de 2,9 polegadas deste episódio, leva cerca de 4 segundos.
- Uma atualização parcial move só os pixels que mudaram, em menos de um segundo (cerca de 0,7 s neste painel), sem flash.

Cada atualização parcial deixa um fantasma fraco da imagem antiga, como o dígito antigo atrás do novo acima. Depois de algumas atualizações parciais, faça uma atualização completa de novo para apagar os fantasmas.
Etapa 1: um novo projeto e-paper
Clique em + New Project e abra a lista Platform. Escolha GxEPD2 (e-paper), uma biblioteca C++ de ZinggJM. A descrição dela no Lopaka resume o que importa: ela "sits on top of Adafruit GFX, so drawing calls and fonts are the familiar ones, but you must name your exact panel class", e a cor dela "is a fixed set of nine constants such as GxEPD_BLACK and GxEPD_RED, and anything else maps to black".
Preencha o restante da caixa de diálogo:
- Title: "Room Sign".
- Screen Size: 296×128, o tamanho de um painel comum de 2,9 polegadas.
A Background Color já é #F8F8F8, um quase branco, como papel. Clique em Salvar.

Etapa 2: desenhe a placa
Clique duas vezes no nome da tela acima da tela de desenho e renomeie Screen 1 para Busy. Depois desenhe a placa de cima para baixo. Todos os textos usam as fontes Adafruit GFX da lista Font no inspetor:
- O nome da sala. Escolha a ferramenta de texto (
Aa), digiteROOM 3e escolhaFreeSansBold12pt7b. Coloque ele no canto superior esquerdo. - O horário de atualização. Adicione
Updated 15:25emFreeSans9pt7b, no canto superior direito. - Um divisor. Escolha a ferramenta de linha e desenhe uma linha sob o cabeçalho, de x = 10 a x = 285 em y = 32.
- O bloco de status. Escolha a ferramenta retângulo, arraste um retângulo e defina X
10, Y42, W128, H76, R6, depois ative Filled. - O status. Adicione
BUSYemFreeSansBold18pt7bpor cima do bloco e defina o Fill color dele como#FFFFFF, branco sobre preto. - A reunião. À direita do bloco, adicione três linhas de texto:
Design reviewemFreeSansBold9pt7b,until 15:30emFreeSans9pt7be5 min leftemFreeSansBold12pt7b.

Os números mantêm a mesma margem de 10 pixels em cada lado. A linha termina em x = 285, dez pixels antes da última coluna, 295. O bloco começa 10 pixels a partir da borda esquerda, e a linha inferior dele é 117, dez linhas acima da última, 127.
Nove cores, e seu painel mostra algumas
Abra o Fill color de qualquer forma e você vai ver como a paleta é curta. Estas nove são as únicas cores que a biblioteca GxEPD2 conhece: GxEPD_BLACK, GxEPD_WHITE, GxEPD_LIGHTGREY, GxEPD_DARKGREY, GxEPD_RED, GxEPD_YELLOW, GxEPD_BLUE, GxEPD_GREEN e GxEPD_ORANGE.

Seu painel mostra só algumas delas. O painel deste episódio é preto e branco; muitos outros adicionam uma terceira cor, vermelha ou amarela. Crie com as cores que o seu painel tem.
Etapa 3: duplique para "free"
Quando a reunião termina, a placa precisa de uma segunda tela. Em vez de desenhar ela de novo, selecione a tela Busy no painel Camadas e clique em Duplicate no inspetor. Renomeie a cópia para Free e mude ela:
BUSYviraFREE, com o Fill color de volta para#000000.- Selecione
rect 4e desative Filled: o bloco vira uma moldura. Updated 15:25viraUpdated 15:30.- As linhas da reunião viram
Next: Standup,at 16:00e30 min free.

Cada tela vira sua própria função no código, drawBusy() e drawFree(), e o painel de código tem uma guia para cada uma.
Etapa 4: o código
Chamadas de desenho familiares
Abra a guia Busy do painel de código. O código é o mesmo tipo de chamadas de desenho dos episódios anteriores:
// [BEGIN lopaka generated]
void drawBusy(void) {
display.fillScreen(GxEPD_WHITE);
// string 1
display.setTextColor(GxEPD_BLACK);
display.setTextWrap(false);
display.setFont(&FreeSansBold12pt7b);
display.setCursor(10, 23);
display.print("ROOM 3");
// string 2
display.setFont(&FreeSans9pt7b);
display.setCursor(170, 21);
display.print("Updated 15:25");
// line 3
display.drawLine(10, 32, 285, 32, GxEPD_BLACK);
// rect 4
display.fillRoundRect(10, 42, 128, 76, 6, GxEPD_BLACK);
// string 5
display.setTextColor(GxEPD_WHITE);
display.setFont(&FreeSansBold18pt7b);
display.setCursor(27, 92);
display.print("BUSY");
// string 6
display.setTextColor(GxEPD_BLACK);
display.setFont(&FreeSansBold9pt7b);
display.setCursor(152, 59);
display.print("Design review");
// string 7
display.setFont(&FreeSans9pt7b);
display.setCursor(152, 81);
display.print("until 15:30");
// string 8
display.setFont(&FreeSansBold12pt7b);
display.setCursor(152, 111);
display.print("5 min left");
display.display();
}
// [END lopaka generated]fillScreen(GxEPD_WHITE)limpa a imagem. O fundo quase branco da tela de desenho corresponde ao branco no painel.fillRoundRect(10, 42, 128, 76, 6, …)é o bloco: x, y, largura, altura e o raio do canto R. Na tela Free, o mesmo bloco édrawRoundRect, só o contorno.setCursor(x, y)coloca o texto na linha de base dele, a linha sobre a qual as letras ficam, como no episódio 1. Para "5 min left", isso é a linha 111.display.display()no fim envia a imagem inteira para o painel com uma atualização completa.
A guia Free tem as mesmas chamadas com os textos novos e drawRoundRect, em uma função chamada drawFree().
As fontes vêm com a biblioteca
As quatro fontes fazem parte da biblioteca Adafruit GFX, sobre a qual a GxEPD2 é construída. Você inclui elas, com o resto da sua configuração, então desative Include fonts nos interruptores ao lado do código.

Seu código acima: a biblioteca, as fontes e seu painel exato
Acima da parte gerada, inclua a biblioteca e as quatro fontes, e nomeie seu painel exato:
#include <GxEPD2_BW.h>
#include <Fonts/FreeSans9pt7b.h>
#include <Fonts/FreeSansBold9pt7b.h>
#include <Fonts/FreeSansBold12pt7b.h>
#include <Fonts/FreeSansBold18pt7b.h>
// 2.9" e-paper 296x128 (Waveshare V2): CS, DC, RST, BUSY
GxEPD2_BW<GxEPD2_290_T94_V2, GxEPD2_290_T94_V2::HEIGHT>
display(GxEPD2_290_T94_V2(5, 17, 16, 4));
#define MINUTE 60000ULGxEPD2_BW é a classe de display preto e branco. Dentro dos colchetes angulares:
GxEPD2_290_T94_V2é o driver para o módulo preto e branco Waveshare de 2,9 polegadas, versão 2. A GxEPD2 tem uma classe separada para quase cada painel, e vários painéis de 2,9 polegadas parecem iguais, mas precisam de classes diferentes. Confira o modelo impresso no cabo flat do painel e encontre a classe que corresponde a ele nos exemplos da GxEPD2.GxEPD2_290_T94_V2::HEIGHTé quantas linhas o programa mantém na memória. Com a altura completa, a imagem inteira cabe em um buffer de quadro de 296 × 128 / 8 = 4.736 bytes, um bit por pixel.
Os quatro números são os pinos do ESP32 aos quais o módulo está ligado: CS 5, DC 17, RST 16 e BUSY 4. MINUTE é um minuto em milissegundos.
Seu código abaixo: completa, parcial, dormir
Abaixo da parte gerada, desenhe a tela busy uma vez e depois atualize só os minutos:
// partial refresh: only the minutes change
void showMinutes(int m) {
display.fillRect(152, 94, 136, 24, GxEPD_WHITE);
display.setFont(&FreeSansBold12pt7b);
display.setTextColor(GxEPD_BLACK);
display.setCursor(152, 111);
display.print(m);
display.print(" min left");
display.displayWindow(152, 94, 136, 24);
}
void setup() {
display.init();
display.setRotation(1);
drawBusy(); // full refresh
for (int m = 4; m >= 1; m--) {
delay(MINUTE);
showMinutes(m); // partial refresh
}
delay(MINUTE);
drawFree(); // full refresh
display.hibernate(); // panel off, the picture stays
}
void loop() {}setRotation(1): o painel tem 128 pixels de largura e 296 de altura quando fica em pé. A rotação 1 vira ele de lado, então ele corresponde à tela de desenho de 296×128.showMinutes()pinta um retângulo branco sobre o antigo "5 min left", da linha 94 à linha 117 e da coluna 152 à 287, imprime o novo número na mesma linha de base, 111, e chamadisplayWindow()com o mesmo retângulo. Só aquela janela é enviada para o painel, como uma atualização parcial, sem flash.setup()desenha Busy com uma atualização completa, atualiza os minutos quatro vezes com atualizações parciais e depois desenha Free com uma atualização completa. Quatro atualizações parciais, depois uma completa: os fantasmas são apagados quando a reunião termina.hibernate()desliga o painel e coloca ele em sono profundo. A imagem fica.
Tudo acontece uma vez em setup(), então loop() está vazio.
Você pode digitar estas linhas direto no painel de código do Lopaka, acima do marcador BEGIN e abaixo do marcador END. Depois clique em Copiar na guia Busy, e o sketch inteiro vem junto. Cole drawFree() da guia Free no fim.
O sketch inteiro
#include <GxEPD2_BW.h>
#include <Fonts/FreeSans9pt7b.h>
#include <Fonts/FreeSansBold9pt7b.h>
#include <Fonts/FreeSansBold12pt7b.h>
#include <Fonts/FreeSansBold18pt7b.h>
// 2.9" e-paper 296x128 (Waveshare V2): CS, DC, RST, BUSY
GxEPD2_BW<GxEPD2_290_T94_V2, GxEPD2_290_T94_V2::HEIGHT>
display(GxEPD2_290_T94_V2(5, 17, 16, 4));
#define MINUTE 60000UL
// [BEGIN lopaka generated]
void drawBusy(void) {
display.fillScreen(GxEPD_WHITE);
// string 1
display.setTextColor(GxEPD_BLACK);
display.setTextWrap(false);
display.setFont(&FreeSansBold12pt7b);
display.setCursor(10, 23);
display.print("ROOM 3");
// string 2
display.setFont(&FreeSans9pt7b);
display.setCursor(170, 21);
display.print("Updated 15:25");
// line 3
display.drawLine(10, 32, 285, 32, GxEPD_BLACK);
// rect 4
display.fillRoundRect(10, 42, 128, 76, 6, GxEPD_BLACK);
// string 5
display.setTextColor(GxEPD_WHITE);
display.setFont(&FreeSansBold18pt7b);
display.setCursor(27, 92);
display.print("BUSY");
// string 6
display.setTextColor(GxEPD_BLACK);
display.setFont(&FreeSansBold9pt7b);
display.setCursor(152, 59);
display.print("Design review");
// string 7
display.setFont(&FreeSans9pt7b);
display.setCursor(152, 81);
display.print("until 15:30");
// string 8
display.setFont(&FreeSansBold12pt7b);
display.setCursor(152, 111);
display.print("5 min left");
display.display();
}
// [END lopaka generated]
// partial refresh: only the minutes change
void showMinutes(int m) {
display.fillRect(152, 94, 136, 24, GxEPD_WHITE);
display.setFont(&FreeSansBold12pt7b);
display.setTextColor(GxEPD_BLACK);
display.setCursor(152, 111);
display.print(m);
display.print(" min left");
display.displayWindow(152, 94, 136, 24);
}
void setup() {
display.init();
display.setRotation(1);
drawBusy(); // full refresh
for (int m = 4; m >= 1; m--) {
delay(MINUTE);
showMinutes(m); // partial refresh
}
delay(MINUTE);
drawFree(); // full refresh
display.hibernate(); // panel off, the picture stays
}
void loop() {}
// [BEGIN lopaka generated]
void drawFree(void) {
display.fillScreen(GxEPD_WHITE);
// string 1
display.setTextColor(GxEPD_BLACK);
display.setTextWrap(false);
display.setFont(&FreeSansBold12pt7b);
display.setCursor(10, 23);
display.print("ROOM 3");
// string 2
display.setFont(&FreeSans9pt7b);
display.setCursor(170, 21);
display.print("Updated 15:30");
// line 3
display.drawLine(10, 32, 285, 32, GxEPD_BLACK);
// rect 4
display.drawRoundRect(10, 42, 128, 76, 6, GxEPD_BLACK);
// string 5
display.setFont(&FreeSansBold18pt7b);
display.setCursor(27, 92);
display.print("FREE");
// string 6
display.setTextColor(GxEPD_BLACK);
display.setFont(&FreeSansBold9pt7b);
display.setCursor(152, 59);
display.print("Next: Standup");
// string 7
display.setFont(&FreeSans9pt7b);
display.setCursor(152, 81);
display.print("at 16:00");
// string 8
display.setFont(&FreeSansBold12pt7b);
display.setCursor(152, 111);
display.print("30 min free");
display.display();
}
// [END lopaka generated]drawFree() vem depois de setup(), que chama ela. Isso não é problema em um sketch do Arduino: as ferramentas do Arduino declaram cada função no topo do sketch para você.
Etapa 5: execute ele
Ligue o módulo a um ESP32
O módulo tem oito fios. Conecte eles a um ESP32 DevKit assim:
- VCC ao 3V3 e GND ao GND. Painéis e-paper precisam de 3,3 V, na energia e nas linhas de dados. Se seu módulo tiver um nono pino, PWR, conecte ele ao 3V3 também.
- DIN ao 23 e CLK ao 18, os pinos SPI de hardware do ESP32.
- CS ao 5, DC ao 17, RST ao 16 e BUSY ao 4, os pinos do construtor.
Na Arduino IDE, instale GxEPD2 de Jean-Marc Zingg pelo Library Manager e aceite Adafruit GFX Library com ela. Escolha a placa ESP32 Dev Module e envie. O sketch é pequeno para um ESP32:
Sketch uses 322220 bytes (24%) of program storage space. Maximum is 1310720 bytes.
Global variables use 28460 bytes (8%) of dynamic memory, leaving 299220 bytes for local variables. Maximum is 327680 bytes.O buffer de quadro de 4.736 bytes faz parte desses 28.460 bytes.
O que você vai ver
No vídeo, a placa roda em um ESP32 simulado com um módulo e-paper de 2,9 polegadas, com um "minuto" acelerado para dois segundos. Observe o começo: o painel pisca em preto e branco, e a placa aparece. Essa é a atualização completa.
Depois os minutos contam para baixo, 4, 3, 2, 1, e só aquele número muda, sem flash. Quando a reunião termina, mais uma atualização completa desenha a tela Free, e o painel vai dormir. A placa fica.

Etapa 6: torne ele vermelho
Quer um pouco de vermelho? Muitos painéis adicionam uma terceira cor. Selecione a tela Busy, selecione o rect 4 dela e defina o Fill color como #FF0000, vermelho da paleta. O código muda uma palavra:
display.fillRoundRect(10, 42, 128, 76, 6, GxEPD_RED);
Para um painel de três cores, mude o topo do sketch para a classe de três cores e o driver de um painel vermelho de 2,9 polegadas:
#include <GxEPD2_3C.h>
// 2.9" three-colour e-paper 296x128 (black, white, red): CS, DC, RST, BUSY
GxEPD2_3C<GxEPD2_290_C90c, GxEPD2_290_C90c::HEIGHT>
display(GxEPD2_290_C90c(5, 17, 16, 4));Em um painel de três cores, o bloco sai vermelho. Mas painéis de três cores são lentos: uma atualização completa deste leva cerca de 26 segundos, e ele não tem atualização parcial rápida, então cada atualização redesenha a tela inteira. Desenhe a placa uma vez e pule a contagem regressiva dos minutos:
void setup() {
display.init();
display.setRotation(1);
drawBusy(); // full refresh, about 26 s on a real panel
display.hibernate();
}A imagem vermelha precisa de um segundo buffer: o sketch ocupa 33.196 bytes de RAM (10%) em vez de 28.460.

Etapa 7: publique ele
Agora envie ele. Clique no ícone de engrenagem ao lado do nome do projeto para abrir as configurações do projeto, ative Publish to Gallery e clique em Salvar.

Um projeto publicado mostra as telas dele só quando você torna elas públicas. Selecione a tela Busy e clique em Make Public no inspetor. A tela recebe um selo Public, e o botão vira Make Private.
Etapa 8: compartilhe o link
Clique em Share na barra superior e escolha Copy public link. O link aponta para esta tela na galeria: https://lopaka.app/gallery/<project>/<screen>.

Envie o link para qualquer pessoa: um colega de equipe, um cliente, um tópico em um fórum. Ela abre o design e o código dele direto no navegador. Com Star, ela mantém ele nos favoritos dela, e com Fork ela recebe a própria cópia em My Projects para construir sobre ela, enquanto a sua fica intacta.

Antes de enviar: cinco erros
Antes de enviar qualquer tela, passe por esta lista de verificação. Estes são os cinco erros que mais vemos em telas embarcadas:
- Texto pequeno demais. Leia a tela de onde as pessoas vão ficar: para uma placa de sala, a porta.
- Cores que o painel não consegue mostrar. Crie com a paleta real do painel: preto e branco, além de vermelho ou amarelo se seu painel tiver.
- Texto que não cabe. Teste o nome mais longo que você vai receber, e não o curto e bonito. "Design review" cabe ao lado do bloco; "Quarterly planning" sai da borda da placa.
- Atualizar com frequência demais. Redesenhe só o que mudou: é mais rápido, e no e-paper economiza a bateria.
- Um dispositivo morto ainda mostra a última imagem. Este é o sorrateiro no e-paper: uma placa com a bateria vazia parece uma que funciona. Sempre mostre quando ela foi atualizada, para ninguém confiar em uma placa desatualizada.

A placa neste episódio cobre o quinto: "Updated 15:25" no canto diz para todo mundo quantos anos a imagem tem.
Recapitulação
- E-paper mantém a imagem sem energia: as partículas ficam onde a última tensão colocou elas, então o painel precisa de energia só para mudar a imagem.
- Atualização completa, atualização parcial, dormir:
display()redesenha tudo com um flash,displayWindow()muda uma área sem ele, ehibernate()desliga o painel enquanto a imagem fica. - Confira, depois envie: passe pela lista de verificação de cinco pontos, publique o projeto, torne a tela pública e compartilhe o link.
Desenhe, pegue o código, veja na tela. Abra o Lopaka e crie sua própria placa em e-paper.