Skip to Content

Controla animaciones Parola en pantalla LED MAX7219

Controla animaciones Parola en pantalla LED MAX7219

En este tutorial aprenderás cómo controlar animaciones Parola en una pantalla de matriz LED MAX7219. Vamos a encadenar animaciones, usar botones para cambiar entre ellas y potenciómetros para ajustar el brillo y la velocidad de las animaciones.

La biblioteca MD_Parola es muy popular y facilita la programación de animaciones en una pantalla, como el texto desplazable. Aunque implementar animaciones individuales es sencillo, puede resultar más complicado encadenar o controlar varias animaciones. Además, a menudo queremos leer datos de sensores sin interrumpir la animación. Este tutorial te mostrará cómo hacerlo.

Componentes necesarios

He utilizado un Arduino Uno para este proyecto, pero cualquier otra placa Arduino, o una placa ESP8266/ESP32 funcionará igual de bien. Aquí usamos una pantalla de matriz LED MAX7219 compuesta por 4 módulos, pero el código y el cableado para pantallas con más o menos módulos es prácticamente el mismo.

Pantalla de matriz LED MAX7219 

Arduino

Arduino Uno

Dupont wire set

Juego de cables Dupont

Half_breadboard56a

Protoboard

USB Data Sync cable Arduino

Cable USB para Arduino UNO

Pulsador

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.

Conexión de la pantalla LED MAX7219 con Arduino

En este tutorial usaremos una pantalla de matriz LED MAX7219 para mostrar animaciones. El controlador de pantalla LED MAX7219 se comunica con el Arduino a través de SPI (Serial Peripheral Interface). Así que lo primero es conectar la pantalla al Arduino usando la interfaz SPI.

Cableado

El siguiente diagrama muestra el cableado necesario entre la pantalla MAX7219 y un Arduino Uno. Asegúrate de conectar al lado de entrada (DIN) y no al lado de salida (DOUT) de la pantalla.

Connecting MAX7219 Display to Arduino UNO
Conexión de la pantalla MAX7219 al Arduino UNO

Empieza conectando los pines de la interfaz SPI: el pin 11 del Arduino se conecta al pin DIN de la pantalla. Luego conecta el pin 3 a CS y el pin 13 a CLK. Por último, conecta 5V a VCC y GND a GND. La siguiente tabla muestra de nuevo las conexiones

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

Los pines elegidos arriba son para SPI por hardware, que es más rápido que SPI por software pero requiere usar pines específicos que varían según la placa. Para más detalles consulta la MAX7219 LED dot matrix display Arduino tutorial .

Ahora vamos a escribir y ejecutar un código de prueba para asegurarnos de que las conexiones son correctas.

Instalar las bibliotecas MD_Parola y MD_MAX72XX

Primero, necesitamos instalar la MD_MAX72XX y la MD_Parola biblioteca. Puedes instalar estas bibliotecas desde el Library Manager del Arduino IDE . Ve a Tools > Manage Libraries , lo que abrirá el Library Manager. Busca «MD_MAX72XX» y aparecerán las bibliotecas relevantes. En la captura de pantalla de abajo puedes ver que ya tengo instaladas las dos bibliotecas (marcadas en amarillo):

Install MD_MAX72XX and MD_Parola library
Instala MD_MAX72XX y la MD_Parola biblioteca

La biblioteca MD_MAX72XX es básicamente el software controlador para la pantalla LED MAX7219. Y la biblioteca MD_Parola la utiliza para crear animaciones como efectos de texto desplazable y sprites. Si quieres saber más sobre las animaciones disponibles, echa un vistazo a la MAX7219 LED dot matrix display Arduino tutorial .

Código de prueba

El siguiente código es un ejemplo mínimo para probar el cableado y el funcionamiento de la pantalla. Simplemente muestra el texto desplazable «Test» en la pantalla.

#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();
  }
}

El código comienza incluyendo las bibliotecas necesarias. La biblioteca SPI está disponible por defecto, y ya instalaste la MD_Parola y la MD_MAX72xx biblioteca anteriormente.

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

A continuación, necesitamos definir algunas constantes y crear el objeto display ( disp ). Es decir, hay que definir el tipo de pantalla, cuántos módulos tiene y qué pin del Arduino está conectado a la señal Chip Select ( CS ).

#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);

Si tienes una pantalla con más o menos módulos, tendrás que ajustar MAX_DEVICES en consecuencia. Además, ten en cuenta que aquí usamos SPI por hardware. Para SPI por software, debes definir los pines utilizados. Consulta la MAX7219 LED dot matrix display Arduino tutorial para más detalles.

