/*
* Modular Double-Height Parola Demo
* Based on "Parola_Double_Height" example by MajicDesigns (LGPL‑2.1).
* Refactored and localised by <Nama Anda>, June 2025.
*/
#include <MD_Parola.h>
#include <MD_MAX72xx.h>
#include <SPI.h>
#include "Font_Data.h"
// ----------------- Pin & Hardware config -----------------
#define MATRIX_TYPE MD_MAX72XX::PAROLA_HW
#define DM_CS_PIN 10
// ----------------- Display topology -----------------
constexpr uint8_t NUM_ZONES = 2;
constexpr uint8_t MODULES_PER_ZN = 4;
constexpr uint8_t NUM_MODULES = NUM_ZONES * MODULES_PER_ZN;
constexpr uint8_t ZN_BOTTOM = 0;
constexpr uint8_t ZN_TOP = 1;
// ----------------- Animation parameters --------------
constexpr uint16_t ANIM_SPEED = 50; // ms per frame
constexpr uint16_t HOLD_TIME = 0; // ms pause when finished
// ----------------- Compile‑time options --------------
#define ENABLE_DEBUG 1
//#define FLIP_TOP_ZONE // uncomment to invert top zone
// ----------------- Parola object -----------------
MD_Parola matrix = MD_Parola(MATRIX_TYPE, DM_CS_PIN, NUM_MODULES);
// ----------------- Message list -----------------
const char* const kTextQueue[] PROGMEM = {
"Peraga dobel modul, 2 font, 2 zone",
"Zona 0 = bagian bawah",
"Zona 1 = bagian atas",
"BIG",
"ABCDEFGHIJKLMNOPQRSTUVWXYZ",
"abcdefghijklmnopqrstuvwxyz",
"0123456789",
"`!@#$%^&*()_+-={};:'\"<>?,./|\\{}",
};
constexpr uint8_t kTextCount = sizeof(kTextQueue) / sizeof(kTextQueue[0]);
// ----------------- State variables -----------------
uint8_t currentMsg = 0;
// ====================================================
// Utility & setup functions
// ====================================================
void initSerial()
{
#if ENABLE_DEBUG
Serial.begin(57600);
Serial.println(F("[Parola Modular Demo]"));
#endif
}
void initDisplay()
{
matrix.begin(NUM_ZONES);
// Define each zone range
matrix.setZone(ZN_BOTTOM, 0, MODULES_PER_ZN - 1);
matrix.setZone(ZN_TOP, MODULES_PER_ZN, NUM_MODULES - 1);
// Set default fonts
matrix.setFont(ZN_BOTTOM, BigFontLower);
matrix.setFont(ZN_TOP, BigFontUpper);
// Double char spacing for double‑height look
matrix.setCharSpacing(matrix.getCharSpacing() * 2);
#ifdef FLIP_TOP_ZONE
matrix.setZoneEffect(ZN_TOP, true, PA_FLIP_UD);
matrix.setZoneEffect(ZN_TOP, true, PA_FLIP_LR);
#endif
}
// ====================================================
// Animation helpers
// ====================================================
void displayMessage(uint8_t idx)
{
const __FlashStringHelper* txt = (const __FlashStringHelper*)pgm_read_ptr(&kTextQueue[idx]);
switch (idx)
{
default: // 0,4,5,6,7
matrix.setFont(ZN_BOTTOM, BigFontLower);
matrix.setFont(ZN_TOP, BigFontUpper);
#ifdef FLIP_TOP_ZONE
matrix.displayZoneText(ZN_BOTTOM, txt, PA_LEFT, ANIM_SPEED, HOLD_TIME, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
matrix.displayZoneText(ZN_TOP, txt, PA_LEFT, ANIM_SPEED, HOLD_TIME, PA_SCROLL_RIGHT, PA_SCROLL_RIGHT);
#else
matrix.displayZoneText(ZN_BOTTOM, txt, PA_RIGHT, ANIM_SPEED, HOLD_TIME, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
matrix.displayZoneText(ZN_TOP, txt, PA_LEFT, ANIM_SPEED, HOLD_TIME, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
#endif
break;
case 1:
matrix.setFont(ZN_BOTTOM, nullptr); // normal font
matrix.setFont(ZN_TOP, BigFontUpper); // big top
matrix.displayZoneText(ZN_BOTTOM, txt, PA_CENTER, ANIM_SPEED, HOLD_TIME, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
matrix.displayZoneText(ZN_TOP, F("BIG"), PA_CENTER, ANIM_SPEED, HOLD_TIME, PA_PRINT, PA_NO_EFFECT);
break;
case 2:
matrix.setFont(ZN_BOTTOM, BigFontLower);
matrix.setFont(ZN_TOP, nullptr);
matrix.displayZoneText(ZN_BOTTOM, F("BIG"), PA_CENTER, ANIM_SPEED, HOLD_TIME, PA_PRINT, PA_NO_EFFECT);
#ifdef FLIP_TOP_ZONE
matrix.displayZoneText(ZN_TOP, txt, PA_CENTER, ANIM_SPEED, HOLD_TIME, PA_SCROLL_RIGHT, PA_SCROLL_RIGHT);
#else
matrix.displayZoneText(ZN_TOP, txt, PA_CENTER, ANIM_SPEED, HOLD_TIME, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
#endif
break;
case 3:
matrix.setFont(ZN_BOTTOM, BigFontLower);
matrix.setFont(ZN_TOP, BigFontUpper);
matrix.displayZoneText(ZN_BOTTOM, txt, PA_CENTER, ANIM_SPEED, 2000, PA_PRINT, PA_NO_EFFECT);
matrix.displayZoneText(ZN_TOP, txt, PA_CENTER, ANIM_SPEED, 2000, PA_PRINT, PA_NO_EFFECT);
break;
}
}
void nextMessage()
{
currentMsg = (currentMsg + 1) % kTextCount;
matrix.displayClear();
matrix.synchZoneStart();
displayMessage(currentMsg);
}
// ====================================================
// Arduino main
// ====================================================
void setup()
{
initSerial();
initDisplay();
displayMessage(currentMsg);
}
void loop()
{
matrix.displayAnimate();
if (matrix.getZoneStatus(ZN_BOTTOM) && matrix.getZoneStatus(ZN_TOP))
{
#if ENABLE_DEBUG
Serial.print(F("Finished msg #")); Serial.println(currentMsg);
#endif
nextMessage();
}
}