Skip to Content

Relógio Digital no Display Redondo CrowPanel 1.28″

Relógio Digital no Display Redondo CrowPanel 1.28″

Neste tutorial, vais aprender a implementar um relógio digital num CrowPanel 1.28″ Round Display com um GC9A01 TFT usando a biblioteca Adafruit_GC9A01A. O código de exemplo para o relógio também te mostrará como usar o ecrã tátil CST816D, o buzzer, o motor de vibração, o relógio em tempo real (RTC) BM8563, o modo deep sleep e a sincronização do tempo pela internet.

Peças Necessárias

Obviamente, vais precisar do módulo CrowPanel ESP32 1.28-inch Round Display para este projeto. Além de um cabo USB-C, que normalmente vem com o módulo de ecrã, não são necessárias outras peças.

CrowPanel ESP32 1.28″ Round Display

Características do CrowPanel 1.28″ Round Display

O módulo CrowPanel 1.28″ é composto por um ecrã TFT redondo (GC9A01) com resolução de 240×240 pixels, um ecrã tátil capacitivo (CST816D) e um ESP32-C3 integrado. A imagem abaixo mostra a frente do módulo:

Front of CrowPanel 1.28" Round Display
Frente do CrowPanel 1.28″ Round Display (source)

Além do ESP32, o módulo contém um buzzer, motor de vibração, um relógio em tempo real (BM8563) com bateria de reserva, suporte para bateria LiPo com carregamento e vários botões. A imagem seguinte mostra a parte traseira do módulo, onde podes ver a maioria das peças:

Back of CrowPanel 1.28" Round Display
Traseira do CrowPanel 1.28″ Round Display (source)

Note que há também um pequeno encoder com suporte a botão (à esquerda), que essencialmente te permite adicionar uma coroa para ajustar a hora como num relógio mecânico de pulso – desde que escrevas o código para isso. Mas, como é totalmente programável, podes controlar todo o tipo de outras funções ou interações da interface de utilizador com ele também.

Para programação (e alimentação) há uma porta USB-C e botões Reset e Boot na parte traseira. Se uma bateria LiPo estiver ligada, será carregada pela porta USB-C.

A tabela seguinte resume as principais características do módulo de ecrã:

Chip PrincipalESP32-C3
ProcessadorProcessador RISC-V de 32 bits, núcleo único, até 160 MHz
Memória384 KB ROM, 400 KB SRAM (16 KB para cache), 8 KB SRAM no RTC
Tamanho1.28 polegadas
Resolução240*240
Interface de SinalSPI
Tipo de ToqueToque Capacitivo
Tipo de PainelTFT LCD, Painel IPS
Profundidade de Cor262K
Brilho350 cd/m²
Ângulo de Visualização178°
BotõesBotão Reset, Botão Boot, Botão Personalizado
InterfaceInterface Tipo-C, Interface para Bateria
EncoderCom função de botão, sem pino (Tamanho do pino: 0.8mm*0.8mm)
AlimentaçãoMódulo: DC5V, Chip Principal: 3.3V
Área Ativa32.51*32.51mm
Dimensões42*42*9.8mm
Peso Líquido15g

Relógio Digital no CrowPanel 1.28″ Round Display

Nesta secção vamos implementar um Relógio Digital no CrowPanel 1.28″ Round Display. O relógio mostrará o nome do dia atual, a hora e a data. Também poderemos alternar entre o formato 24h e 12h tocando num botão no ecrã. A imagem abaixo mostra o mostrador do relógio com as suas funcionalidades.

Clock face with features
Mostrador do relógio com funcionalidades

Além disso, o relógio pode ser colocado em modo deep sleep ao pressionar o botão físico à direita (traseira) do módulo. Um segundo pressionar acorda o relógio novamente. Isto é essencial se quiseres usar o relógio com bateria.

Também emitiremos um som a cada hora e forneceremos feedback tátil ao pressionar o botão tátil ativando o motor de vibração.