En la función setup , el código prepara la pantalla, ajusta el brillo y limpia la pantalla. En el último paso, definimos una animación que desplaza el texto » Test » de izquierda a derecha con una velocidad de 100, es decir, un retardo de 100 ms entre cada paso de la animación. Así que cuanto menor sea el valor, más rápido irá.

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

El bucle principal loop ejecuta continuamente la animación llamando a displayAnimate() . Si esta función devuelve true, la animación ha terminado y la pantalla se reinicia para otro ciclo. Es decir, el texto vuelve a desplazarse desde el principio.

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

Si subes y ejecutas este código deberías ver el texto «Test» desplazándose de izquierda a derecha en tu pantalla MAX7219:

Scrolling Test Output on MAX7219 LED Display
Salida de prueba de desplazamiento en pantalla LED MAX7219

¡Si eso funciona, enhorabuena! Ya estamos listos para más animaciones.

Encadenar animaciones

Una pregunta común es cómo crear una cadena o secuencia de animaciones que se ejecuten una tras otra. Por ejemplo, supongamos que queremos desplazar el texto «Left» hacia la izquierda y después el texto «Right» hacia la derecha, repitiendo el ciclo.

Chaining of two animations
Encadenamiento de dos animaciones

Sin embargo, no podemos interrumpir el bucle de animación ni encadenar fácilmente dos bucles uno tras otro. De alguna manera, tenemos que cambiar de animación dentro del bucle.

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

El siguiente código muestra cómo se puede lograr esto:

#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);
  }
}

Veamos este código más de cerca. Las sentencias include, las definiciones de constantes y la creación del display MD_Parola son iguales que antes.

Función switchAnimation

Lo nuevo es la función switchAnimation() . Recibe el id de una animación y establece la animación de desplazamiento a la izquierda o a la derecha. Ten en cuenta que switchAnimation() no ejecuta la animación, solo le indica a la pantalla qué animación debe ejecutar. La ejecución real de la animación la realiza displayAnimate() en el bucle 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;      
  }
}

Función setup

La función setup permanece prácticamente igual. Como antes, prepara la pantalla, ajusta el brillo y limpia la pantalla. Sin embargo, en el último paso llamamos a switchAnimation(0), que establece la animación con id=0. En este caso, es la animación de desplazamiento del texto «Left».

Función loop

En la función loop definimos la variable animationId para llevar el control de la animación actual. Es static , lo que significa que su valor se mantiene entre iteraciones.

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

Dentro del bucle, la llamada a displayAnimate() ejecuta la animación actual. Cuando una animación termina (el texto se ha desplazado completamente), esta función devuelve true y entramos en el cuerpo del if -statement. Ahí realizamos un reinicio y cambiamos a la siguiente animación.

La expresión ++animationId % 2 incrementa el id de la animación hasta que llega a 2 y luego lo vuelve a poner a 0. Si tienes más de 2 animaciones que quieres encadenar, debes reemplazar el 2 por el número de animaciones que tengas. Además, estas animaciones deben estar definidas en la función switchAnimation() .

Aquí tienes un código de plantilla para una secuencia de tres animaciones:

...

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);
  }
}

Si subes y ejecutas este código deberías ver la siguiente animación:

Chained "Left" and "Right" animation
Animación encadenada «Left» y «Right»

En la siguiente sección te muestro cómo cambiar entre animaciones usando entradas externas, como pulsadores.

Cambiar animaciones

A menudo queremos cambiar entre animaciones según alguna entrada externa. Esto puede ser un pulsador, un mando a distancia, un sensor de movimiento o datos de sensores como la temperatura. En el siguiente ejemplo, vamos a usar dos pulsadores para activar la animación «Left» o «Right».

Switching two animations
Cambio entre dos animaciones

Empecemos añadiendo los dos pulsadores a nuestro circuito.

Cableado

Añadir los dos pulsadores es sencillo. Solo tienes que conectar un pin de ambos botones a tierra (GND) y el otro pin a los pines 7 y 8 del Arduino, como se muestra a continuación:

Wiring for Control Buttons
Cableado para botones de control

Ten cuidado al conectar el botón y usa los pines que realmente se activan. No necesitamos resistencias pull-up externas, ya que vamos a usar las pull-ups internas del Arduino.

Código para cambiar de animación

