A chain of MAX7219 8×8 matrices makes a bright scrolling sign — a favourite for Wi-Fi notice boards and stock or cricket-score tickers on a NodeMCU.
The matrix is a 5 V part, so the data lines go through a level shifter. The ESP8266’s hardware SPI handles DIN (D7) and CLK (D5); chip select is on D1, because the shifter’s pull-ups would stop the board booting if CS were on the usual D8.
Wiring
| NodeMCU ESP8266 | MAX7219 8×8 LED matrix (4-in-1 FC-16 module) | Wire | Notes |
|---|---|---|---|
| VIN | VCC | red | 5 V from USB. At intensity 2 a 4-in-1 module draws roughly 100–200 mA; full brightness needs its own 5 V supply. |
| GND | GND | black | |
| D7 | DIN | green | Via level shifter — GPIO13, HSPI MOSI. |
| D5 | CLK | yellow | Via level shifter — GPIO14, HSPI SCK. |
| D1 | CS | purple | Via level shifter — GPIO5. Shifter LV = 3V3, HV = VIN (5 V), GND shared. |
5 V part, 3.3 V board. The MAX7219 runs on 5 V and expects a HIGH of at least 3.5 V. A 3.3 V board (ESP32, Pico) often works, but not reliably — for a dependable build, buffer DIN, CS and CLK with a 74HCT125 or 74AHCT125 powered from 5 V.
Board quirks
- The MAX7219 runs at 5 V and wants about 3.5 V for a HIGH, so the ESP8266’s 3.3 V signals go through a level shifter. Without it the matrix often works on the bench and then shows garbage on a warm day.
- CS is on D1, not D8. D8 (GPIO15) is the usual HSPI chip select, but a BSS138 shifter has a 10 kΩ pull-up on each channel — on D8 that holds GPIO15 high at reset and the NodeMCU won’t boot.
- Hardware SPI uses D5 (SCK) and D7 (MOSI). D6 (MISO) is claimed by
SPI.begin()even though the MAX7219 never talks back, so don’t use D6 for anything else in this sketch. - Most 4-in-1 red modules are
FC16_HW. If letters appear mirrored or rotated, tryGENERIC_HW(single DIP-chip boards) orICSTATION_HW. - For smooth scrolling with effects, the MD_Parola library builds on MD_MAX72XX — this sketch keeps to the base library to show how the columns move.
Code
// MAX7219 4-in-1 LED matrix (FC-16 style) on a NodeMCU (ESP8266), hardware SPI through a level shifter.
// VCC → VIN (5 V), GND → GND, DIN → D7 (GPIO13, MOSI), CLK → D5 (GPIO14, SCK), CS → D1 (GPIO5).
// Shifter: LV = 3V3, HV = VIN, GND shared. CS is NOT on D8: the shifter's pull-up would hold
// GPIO15 high at reset and the board would not boot.
#include <SPI.h>
#include <MD_MAX72xx.h>
const uint8_t CS_PIN = D1;
const uint8_t MODULES = 4; // 8x8 blocks in the chain
MD_MAX72XX mx(MD_MAX72XX::FC16_HW, CS_PIN, MODULES); // single DIP-chip boards: GENERIC_HW
const char MSG[] = " NodeMCU + MAX7219 ";
uint16_t msgPos = 0;
uint8_t glyph[8];
uint8_t glyphLen = 0, glyphCol = 0;
void setup() {
Serial.begin(115200);
Serial.println();
if (!mx.begin()) {
Serial.println("MAX7219 init failed");
while (true) delay(100);
}
mx.control(MD_MAX72XX::INTENSITY, 2); // 0-15; low brightness keeps the current low
mx.clear();
Serial.println("MAX7219 ready - scrolling text");
}
void loop() {
if (glyphCol >= glyphLen) { // next character
char c = MSG[msgPos++];
if (MSG[msgPos] == '\0') msgPos = 0;
glyphLen = mx.getChar(c, sizeof(glyph) - 1, glyph);
glyph[glyphLen++] = 0; // one blank column between letters
glyphCol = 0;
}
mx.transform(MD_MAX72XX::TSL); // shift everything one column left
mx.setColumn(0, glyph[glyphCol++]); // feed the new column in on the right
delay(40); // delay() also gives Wi-Fi and background tasks time
}// MAX7219 4-in-1 LED matrix (FC-16 style) on a NodeMCU (ESP8266), hardware SPI through a level shifter.
// VCC → VIN (5 V), GND → GND, DIN → D7 (GPIO13, MOSI), CLK → D5 (GPIO14, SCK), CS → D1 (GPIO5).
// Shifter: LV = 3V3, HV = VIN, GND shared. CS is NOT on D8: the shifter's pull-up would hold
// GPIO15 high at reset and the board would not boot.
#include <SPI.h>
#include <MD_MAX72xx.h>
const uint8_t CS_PIN = D1;
const uint8_t MODULES = 4; // 8x8 blocks in the chain
MD_MAX72XX mx(MD_MAX72XX::FC16_HW, CS_PIN, MODULES); // single DIP-chip boards: GENERIC_HW
const char MSG[] = " NodeMCU + MAX7219 ";
uint16_t msgPos = 0;
uint8_t glyph[8];
uint8_t glyphLen = 0, glyphCol = 0;
void setup() {
Serial.begin(115200);
Serial.println();
if (!mx.begin()) {
Serial.println("MAX7219 init failed");
while (true) delay(100);
}
mx.control(MD_MAX72XX::INTENSITY, 2); // 0-15; low brightness keeps the current low
mx.clear();
Serial.println("MAX7219 ready - scrolling text");
}
void loop() {
if (glyphCol >= glyphLen) { // next character
char c = MSG[msgPos++];
if (MSG[msgPos] == '\0') msgPos = 0;
glyphLen = mx.getChar(c, sizeof(glyph) - 1, glyph);
glyph[glyphLen++] = 0; // one blank column between letters
glyphCol = 0;
}
mx.transform(MD_MAX72XX::TSL); // shift everything one column left
mx.setColumn(0, glyph[glyphCol++]); // feed the new column in on the right
delay(40); // delay() also gives Wi-Fi and background tasks time
}Library: MD_MAX72XX (majicDesigns) — compiled with MD_MAX72XX 3.5.1.
Expected serial output
MAX7219 ready - scrolling textMAX7219 ready - scrolling text
Common errors
Board won’t boot with the matrix connected
A shifter channel is on D8 or the matrix is pulling D3/D4. Keep CS on D1 as shown. See boot mode (1,6).
Text is mirrored, upside down or jumbled per block
Wrong hardware type. Change FC16_HW to GENERIC_HW or ICSTATION_HW.
All LEDs on at full brightness
The chip never got the init sequence — DIN or CLK is not reaching it. Check the shifter has both LV (3V3) and HV (VIN) connected.