I am trying to create a cycling display which will cycle between screens showing texts with animations from the MD_Parola library and then show the time with the animation in the MD_MAX72XX library.
This is the animation for the clock that I desire
See for the first video and skip to 1:03 for the clock animation.
Hardware and Software I am using :-
- Arduino nano
- MAX7219 controlled dot matrix modules.(They are classified as FC16_HW in the Parola library).
- DS1307 rtc module.
- MD_Parola library for text animations & MD_MAX72XX library for clock animation.
I was guided by the creator of the library @marco_c for this exact approach here :-
This is what I am facing,
So far I am able to convert the pushwheel example( the example he was telling me to use for the clock animation) to work with the MD_Parola library. See for the updateDisplay() and displayValue() functions in the given below code.
I tried to use a flag ( displayflag ) to keep the Parola and MAX72XX functions separately, which work until its time to display the clock, which seems to revert back to the sprite animation as soon as the first LSB of the second needs to be animated, I cannot even see the animation completed and it skips back to sprite animation rather than to wait for the 5 seconds that I desire.
It seems to me that the MD_MAX72XX functions are either resetting the microcontroller to begin initialization or the displayflag variable changes unintentionally to true as soon as the time comes to animate.
Please note that if there is no need for animation i.e. for example if I put now.hour() in place of now.second() in the displayValue() function the displayflag never sets back to true and gets stuck on the displayValue() function display the clock and don't revert back to the cycle.
Here is my code :-
#include <MD_Parola.h>
#include <MD_MAX72xx.h>
#include <SPI.h>
#include "RTClib.h"
#define MAX_DEVICES 8
#define HARDWARE_TYPE MD_MAX72XX::FC16_HW
#define CLK_PIN 13
#define DATA_PIN 11
#define CS_PIN 10
MD_Parola matrix = MD_Parola(HARDWARE_TYPE, DATA_PIN, CLK_PIN, CS_PIN, MAX_DEVICES);
RTC_DS1307 rtc;
#define CHAR_SPACING 1 // pixels between characters
#define CHAR_COLS 8 // should match the fixed width character columns
#define ANIMATION_FRAME_DELAY 10 // in milliseconds
boolean displayflag = true;
int prevseconds = 0;
int counter = 0;
int seconds = 0;
struct displayParameters {
const char *message;
uint16_t speed;
uint16_t pause;
textEffect_t entryEffect;
textEffect_t exitEffect;
};
displayParameters params[] = {
{ "INTRO", 30, 1000, PA_SPRITE, PA_SPRITE },
{ "Wait", 20, 1000, PA_GROW_UP, PA_GROW_UP },
};
const uint8_t F_PMAN1 = 6;
const uint8_t W_PMAN1 = 8;
const uint8_t PROGMEM pacman1[F_PMAN1 * W_PMAN1] = // gobbling pacman animation
{
0x00,
0x81,
0xc3,
0xe7,
0xff,
0x7e,
0x7e,
0x3c,
0x00,
0x42,
0xe7,
0xe7,
0xff,
0xff,
0x7e,
0x3c,
0x24,
0x66,
0xe7,
0xff,
0xff,
0xff,
0x7e,
0x3c,
0x3c,
0x7e,
0xff,
0xff,
0xff,
0xff,
0x7e,
0x3c,
0x24,
0x66,
0xe7,
0xff,
0xff,
0xff,
0x7e,
0x3c,
0x00,
0x42,
0xe7,
0xe7,
0xff,
0xff,
0x7e,
0x3c,
};
struct digitData {
uint8_t oldValue, newValue; // ASCII value for the character
uint8_t index; // animation progression index
uint32_t timeLastFrame; // time the last frame started animating
uint8_t charCols; // number of valid cols in the charMap
uint8_t charMap[CHAR_COLS]; // character font bitmap
};
void updateDisplay(uint16_t numDigits, struct digitData *d)
// do the necessary to display current bitmap buffer to the LED display
{
uint8_t curCol = 0;
matrix.getGraphicObject()->control(MD_MAX72XX::UPDATE, MD_MAX72XX::OFF);
matrix.getGraphicObject()->clear();
for (int8_t i = numDigits - 1; i >= 0; i--) {
for (int8_t j = d[i].charCols - 1; j >= 0; j--) {
matrix.getGraphicObject()->setColumn(curCol++, d[i].charMap[j]);
}
curCol += CHAR_SPACING;
}
matrix.getGraphicObject()->control(MD_MAX72XX::UPDATE, MD_MAX72XX::ON);
}
boolean displayValue(uint16_t value)
// Display the required value on the LED matrix and return true if an animation is current
// Finite state machine will ignore new values while animations are underway.
// Needs to be called repeatedly to ensure animations are completed smoothly.
{
const uint8_t DIGITS_SIZE = 2;
static struct digitData digit[DIGITS_SIZE];
const uint8_t ST_INIT = 0, ST_WAIT = 1, ST_ANIM = 2;
static uint8_t state = ST_INIT;
// finite state machine to control what we do
switch (state) {
case ST_INIT: // Initialize the display - done once only on first call
for (int8_t i = DIGITS_SIZE - 1; i >= 0; i--) {
// separate digits
digit[i].oldValue = '0' + value % 10;
value = value / 10;
}
// Display the starting number
for (int8_t i = DIGITS_SIZE - 1; i >= 0; i--) {
digit[i].charCols = matrix.getGraphicObject()->getChar(digit[i].oldValue, CHAR_COLS, digit[i].charMap);
}
updateDisplay(DIGITS_SIZE, digit);
// Now we wait for a change
state = ST_WAIT;
break;
case ST_WAIT: // not animating - save new value digits and check if we need to animate
for (int8_t i = DIGITS_SIZE - 1; i >= 0; i--) {
// separate digits
digit[i].newValue = '0' + value % 10;
value = value / 10;
if (digit[i].newValue != digit[i].oldValue) {
// a change has been found - we will be animating something
state = ST_ANIM;
// initialize animation parameters for this digit
digit[i].index = 0;
digit[i].timeLastFrame = 0;
}
}
if (state == ST_WAIT) // no changes - keep waiting
break;
// else fall through as we need to animate from now
case ST_ANIM: // currently animating a change
// work out the new intermediate bitmap for each character
// 1. Get the 'new' character bitmap into temp buffer
// 2. Shift this buffer down or up by current index amount
// 3. Shift the current character by one pixel up or down
// 4. Combine the new partial character and the existing character to produce a frame
for (int8_t i = DIGITS_SIZE - 1; i >= 0; i--) {
if ((digit[i].newValue != digit[i].oldValue) && // values are different
(millis() - digit[i].timeLastFrame >= ANIMATION_FRAME_DELAY)) // timer has expired
{
uint8_t newChar[CHAR_COLS] = { 0 };
matrix.getGraphicObject()->getChar(digit[i].newValue, CHAR_COLS, newChar);
if (((digit[i].newValue > digit[i].oldValue) || // incrementing
(digit[i].oldValue == '9' && digit[i].newValue == '0'))
&& // wrapping around on increase
!(digit[i].oldValue == '0' && digit[i].newValue == '9')) // not wrapping around on decrease
{
// scroll down
for (uint8_t j = 0; j < digit[i].charCols; j++) {
newChar[j] = newChar[j] >> (COL_SIZE - 1 - digit[i].index);
digit[i].charMap[j] = digit[i].charMap[j] << 1;
digit[i].charMap[j] |= newChar[j];
}
} else {
// scroll up
for (uint8_t j = 0; j < digit[i].charCols; j++) {
newChar[j] = newChar[j] << (COL_SIZE - 1 - digit[i].index);
digit[i].charMap[j] = digit[i].charMap[j] >> 1;
digit[i].charMap[j] |= newChar[j];
}
}
// set new parameters for next animation and check if we are done
digit[i].index++;
digit[i].timeLastFrame = millis();
if (digit[i].index >= COL_SIZE)
digit[i].oldValue = digit[i].newValue; // done animating
}
}
updateDisplay(DIGITS_SIZE, digit); // show new display
// are we done animating?
{
boolean allDone = true;
for (uint8_t i = 0; allDone && (i < DIGITS_SIZE); i++) {
allDone = allDone && (digit[i].oldValue == digit[i].newValue);
}
if (allDone) state = ST_WAIT;
}
break;
default:
state = 0;
}
return (state == ST_WAIT); // animation has ended
}
// const int debugled = 12;
// boolean debugflag = false;
void setup() {
rtc.begin();
matrix.begin();
// pinMode(debugled, OUTPUT);
matrix.setSpriteData(pacman1, W_PMAN1, F_PMAN1, pacman1, W_PMAN1, F_PMAN1);
}
void loop() {
DateTime now = rtc.now();
static uint8_t index = 0;
if (displayflag == false) {
displayValue(now.hour());
if (now.second() != prevseconds) {
counter++;
prevseconds = now.second();
}
} else {
counter = 0;
if (matrix.displayAnimate()) {
if (index >= ARRAY_SIZE(params)) {
matrix.displayReset();
displayflag = false;
}
matrix.displayText(params[index].message, PA_CENTER, params[index].speed, params[index].pause, params[index].entryEffect, params[index].exitEffect);
index++;
}
}
}
I am sorry in advance for any breach in forum rules as my english is quite poor.
Any help is highly appreciated