El código para cambiar entre animaciones es una simple extensión del código para animaciones encadenadas.

#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();
}

La parte que incluye las bibliotecas necesarias, define las constantes y crea el objeto display MD_Parola es prácticamente igual. La única diferencia es que definimos los pines donde están conectados los dos botones.

#define BTN1_PIN 6
#define BTN2_PIN 7

También la función switchAnimation() es la misma que antes. Pero ahora tenemos una nueva función setAnimation() :

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

Recibe un pin de botón y un id de animación, y comprueba si el botón en el pin especificado está pulsado llamando a !digitalRead(btn_pin) . Ten en cuenta que la lógica está invertida, ya que ponemos el pin GPIO a bajo cuando se pulsa el botón.

Si el botón está pulsado, reiniciamos y limpiamos la pantalla, y cambiamos a la animación asignada a ese botón. El retardo de 200 ms es para el rebote del botón. Este retardo está bien aquí, ya que acabamos de cambiar de animación y por tanto no retrasamos una animación en curso.

A continuación, movemos el código que ejecuta una animación del bucle principal a una nueva función animate() . Así la función loop queda mucho más limpia.

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

La función setup() es prácticamente igual, pero configuramos el modo de los dos pines a los que están conectados los botones. Ten en cuenta que usamos INPUT_PULLUP , para usar la pull-up resistors interna.

void setup() {
  ...

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

La función loop ahora es muy sencilla. Solo asignamos el id de una animación a los botones mediante setAnimation() y luego llamamos a animate() para ejecutar la animación activa. Fíjate que el código se lee como una pequeña historia o receta. Así es como queremos que quede nuestro código.

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

¡Sube el código y pruébalo! Los siguientes clips cortos muestran las dos animaciones que deberías ver al pulsar el botón izquierdo (BTN1) o el derecho (BTN2).

Animation for left button
Animación para el botón izquierdo
Animation for right button
Animación para el botón derecho

En la siguiente sección vamos a modificar la función de los dos botones.

Alternar o pausar animaciones

En vez de usar un botón para cada animación, usaremos un botón (BTN1) para alternar la animación y el otro botón (BTN2) para pausar la animación en curso. El siguiente código muestra cómo hacerlo.

#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();
}

Hay algunos cambios respecto al código anterior. Primero, la función switchAnimation() ya no recibe un animationId como argumento, sino que usa una variable global para llevar el control de la animación en curso.

En segundo lugar, tenemos dos funciones nuevas: toggleAnimation() y pauseAnimation() . Veamos primero toggleAnimation() .

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

Esta función comprueba si se ha pulsado BTN1. Si es así, primero reinicia y limpia la pantalla. Luego actualiza el animationId a la siguiente animación y llama a switchAnimation() para establecer la animación de ese id. Al final, tenemos el típico retardo para el rebote del botón.

La función pauseAnimation() comprueba si se ha pulsado BTN2 y, si es así, alterna la bandera isPaused . Según el valor de esta bandera, la animación de la pantalla se suspende o no.

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

La función setup es prácticamente igual y la función loop, de nuevo, se lee como una pequeña historia:

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

En cada iteración gestionamos el cambio o la pausa de la animación. Como ves, es fácil cambiar la función de los botones y mantener todo bien organizado.

En las dos siguientes secciones vamos un paso más allá y añadimos un potenciómetro para ajustar el brillo o la velocidad de una animación en curso.

Cambiar el brillo de la animación

En esta parte veremos cómo ajustar el brillo del texto mostrado mientras la animación está en marcha.

Cableado

Empecemos añadiendo un potenciómetro a nuestro circuito. Lo usaremos para ajustar el brillo o la intensidad de la pantalla de forma analógica. Yo he usado un potenciómetro de 10kΩ, pero cualquier valor entre 1kΩ y 100kΩ funcionará igual.

Wiring for Potentiometer
Cableado para el potenciómetro

Conecta el pin central del potenciómetro (POT) a la entrada analógica A0 del Arduino. Luego conecta 5V y GND a los otros dos pines del potenciómetro. El orden no importa mucho, pero afectará a si el brillo aumenta al girar el potenciómetro a la izquierda o a la derecha. Si prefieres una dirección concreta, simplemente intercambia las conexiones de 5V y GND en el potenciómetro.

Código para cambiar el brillo

Aquí tienes el código que usábamos antes, pero con la funcionalidad adicional para ajustar el brillo de la pantalla según la posición del 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();
}

