← All projects
Displays Beginner

MAX7219 LED Matrix Ticker with Text from Your Browser

Four 8×8 matrices make a bright scrolling ticker. Change the text from your phone via a simple web form — for a workshop, café or kids' room.

Difficulty
Beginner
Time
1 h
Category
Displays

What you get

The FC-16 module is four 8×8 LED matrices driven by MAX7219 chips, chained together (32×8 dots in total). The MD_Parola library handles fonts, scrolling and dozens of animations, and the ESP32 adds a web form: open the page on your phone, type some text — and it scrolls across the display right away.

Parts

  • ESP32 DevKit
  • MAX7219 4-in-1 (FC-16) module, 32×8
  • 5 female-female jumper wires

Wiring

MAX7219 ESP32
VCC 5V (VIN)
GND GND
DIN GPIO 23 (MOSI)
CS GPIO 5
CLK GPIO 18 (SCK)

At full brightness the matrices can draw up to 1 A — for long chains power them from a separate 5 V supply. The MAX7219 usually accepts the ESP32's 3.3 V logic just fine.

Libraries

  • MD_Parola
  • MD_MAX72XX (dependency)

Code

#include <WiFi.h>
#include <WebServer.h>
#include <SPI.h>
#include <MD_Parola.h>
#include <MD_MAX72xx.h>

#define HARDWARE_TYPE MD_MAX72XX::FC16_HW
#define MAX_DEVICES   4
#define CS_PIN        5

const char* WIFI_SSID = "YOUR_SSID";
const char* WIFI_PASS = "YOUR_PASSWORD";

MD_Parola display(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);
WebServer server(80);

char message[128] = "Hello from ESP32!";

const char PAGE[] = R"rawliteral(
<!doctype html><html><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>LED Ticker</title>
<style>body{font-family:system-ui;max-width:420px;margin:40px auto;padding:0 16px}
input,button{font-size:1.1rem;padding:10px;width:100%;box-sizing:border-box;margin-top:8px}</style>
</head><body>
<h2>LED Ticker</h2>
<form action="/set">
  <input name="msg" maxlength="120" placeholder="Text (Latin letters)" autofocus>
  <input name="speed" type="range" min="20" max="150" value="60">
  <button>Show</button>
</form></body></html>
)rawliteral";

void handleSet() {
  String text = server.arg("msg");
  // The default font is ASCII-only — drop everything else
  String clean;
  for (unsigned int i = 0; i < text.length(); i++) {
    char c = text[i];
    if (c >= 32 && c < 127) clean += c;
  }
  clean.trim();
  if (clean.length()) {
    clean.toCharArray(message, sizeof(message));
  }
  int speed = server.arg("speed").toInt();
  if (speed >= 20 && speed <= 150) display.setSpeed(speed);  // ms per step: lower is faster
  display.displayReset();

  server.sendHeader("Location", "/");
  server.send(303);
}

void setup() {
  Serial.begin(115200);
  display.begin();
  display.setIntensity(3);   // brightness 0..15
  display.displayText(message, PA_LEFT, 60, 0, PA_SCROLL_LEFT, PA_SCROLL_LEFT);

  WiFi.begin(WIFI_SSID, WIFI_PASS);
  while (WiFi.status() != WL_CONNECTED) {
    display.displayAnimate();
    delay(10);
  }
  // Show the IP so you know where to go
  snprintf(message, sizeof(message), "IP %s", WiFi.localIP().toString().c_str());
  display.displayReset();

  server.on("/", [] { server.send(200, "text/html", PAGE); });
  server.on("/set", handleSet);
  server.begin();
}

void loop() {
  server.handleClient();
  if (display.displayAnimate()) {
    display.displayReset();   // scroll finished — start over
  }
}

Testing

  1. On power-up the display scrolls Hello from ESP32!, then IP 192.168.x.x once Wi-Fi connects.
  2. Open that address on your phone, type some text, move the speed slider and tap Show.

If something's off

  • Text is mirrored or scrambled across matrices — try another hardware type: PAROLA_HW, GENERIC_HW or ICSTATION_HW.
  • All dots lit or nothing at all — check DIN/CLK/CS and the shared ground.
  • Non-Latin alphabets: the default font doesn't include them. MD_MAX72XX supports custom fonts — the library repo has instructions and a font builder tool.

Ideas

  • A clock: show NTP time (see the "Accurate OLED Clock" project) with PA_CENTER and no scrolling.
  • A YouTube subscriber counter or exchange rates via an HTTP API.
  • Chain 8–12 modules for a big board — just change MAX_DEVICES.