Skip to Content

Coordinate Parola Zone Animations on MAX7219 Display

Coordinate Parola Zone Animations on MAX7219 Display

In this tutorial you will learn how to coordinate Parola zone animations on a MAX7219 LED Matrix Display. The MD_Parola library is a popular library that makes the programming of animations such as the scrolling of text very easy. It also allows you to divide a display in multiple zones and to run different animations in those zones.

However, coordinating animations in multiple zones is not easy. For instance, how to restart an animation in one zone, without affecting the animation in a another zone? Or how to restart all animations only when all animations have completed, even if they are running at different speeds?

This tutorial will give you a code framework to handle such animation scenarios easily.

Required Parts

I used an Arduino Uno for this project but any other Arduino board, or an ESP8266/ESP32 board will also work. Furthermore, I used MAX7219 LED Matrix Display with 4 modules. But the code and wiring for displays with different numbers of modules is essentially the same.

MAX7219 LED Matrix Display 

Arduino

Arduino Uno

Dupont wire set

Dupont Wire Set

USB Data Sync cable Arduino

USB Cable for Arduino UNO

Makerguides.com is a participant in the Amazon Services LLC Associates Program, an affiliate advertising program designed to provide a means for sites to earn advertising fees by advertising and linking to products on Amazon.com. As an Amazon Associate we earn from qualifying purchases.

Connecting MAX7219 LED Display with Arduino

In this tutorial we will use a MAX7219 LED Matrix Display to display animations. The MAX7219 LED display driver communicates with the Arduino through SPI (Serial Peripheral Interface). Therefore, the first thing to do is to connect the Display to the Arduino using the SPI interface.

Wiring

The following diagram shows you the wiring between the MAX7219 Display and an Arduino Uno. Make sure you are connecting to the input (DIN) and not the output side (DOUT) of the display.

Connecting MAX7219 Display to Arduino UNO
Connecting MAX7219 Display to Arduino UNO

Start by connecting the SPI interface pins: Pin 11 of the Arduino is connected to the DIN pin of the Display. Then connect Pin 3 to CS and Pin 13 to CLK. Finally, connect 5V to VCC and GND to GND. The following table shows the connections again

DisplayArduino
VCC5 V
GNDGND
DIN11 (MOSI)
CS3 (SS)
CLK13 (SCK)

The pins above are for hardware SPI, which is faster than software SPI but requires you to use specific pins that differ between boards. For more details see the MAX7219 LED dot matrix display Arduino tutorial.

Next let’s write and run some test code to ensure that the connections are correct.

Install MD_Parola and MD_MAX72XX libraries

First, you need to install the MD_MAX72XX and the MD_Parola library. Open the Arduino IDE and go to Tools > Manage Libraries which opens the Library Manager. Search for “MD_MAX72XX” and the relevant libraries will be listed. The screenshot below shows you the two libraries (marked yellow) after they have been installed.

Install MD_MAX72XX and MD_Parola library
Install MD_MAX72XX and MD_Parola library

The MD_MAX72XX library is essentially the driver software for the MAX7219 LED Display. The MD_Parola library uses it to create animations like scrolling and sprite text effects. If you want to learn more about the available animations have a look at the MAX7219 LED dot matrix display Arduino tutorial.

Test Code

The following code is a minimal example to test the wiring and function of the display. It simply scrolls the text “Test” on the display.

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

If you upload and run this code you should see the following output on your MAX7219 Display:

Scrolling Test Output on MAX7219 LED Display
Scrolling Test Output on MAX7219 LED Display

For more details on this test code have a look at the Control Parola Animations on MAX7219 LED Display tutorial, where we use the same test code.

Note, if you use a different type of MAX7219 LED Display or have a display with more or less modules you will need to adjust the following definitions:

#define HARDWARE_TYPE MD_MAX72XX::FC16_HW
#define MAX_DEVICES 4

Similarly, if you use software SPI instead of hardware SPI, you will need to change how the display object is created:

MD_Parola disp = MD_Parola(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);