Veamos los cambios en el código. Solo necesitamos una función nueva que lea el valor del potenciómetro con analogRead(), lo mapee a un valor de intensidad entre 0 y 15 y luego ajuste la intensidad/brillo de la pantalla.

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

Luego hay que llamar a adjustBrightness() en el bucle principal para ajustar el brillo de la pantalla mientras la animación está en marcha. Y eso es todo. Ahora podemos alternar y pausar animaciones, y cambiar el brillo.

En la siguiente sección haremos algo muy parecido, pero en vez de ajustar el brillo de la pantalla, ajustaremos la velocidad de la animación.

Cambiar la velocidad de la animación

Aquí tienes el código completo para ajustar la velocidad de la animación según la posición del 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 es un poco más complejo, ya que hay que añadir una función nueva y cambiar la función switchAnimation() . En la función switchAnimation() ahora obtenemos la velocidad actual de la pantalla y la usamos al cambiar a otra animación:

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;
  }
}

Y tenemos la nueva función adjustSpeed() , que es muy parecida a la función de adjustBrightness() anterior. La función adjustSpeed() lee el valor del potenciómetro con analogRead(), lo mapea a un valor de velocidad entre 10 y 1000 y luego ajusta la velocidad de la pantalla.

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

Cambiar el contenido de la animación

Por último, quiero mostrarte un método sencillo para actualizar el contenido de una animación. Esto te será útil si quieres mostrar datos de sensores, por ejemplo, un valor de temperatura o la hora.

En vez de usar un sensor específico, en el ejemplo de abajo reutilizamos el potenciómetro para simular lecturas de sensores. Pero podrías sustituir fácilmente el potenciómetro por cualquier otro 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();
}

La funcionalidad principal para la actualización de la animación está en la función updateAnimation() . Lee el valor actual del sensor, en este caso el valor del potenciómetro, lo formatea y lo escribe en una cadena buffer mediante sprintf , y finalmente actualiza el texto mostrado mediante displayText() . Si subes y ejecutas el código deberías ver la siguiente animación:

Animation update from potentiometer reading
Actualización de la animación a partir de la lectura del potenciómetro

Ten en cuenta que el tamaño del buffer de la cadena está fijado en 32 caracteres. Si quieres mostrar textos más largos, tendrás que aumentar el tamaño del buffer.

La función updateAnimation() se puede cambiar fácilmente para mostrar otros datos. Supongamos que en vez del potenciómetro conectas un sensor de temperatura DHT11. Solo tendrías que cambiar la función así:

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);
}

Esto supone, por supuesto, que has conectado el sensor DHT11 y configurado su función en el código. Consulta el tutorial sobre How to use DHT11 and DHT22 Sensors with Arduino para más información.

Ten en cuenta que la actualización del contenido ocurre cuando empieza una nueva animación, pero no durante la ejecución de una animación. Por ejemplo, si usamos la animación de texto desplazable, el texto no se actualizará mientras se desplaza, pero sí cuando empiece un nuevo ciclo. Puedes verlo en la función animate() , donde llamamos a updateAnimation() cuando la animación actual termina.

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

¡Y eso es todo! Varios ejemplos, que espero que te sean útiles, sobre cómo controlar diferentes funciones de una animación usando entradas digitales o analógicas, sin interrumpir la animación. Si tienes alguna pregunta, no dudes en dejar un comentario.

Conclusiones

La biblioteca Parola es genial para animaciones, pero puede ser un poco complicado entender cómo controlar, cambiar o actualizar animaciones sin interrumpirlas. Espero que este tutorial te haya dado toda la información para lograrlo en tus proyectos.

Hemos usado botones y un potenciómetro como entradas para controlar las animaciones. Pero es fácil usar otras entradas digitales o analógicas. Por ejemplo, podrías controlar las animaciones con un mando a distancia por infrarrojos, o usar una fotorresistencia para ajustar el brillo de la pantalla según la luz ambiente, y mostrar datos meteorológicos actualizados de internet.

Además, la biblioteca Parola te permite dividir una pantalla en varias zonas que pueden ejecutar diferentes animaciones, por ejemplo, una zona para un reloj y otra para datos meteorológicos. Si quieres saber más sobre esto, echa un vistazo al tutorial de la Coordinate Parola Zone Animations on MAX7219 Display .

Por último, aunque hemos usado un Arduino en este tutorial, cualquier otro microcontrolador común como ESP32 o ESP8266 funcionará igual de bien.

¡Y ahora, disfruta animando cosas ; )