// https://www.hackster.io/mdraber/create-text-animations-with-parola-library-for-arduino-b67955
//MAX7219 - using Parola Library to create complex text animations/transition
#include <MD_Parola.h>
#include <MD_MAX72xx.h>
// Define hardware type and pins
#define HARDWARE_TYPE MD_MAX72XX::FC16_HW
#define MAX_DEVICES 8 // Number of 8x8 LED matrix modules cascaded
#define CLK_PIN 13
#define DATA_PIN 11
#define CS_PIN 10
// Create an MD_MAX72XX object for low-level pixel control.
// This object manages the direct interaction with the MAX7219 chips.
MD_MAX72XX matrix = MD_MAX72XX(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);
// Create a Parola object for text animations.
// This instance will use the same hardware settings as the 'matrix' object.
MD_Parola P = MD_Parola(HARDWARE_TYPE, CS_PIN, MAX_DEVICES); // Original correct constructor for Parola
// --- Existing Text Animation Definitions ---
struct animations
{
textEffect_t anim_in; // Animation type
textEffect_t anim_out; // Animation type
const char * textOut; // Text to display
uint16_t speed; // Animation speed (multiplier for library default)
uint16_t pause; // pause (multiplier for library default)
textPosition_t just;
};
animations animList[] =
{
{ PA_DISSOLVE, PA_DISSOLVE, "WIDE VEHICLE", 5, 5, PA_CENTER },
{ PA_SCROLL_LEFT, PA_SCROLL_LEFT, "!! CAUTION !!", 4, 0, PA_CENTER },
{ PA_CLOSING_CURSOR, PA_GROW_DOWN, "REAR STEER", 5, 5, PA_CENTER },
};
// --- NEW GLOBAL VARIABLES FOR MODE CONTROL ---
enum DisplayMode {
MODE_TEXT_ANIMATIONS,
MODE_STROBE,
MODE_LEFT_ARROW,
MODE_RIGHT_ARROW,
MODE_ALTERNATING_ARROWS // Added for a more dynamic demo
};
DisplayMode currentMode = MODE_TEXT_ANIMATIONS;
unsigned long lastModeChangeTime = 0;
unsigned long modeDuration = 5000; // 5 seconds for each new mode (strobe, arrows)
// For Strobe Effect
bool strobeState = false;
unsigned long lastStrobeToggle = 0;
unsigned int strobeDelay = 500; // Milliseconds between strobe flashes
// For Text Animation Cycling
uint8_t textAnimIndex = 0;
// --- NEW FUNCTIONS FOR TRAFFIC ADVISOR EFFECTS ---
void runStrobe() {
unsigned long currentMillis = millis();
if (currentMillis - lastStrobeToggle >= strobeDelay) {
strobeState = !strobeState;
if (strobeState) {
// Manually fill screen by setting all columns to all LEDs on
for (uint8_t col = 0; col < (MAX_DEVICES * 8); col++) {
matrix.setColumn(col, 0b11111111); // All 8 LEDs in the column on
}
} else {
// Manually clear screen by setting all columns to all LEDs off
for (uint8_t col = 0; col < (MAX_DEVICES * 8); col++) {
matrix.setColumn(col, 0b00000000); // All 8 LEDs in the column off
}
}
matrix.update(); // Update the physical display with the new state
lastStrobeToggle = currentMillis;
}
}
// Function to draw a left arrow across the entire display
void drawLeftArrow() {
matrix.clear(); // Clear the entire display using the MD_MAX72xx object
// Arrow shaft across the full width
for (int col = 0; col < MAX_DEVICES * 8; col++) {
matrix.setColumn(col, 0b00111100); // Draw a horizontal line (rows 3-6)
}
// Arrowhead on the left side
// These patterns are adjusted to be consistent within the 8x8 matrix
// and drawn on the first few columns
matrix.setColumn(0, 0b00011000); // Pointy tip
matrix.setColumn(1, 0b00111100); // Wider
matrix.setColumn(2, 0b01111110); // Even wider
matrix.update(); // Update the physical display
}
// Function to draw a right arrow across the entire display
void drawRightArrow() {
matrix.clear(); // Clear the entire display
// Arrow shaft across the full width
for (int col = 0; col < MAX_DEVICES * 8; col++) {
matrix.setColumn(col, 0b00111100); // Draw a horizontal line (rows 3-6)
}
// Arrowhead on the right side
// Drawn on the last few columns of the entire cascaded display
matrix.setColumn(MAX_DEVICES * 8 - 1, 0b00011000); // Pointy tip
matrix.setColumn(MAX_DEVICES * 8 - 2, 0b00111100); // Wider
matrix.setColumn(MAX_DEVICES * 8 - 3, 0b01111110); // Even wider
matrix.update(); // Update the physical display
}
// Function to draw alternating left and right arrows
void runAlternatingArrows() {
static unsigned long lastArrowToggle = 0;
static bool showLeft = true;
unsigned int arrowToggleDelay = 1000; // Half a second per arrow
unsigned long currentMillis = millis();
if (currentMillis - lastArrowToggle >= arrowToggleDelay) {
if (showLeft) {
drawLeftArrow();
} else {
drawRightArrow();
}
showLeft = !showLeft; // Toggle for next time
lastArrowToggle = currentMillis;
}
}
void setup() {
matrix.begin(); // Initialize the MD_MAX72xx matrix display
P.begin(); // Initialize the MD_Parola object
// Set the intensity using the MD_Parola object.
// This correctly applies brightness to the underlying MD_MAX72xx hardware.
P.setIntensity(5); // Set a medium brightness (0-15)
P.setPause(0); // Ensure no global pause for animations
// Initialize speeds for existing animations
for (uint8_t i = 0; i < ARRAY_SIZE(animList); i++) {
animList[i].speed *= P.getSpeed();
animList[i].pause *= 250;
}
lastModeChangeTime = millis(); // Initialize timer for the first mode
}
void loop() {
unsigned long currentMillis = millis();
// --- Mode Switching Logic ---
if (currentMillis - lastModeChangeTime >= modeDuration) {
lastModeChangeTime = currentMillis; // Reset timer for the new mode
// Cycle through modes
switch (currentMode) {
case MODE_TEXT_ANIMATIONS:
currentMode = MODE_STROBE;
matrix.clear(); // Clear display before new mode
matrix.update(); // Update display after clear
break;
case MODE_STROBE:
currentMode = MODE_LEFT_ARROW;
matrix.clear(); // Clear
matrix.update(); // Update
break;
case MODE_LEFT_ARROW:
currentMode = MODE_RIGHT_ARROW;
matrix.clear(); // Clear
matrix.update(); // Update
break;
case MODE_RIGHT_ARROW:
currentMode = MODE_ALTERNATING_ARROWS;
matrix.clear(); // Clear
matrix.update(); // Update
break;
case MODE_ALTERNATING_ARROWS:
currentMode = MODE_TEXT_ANIMATIONS; // Loop back to text animations
textAnimIndex = 0; // Reset text animation index for the next cycle
matrix.clear(); // Clear
matrix.update(); // Update
break;
}
}
// --- Execute Current Mode ---
switch (currentMode) {
case MODE_TEXT_ANIMATIONS:
if (P.displayAnimate()) { // animates and returns true when an animation is completed
if (textAnimIndex < ARRAY_SIZE(animList)) { // Only advance if there are more animations
P.displayText(animList[textAnimIndex].textOut, animList[textAnimIndex].just, animList[textAnimIndex].speed,
animList[textAnimIndex].pause, animList[textAnimIndex].anim_in, animList[textAnimIndex].anim_out);
textAnimIndex++; // then set up for next text effect
}
}
break;
case MODE_STROBE:
runStrobe();
break;
case MODE_LEFT_ARROW:
drawLeftArrow();
break;
case MODE_RIGHT_ARROW:
drawRightArrow();
break;
case MODE_ALTERNATING_ARROWS:
runAlternatingArrows(); // This function handles its own internal timing and drawing
break;
}
}