Finalmente, o relógio sincronizará automaticamente o Relógio em Tempo Real (RTC) interno com um servidor de tempo da internet (SNTP) se o WiFi estiver disponível.

Com isso, explorámos a maioria das funcionalidades técnicas do CrowPanel Display – exceto Bluetooth e o Encoder.

Código para Relógio Digital no Ecrã GC9A01

O ecrã do CrowPanel 1.28″ é um GC9A01 com um ecrã tátil CST816D. Usaremos a biblioteca Adafruit_GC9A01A para desenhar no ecrã, pois é muito mais simples que a biblioteca LVGL usada para o demo code que acompanha o CrowPanel.

No entanto, se quiseres implementar uma interface de utilizador mais complexa com menus drop-down e outras funcionalidades, a biblioteca LVGL é a melhor opção.

Abaixo está o código para o Relógio Digital. É um código extenso e sugiro que apenas o explores para teres uma visão geral. Vamos discutir os detalhes a seguir.

#include "WiFi.h"
#include "Adafruit_GFX.h"
#include "Adafruit_GC9A01A.h"
#include "I2C_BM8563.h"
#include "CST816D.h"

// I2C
#define SDA 4
#define SCL 5
#define PI4IO_I2C_ADDR 0x43

// TFT display
#define TFT_CS 10
#define TFT_DC 2
#define TFT_MOSI 7
#define TFT_SCLK 6
#define TFT_RST 4
#define TFT_BKL 2

#define DW 240
#define DH 240

// Touch panel
#define TP_INT 0
#define TP_RST 3


#define BUTTON 1
#define BUZZER 3
#define MOTOR 0

const char* SSID = "SSID";
const char* PWD = "PASSWORD";