More information on this can be found in the MAX7219 LED dot matrix display Arduino tutorial.

In the following sections, we have a closer look at zones, how to create and use them, and most importantly how to coordinate animations that run in different zones.

What are Parola Zones

The Parola library treats a MAX7219 LED Display as a sequence of 8×8 LED modules, numbered from 0 to n-1. A Zone is a section of consecutive modules. Each zone can be controlled independently from other zones. The example below shows a MAX7219 LED Display with four modules (0…3) and two zone definitions.

Parola Display with 4 Modules and 2 Zones
Parola Display with 4 Modules and 2 Zones

Zone 0 includes the modules 0 and 1, and Zone 1 includes the modules 2 and 3. Note that the numbering of modules start a 0 and from the input side (right side) of the display. Within each zone you can run different animation with different fonts, text effects, text strings, speeds and even brightness’s.

Zones should not overlap, can be of different length and the zone index does not have to be in the same sequential order as the modules. For instance, the following example shows another valid zone definition. Here we have three zones (0,1,2) but they are of different length and the zone indies are not in sequential order (0,2,1).

Parola Display with 4 Modules and 3 Zones

How to define Parola Zones

Let’s say we want to define the following display zones. Zone 0 runs from module 0 to module 1, and Zone 1 runs from module 2 to module 3:

Defining 2 Zones
Defining 2 Zones

In the setup function, first call begin() with the total number of Zones (here 2). Then you define the individual zones by calling setZone(z, start, end). The first argument z is the zone id (0 or 1, in our case), and then the index of the first and the last module of that zone.

#define MAX_ZONES 2

void setup() {
  disp.begin(MAX_ZONES);
  disp.setZone(0, 0, 1);
  disp.setZone(1, 2, 3);
}

As mentioned before, zones should not overlap but they can. However, if they overlap, you must ensure that overlapping zones do not run animations at the same time, otherwise you will get strange artefacts.

Once you have the zones defined, you can assign animations to those zones. For instance, the following line of code defines an animation for zone 0 that scrolls the text “Left” to the left. More about this in the next section.

  disp.displayZoneText(0, "Left", PA_CENTER, 100, 0, PA_SCROLL_RIGHT, PA_SCROLL_RIGHT);

How to animate Parola Zones

Most Parola animation related functions come in two versions. One version that works on the entire display, and another version that works within a specified zone. Have a look at the Parola Documentation for an overview.

For instance, you can set the brightness of the entire display or for a specific zone by calling setIntensity either without or with a zone index.

setIntensity(intensity);  // entire display
setIntensity(z, intensity);  // zone z

The same is the case for most other functions for setting fonts, text effects, text strings, speeds and so one. The following code excerpt shows how to display and animate different texts in two zones. In the first zone (0) we scroll the text “0” to the right, and in the second zone (1), we scroll the text “1” to the left.

#define SPEED 100

