Pular para o conteúdo

Lopaka Basics 10: e-paper e envio ​

Um módulo e-paper de 2,9 polegadas mostrando uma placa de sala de reunião com ROOM 3, Updated 15:25, um bloco BUSY preto, Design review, until 15:30 e 4 min left

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.

Dois displays lado a lado com a energia cortada: o TFT com backlight está escuro em 0 W, o módulo e-paper ainda mostra a placa ROOM 3 BUSY em 0 W

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.

Como a tinta eletrônica funciona: três cápsulas entre um eletrodo superior transparente e um eletrodo inferior, com partículas negativas no topo e partículas positivas brancas na parte inferior; o pixel que você vê fica escuro com 0 W, 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.

Atualização completa versus atualização parcial: à esquerda, a placa inteira pisca e leva cerca de 4 segundos; à direita, só os minutos mudam em menos de um segundo, sem flash, e um fantasma fraco do dígito antigo aparece atrás do novo 1

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.

A caixa de diálogo Create New Project com Platform GxEPD2 (e-paper), o título Room Sign, o tamanho de tela 296×128 e a cor de fundo #F8F8F8

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:

  1. O nome da sala. Escolha a ferramenta de texto (Aa), digite ROOM 3 e escolha FreeSansBold12pt7b. Coloque ele no canto superior esquerdo.
  2. O horário de atualização. Adicione Updated 15:25 em FreeSans9pt7b, no canto superior direito.
  3. Um divisor. Escolha a ferramenta de linha e desenhe uma linha sob o cabeçalho, de x = 10 a x = 285 em y = 32.
  4. O bloco de status. Escolha a ferramenta retângulo, arraste um retângulo e defina X 10, Y 42, W 128, H 76, R 6, depois ative Filled.
  5. O status. Adicione BUSY em FreeSansBold18pt7b por cima do bloco e defina o Fill color dele como #FFFFFF, branco sobre preto.
  6. A reunião. À direita do bloco, adicione três linhas de texto: Design review em FreeSansBold9pt7b, until 15:30 em FreeSans9pt7b e 5 min left em FreeSansBold12pt7b.

A tela Busy no editor do Lopaka: ROOM 3 e Updated 15:25 acima de uma linha divisória, um bloco preto arredondado com BUSY em branco, e Design review, until 15:30 e 5 min left à direita; as camadas string 1 a string 8, line 3 e rect 4 à esquerda

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.

A paleta GxEPD2 de 9 cores: preto, branco, cinza claro, cinza escuro, vermelho, amarelo, azul, verde e laranja; seu painel mostra preto e branco, além de vermelho ou amarelo

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:

  • BUSY vira FREE, com o Fill color de volta para #000000.
  • Selecione rect 4 e desative Filled: o bloco vira uma moldura.
  • Updated 15:25 vira Updated 15:30.
  • As linhas da reunião viram Next: Standup, at 16:00 e 30 min free.

As duas telas lado a lado na tela de desenho: Busy com o bloco BUSY preto, e Free com FREE em uma moldura contornada, Next: Standup, at 16:00 e 30 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:

cpp
// [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.

O painel de código com as guias Busy e Free: a função drawBusy gerada começa depois do marcador BEGIN, e Include fonts está desativado nos interruptores à direita

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:

cpp
#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

GxEPD2_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:

cpp
// 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 chama displayWindow() 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 ​

cpp
#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.

O módulo e-paper de 2,9 polegadas depois da reunião, dormindo: ROOM 3, Updated 15:30, FREE em uma moldura contornada, Next: Standup, at 16:00 e 30 min free

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:

cpp
display.fillRoundRect(10, 42, 128, 76, 6, GxEPD_RED);

A tela Busy com um bloco vermelho atrás do BUSY branco, o inspetor mostrando rect 4 com Fill color #FF0000, e a linha fillRoundRect(10, 42, 128, 76, 6, GxEPD_RED) destacada no código

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:

cpp
#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:

cpp
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.

A mesma placa em um módulo e-paper de três cores de 2,9 polegadas: ROOM 3, Updated 15:25, um bloco BUSY vermelho, Design review, until 15:30 e 5 min left

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.

A caixa de diálogo Edit Project para Room Sign com Platform GxEPD2 (e-paper), tamanho de tela 296 por 128 e Publish to Gallery marcado

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.

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>.

O menu Share aberto com Copy public link, Presentation e Save As; a tela Busy tem um selo Public, o projeto está marcado Public project, e o inspetor mostra Make Private

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.

A página da galeria de Room Sign: a tela Busy com o bloco vermelho, o código GxEPD2 gerado dela abaixo, e Star, Fork e Share no canto superior direito

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:

  1. Texto pequeno demais. Leia a tela de onde as pessoas vão ficar: para uma placa de sala, a porta.
  2. 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.
  3. 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.
  4. Atualizar com frequência demais. Redesenhe só o que mudou: é mais rápido, e no e-paper economiza a bateria.
  5. 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.

Antes de enviar, cinco erros que mais vemos: uma placa de sala mostrando Updated yesterday 15:25 com uma bateria em 0%, ao lado de uma lista: texto grande o suficiente, só cores do painel, texto que cabe, atualizar só o que muda e mostrar updated at, tudo marcado

A placa neste episódio cobre o quinto: "Updated 15:25" no canto diz para todo mundo quantos anos a imagem tem.

Recapitulação ​

  1. 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.
  2. Atualização completa, atualização parcial, dormir: display() redesenha tudo com um flash, displayWindow() muda uma área sem ele, e hibernate() desliga o painel enquanto a imagem fica.
  3. 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.