#include <MD_Parola.h> // Biblioteca para controlar displays com texto animado
#include <MD_MAX72xx.h> // Biblioteca base para controle de displays LED com o chip MAX72xx
#include <SPI.h> // Biblioteca de comunicação SPI (usada pelos displays)
// Define se a interface de controle (potenciômetro, botão, etc) será usada
#define USE_UI_CONTROL 0
#if USE_UI_CONTROL
#include <MD_UISwitch.h> // Biblioteca para ler botões físicos se a interface de controle estiver habilitada
#endif
// Define se as mensagens de debug serão mostradas no monitor serial
#define DEBUG 0
#if DEBUG
#define PRINT(s, x) { Serial.print(F(s)); Serial.print(x); } // Imprime uma string seguida de um valor
#define PRINTS(x) Serial.print(F(x)) // Imprime apenas uma string
#define PRINTX(x) Serial.println(x, HEX) // Imprime um valor em hexadecimal
#else
#define PRINT(s, x)
#define PRINTS(x)
#define PRINTX(x)
#endif
// Define tipo de hardware e número de módulos MAX72xx conectados
#define HARDWARE_TYPE MD_MAX72XX::PAROLA_HW // Tipo de display Parola
#define MAX_DEVICES 4 // Número de módulos em série (cada módulo = 8x8 LEDs)
#define CLK_PIN 18 // Pino do clock (usado em SPI software)
#define DATA_PIN 23 // Pino de dados (usado em SPI software)
#define CS_PIN 21 // Pino chip select (ativo em SPI hardware/software)
// Cria o objeto da biblioteca Parola usando SPI por hardware
MD_Parola P = MD_Parola(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);
// (Comentado) Alternativa usando SPI por software
//MD_Parola P = MD_Parola(HARDWARE_TYPE, DATA_PIN, CLK_PIN, CS_PIN, MAX_DEVICES);
// Parâmetros de rolagem do texto
#if USE_UI_CONTROL
const uint8_t SPEED_IN = A5; // Pino do potenciômetro para controlar a velocidade
const uint8_t DIRECTION_SET = 8; // Pino do botão para trocar a direção da rolagem
const uint8_t INVERT_SET = 9; // Pino do botão para inverter o display
const uint8_t SPEED_DEADBAND = 5; // Margem para evitar pequenas variações no potenciômetro
#endif
// Configurações iniciais da animação de texto
uint8_t scrollSpeed = 60; // Velocidade de rolagem
textEffect_t scrollEffect = PA_SCROLL_LEFT; // Efeito de rolagem para a esquerda
textPosition_t scrollAlign = PA_LEFT; // Alinhamento do texto à esquerda
uint16_t scrollPause = 4000; // Tempo de pausa entre as animações (ms)
// Buffers para armazenar a mensagem atual e a nova mensagem
#define BUF_SIZE 75 // Tamanho máximo do buffer da mensagem
char curMessage[BUF_SIZE] = { "" }; // Mensagem atual exibida
char newMessage[BUF_SIZE] = { "of the king the power the best " }; // Mensagem inicial
bool newMessageAvailable = true; // Flag indicando que há nova mensagem para exibir
#if USE_UI_CONTROL
// Cria objetos para ler os botões físicos
MD_UISwitch_Digital uiDirection(DIRECTION_SET);
MD_UISwitch_Digital uiInvert(INVERT_SET);
// Função para controlar a interface física (potenciômetro e botões)
void doUI(void)
{
// Lê o potenciômetro e ajusta a velocidade se houver mudança significativa
{
int16_t speed = map(analogRead(SPEED_IN), 0, 1023, 10, 150);
if ((speed >= ((int16_t)P.getSpeed() + SPEED_DEADBAND)) ||
(speed <= ((int16_t)P.getSpeed() - SPEED_DEADBAND)))
{
P.setSpeed(speed);
scrollSpeed = speed;
PRINT("\nChanged speed to ", P.getSpeed());
}
}
// Verifica se o botão de direção foi pressionado
if (uiDirection.read() == MD_UISwitch::KEY_PRESS)
{
PRINTS("\nChanging scroll direction");
scrollEffect = (scrollEffect == PA_SCROLL_LEFT ? PA_SCROLL_RIGHT : PA_SCROLL_LEFT);
P.setTextEffect(scrollEffect, scrollEffect);
P.displayClear(); // Limpa o display
P.displayReset(); // Reinicia a animação
}
// Verifica se o botão de inversão foi pressionado
if (uiInvert.read() == MD_UISwitch::KEY_PRESS)
{
PRINTS("\nChanging invert mode");
P.setInvert(!P.getInvert()); // Inverte o estado atual do display (branco/preto)
}
}
#endif // USE_UI_CONTROL
// Função para ler dados vindos pela porta serial
void readSerial(void)
{
static char *cp = newMessage; // Ponteiro para inserir os caracteres
while (Serial.available()) // Enquanto houver dados na serial
{
*cp = (char)Serial.read(); // Lê caractere e armazena no buffer
// Se chegou ao fim da linha ou encheu o buffer
if ((*cp == '\n') || (cp - newMessage >= BUF_SIZE - 2))
{
*cp = '\0'; // Finaliza a string
cp = newMessage; // Reinicia o ponteiro
newMessageAvailable = true; // Sinaliza nova mensagem
}
else
cp++; // Avança para o próximo caractere
}
}
// Função de inicialização (roda uma vez ao ligar o Arduino)
void setup()
{
Serial.begin(57600); // Inicia a comunicação serial
Serial.print("\n[Parola Scrolling Display]\nType a message for the scrolling display\nEnd message line with a newline");
#if USE_UI_CONTROL
uiDirection.begin(); // Inicializa botão de direção
uiInvert.begin(); // Inicializa botão de inversão
pinMode(SPEED_IN, INPUT); // Define pino do potenciômetro como entrada
doUI(); // Aplica configurações iniciais da interface
#endif
P.begin(); // Inicializa o display Parola
P.displayText(curMessage, scrollAlign, scrollSpeed, scrollPause, scrollEffect, scrollEffect); // Define texto e efeitos
}
// Função principal (roda em loop)
void loop()
{
#if USE_UI_CONTROL
doUI(); // Lê interface física, se habilitada
#endif
if (P.displayAnimate()) // Avança a animação. Retorna true quando a animação atual termina
{
if (newMessageAvailable) // Se houver nova mensagem
{
strcpy(curMessage, newMessage); // Copia nova mensagem para a atual
newMessageAvailable = false; // Marca como já usada
}
P.displayReset(); // Reinicia a animação com o novo texto
}
readSerial(); // Verifica se há entrada serial com nova mensagem
}