Skip to Content

Controlar Animações Parola no Ecrã LED MAX7219

Controlar Animações Parola no Ecrã LED MAX7219

Neste tutorial vais aprender a controlar animações Parola num ecrã de matriz de LEDs MAX7219. Vamos encadear animações, usar botões para alternar entre elas e potenciômetros para ajustar o brilho e a velocidade das animações.

A biblioteca MD_Parola é bastante popular e facilita muito a programação de animações num display, como texto em scroll. Embora animações simples sejam fáceis de implementar, pode ser mais complicado encadear ou controlar várias animações. Além disso, muitas vezes queremos ler dados de sensores sem interromper a animação. Este tutorial vai mostrar-te como fazer isso.

Componentes Necessários

Usei um Arduino Uno para este projeto, mas qualquer outra placa Arduino, ou uma placa ESP8266/ESP32, funcionará igualmente bem. Aqui estamos a usar um ecrã de matriz de LEDs MAX7219 composto por 4 módulos. No entanto, o código e a ligação para ecrãs com mais ou menos módulos é praticamente igual.

Ecrã de Matriz de LEDs MAX7219 

Arduino

Arduino Uno

Dupont wire set

Conjunto de Cabos Dupont

Half_breadboard56a

Breadboard

USB Data Sync cable Arduino

Cabo USB para Arduino UNO

Botão de Pressão

Potenciómetro 10KΩ

Makerguides is a participant in affiliate advertising programs designed to provide a means for sites to earn advertising fees by linking to Amazon, AliExpress, Elecrow, and other sites. As an Affiliate we may earn from qualifying purchases.

Ligar o Ecrã LED MAX7219 ao Arduino

Neste tutorial vamos usar um ecrã de matriz de LEDs MAX7219 para mostrar animações. O controlador do ecrã MAX7219 comunica com o Arduino através de SPI (Serial Peripheral Interface). Portanto, o primeiro passo é ligar o ecrã ao Arduino usando a interface SPI.

Ligações

O diagrama seguinte mostra as ligações necessárias entre o ecrã MAX7219 e um Arduino Uno. Certifica-te de que estás a ligar ao lado de entrada (DIN) e não ao lado de saída (DOUT) do ecrã.

Connecting MAX7219 Display to Arduino UNO
Ligar o Ecrã MAX7219 ao Arduino UNO

Começa por ligar os pinos da interface SPI: O pino 11 do Arduino liga ao pino DIN do ecrã. Depois liga o pino 3 ao CS e o pino 13 ao CLK. Por fim, liga 5V ao VCC e GND ao GND. A tabela seguinte mostra novamente as ligações:

Ecrã Arduino
VCC 5 V
GND GND
DIN 11 (MOSI)
CS 3 (SS)
CLK 13 (SCK)

Os pinos escolhidos acima são para SPI por hardware, que é mais rápido do que SPI por software, mas obriga-te a usar pinos específicos que variam entre placas. Para mais detalhes vê o MAX7219 LED dot matrix display Arduino tutorial .

De seguida, vamos escrever e correr algum código de teste para garantir que as ligações estão corretas.

Instalar as bibliotecas MD_Parola e MD_MAX72XX

Primeiro, precisamos de instalar a MD_MAX72XX e a MD_Parola biblioteca. Podes instalar estas bibliotecas através do Library Manager do Arduino IDE . Vai a Tools > Manage Libraries para abrir o Library Manager. Procura por “MD_MAX72XX” e as bibliotecas relevantes vão aparecer na lista. Na imagem abaixo podes ver que já instalei as duas bibliotecas (marcadas a amarelo):

Install MD_MAX72XX and MD_Parola library
Instalar MD_MAX72XX e MD_Parola biblioteca

A biblioteca MD_MAX72XX é basicamente o driver para o ecrã LED MAX7219. E a biblioteca MD_Parola usa-a para criar animações como efeitos de texto em scroll e sprites. Se quiseres saber mais sobre as animações disponíveis, vê o MAX7219 LED dot matrix display Arduino tutorial .

Código de Teste

O código seguinte é um exemplo mínimo para testar as ligações e o funcionamento do ecrã. Mostra simplesmente o texto “Test” em scroll no ecrã.