bool is_24h = true;
const char* TIMEZONE = "AEST-10AEDT,M10.1.0,M4.1.0/3";
const char* DAYSTR[] = { "SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT" };
const char* MONTHSTR[] = { "Jan", "Feb", "Mar", "Apr", "May",
                           "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };

// touch button
const int bx = DW / 2;
const int by = DH - 30;
const int br = 10;

Adafruit_GC9A01A tft(TFT_CS, TFT_DC, TFT_MOSI, TFT_SCLK);
I2C_BM8563 rtc(I2C_BM8563_DEFAULT_ADDRESS, Wire);
CST816D touch(SDA, SCL, TP_RST, TP_INT);


void init_ioex() {
  Wire.begin(SDA, SCL);
  Wire.beginTransmission(PI4IO_I2C_ADDR);
  Wire.write(0x01);  // test register
  Wire.endTransmission();
  Wire.requestFrom(PI4IO_I2C_ADDR, 1);
  uint8_t rxdata = Wire.read();

  Wire.beginTransmission(PI4IO_I2C_ADDR);
  Wire.write(0x03);                                                  
  Wire.write((1 << 0) | (1 << 1) | (1 << 2) | (1 << 3) | (1 << 4));  
  Wire.endTransmission();

  Wire.beginTransmission(PI4IO_I2C_ADDR);
  Wire.write(0x07);                                                     
  Wire.write(~((1 << 0) | (1 << 1) | (1 << 2) | (1 << 3) | (1 << 4))); 
  Wire.endTransmission();
}

void write_ioex(uint8_t pin_number, bool value) {
  Wire.beginTransmission(PI4IO_I2C_ADDR);
  Wire.write(0x05);  // test register
  Wire.endTransmission();
  Wire.requestFrom(PI4IO_I2C_ADDR, 1);
  uint8_t rxdata = Wire.read();

  Wire.beginTransmission(PI4IO_I2C_ADDR);
  Wire.write(0x05);  // Output register

  if (!value)
    Wire.write((~(1 << pin_number)) & rxdata);  
  else
    Wire.write((1 << pin_number) | rxdata);  
  Wire.endTransmission();

  Wire.beginTransmission(PI4IO_I2C_ADDR);
  Wire.write(0x05);  // test register
  Wire.endTransmission();
  Wire.requestFrom(PI4IO_I2C_ADDR, 1);
  rxdata = Wire.read();
}

void set_rtc_time() {
  rtc.begin();

  struct tm t;
  getLocalTime(&t);

  I2C_BM8563_TimeTypeDef ts;
  ts.hours = t.tm_hour;
  ts.minutes = t.tm_min;
  ts.seconds = t.tm_sec;
  rtc.setTime(&ts);

  I2C_BM8563_DateTypeDef ds;
  ds.weekDay = t.tm_wday;
  ds.month = t.tm_mon + 1;
  ds.date = t.tm_mday;
  ds.year = t.tm_year + 1900;
  rtc.setDate(&ds);
}

void sync_time() {
  WiFi.begin(SSID, PWD);
  for (int i = 0; (i < 5) && (WiFi.status() != WL_CONNECTED); i++)
    delay(100);
  if (WiFi.status() == WL_CONNECTED) {
    configTzTime(TIMEZONE, "pool.ntp.org");
    set_rtc_time();
  }
}

void display_time() {
  static char buf[16];
  static I2C_BM8563_DateTypeDef ds;
  static I2C_BM8563_TimeTypeDef ts;

  rtc.getDate(&ds);
  rtc.getTime(&ts);

  tft.setTextColor(GC9A01A_LIGHTGREY, GC9A01A_BLACK);
  tft.setCursor(80, 60);
  tft.setTextSize(4);
  tft.print(DAYSTR[ds.weekDay]);

  if (is_24h) {
    sprintf(buf, "%02d:%02d:%02d", ts.hours, ts.minutes, ts.seconds);
  } else {
    int display_hours = ts.hours % 12;
    display_hours = display_hours ? display_hours : 12;
    const char* period = ts.hours >= 12 ? "pm" : "am";
    sprintf(buf, "%02d:%02d %s", display_hours, ts.minutes,  period);
  }

  tft.setTextColor(is_24h ? GC9A01A_WHITE : GC9A01A_YELLOW, GC9A01A_BLACK);
  tft.setCursor(45, 110);
  tft.setTextSize(3);
  tft.print(buf);

  sprintf(buf, "%2d %s %04d", ds.date, MONTHSTR[ds.month], ds.year);
  tft.setTextColor(GC9A01A_WHITE, GC9A01A_BLACK);
  tft.setCursor(45, 150);
  tft.setTextSize(2);
  tft.print(buf);
}

void sound_hour() {
  static I2C_BM8563_TimeTypeDef ts;
  rtc.getTime(&ts);  
  if (ts.minutes == 0 && ts.seconds == 0) {
    tone(BUZZER, 1000);
    delay(50);
    tone(BUZZER, 0);
    delay(500);
  }
}

void check_touch() {
  uint8_t gesture;
  uint16_t x, y;
  bool touched = touch.getTouch(&x, &y, &gesture);
  if (touched) {
    if (abs(x - bx) < 2*br && abs(y - by) < 2*br) {
      write_ioex(MOTOR, true);
      delay(50);
      write_ioex(MOTOR, false);      
      is_24h = !is_24h;
      display_touch();
      delay(500);
    }
  }
}

void display_touch() {
  if (is_24h) {
    tft.fillCircle(bx, by, br, GC9A01A_BLACK);
    tft.drawCircle(bx, by, br, GC9A01A_DARKGREY);
  } else {
    tft.fillCircle(bx, by, br, GC9A01A_DARKGREY);
  }
}

void check_button() {
  if (digitalRead(BUTTON) == LOW) {
    tft.fillScreen(GC9A01A_BLACK);
    write_ioex(TFT_BKL, false);  // Switch off TFT backlight
    delay(300);
    esp_deep_sleep_start();
  }
}

void init_deep_sleep() {
  pinMode(BUTTON, INPUT);
  esp_deep_sleep_enable_gpio_wakeup(1ULL << BUTTON, ESP_GPIO_WAKEUP_GPIO_LOW);
}

void init_buzzer() {
  pinMode(BUZZER, OUTPUT);
  digitalWrite(BUZZER, LOW);
}

void init_tft() {
  tft.begin();
  tft.setRotation(0);
  tft.fillScreen(GC9A01A_BLACK);
  display_touch();
}

void set_ioex_pins() {
  write_ioex(TFT_RST, true);  // TFT Reset
  write_ioex(TP_RST, true);  // Touch panel reset
  write_ioex(TFT_BKL, true);  // TFT backlight
}

void setup() {
  Serial.begin(115200);  
  init_ioex();
  set_ioex_pins();    
  touch.begin();
  init_buzzer();
  init_deep_sleep();
  sync_time();  
  init_tft();
}

void loop() {
  static unsigned long st = millis();

  if (millis() - st > 300) {
    display_time();
    sound_hour();
    st = millis();
  }

  check_touch();
  check_button();
  yield();
}

Vamos dividir o código em componentes para uma compreensão mais clara.

Bibliotecas

O código começa por incluir as bibliotecas necessárias para WiFi, gráficos, controlo do ecrã, relógio em tempo real (RTC) e funcionalidade do painel tátil.

#include "WiFi.h"
#include "Adafruit_GFX.h"
#include "Adafruit_GC9A01A.h"
#include "I2C_BM8563.h"
#include "CST816D.h"

Terás de instalar a biblioteca Adafruit_GC9A01A. Basta abrir o Library Manager, procurar por “Adafruit_GC9A01A” e clicar em “INSTALL”. A imagem abaixo mostra como deve ficar após a instalação:

Adafruit_GC9A01A Library installed
Biblioteca Adafruit_GC9A01A instalada

Além disso, precisarás dos ficheiros para o Relógio em Tempo Real I2C_BM8563 e para o ecrã tátil CST816D. Descarrega o Arduino Demo Code, descompacta-o e vai para a pasta ESP32_1.28_Arduino_Demo. Deverá conter as seguintes subpastas:

CrowPanel 1.28" Round Display demo code folder
Pasta do código demo do CrowPanel 1.28″ Round Display

Nas subpastas LvglWidgets e RTC encontrarás os ficheiros necessários. Copia-os para a pasta do projeto do teu código do relógio digital. O conteúdo da pasta do projeto deverá ser assim:

Digital Clock project folder
Pasta do projeto Relógio Digital

Como podes ver, nomeei o sketch Arduino como “CrowPanel-Digital-Clock-1-28-Inch.ino” e, como o nome da pasta deve coincidir com o do sketch, é “CrowPanel-Digital-Clock-1-28-Inch”. Mas podes nomear de outra forma, só certifica-te que o nome do sketch e da pasta coincidem.

No entanto, para facilitar, também zippei o meu projeto e podes descarregá-lo usando o seguinte link:

Constantes

De seguida, definimos constantes para a comunicação I2C, pinos do ecrã TFT e pinos do painel tátil.

#define SDA 4
#define SCL 5
#define PI4IO_I2C_ADDR 0x43
#define TFT_CS 10
#define TFT_DC 2
#define TFT_MOSI 7
#define TFT_SCLK 6
#define TFT_RST 4
#define TFT_BKL 2

#define DW 240
#define DH 240

// Touch panel
#define TP_INT 0
#define TP_RST 3

#define BUTTON 1
#define BUZZER 3
#define MOTOR 0

Podes encontrar estas definições de pinos no código de exemplo e no Schematics of the CrowPanel 1.28″ Display. Especialmente interessante é a secção abaixo que mostra o conector do TFT e do painel tátil:

Schematics for TFT and Touch Panel Connector
Esquemas do conector TFT e painel tátil (source)

É importante notar que o módulo CrowPanel contém um expansor GPIO (PI4IOE5V6408), pois o ecrã TFT utiliza a maioria dos pinos do ESP32. Isso significa que alguns pinos são GPIO do ESP32, por exemplo (BUZZER) e outros são controlados via o expansor GPIO. Especificamente, LCD_RESET (TFT_RST=4), TP_RESET (TP_RST=3), o LED de backlight LED_P2 (TFT_BKL=2) e o motor de vibração MOTOR_P0 (MOTOR=0). Dá uma vista de olhos nos esquemas do expansor GPIO abaixo:

Schematics for PI4IOE5V6408 GPIO Expander
Esquemas do expansor GPIO PI4IOE5V6408 (source)

Verás mais tarde no código que os pinos do ESP32 e do expansor GPIO precisam de ser controlados de forma diferente.

Variáveis Globais

Declaramos várias variáveis globais, incluindo credenciais WiFi, definições de fuso horário e arrays para nomes dos dias e meses. Também definimos uma variável booleana para alternar entre os formatos 12h e 24h.

const char* SSID = "SSID";
const char* PWD = "PASSWORD";

bool is_24h = true;

const char* TIMEZONE = "AEST-10AEDT,M10.1.0,M4.1.0/3";

const char* DAYSTR[] = { "SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT" };
const char* MONTHSTR[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };

Note que terás de substituir as credenciais WiFi (SSID, PWD) pelas credenciais da tua rede WiFi.

Muito provavelmente, também quererás alterar o TIMEZONE. Eu uso AEST-10AEDT,M10.1.0,M4.1.0/3, que corresponde a Melbourne, Austrália. Esta string indica o desfasamento do horário padrão e as regras para o horário de verão.

As partes desta definição de fuso horário são as seguintes

  • AEST: Australian Eastern Standard Time
  • -10: Desfasamento UTC de 10 horas à frente do Tempo Universal Coordenado (UTC)
  • AEDT: Australian Eastern Daylight Time
  • M10.1.0: A transição para o horário de verão ocorre no 1º domingo de outubro
  • M4.1.0/3: A transição de volta para o horário padrão ocorre no 1º domingo de abril, com uma diferença de 3 horas em relação ao UTC.

Para outras definições de fuso horário, consulta o Posix Timezones Database. Basta copiar a string que lá encontras e alterar a constante TIMEZONE em conformidade.

As restantes constantes são apenas strings para os nomes dos dias e meses. Podes substituí-las por strings noutra língua, por exemplo. Mas terás de manter comprimentos semelhantes.

Funções de Inicialização

O código contém várias funções para inicializar vários componentes. A função init_ioex() configura a comunicação I2C e o expansor I/O.

void init_ioex() {
  Wire.begin(SDA, SCL);
  Wire.beginTransmission(PI4IO_I2C_ADDR);
  Wire.write(0x01);  // test register
  ...
}

Este código origina-se do ficheiro de código demo LvglWidgets.ino. Alterei ligeiramente o nome e removi algumas instruções de impressão, mas de resto é o mesmo código.

O mesmo para a função relacionada write_ioex() abaixo, que permite escrever nos pinos GPIO do expansor IO:

void write_ioex(uint8_t pin_number, bool value) {
  Wire.beginTransmission(PI4IO_I2C_ADDR);
  Wire.write(0x05);  // test register
  ...
}

A função init_tft() define as configurações padrão para desenhar no ecrã TFT e também desenha o círculo branco que representa o botão tátil chamando display_touch():

void init_tft() {
  tft.begin();
  tft.setRotation(0);
  tft.fillScreen(GC9A01A_BLACK);
  display_touch();
}

A função init_deep_sleep() configura o botão que desperta o ESP32 do modo deep sleep:

void init_deep_sleep() {
  pinMode(BUTTON, INPUT);
  esp_deep_sleep_enable_gpio_wakeup(1ULL << BUTTON, ESP_GPIO_WAKEUP_GPIO_LOW);
}

E finalmente, a função init_buzzer() configura o pino do buzzer.

void init_buzzer() {
  pinMode(BUZZER, OUTPUT);
  digitalWrite(BUZZER, LOW);
}

RTC e Sincronização de Tempo

A função set_rtc_time() inicializa o RTC e define a hora e data atuais com base na hora local obtida pela ligação WiFi.

void set_rtc_time() {
  rtc.begin();
  struct tm t;
  getLocalTime(&t);
  // Set RTC time and date...
}

Essa hora local é sincronizada com um servidor de tempo da internet (servidor SNTP) via a função sync_time():

void sync_time() {
  WiFi.begin(SSID, PWD);
  for (int i = 0; (i < 5) && (WiFi.status() != WL_CONNECTED); i++)
    delay(100);
  if (WiFi.status() == WL_CONNECTED) {
    configTzTime(TIMEZONE, "pool.ntp.org");
    set_rtc_time();
  }
}

Note que a função tenta 5 vezes ligar ao WiFi e depois desiste. Isto é intencional! Se usares o relógio fora da tua rede WiFi, ele não consegue conectar nem sincronizar ao reiniciar. Isso é aceitável, pois o RTC manterá o controlo do tempo.

No entanto, como só sincronizamos ao reiniciar o ESP32 e o RTC não tem noção de horário de verão, o relógio não ajustará automaticamente a mudança entre DST e ST e vice-versa. Existem soluções para isso. Consulta o tutorial Real-Time-Clock DS3231 with ESP32.

Exibição do Tempo

A função display_time() obtém a hora e data atuais do RTC e exibe-as no ecrã TFT nos locais apropriados. Dependendo do estado da flag is_24h, formata a hora em 12 ou 24 horas.

void display_time() {
  static char buf[16];
  static I2C_BM8563_DateTypeDef ds;
  static I2C_BM8563_TimeTypeDef ts;
  ...
  if (is_24h) {
    sprintf(buf, "%02d:%02d:%02d", ts.hours, ts.minutes, ts.seconds);
  } else {
    int display_hours = ts.hours % 12;
    display_hours = display_hours ? display_hours : 12;
    const char* period = ts.hours >= 12 ? "pm" : "am";
    sprintf(buf, "%02d:%02d %s", display_hours, ts.minutes,  period);
  }
  ...
}

Note que não é fácil usar uma fonte diferente para exibir hora e data. A biblioteca Adafruit_GC9A01A permite definir uma cor de fundo ao imprimir texto, o que apaga a hora e data previamente impressas.

tft.setTextColor(GC9A01A_WHITE, GC9A01A_BLACK);
...
tft.print(buf);

No entanto, isto só funciona para a fonte padrão, não para fontes proporcionais. Para mais informações, vê o Adafruit GFX Graphics Library Documentation. Uma solução seria usar um Canvas, mas isso é mais complexo e a atualização do ecrã TFT pode ser lenta.

Botão Tátil

A função check_touch() deteta eventos de toque no painel tátil. Se o toque estiver próximo da área definida do botão (o círculo branco), ativa o motor de vibração por um curto período para fornecer feedback tátil.

void check_touch() {
  uint8_t gesture;
  uint16_t x, y;
  bool touched = touch.getTouch(&x, &y, &gesture);
  if (touched) {
    if (abs(x - bx) < 2*br && abs(y - by) < 2*br) {
      write_ioex(MOTOR, true);
      delay(50);
      write_ioex(MOTOR, false);      
      is_24h = !is_24h;
      display_touch();
      delay(500);
    }
  }
}

Depois alterna o formato da hora e atualiza o ecrã. A imagem seguinte mostra os dois estados do ecrã:

24h and 12h states of clock display
Estados 24h e 12h do mostrador do relógio

O pequeno círculo branco na parte inferior do ecrã representa o botão tátil e é desenhado pela função display_touch(). Dependendo do seu estado, é desenhado como um círculo preenchido ou vazio:

void display_touch() {
  if (is_24h) {
    tft.fillCircle(bx, by, br, GC9A01A_BLACK);
    tft.drawCircle(bx, by, br, GC9A01A_DARKGREY);
  } else {
    tft.fillCircle(bx, by, br, GC9A01A_DARKGREY);
  }
}

Botão Deep Sleep

A função check_button() verifica se o botão físico na lateral do módulo está pressionado. Se estiver, coloca o dispositivo em modo deep sleep. O mais importante é que também desliga o LED de backlight do ecrã TFT para poupar bateria.

void check_button() {
  if (digitalRead(BUTTON) == LOW) {
    tft.fillScreen(GC9A01A_BLACK);
    write_ioex(TFT_BKL, false);  // Switch off TFT backlight
    delay(300);
    esp_deep_sleep_start();
  }
}

Um segundo pressionar no botão acorda o ESP32 novamente. Note que este botão tem um resistor pullup interno e, por isso, fica LOW quando pressionado. Vê os esquemas abaixo:

Esquemas do Botão (source)

Funções de Configuração

A função setup() inicializa a comunicação serial, expansor I/O, painel tátil, buzzer e ecrã TFT, e sincroniza a hora.

void setup() {
  Serial.begin(115200);  
  init_ioex();
  ...
}

Ou seja, o relógio só é sincronizado com a hora da internet ao reiniciar. Isto poupa bateria, pois podes desligar o WiFi após o reinício. No entanto, podes decidir sincronizar com mais frequência, por exemplo, uma vez por hora para captar a mudança para o horário de verão.

Função Loop

A função loop() atualiza continuamente a exibição da hora e data a cada 300 milissegundos e verifica eventos de toque e o estado do botão. Podes adicionar um pequeno delay de 50ms em vez da chamada yield().

void loop() {
  static unsigned long st = millis();

  if (millis() - st > 300) {
    display_time();
    sound_hour();
    st = millis();
  }

  check_touch();
  check_button();
  yield();
}

Demonstração do Relógio Digital

O vídeo curto seguinte mostra o relógio em ação. Podes ver a hora a avançar, a alternância entre os formatos 24h e 12h ao pressionar o botão tátil, e o modo deep sleep:

Demo of the Digital Clock on CrowPanel 1.28" Display
Demonstração do Relógio Digital no CrowPanel 1.28″ Display

E está feito! Explorámos a maioria das funcionalidades do CrowPanel 1.28″ Display e agora deves estar numa boa posição para implementar o teu próprio relógio com as suas funcionalidades únicas.

Conclusões

Neste tutorial aprendeste a implementar um relógio digital num CrowPanel 1.28″ Display Module. Usámos muitas funcionalidades do módulo de ecrã, incluindo o ecrã tátil, buzzer, motor de vibração e RTC. Também aprendemos a sincronizar o RTC com um servidor de tempo da internet e a colocar o relógio em modo deep sleep.

Se quiseres aprender mais sobre sincronização de tempo, vê o tutorial How to synchronize ESP32 clock with SNTP server, e o tutorial Real-Time-Clock DS3231 with ESP32 ajudará a implementar suporte para horário de verão.

E, se quiseres implementar um Relógio Analógico, o Analog Clock on e-Paper Display terá informações úteis, apesar de ser direcionado para um ecrã E-Paper e não TFT.

Caso contrário, há muitas funções que podes adicionar ao teu relógio. Uma óbvia seria a exibição de informações meteorológicas. Vê o tutorial Simple ESP32 Internet Weather Station para um exemplo. Também há informações sobre como integrar o módulo connect the CrowPanel no Home Assistant.

Se tiveres dúvidas, sente-te à vontade para deixá-las na secção de comentários.

Divirta-te e boas criações ; )

Links

Abaixo alguns links que achei úteis ao escrever este tutorial:

CrowPanel ESP32 1.28-inch Round Display

Folhas de dados