void setup() {
  disp.begin(MAX_ZONES);
  disp.setZone(0, 0, 1);
  disp.setZone(1, 2, 3);

  disp.displayZoneText(0, "0", PA_CENTER, SPEED, 0, PA_SCROLL_RIGHT, PA_SCROLL_RIGHT);
  disp.displayZoneText(1, "1", PA_CENTER, SPEED, 0, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
}

In the loop function, we then just call the usual displayAnimate() to find out if any of the two animations has completed and if so, we reset the display for all zones via displayReset().

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

Note that you could also reset zones individually by calling displayReset(z) with a zone identifier. We will use this later.

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

This works fine, especially since both animations here run at the same speed (100). But if the animations run at different speeds or have texts of different lengths, then one animation will complete before the other. How do we handle this case and specify what exactly should happen.

This coordination between animations across multiple zones is the topic of the next section.

How to coordinate Parola Zones

As mentioned above the default Parola loop calls displayAnimate(), which will return true if any animation in any zone is complete. You can find out which one is complete by calling getZoneStatus(z) with the zone identifier z.

For instance, if you have two zones, you could restart the animations in both zones when both animations are complete by writing the following code:

void loop() {
  if (disp.displayAnimate()) {
    if(disp.getZoneStatus(0) && disp.getZoneStatus(1)) {  
      disp.displayReset();
    }
  }
}

This works fine and it perfect for this simple case. But if you have slightly more complex scenarios or more zones, you easily end up with very confusing code. For instance, let’s assume we have two animations in two zones and one animation is faster than the other. This results in at least six different possible animation scenarios

  1. Let both animations run only once
  2. Let first animation run once, repeat the other
  3. Let second animation run once, repeat the other
  4. Repeat both animations when both animations are finished
  5. Repeat faster animation while slower animation is still running
  6. Restart slower animation when faster animation is complete

In the following sections we will implement these six animation scenarios. But to simplify things we will first define a helper function getStatus().

getStatus function

Instead of calling getZoneStatus(z) for each zone the following getStatus() function returns a bit vector that indicates the status of all zones at once.

byte getStatus() {
  static byte status = 0;
  disp.displayAnimate();
  for (int z = 0; z < MAX_ZONES; z++) {
    disp.getZoneStatus(z) ? bitSet(status, z) : bitClear(status, z);
  }
  return status;
}

Internally, the function iterates over all zones and sets the zone status in the status byte. This means the maximal number of zones is limited to 8, but that is usually more than sufficient.

The following picture shows an example of bit or status vector that getStatus() returns. In this example the animations in zone 1 and zone 3 are complete:

Status vector returned by getStatus
Status vector returned by getStatus

If you call getStatus() in the main loop you could easily print out the status vector.

void loop() {
  Serial.println(getStatus(), BIN);
}

Note that the getStatus() internally also calls displayAnimate(), so there is no need to call it in the loop function anymore.

We are going to use getStatus() in the following sections to implement the six different animation scenarios mentioned before.

Code example with two Zones

Let’s start with a code example that has everything except the implementation for the loop function.

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

#define HARDWARE_TYPE MD_MAX72XX::FC16_HW
#define MAX_DEVICES 4
#define MAX_ZONES 2
#define CS_PIN 3

MD_Parola disp = MD_Parola(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);

byte getStatus() {
  static byte status = 0;
  disp.displayAnimate();
  for (int z = 0; z < MAX_ZONES; z++) {
    disp.getZoneStatus(z) ? bitSet(status, z) : bitClear(status, z);
  }
  return status;
}

void setup() {
  disp.begin(MAX_ZONES);
  disp.setZone(0, 0, 1);
  disp.setZone(1, 2, 3);
  disp.setIntensity(0);
  disp.displayClear();
  disp.displayZoneText(0, "0", PA_CENTER, 100, 0, PA_SCROLL_RIGHT, PA_SCROLL_RIGHT);
  disp.displayZoneText(1, "1", PA_CENTER, 200, 0, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
}

void loop() {
  // TODO
}

In this code, firstly, we include the required libraries and the define the constants for the MAX7219 Display. If you have a different display type or a different number of modules, you need to change these constants.

Next we create the display object disp and implement the getStatus() function as shown above.

In the setup function we define two animations in two zones. The first animation in zone 0 scrolls the text “0” to the right. The second animation in zone 1 scrolls the text “1” to the left. The picture below depicts the two zones with their indices.

Defining 2 Zones
Defining 2 Zones

Note that the first animation in zone 0 runs faster (speed=100) than the second animation (speed=200) in zone 1. The short video below shows how this looks like.

Two animations in two zones with different speeds
Two animations in two zones with different speeds

We can keep the code above the same for all six animation scenarios and only have to change the implementation of the loop function.

Let both animations run only once

Let’s start with the simplest case. We want both animations to run only once until they are complete. In this case the loop function just has to call getStatus() and doesn’t need to do anything else.

void loop() {
  getStatus();
}

You probably don’t need this very often but if you implement and run this, it looks like this:

Both animations run only once
Both animations run only once

Note, however, since the video is looped it looks like as if the animation would restart. This is actually not happing and the animation runs only once and then the display stays empty forever. Useful maybe for a start-up message that appears only once.

Let first animation run once, repeat the other

In the next scenario we let the first animation in zone 0 run only once but repeat the second. This means whenever the animation in zone 1 completes, we have to reset the display for this zone. Below is the code for the loop function for this scenario.

void loop() {
  if(getStatus() & 0b00000010) {
    disp.displayReset(1);
  }
}

We get the status and perform a bitwise AND (&) operation to check if the animation in zone 1 has finished. If that is the case we call displayReset(1) for this zone to repeat the animation. The following clip shows the resulting animation:

First animation runs once the other repeats
First animation runs once the other repeats

Again, since the video clip is looped it looks like if the first animation repeats itself, but this is actually not the case.

Let second animation run once, repeat the other

In the next scenario we swap the roles of the first and the second animation. We let the second animation run once and repeat the first one. The code of the loop function is essentially the same as before, we just change the zone we are looking at.

void loop() {
  if(getStatus() & 0b00000001) {
    disp.displayReset(0);
  }
}

The video clip below shows the corresponding animation.

Second animation runs once the other repeats
Second animation runs once the other repeats

Repeat both animations when both animations are finished

Now, let us wait until both animations have completed and then reset both of them. This is easily one. We just have to check whether both status bits are set. Note that in this case we do not perform a bitwise AND (&) but check for equality (=)!

void loop() {
  if(getStatus() == 0b00000011) {
    disp.displayClear();
    disp.displayReset();
  }
}

In the following clip you can see how the animation looks like in this case.

Repeat both animations when both have finished
Repeat both animations when both have finished

Repeat faster animation while slower animation is still running

Since the two animations are running at different speeds we need to decide what we want to do, when the faster animations finishes. Do we want to restart the faster animation immediately or do we want to wait until the slower on has finished. We have covered the latter case in the section above.

Below is the code that covers the first case. We simply check for each of the zones if it has completed and if so, we restart the corresponding animation.

void loop() {
  if(getStatus() & 0b00000001) {
    disp.displayReset(0);
  }
  if(getStatus() & 0b00000010) {
    disp.displayReset(1);
  }  
}

Here is the video clip that shows this scenario in action.

Restart faster animation while slower is running

Restart slower animation when faster animation is complete

The last animation scenario is the reversed case of the previous one. In this scenario we want to restart the slower animation as soon as the faster animation is complete. Note that the code is quite different from the previous one.

void loop() {
  if(getStatus() & 0b00000001) {
      disp.displayReset(1);
      disp.displayClear(1);
      disp.displayReset(0);
  } 
}

Specifically, we need to clear zone 1, since we restart the animation in zone 1 before it is finished. If you don’t clear the display you will be left with artefacts.

The video clip below shows this animation. Note that the left scrolling of the “1” is interrupted and reset the moment the right scrolling of the “0” is complete.

Restart slower animation while faster is running

Conclusions

In this tutorial you learned how to coordinate Parola animations that run in multiple zones. The examples above used only two zones and are comparatively simple. However, the moment you have more than two zones the getStatus() function will simplify your code a lot.

For instance, you can list all possible zone statuses in a switch statement and handle them accordingly. Here is an example for two zones that you can easily extend to more zones.

void loop() {
  switch (getStatus()) {
    case 0b00000001:
      ...
      break;
    case 0b00000010:
      ...
      break;
    case 0b00000011:
      ...
      break;
  }
}

We haven’t used this construct in the code examples above, since with two zones it doesn’t really simplify the code, but with more zones it will.

Also if you want to chain multiple animations or control them through external inputs have a look at the Control Parola Animations on MAX7219 LED Display tutorial.

If you want to manipulate individual pixels of the LED Matrix, the Parola library is not the right choice and you better use FastLED or something similar. For an example, see the post Game of Life on a Dot Matrix Display with MAX7219.

I hope you find this tutorial useful. Feel free to leave a comment if you have any questions.