#include "MD_Parola.h"
#include "MD_MAX72xx.h"
#include "SPI.h"

#define HARDWARE_TYPE MD_MAX72XX::FC16_HW
#define MAX_DEVICES 4
#define CS_PIN 3  

MD_Parola disp = MD_Parola(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);

void setup() {
  disp.begin();
  disp.setIntensity(0);
  disp.displayClear();
  disp.displayText("Test", PA_CENTER, 100, 0, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
}

void loop() {
  if (disp.displayAnimate()) {
    disp.displayReset();
  }
}

O código começa por incluir as bibliotecas necessárias. A SPI biblioteca já vem por defeito, e instalaste a MD_Parola e a MD_MAX72xx biblioteca acima.

#include "MD_Parola.h"
#include "MD_MAX72xx.h"
#include "SPI.h"

De seguida, precisamos de definir algumas constantes e criar o objeto do ecrã ( disp ). Nomeadamente, temos de definir o tipo de ecrã, quantos módulos tem e a que pino do Arduino está ligado o Chip Select ( CS ) signal.

#define HARDWARE_TYPE MD_MAX72XX::FC16_HW
#define MAX_DEVICES 4
#define CS_PIN 3  

MD_Parola disp = MD_Parola(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);

Se tiveres um ecrã com mais ou menos módulos, tens de definir MAX_DEVICES em conformidade. Nota também que estamos a usar SPI por hardware. Para SPI por software, tens de definir os pinos usados. Vê o MAX7219 LED dot matrix display Arduino tutorial para detalhes.

Na função setup , o código prepara o ecrã, define o brilho e limpa o display. No último passo, definimos uma animação que faz scroll do texto ” Test ” da esquerda para a direita com uma velocidade de 100, ou seja, um atraso de 100ms entre cada passo da animação. Portanto, quanto menor o valor, mais rápido.

void setup() {
  disp.begin();
  disp.setIntensity(0);
  disp.displayClear();
  disp.displayText("Test", PA_CENTER, 100, 0, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
}

A função principal loop corre continuamente a animação chamando displayAnimate() . Se esta função devolver true, a animação terminou e o ecrã reinicia para outro ciclo. Ou seja, o texto volta a fazer scroll.

void loop() {
  if (disp.displayAnimate()) {
    disp.displayReset();
  }
}

Se carregares e correres este código, deves ver o texto “Test” a fazer scroll da esquerda para a direita no teu ecrã MAX7219:

Scrolling Test Output on MAX7219 LED Display
Saída do Teste de Scroll no Ecrã LED MAX7219

Se isso funcionar, parabéns! Estamos prontos para mais animações.

Encadear Animações

Uma dúvida comum é como criar uma sequência de animações que são executadas uma após a outra. Por exemplo, imagina que queremos fazer scroll do texto “Left” para a esquerda e depois “Right” para a direita, repetindo o ciclo.

Chaining of two animations
Encadeamento de duas animações

No entanto, não podemos interromper o ciclo da animação ou encadear facilmente dois ciclos seguidos. De alguma forma, temos de alternar as animações dentro do loop.

void loop() {
  if (disp.displayAnimate()) {
    disp.displayReset();
  }
}

O código seguinte mostra como isto pode ser feito:

#include "MD_Parola.h"
#include "MD_MAX72xx.h"
#include "SPI.h"

#define HARDWARE_TYPE MD_MAX72XX::FC16_HW
#define MAX_DEVICES 4
#define CS_PIN 3  

MD_Parola disp = MD_Parola(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);

void switchAnimation(int animationId) {
  switch(animationId) {
    case 0:
      disp.displayText("Left", PA_CENTER, 100, 0, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
      break;
    case 1:
      disp.displayText("Right", PA_CENTER, 100, 0, PA_SCROLL_RIGHT, PA_SCROLL_RIGHT);
      break;      
  }
}

void setup() {
  disp.begin();
  disp.setIntensity(0);
  disp.displayClear();
  switchAnimation(0);
}

void loop() {
  static int animationId = 0;
  if (disp.displayAnimate()) {
    disp.displayReset();
    switchAnimation(++animationId % 2);
  }
}

Vamos analisar este código com mais atenção. Os includes, as definições de constantes e a criação do display MD_Parola são iguais ao exemplo anterior.

função switchAnimation

O que é novo é a função switchAnimation() . Ela recebe o id de uma animação e define se é a animação de scroll para a esquerda ou para a direita. Nota que switchAnimation() não executa a animação. Apenas diz ao ecrã qual a animação a executar. A execução real da animação é feita por displayAnimate() no loop principal.

void switchAnimation(int animationId) {
  switch(animationId) {
    case 0:
      disp.displayText("Left", PA_CENTER, 100, 0, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
      break;
    case 1:
      disp.displayText("Right", PA_CENTER, 100, 0, PA_SCROLL_RIGHT, PA_SCROLL_RIGHT);
      break;      
  }
}

função setup

A função setup mantém-se praticamente igual. Como antes, prepara o ecrã, define o brilho e limpa o display. No último passo, chamamos switchAnimation(0), que define a animação com id=0. Neste caso, é o scroll do texto “Left”.

função loop

Na função loop definimos a variável animationId para acompanhar a animação atual. É static , o que significa que mantém o valor entre ciclos.

void loop() {
  static int animationId = 0;
  if (disp.displayAnimate()) {
    disp.displayReset();
    switchAnimation(++animationId % 2);
  }
}

Dentro do loop, a chamada a displayAnimate() executa a animação atual. Quando uma animação termina (texto completamente em scroll), esta função devolve true e entramos no corpo do if . Aí fazemos um reset e alternamos para a próxima animação.

A expressão ++animationId % 2 , incrementa o id da animação até chegar a 2 e depois volta a 0. Se quiseres encadear mais de 2 animações, basta substituir o 2 pelo número de animações que tens. Estas animações devem estar definidas na função switchAnimation() .

Aqui tens um código de exemplo para uma sequência de três animações:

...

void switchAnimation(int animationId) {
  switch(animationId) {
    case 0:
      disp.displayText(...);
      break;
    case 1:
      disp.displayText(...);
      break;    
    case2:
      disp.displayText(...);
      break;     
  }
}

void setup() {
...
}

void loop() {
  static int animationId = 0;
  if (disp.displayAnimate()) {
    disp.displayReset();
    switchAnimation(++animationId % 3);
  }
}

Se carregares e correres este código, deves ver a seguinte animação:

Chained "Left" and "Right" animation
Animação encadeada “Left” e “Right”

Na próxima secção, mostro-te como alternar entre animações com base em entradas externas, como botões de pressão.

Alternar Animações

Muitas vezes queremos alternar entre animações com base em alguma entrada externa. Pode ser um botão, um comando remoto, um sensor de movimento ou dados de sensores como temperatura. No exemplo seguinte, vamos usar dois botões para ativar a animação “Left” ou “Right”.

Switching two animations
Alternar entre duas animações

Vamos começar por adicionar os dois botões ao nosso circuito.

Ligações

Adicionar os dois botões é simples. Basta ligar um pino de cada botão ao GND e o outro ao pino 7 e pino 8 do Arduino, como mostra a imagem abaixo:

Wiring for Control Buttons
Ligações para Botões de Controlo

Tem atenção ao ligar o botão para usares os pinos que realmente mudam de estado. Não precisamos de resistências pull-up, pois vamos usar as pull-ups internas do Arduino.

Código para Alternar Animação

O código para alternar entre animações é uma extensão simples do código de animações encadeadas.

#include "MD_Parola.h"
#include "MD_MAX72xx.h"
#include "SPI.h"

#define HARDWARE_TYPE MD_MAX72XX::FC16_HW
#define MAX_DEVICES 4
#define CS_PIN 3

#define BTN1_PIN 6
#define BTN2_PIN 7

MD_Parola disp = MD_Parola(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);

void switchAnimation(int animationId) {
  switch (animationId) {
    case 0:
      disp.displayText("Left", PA_CENTER, 100, 0, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
      break;
    case 1:
      disp.displayText("Right", PA_CENTER, 100, 0, PA_SCROLL_RIGHT, PA_SCROLL_RIGHT);
      break;
  }
}

void setAnimation(int btn_pin, int animationId) {
  if (!digitalRead(btn_pin)) {
    disp.displayReset();
    disp.displayClear();
    switchAnimation(animationId);
    delay(200);
  }
}

void animate() {
  if (disp.displayAnimate()) {
    disp.displayReset();
  }
}

void setup() {
  disp.begin();
  disp.setIntensity(0);
  disp.displayClear();
  setAnimation(0);

  pinMode(BTN1_PIN, INPUT_PULLUP);
  pinMode(BTN2_PIN, INPUT_PULLUP);
}

void loop() {
  setAnimation(BTN1_PIN, 0);
  setAnimation(BTN2_PIN, 1);
  animate();
}

A parte que inclui as bibliotecas necessárias, define as constantes e cria o objeto MD_Parola mantém-se praticamente igual. A única diferença é que definimos os pinos onde os dois botões estão ligados.

#define BTN1_PIN 6
#define BTN2_PIN 7

Também a função switchAnimation() é igual à anterior. Mas agora temos uma nova função setAnimation() :

void setAnimation(int btn_pin, int animationId) {
  if (!digitalRead(btn_pin)) {
    disp.displayReset();
    disp.displayClear();
    switchAnimation(animationId);
    delay(200);
  }
}

Recebe um pino de botão e um id de animação e verifica se o botão nesse pino foi pressionado chamando !digitalRead(btn_pin) . Nota que a lógica está invertida, pois puxamos o pino GPIO para baixo quando o botão é pressionado.

Se o botão for pressionado, fazemos reset e limpamos o display, e alternamos para a animação atribuída a esse botão. O atraso de 200ms serve para debouncing do botão. Este atraso não é problemático, pois acabámos de mudar de animação e não estamos a atrasar uma animação em curso.

De seguida, movemos o código que executa uma animação do loop principal para uma nova função animate() . Isto torna a função loop muito mais limpa.

void animate() {
  if (disp.displayAnimate()) {
    disp.displayReset();
  }
}

A função setup() é praticamente igual, mas definimos o modo dos dois pinos dos botões. Nota que definimos INPUT_PULLUP , para usar o pull-up resistors interno.

void setup() {
  ...

  pinMode(BTN1_PIN, INPUT_PULLUP);
  pinMode(BTN2_PIN, INPUT_PULLUP);
}

A função loop ficou agora muito simples. Basta atribuir o id de uma animação aos botões via setAnimation() e depois chamar animate() para executar a animação ativa. Repara que o código lê-se quase como uma pequena história ou receita. É assim que queremos que o nosso código fique.

void loop() {
  setAnimation(BTN1_PIN, 0);
  setAnimation(BTN2_PIN, 1);
  animate();
}

Carrega o código e experimenta! Os pequenos vídeos abaixo mostram as duas animações que deves ver ao pressionar o botão da esquerda (BTN1) ou da direita (BTN2).

Animation for left button
Animação para o botão esquerdo
Animation for right button
Animação para o botão direito

Na próxima secção vamos modificar a função dos dois botões.

Alternar ou Pausar Animações

Em vez de usar um botão para cada animação, vamos usar um botão (BTN1) para alternar a animação e o outro (BTN2) para pausar a animação em curso. O código seguinte mostra como fazer isso.

#include "MD_Parola.h"
#include "MD_MAX72xx.h"
#include "SPI.h"

#define HARDWARE_TYPE MD_MAX72XX::FC16_HW
#define MAX_DEVICES 4
#define CS_PIN 3

#define BTN1_PIN 6
#define BTN2_PIN 7

int animationId = 0;

MD_Parola disp = MD_Parola(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);

void switchAnimation() {
  switch (animationId) {
    case 0:
      disp.displayText("Left", PA_CENTER, 100, 0, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
      break;
    case 1:
      disp.displayText("Right", PA_CENTER, 100, 0, PA_SCROLL_RIGHT, PA_SCROLL_RIGHT);
      break;
  }
}

void toggleAnimation() {
  if (!digitalRead(BTN1_PIN)) {    
    disp.displayReset();
    disp.displayClear();
    animationId = (animationId + 1) % 2;
    switchAnimation();
    delay(200);
  }
}

void pauseAnimation() {
  static bool isPaused = false;
  if (!digitalRead(BTN2_PIN)) {
    isPaused = !isPaused;
    disp.displaySuspend(isPaused);
    delay(200);
  }
}

void animate() {
  if (disp.displayAnimate()) {
    disp.displayReset();
  }
}

void setup() {
  disp.begin();
  disp.setIntensity(0);
  disp.displayClear();
  switchAnimation();

  pinMode(BTN1_PIN, INPUT_PULLUP);
  pinMode(BTN2_PIN, INPUT_PULLUP);
}

void loop() {
  toggleAnimation();
  pauseAnimation();
  animate();
}

Há algumas alterações em relação ao código anterior. Primeiro, a função switchAnimation() já não recebe um animationId como argumento, mas usa uma variável global para acompanhar a animação em curso.

Em segundo lugar, temos duas novas funções: toggleAnimation() e pauseAnimation() . Vamos analisar primeiro a toggleAnimation() .

void toggleAnimation() {
  if (!digitalRead(BTN1_PIN)) {    
    disp.displayReset();
    disp.displayClear();
    animationId = (animationId + 1) % 2;
    switchAnimation();
    delay(200);
  }
}

Esta função verifica se o BTN1 foi pressionado. Se sim, faz reset e limpa o display. Depois atualiza o animationId para a próxima animação e chama switchAnimation() para definir a animação desse id. No fim, temos o habitual atraso para debouncing do botão.

A função pauseAnimation() verifica se o BTN2 foi pressionado e, nesse caso, alterna o estado da flag isPaused . Dependendo do valor desta flag, a animação do display é suspensa ou não.

void pauseAnimation() {
  static bool isPaused = false;
  if (!digitalRead(BTN2_PIN)) {
    isPaused = !isPaused;
    disp.displaySuspend(isPaused);
    delay(200);
  }
}

A função setup mantém-se praticamente igual e a função loop continua a ser muito clara e organizada:

void loop() {
  toggleAnimation();
  pauseAnimation();
  animate();
}

Em cada iteração tratamos da alternância ou suspensão da animação. Como vês, é fácil mudar a função dos botões e manter tudo bem organizado.

Nas próximas duas secções vamos mais longe e adicionamos um potenciómetro para ajustar o brilho ou a velocidade de uma animação em curso.

Alterar o Brilho da Animação

Nesta parte vamos ver como ajustar o brilho do texto apresentado, enquanto a animação está a correr.

Ligações

Começamos por adicionar um potenciómetro ao circuito. Vamos usá-lo para ajustar o brilho do display de forma analógica. Usei um potenciómetro de 10kΩ, mas qualquer valor entre 1kΩ e 100kΩ serve.

Wiring for Potentiometer
Ligações para o Potenciómetro

Liga o pino do meio do potenciómetro (POT) à entrada analógica A0 do Arduino. Depois liga 5V e GND aos outros dois pinos do potenciómetro. A ordem não é muito importante, mas vai afetar se o brilho aumenta ao girar para a esquerda ou direita. Troca as ligações de 5V e GND no potenciómetro se preferires um sentido específico, por exemplo, girar para a direita aumenta o brilho.

Código para alterar o Brilho

Aqui está o código que usámos antes, agora com a funcionalidade adicional de ajustar o brilho do display conforme a posição do potenciómetro.

#include "MD_Parola.h"
#include "MD_MAX72xx.h"
#include "SPI.h"

#define HARDWARE_TYPE MD_MAX72XX::FC16_HW
#define MAX_DEVICES 4
#define CS_PIN 3

#define BTN1_PIN 6
#define BTN2_PIN 7

int animationId = 0;

MD_Parola disp = MD_Parola(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);

void switchAnimation() {
  switch (animationId) {
    case 0:
      disp.displayText("Left", PA_CENTER, 100, 0, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
      break;
    case 1:
      disp.displayText("Right", PA_CENTER, 100, 0, PA_SCROLL_RIGHT, PA_SCROLL_RIGHT);
      break;
  }
}

void toggleAnimation() {
  if (!digitalRead(BTN1_PIN)) {    
    disp.displayReset();
    disp.displayClear();
    animationId = (animationId + 1) % 2;
    switchAnimation();
    delay(200);
  }
}

void pauseAnimation() {
  static bool isPaused = false;
  if (!digitalRead(BTN2_PIN)) {
    isPaused = !isPaused;
    disp.displaySuspend(isPaused);
    delay(200);
  }
}

void adjustBrightness() {
  int val = analogRead(A0);
  int intensity = map(val, 0, 1023, 0, 15);
  disp.setIntensity(intensity);
}

void animate() {
  if (disp.displayAnimate()) {
    disp.displayReset();
  }
}

void setup() {
  disp.begin();
  disp.setIntensity(0);
  disp.displayClear();
  switchAnimation();

  pinMode(BTN1_PIN, INPUT_PULLUP);
  pinMode(BTN2_PIN, INPUT_PULLUP);
}

void loop() {
  toggleAnimation();
  pauseAnimation();
  adjustBrightness();
  animate();
}

Vamos ver as alterações no código. Só precisamos de uma nova função que lê o valor do potenciómetro via analogRead(), mapeia para um valor de intensidade entre 0 e 15 e depois define a intensidade/brilho do display.

void adjustIntensity() {
  int val = analogRead(A0);
  int intensity = map(val, 0, 1023, 0, 15);
  disp.setIntensity(intensity);
}

Depois, basta chamar adjustBrightness() no loop principal para ajustar o brilho do display enquanto a animação está a correr. E é só isso. Agora podemos alternar e pausar animações, e mudar o brilho.

Na próxima secção, vamos fazer algo muito parecido, mas em vez de ajustar o brilho do display, vamos ajustar a velocidade da animação.

Alterar a Velocidade da Animação

Aqui está o código completo para definir a velocidade da animação conforme a posição do potenciómetro.

#include "MD_Parola.h"
#include "MD_MAX72xx.h"
#include "SPI.h"

#define HARDWARE_TYPE MD_MAX72XX::FC16_HW
#define MAX_DEVICES 4
#define CS_PIN 3

#define BTN1_PIN 6
#define BTN2_PIN 7

int animationId = 0;

MD_Parola disp = MD_Parola(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);

void switchAnimation() {
  int speed = disp.getSpeed();
  switch (animationId) {
    case 0:
      disp.displayText("Left", PA_CENTER, speed, 0, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
      break;
    case 1:
      disp.displayText("Right", PA_CENTER, speed, 0, PA_SCROLL_RIGHT, PA_SCROLL_RIGHT);
      break;
  }
}

void toggleAnimation() {
  if (!digitalRead(BTN1_PIN)) {
    disp.displayReset();
    disp.displayClear();
    animationId = (animationId + 1) % 2;
    switchAnimation();
    delay(200);
  }
}

void pauseAnimation() {
  static bool isPaused = false;
  if (!digitalRead(BTN2_PIN)) {
    isPaused = !isPaused;
    disp.displaySuspend(isPaused);
    delay(200);
  }
}

void adjustSpeed() {
  int val = analogRead(A0);
  int speed = map(val, 0, 1023, 10, 1000);
  disp.setSpeed(speed);
}

void animate() {
  if (disp.displayAnimate()) {
    disp.displayReset();
  }
}

void setup() {
  disp.begin();
  disp.setIntensity(0);
  disp.displayClear();
  switchAnimation();

  pinMode(BTN1_PIN, INPUT_PULLUP);
  pinMode(BTN2_PIN, INPUT_PULLUP);
}

void loop() {
  toggleAnimation();
  pauseAnimation();
  adjustSpeed();
  animate();
}

Este código é um pouco mais complexo, pois temos de adicionar uma nova função e alterar a função switchAnimation() . Na função switchAnimation() agora obtemos a velocidade atual do display e usamos esse valor ao alternar para outra animação:

void switchAnimation() {
  int speed = disp.getSpeed();
  switch (animationId) {
    case 0:
      disp.displayText("Left", PA_CENTER, speed, 0, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
      break;
    case 1:
      disp.displayText("Right", PA_CENTER, speed, 0, PA_SCROLL_RIGHT, PA_SCROLL_RIGHT);
      break;
  }
}

E temos a nova função adjustSpeed() , que é muito semelhante à função de brilho anterior adjustBrightness() . A função adjustSpeed() lê o valor do potenciómetro via analogRead(), mapeia para um valor de velocidade entre 10 e 1000 e define a velocidade do display em conformidade.

void adjustSpeed() {
  int val = analogRead(A0);
  int speed = map(val, 0, 1023, 10, 1000);
  disp.setSpeed(speed);
}

Alterar o Conteúdo da Animação

Por fim, quero mostrar-te um método simples para atualizar o conteúdo de uma animação. Vais precisar disto se quiseres mostrar dados de sensores, por exemplo, um valor de temperatura ou a hora.

Em vez de usar um sensor específico, no exemplo abaixo reutilizamos o potenciómetro para simular leituras de sensores. Mas podes facilmente trocar o potenciómetro por qualquer outro sensor.

#include "MD_Parola.h"
#include "MD_MAX72xx.h"
#include "SPI.h"

#define HARDWARE_TYPE MD_MAX72XX::FC16_HW
#define MAX_DEVICES 4
#define CS_PIN 3

MD_Parola disp = MD_Parola(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);

void updateAnimation() {
  static char buffer[32] = "";
  int value = analogRead(A0);
  sprintf(buffer, "value=%d", value);
  disp.displayText(buffer, PA_CENTER, 50, 0, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
}

void animate() {
  if (disp.displayAnimate()) {
    disp.displayReset();
    updateAnimation();
  }
}

void setup() {
  disp.begin();
  disp.setIntensity(0);
  disp.displayClear();
  updateAnimation();
}

void loop() {
  animate();
}

A funcionalidade principal para atualizar a animação está na função updateAnimation() . Lê o valor atual do sensor, neste exemplo o valor do potenciómetro, formata e escreve para uma string buffer via sprintf , e finalmente atualiza o texto apresentado via displayText() . Se carregares e correres o código, deves ver a seguinte animação:

Animation update from potentiometer reading
Atualização da animação a partir da leitura do potenciómetro

Nota que o tamanho do buffer da string está definido para 32 caracteres. Se quiseres mostrar textos maiores, tens de aumentar o tamanho do buffer.

A função updateAnimation() pode ser facilmente alterada para mostrar outros dados. Imagina que, em vez do potenciómetro, ligas um sensor de temperatura DHT11. Só precisas de alterar a função assim:

float t = dht.readTemperature();
void updateAnimation() {
  static char buffer[32] = "";
  float temp = dht.readTemperature();
  sprintf(text, "Temp=%.2f", temp);
  disp.displayText(buffer, PA_CENTER, 50, 0, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
}

Isto assume, claro, que já ligaste o sensor DHT11 e configuraste a sua função no código. Vê o tutorial sobre How to use DHT11 and DHT22 Sensors with Arduino para mais informações.

Nota que a atualização do conteúdo acontece quando uma nova animação começa, mas não durante a execução de uma animação. Por exemplo, se estiveres a usar a animação de texto em scroll, o texto não vai atualizar enquanto está a fazer scroll. Só atualiza quando começa um novo ciclo. Podes ver isto na função animate() , onde chamamos updateAnimation() quando a animação atual termina.

void animate() {
  if (disp.displayAnimate()) {
    disp.displayReset();
    updateAnimation();
  }
}

E pronto! Vários exemplos, espero que úteis, de como controlar diferentes funcionalidades de uma animação usando entradas digitais ou analógicas, sem interromper a animação. Se tiveres dúvidas, deixa um comentário.

Conclusões

A biblioteca Parola é ótima para animações, mas pode ser um pouco confusa ao início para perceber como controlar, alterar ou atualizar animações sem as interromper. Espero que este tutorial te tenha dado toda a informação para conseguires isso nos teus projetos.

Usámos botões e um potenciómetro como entradas para controlar as animações. Mas é fácil usar outros tipos de entradas digitais ou analógicas. Por exemplo, podes controlar as animações com um comando IR, ou usar uma fotoresistência para ajustar o brilho do display conforme a luz ambiente, e mostrar dados meteorológicos atualizados da internet.

Além disso, a biblioteca Parola permite dividir o ecrã em várias zonas que podem correr animações diferentes, por exemplo, uma zona para um relógio e outra para dados meteorológicos. Se quiseres saber mais sobre isso, vê o tutorial Coordinate Parola Zone Animations on MAX7219 Display .

Por fim, embora tenhamos usado um Arduino neste tutorial, qualquer outro microcontrolador comum como o ESP32 ou ESP8266 também serve.

Agora diverte-te a animar coisas ; )