Manual

do

Maker

.

com

ESP32-S3 SPI e displays

ESP32-S3 SPI e displays

Mais um título difícil de definir. "ESP32-S3 SPI e displays" não diz muito, mas a imagem de destaque deve passar um pouco mais de informação sobre o que veremos aqui.

O ESP32-S3 dev kit possui duas portas USB-C, sendo uma para a UART e a outra para USB host. Não tenho o menor interesse nisso nesse momento, mas me encanta a quantidade absurda de memória flash que tem essa placa. Tanto que no artigo "Como usar mais de um display SPI" só foi possível utilizar 2 displays para animar 3 frames em cada. Não fosse animação, aí sim, vários displays mostrando informações diferentes. É até interessante para fazer painel de computador desktop, pegando velocidade da fan, temperaturas etc. Chega de escrever e vamos ao objetivo.

Passo 1: Criar um projeto

Crie um projeto escolhendo a placa ESP32-S3 dev kit. Pode ser na IDE do Arduino ou no VS Code, utilizando PlatformIO. Se você nunca usou o VS Code com PlatformIO, experimente. Tenho muita convicção de que não vai se arrepender.

Passo 2: Instalar a biblioteca TFT_eSPI

Essa biblioteca é magnífica e facílima de configurar. Instale a biblioteca e configure os pinos e tipo de display no arquivo User_Setup.h. Se estiver utilizando essa placa da Curto Circuito, a configuração completa do arquivo deve ficar assim:

#define USER_SETUP_INFO "User_Setup"
#define GC9A01_DRIVER
#define TFT_HEIGHT 240 // GC9A01 240 x 240
#define TFT_MOSI MOSI //11
#define TFT_SCLK SCK //12
#define TFT_CS   -1 //32  // Chip select control pin
#define TFT_DC   21  // Data Command control pin
#define TFT_RST  45 // ligado no RST, ou um GPIO qualquer  // Reset pin (could connect to RST pin)
                        // Use an Arduino pin for initial testing as connecting to processor reset
                        // may not work (pulse too short at power up?)
#define LOAD_GLCD   // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH
#define LOAD_FONT2  // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters
#define LOAD_FONT4  // Font 4. Medium 26 pixel high font, needs ~5848 bytes in FLASH, 96 characters
#define LOAD_FONT6  // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm
#define LOAD_FONT7  // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:-.
#define LOAD_FONT8  // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-.
#define LOAD_GFXFF  // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts
#define SMOOTH_FONT
#define SPI_FREQUENCY  27000000
#define SPI_READ_FREQUENCY  20000000

Passo 3: Wiring do display com ESP32-S3

No artigo "Como usar SPI no ESP32S3" discorri bastante sobre os detalhes. O artigo tem informação importante para conhecimento relacionado ao SPI, recomendo a leitura.

Com o passo anterior, sabemos que MOSI vai ao GPIO 11, assim como SCLK vai ao GPIO 12.

O DC é de livre escolha, assim como o reset. O DC foi ao GPIO 21 e o reset foi ao GPIO 45. Poderia ir diretamente ao pino de reset da MCU.

O CS está configurado como -1 porque não vamos definí-lo na biblioteca. Esse controle faremos de forma interativa, pois o pino CS (Chip Select) é o que determina o receptor da mensagem - no caso, o respectivo frame da animação.

Passo 4: Criar a animação

A animação é feita a partir de um array bi-dimensional. Esse array contém o número de frames com os respectivos arrays de bytes que formam a imagem. Você não precisa saber nada disso e nem terá trabalho para criar sua animação.

a . Baixe um gif animado que deseja exibir no display

Recomendo dois sites. O primeiro é esse especializado em gifs . Bem, esse outro também é especializado em gifs animados. Nesse segundo link já deixei "no ponto" para baixar animações circulares.

b . Baixe o EasyMaker Animation Studio

O EasyMaker Animation Studio é um programa que criei para auxiliar na criação de animações para displays. Clique em Releases do lado direito, mais ou menos no centro vertical da tela. Baixe o instalador EasyMaker-AS.zip. Se preferir um link direto para o programa, aqui está.

Sugiro que dê uma pausa e assista o vídeo de apresentação dos recursos do EasyMaker Animation Studio. Se preferir um vídeo tutorial mostrando como usar em um caso real, assista o vídeo "Gif animado no ESP32".

Talvez um resumo seja o suficiente, apesar de que o vídeo não consumirá mais de 3 minutos do seu tempo. Basicamente:

  • Abra o EasyMaker-AS
  • Abra o gif animado
  • Selecione os frames que deseja
  • Clique em Send
  • Confira se a animação está boa para você;
  • Redimensione para 240x240
  • Clique em Apply e Code

Passo 5: Adicione sua animação ao projeto

Copie o arquivo EasyMaker.h para seu projeto. Se for mais de 1 display, renomeie-o para o nome que preferir, mantendo a extensão .h, então faça o include desse header em seu projeto. Lembre-se de mudar (ou remover) a duplicidade de dimensão nos arquivos de animação. Também renomeie a animação, para o caso de mais de 1 display. Exemplo:

int EasyMaker_width=240;
int EasyMaker_height=240;
const unsigned short EasyMaker[6][57600] ={

Esse é o início do arquivo original criado pelo EasyMaker_AS. Se tiver mais de uma animação, renomeie as dimensões e a animação:

int bolhas_width=240; //ou remova as dimensões, deixando EasyMaker_xx apenas em 1
int bolhas_height=240;
const unsigned short bolhas[6][57600] ={

Passo final: Ajuste esse sketch para seus displays

Nesse sketch precisamos:

  • Fazer o include da animação
  • Definir o número de displays em NUM_OF_DSPs
  • Definir os pinos CS de cada display (CS_DSP_x)
  • Adicionar os CSs à variável CSs
  • Adicionar a animação ao ponteiro de arrays (animations).
  • Definir o número de frames dentro da função defineNumOfFrames

A função com número de frames não consegue interagir com o ponteiro de arrays, por isso tem que ser definido um a um, invés de sizeof(anim) / sizeof(anim[0]). Repare que cada uma das animações tem um número de frames diferente, ou seja, não precisa ser tudo igual! Dá pra mudar o intervalo também, mas não quis implementar isso agora. Mudando os intervalos, podemos controlar a velocidade de cada animação independentemente.

#include <Arduino.h>

#include "NotoSansBold36.h"

//INCLUA AS ANIMACOES
#include "heartbeat.h"
#include "bubbles.h"
#include "plasma.h"
#include "moving.h"

#include <TFT_eSPI.h>

#define AA_FONT_LARGE NotoSansBold36

#define NUM_OF_DSPs     4  //defina o número de displays
#define CS_DSP_1        47 //defina o GPIO para usar como CS
#define CS_DSP_2        48
#define CS_DSP_3        20
#define CS_DSP_4        19

const unsigned short FRAME_DIMENSION  = 57600;

uint8_t CSs[NUM_OF_DSPs]              = {CS_DSP_1,CS_DSP_2,CS_DSP_3,CS_DSP_4}; //adicione o display
int frame_interval                    = 100;
unsigned int timer                    = millis();
unsigned int next_frame               = 0;
uint8_t number_of_frames[NUM_OF_DSPs] = {0}; 
uint8_t show_frame[NUM_OF_DSPs]       = {0};

//ADICIONE AS ANIMACOES
const unsigned short (*animations[NUM_OF_DSPs])[FRAME_DIMENSION] = {Heartbeat,plasma,bubbles,moving};

TFT_eSPI tft = TFT_eSPI();

void defineNumOfFrames(){
    //Infelizmente não é possível passar fazendo a divisão de sizeof(array) / sizeof(array[0])
    //Isso porque as animações são um array de ponteiros agora.
    //ADICIONE O NÚMERO DE FRAMES DA ANIMAÇÃO DO RESPECTIVO DISPLAY
    number_of_frames[0] = 6;
    number_of_frames[1] = 3;
    number_of_frames[2] = 5;
    number_of_frames[3] = 4;
}

void setup() {
  Serial.begin(9600);
  vTaskDelay(pdMS_TO_TICKS(1000));
  defineNumOfFrames();

  for (uint8_t i=0;i<NUM_OF_DSPs;i++){
    pinMode(CSs[i],OUTPUT);
    digitalWrite(CSs[i],LOW);
  }

  
  tft.init();                       
  tft.setSwapBytes(true);           
  tft.setRotation(3);                
  tft.fillScreen(TFT_PURPLE);      

  for (uint8_t i=0;i<NUM_OF_DSPs;i++){
    digitalWrite(CSs[i], HIGH);
    tft.pushImage(0,0,EasyMaker_height,EasyMaker_width,animations[i][0]); 
    digitalWrite(CSs[i], LOW);
  }

  vTaskDelay(pdMS_TO_TICKS(200));

}

void showImage(){ 
  next_frame = millis()-timer;
  if (next_frame > frame_interval){
    for (uint8_t i=0;i<NUM_OF_DSPs;i++){
      show_frame[i] = show_frame[i] > number_of_frames[i]-2 ? 0 : show_frame[i]+1;
    }    
    timer = millis();
  }
  
  for (uint8_t i=0;i<NUM_OF_DSPs;i++){
    digitalWrite(CSs[i],LOW);      
    tft.pushImage(0,0,EasyMaker_height,EasyMaker_width,animations[i][show_frame[i]]); 
    digitalWrite(CSs[i],HIGH); 
  }
  
}

void loop(){
  showImage();
}

Vídeo

Ainda vou preparar o vídeo porque nesse caso será importante mostrar todo o processo "e" a animação. Então aproveite e inscreva-se em nosso canal Do bit Ao Byte no Youtube ative as notificações para quando o vídeo for publicado!

Nome do Autor

Djames Suhanko

Autor do blog "Do bit Ao Byte / Manual do Maker".

Viciado em embarcados desde 2006.
LinuxUser 158.760, desde 1997.