Scrolling a message
Thirty-two columns holds about four characters. Anything longer has to move, and the number in the middle of displayText is not a speed — it is the delay between frames, one pixel each.
Why it has to move
Thirty-two columns is four characters in a fixed-width font and five or six in a proportional one. A sentence does not fit, and there is no smaller font worth having — eight pixels tall is already the whole display.
So the message moves through the window, one pixel at a time.
displayText(message, PA_LEFT, 80, 0, PA_SCROLL_LEFT, PA_SCROLL_LEFT) takes
six arguments and only two of them usually get changed:
| Argument | Here | What it is |
|---|---|---|
message | the string | Kept by pointer, not copied |
PA_LEFT | alignment | Where it rests when it is not moving |
80 | frame delay | Milliseconds between frames. One pixel per frame |
0 | pause | How long it waits at the end before leaving |
PA_SCROLL_LEFT | entry | How it arrives |
PA_SCROLL_LEFT | exit | How it leaves |
The third one is the one people misread. It is not a speed in any unit of distance; it is a delay, and smaller means faster. Below about 40 ms the letters blur; above about 100 ms it is slow enough to be irritating on a desk. The repository's 80 ms is a good default.
What makes a message long
A message takes its own width plus the 32 columns of the screen to get all the
way through. "Lonely Binary - 32x8 MAX7219" is 28 characters — at roughly six
columns each in the proportional font, that is around 170 columns of travel
plus 32 more, so about 200 frames, which at 80 ms is a little over fifteen
seconds before the beginning comes round again.
That is the number to think about, not the frame delay. If the wait feels too long, shorten the message. Speeding up the frames only makes it harder to read.
loop() has to stay free
displayAnimate() is what advances the scroll, and it advances it by one frame
per call. It does its own timing internally — it returns immediately if the
frame delay has not elapsed — so the correct way to use it is to call it as
often as possible and never block.
That means no delay() in loop(). If the sketch also has to read a sensor or
check the network, do it with a millis() comparison the way
a clock that fits does, so the loop
keeps turning.

The code
A message that scrolls right to left forever. displayAnimate() advances it one pixel each time it is called and returns true when the message has left the screen, which is where displayReset() starts it again.
// Wiring for this sketch. Use the header marked IN, on the back of the board.
//
// ESP32-S3 5V -> VCC Uno 5V -> VCC
// ESP32-S3 GND -> GND Uno GND -> GND
// ESP32-S3 GPIO 10 -> CS Uno D10 -> CS
// ESP32-S3 GPIO 11 -> DIN Uno D11 -> DIN
// ESP32-S3 GPIO 12 -> CLK Uno D12 -> CLK
//
// Arduino IDE: Tools > Manage Libraries, install "MD_Parola" by majicDesigns
// and let it install MD_MAX72XX with it. Then:
// Tools > Board: "Arduino Uno", or an ESP32-S3 board from the esp32 core
// Tools > Port: whichever port appears when you plug the board in
#include <MD_Parola.h>
#include <MD_MAX72XX.h>
#include <SPI.h>
#define HARDWARE_TYPE MD_MAX72XX::FC16_HW
#define MAX_DEVICES 4
#define DATA_PIN 11
#define CLK_PIN 12
#define CS_PIN 10
MD_Parola display = MD_Parola(HARDWARE_TYPE, DATA_PIN, CLK_PIN, CS_PIN, MAX_DEVICES);
const char *message = "Lonely Binary - 32x8 MAX7219";
void setup() {
display.begin();
display.setIntensity(5);
display.displayClear();
// text, alignment, frame delay in ms, pause at the end, entry, exit
display.displayText(message, PA_LEFT, 80, 0, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
}
void loop() {
if (display.displayAnimate()) {
display.displayReset();
}
}Keep loop() free of delay(). The animation is advanced by displayAnimate(), so anything that blocks the loop stops the scroll dead — a delay(1000) in here makes the message move one pixel per second.
When it does not work
Something is blocking loop(). displayAnimate() has to be called over and over — it advances the animation by one frame per call and does its own timing. A delay() anywhere in loop() throttles the scroll to that rate.
displayReset() is missing, or the return value of displayAnimate() is not being checked. Without it the animation finishes and nothing restarts it.
Swap PA_SCROLL_LEFT for PA_SCROLL_RIGHT in both the entry and exit positions. They are separate arguments on purpose — a message can arrive one way and leave another — which means a half-changed call gives you something that scrolls in and then out backwards.
displayText keeps a pointer to your string rather than a copy, so writing new characters into the same buffer works and passing a new string means calling displayText again. The safest pattern is one char buffer, written with sprintf, and displayReset() after each change.
Five characters on a thirty-two column screen, which works in one library and not in the other, for a reason that has nothing to do with the hardware.
A clock that fits →Edit this page — content/books/matrix-32x8/scrolling-a-message.mdx
Questions about this product
See what other owners have asked, and read their solutions.
32x8 LED Matrix MAX7219, 3-Pack
Loading discussions…
Discuss this article
Ask about this page. The answer stays here, on the page it belongs to, for whoever hits the same wall next.